Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-or-later
3 : *
4 : * This file is part of colopresso
5 : *
6 : * Copyright (C) 2025-2026 COLOPL, Inc.
7 : *
8 : * Author: Go Kudo <g-kudo@colopl.co.jp>
9 : * Developed with AI (LLM) code assistance. See `NOTICE` for details.
10 : */
11 :
12 : #include <errno.h>
13 : #include <math.h>
14 : #include <stdbool.h>
15 : #include <stdint.h>
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 : #include <string.h>
19 :
20 : #include <webp/encode.h>
21 :
22 : #include <colopresso.h>
23 :
24 : #include "internal/log.h"
25 : #include "internal/webp.h"
26 :
27 : static int g_last_webp_error = 0;
28 :
29 27 : static void apply_webp_config(WebPConfig *webp_config, const cpres_config_t *config)
30 : {
31 27 : webp_config->quality = config->webp_quality;
32 27 : webp_config->target_size = config->webp_target_size;
33 27 : webp_config->target_PSNR = config->webp_target_psnr;
34 27 : webp_config->method = config->webp_method;
35 27 : webp_config->segments = config->webp_segments;
36 27 : webp_config->sns_strength = config->webp_sns_strength;
37 27 : webp_config->filter_strength = config->webp_filter_strength;
38 27 : webp_config->filter_sharpness = config->webp_filter_sharpness;
39 27 : webp_config->filter_type = config->webp_filter_type;
40 27 : webp_config->autofilter = config->webp_autofilter;
41 27 : webp_config->alpha_compression = config->webp_alpha_compression;
42 27 : webp_config->alpha_filtering = config->webp_alpha_filtering;
43 27 : webp_config->alpha_quality = config->webp_alpha_quality;
44 27 : webp_config->pass = config->webp_pass;
45 27 : webp_config->preprocessing = config->webp_preprocessing;
46 27 : webp_config->partitions = config->webp_partitions;
47 27 : webp_config->partition_limit = config->webp_partition_limit;
48 27 : webp_config->emulate_jpeg_size = config->webp_emulate_jpeg_size;
49 27 : webp_config->thread_level = (config->webp_thread_level > 0) ? 1 : 0;
50 27 : webp_config->low_memory = config->webp_low_memory;
51 27 : webp_config->near_lossless = config->webp_near_lossless;
52 27 : webp_config->exact = config->webp_exact;
53 27 : webp_config->use_delta_palette = config->webp_use_delta_palette;
54 27 : webp_config->use_sharp_yuv = config->webp_use_sharp_yuv;
55 27 : webp_config->lossless = config->webp_lossless;
56 27 : }
57 :
58 1 : extern int webp_get_last_error(void)
59 : {
60 1 : return g_last_webp_error;
61 : }
62 :
63 28 : extern void webp_set_last_error(int error_code)
64 : {
65 28 : g_last_webp_error = error_code;
66 28 : }
67 :
68 30 : extern cpres_error_t webp_encode_rgba_to_memory(uint8_t *rgba_data, uint32_t width, uint32_t height,
69 : uint8_t **webp_data, size_t *webp_size,
70 : const cpres_config_t *config)
71 : {
72 : WebPConfig webp_config;
73 : WebPPicture picture;
74 : WebPMemoryWriter writer;
75 :
76 30 : if (!rgba_data || !webp_data || !webp_size || !config) {
77 0 : return CPRES_ERROR_INVALID_PARAMETER;
78 : }
79 :
80 30 : if (!isfinite(config->webp_quality) || !isfinite(config->webp_target_psnr)) {
81 2 : return CPRES_ERROR_INVALID_PARAMETER;
82 : }
83 :
84 28 : memset(&webp_config, 0, sizeof(webp_config));
85 28 : memset(&picture, 0, sizeof(picture));
86 28 : memset(&writer, 0, sizeof(writer));
87 :
88 28 : colopresso_log(CPRES_LOG_LEVEL_DEBUG, "Starting WebP encoding to memory - %dx%d pixels", width,
89 : height);
90 :
91 28 : if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, config->webp_quality)) {
92 1 : return CPRES_ERROR_INVALID_PARAMETER;
93 : }
94 :
95 27 : apply_webp_config(&webp_config, config);
96 :
97 27 : if (!WebPValidateConfig(&webp_config)) {
98 0 : return CPRES_ERROR_INVALID_PARAMETER;
99 : }
100 :
101 27 : if (!WebPPictureInit(&picture)) {
102 0 : return CPRES_ERROR_OUT_OF_MEMORY;
103 : }
104 :
105 27 : picture.width = (int)width;
106 27 : picture.height = (int)height;
107 27 : picture.use_argb = 1;
108 :
109 27 : if (!WebPPictureImportRGBA(&picture, rgba_data, width * 4)) {
110 0 : WebPPictureFree(&picture);
111 :
112 0 : return CPRES_ERROR_ENCODE_FAILED;
113 : }
114 :
115 27 : WebPMemoryWriterInit(&writer);
116 27 : picture.writer = WebPMemoryWrite;
117 27 : picture.custom_ptr = &writer;
118 :
119 27 : colopresso_log(CPRES_LOG_LEVEL_DEBUG, "Starting WebP encoding (memory)...");
120 :
121 27 : if (!WebPEncode(&webp_config, &picture)) {
122 0 : webp_set_last_error(picture.error_code);
123 0 : colopresso_log(CPRES_LOG_LEVEL_ERROR, "WebP encoding failed - error code: %d",
124 0 : picture.error_code);
125 0 : WebPMemoryWriterClear(&writer);
126 0 : WebPPictureFree(&picture);
127 :
128 0 : return CPRES_ERROR_ENCODE_FAILED;
129 : }
130 :
131 27 : webp_set_last_error(0);
132 :
133 27 : colopresso_log(CPRES_LOG_LEVEL_DEBUG, "WebP encoding successful - size: %zu bytes",
134 : writer.size);
135 :
136 27 : *webp_data = (uint8_t *)malloc(writer.size);
137 27 : if (!*webp_data) {
138 0 : WebPMemoryWriterClear(&writer);
139 0 : WebPPictureFree(&picture);
140 :
141 0 : return CPRES_ERROR_OUT_OF_MEMORY;
142 : }
143 :
144 27 : memcpy(*webp_data, writer.mem, writer.size);
145 27 : *webp_size = writer.size;
146 :
147 27 : WebPMemoryWriterClear(&writer);
148 27 : WebPPictureFree(&picture);
149 :
150 27 : return CPRES_OK;
151 : }
|