webp-ffi 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,57 @@
1
- #include <assert.h>
2
1
  #include <stdio.h>
3
2
  #include <stdlib.h>
4
3
  #include <string.h>
5
4
 
6
5
  // utils
6
+ #include "./metadata.h"
7
7
  #include "./util.h"
8
8
  #include "./webp_ffi.h"
9
9
 
10
+ // libwebp 1.0.0 is the oldest supported version (encoder ABI 0x020e)
11
+ #if WEBP_ENCODER_ABI_VERSION < 0x020e || WEBP_DECODER_ABI_VERSION < 0x0208
12
+ #error "webp-ffi needs libwebp 1.0.0 or newer"
13
+ #endif
14
+
10
15
  #if defined(__cplusplus) || defined(c_plusplus)
11
16
  extern "C" {
12
17
  #endif
13
18
 
19
+ // Error codes returned to Ruby. lib/webp/error.rb has the messages.
20
+ enum {
21
+ ENCODE_VERSION_MISMATCH = 1,
22
+ ENCODE_INVALID_CONFIG = 2,
23
+ ENCODE_CANNOT_READ_INPUT = 3,
24
+ ENCODE_CANNOT_OPEN_OUTPUT = 4,
25
+ ENCODE_CANNOT_CROP = 5,
26
+ ENCODE_CANNOT_RESIZE = 6,
27
+ ENCODE_FAILED = 7,
28
+ ENCODE_OUT_OF_MEMORY = 8
29
+ };
30
+
31
+ enum {
32
+ DECODE_VERSION_MISMATCH = 1,
33
+ DECODE_INVALID_WEBP = 2,
34
+ DECODE_INVALID_FORMAT = 3,
35
+ DECODE_FAILED = 4,
36
+ DECODE_CANNOT_READ_INPUT = 5,
37
+ DECODE_CANNOT_WRITE_OUTPUT = 6,
38
+ DECODE_OUT_OF_MEMORY = 7
39
+ };
40
+
14
41
  static int EncodeWriter(const uint8_t* data, size_t data_size,
15
42
  const WebPPicture* const pic) {
16
43
  FILE* const out = (FILE*)pic->custom_ptr;
17
44
  return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
18
45
  }
19
46
 
20
- static void AllocExtraInfo(WebPPicture* const pic) {
21
- const int mb_w = (pic->width + 15) / 16;
22
- const int mb_h = (pic->height + 15) / 16;
23
- pic->extra_info = (uint8_t*)malloc(mb_w * mb_h * sizeof(*pic->extra_info));
24
- }
25
-
26
47
  // main functions
27
48
 
28
- void decoder_version(char *version) {
29
- int v = WebPGetDecoderVersion();
30
- sprintf(version, "%d.%d.%d",
31
- (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
49
+ int decoder_version(void) {
50
+ return WebPGetDecoderVersion();
32
51
  }
33
52
 
34
- void encoder_version(char *version) {
35
- int v = WebPGetEncoderVersion();
36
- sprintf(version, "%d.%d.%d",
37
- (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
53
+ int encoder_version(void) {
54
+ return WebPGetEncoderVersion();
38
55
  }
39
56
 
40
57
  int webp_get_info(const uint8_t* data, size_t data_size, int* width, int* height) {
@@ -45,254 +62,556 @@ int webp_get_info(const uint8_t* data, size_t data_size, int* width, int* height
45
62
  }
46
63
  }
47
64
 
65
+ int webp_get_features(const uint8_t* data, size_t data_size,
66
+ FfiWebpFeatures* features) {
67
+ WebPBitstreamFeatures bitstream;
68
+ const VP8StatusCode status = WebPGetFeatures(data, data_size, &bitstream);
69
+ if (status != VP8_STATUS_OK) return (int)status;
70
+ features->width = bitstream.width;
71
+ features->height = bitstream.height;
72
+ features->has_alpha = bitstream.has_alpha;
73
+ features->has_animation = bitstream.has_animation;
74
+ features->format = bitstream.format;
75
+ return 0;
76
+ }
48
77
 
78
+ // Frees the output of the in-memory functions.
79
+ void webp_free_data(uint8_t* data) {
80
+ free(data);
81
+ }
49
82
 
50
- int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeConfig *encode_config) {
51
- int return_value = -1;
52
- FILE *out = NULL;
53
- int keep_alpha = 1;
54
- WebPPicture picture;
55
- WebPConfig config;
83
+ // Initializes 'config' and 'picture' and applies the options from Ruby.
84
+ static int SetupEncoder(const FfiWebpEncodeConfig* const encode_config,
85
+ WebPConfig* const config, WebPPicture* const picture) {
86
+ if (!WebPPictureInit(picture) || !WebPConfigInit(config)) {
87
+ return ENCODE_VERSION_MISMATCH;
88
+ }
56
89
 
57
- if (!WebPPictureInit(&picture) ||
58
- !WebPConfigInit(&config)) {
59
- //fprintf(stderr, "Error! Version mismatch!\n");
60
- return 1;
90
+ // presets first, so the options below can still change their settings
91
+ if (encode_config->preset >= WEBP_PRESET_DEFAULT && encode_config->preset <= WEBP_PRESET_TEXT) {
92
+ const float quality = (encode_config->quality >= 0 && encode_config->quality <= 100)
93
+ ? encode_config->quality : 75.f;
94
+ if (!WebPConfigPreset(config, (WebPPreset)encode_config->preset, quality)) {
95
+ return ENCODE_VERSION_MISMATCH;
96
+ }
97
+ }
98
+ if (encode_config->lossless_level >= 0 && encode_config->lossless_level <= 9) {
99
+ if (!WebPConfigLosslessPreset(config, encode_config->lossless_level)) {
100
+ return ENCODE_INVALID_CONFIG;
101
+ }
61
102
  }
62
103
 
63
- // OPTIONS BEGIN
64
104
  if (encode_config->lossless == 0 || encode_config->lossless == 1){
65
- config.lossless = encode_config->lossless;
105
+ config->lossless = encode_config->lossless;
66
106
  }
67
-
68
- #if (ENC_MAJ_VERSION == 0 && ENC_MIN_VERSION > 4) || ENC_MAJ_VERSION > 0
69
107
  if (encode_config->near_lossless >= 0 && encode_config->near_lossless <= 100){
70
- config.near_lossless = encode_config->near_lossless;
71
- config.lossless = 1; // use near-lossless only with lossless
108
+ config->near_lossless = encode_config->near_lossless;
109
+ config->lossless = 1; // use near-lossless only with lossless
72
110
  }
73
- #endif
74
-
75
111
  if (encode_config->quality >= 0 && encode_config->quality <= 100){
76
- config.quality = encode_config->quality;
112
+ config->quality = encode_config->quality;
77
113
  }
78
114
  if (encode_config->method >= 0 && encode_config->method <= 6){
79
- config.method = encode_config->method;
115
+ config->method = encode_config->method;
80
116
  }
81
117
  if (encode_config->target_size > 0){
82
- config.target_size = encode_config->target_size;
118
+ config->target_size = encode_config->target_size;
83
119
  }
84
120
  if (encode_config->target_PSNR > 0){
85
- config.target_PSNR = encode_config->target_PSNR;
121
+ config->target_PSNR = encode_config->target_PSNR;
86
122
  }
87
123
  if (encode_config->segments >= 0 && encode_config->segments <= 4){
88
- config.segments = encode_config->segments;
124
+ config->segments = encode_config->segments;
89
125
  }
90
126
  if (encode_config->sns_strength >= 0 && encode_config->sns_strength <= 100){
91
- config.sns_strength = encode_config->sns_strength;
127
+ config->sns_strength = encode_config->sns_strength;
92
128
  }
93
129
  if (encode_config->filter_strength >= 0 && encode_config->filter_strength <= 100){
94
- config.filter_strength = encode_config->filter_strength;
130
+ config->filter_strength = encode_config->filter_strength;
95
131
  }
96
132
  if (encode_config->filter_sharpness >= 0 && encode_config->filter_sharpness <= 7){
97
- config.filter_sharpness = encode_config->filter_sharpness;
133
+ config->filter_sharpness = encode_config->filter_sharpness;
98
134
  }
99
135
  if (encode_config->filter_type == 0 || encode_config->filter_type == 1){
100
- config.filter_type = encode_config->filter_type;
136
+ config->filter_type = encode_config->filter_type;
101
137
  }
102
138
  if (encode_config->autofilter == 0 || encode_config->autofilter == 1){
103
- config.autofilter = encode_config->autofilter;
139
+ config->autofilter = encode_config->autofilter;
104
140
  }
105
141
  if (encode_config->alpha_compression == 0 || encode_config->alpha_compression == 1){
106
- config.alpha_compression = encode_config->alpha_compression;
142
+ config->alpha_compression = encode_config->alpha_compression;
107
143
  }
108
144
  if (encode_config->alpha_filtering >= 0 && encode_config->alpha_filtering <= 2){
109
- config.alpha_filtering = encode_config->alpha_filtering;
145
+ config->alpha_filtering = encode_config->alpha_filtering;
110
146
  }
111
147
  if (encode_config->alpha_quality >= 0 && encode_config->alpha_quality <= 100){
112
- config.alpha_quality = encode_config->alpha_quality;
148
+ config->alpha_quality = encode_config->alpha_quality;
113
149
  }
114
150
  if (encode_config->pass >= 0 && encode_config->pass <= 10){
115
- config.pass = encode_config->pass;
151
+ config->pass = encode_config->pass;
116
152
  }
117
153
  if (encode_config->show_compressed >= 0){
118
- config.show_compressed = encode_config->show_compressed;
154
+ config->show_compressed = encode_config->show_compressed;
119
155
  }
120
156
  if (encode_config->preprocessing == 0 || encode_config->preprocessing == 1){
121
- config.preprocessing = encode_config->preprocessing;
157
+ config->preprocessing = encode_config->preprocessing;
122
158
  }
123
159
  if (encode_config->partitions >= 0 && encode_config->partitions <= 3){
124
- config.partitions = encode_config->partitions;
160
+ config->partitions = encode_config->partitions;
125
161
  }
126
162
  if (encode_config->partition_limit >= 0 && encode_config->partition_limit <= 100){
127
- config.partition_limit = encode_config->partition_limit;
163
+ config->partition_limit = encode_config->partition_limit;
164
+ }
165
+ if (encode_config->image_hint >= WEBP_HINT_DEFAULT && encode_config->image_hint < WEBP_HINT_LAST){
166
+ config->image_hint = (WebPImageHint)encode_config->image_hint;
167
+ }
168
+ if (encode_config->exact == 0 || encode_config->exact == 1){
169
+ config->exact = encode_config->exact;
170
+ }
171
+ if (encode_config->use_sharp_yuv == 0 || encode_config->use_sharp_yuv == 1){
172
+ config->use_sharp_yuv = encode_config->use_sharp_yuv;
173
+ }
174
+ if (encode_config->thread_level == 0 || encode_config->thread_level == 1){
175
+ config->thread_level = encode_config->thread_level;
128
176
  }
177
+ if (encode_config->low_memory == 0 || encode_config->low_memory == 1){
178
+ config->low_memory = encode_config->low_memory;
179
+ }
180
+ if (encode_config->emulate_jpeg_size == 0 || encode_config->emulate_jpeg_size == 1){
181
+ config->emulate_jpeg_size = encode_config->emulate_jpeg_size;
182
+ }
183
+ // a size means the input is raw YUV
129
184
  if ((encode_config->width | encode_config->height) > 0){
130
- picture.width = encode_config->width;
131
- picture.height = encode_config->height;
185
+ picture->width = encode_config->width;
186
+ picture->height = encode_config->height;
132
187
  }
133
- // OPTIONS END
134
188
 
135
- if (!WebPValidateConfig(&config)) {
136
- //fprintf(stderr, "Error! Invalid configuration.\n");
137
- return_value = 2;
138
- goto Error;
189
+ // The rules below come from libwebp's examples/cwebp.c.
190
+ // A target size or PSNR is only searched for with more than one pass.
191
+ if ((config->target_size > 0 || config->target_PSNR > 0) && config->pass == 1) {
192
+ config->pass = 6;
139
193
  }
194
+ // Keep RGB samples when the encoding needs them; otherwise the readers
195
+ // convert to YUV right away, which loses detail before lossless encoding
196
+ // and makes sharp YUV conversion impossible.
197
+ picture->use_argb = config->lossless || config->use_sharp_yuv ||
198
+ config->preprocessing > 0 ||
199
+ (encode_config->crop_w | encode_config->crop_h) > 0 ||
200
+ (encode_config->resize_w | encode_config->resize_h) > 0;
140
201
 
141
- if (!UtilReadPicture(in_file, &picture, keep_alpha)) {
142
- //fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
143
- return_value = 3;
144
- goto Error;
202
+ if (!WebPValidateConfig(config)) {
203
+ return ENCODE_INVALID_CONFIG;
145
204
  }
205
+ return 0;
206
+ }
146
207
 
147
- out = fopen(out_file, "wb");
148
- if (out == NULL) {
149
- //fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
150
- return_value = 4;
151
- goto Error;
152
- }
153
- picture.writer = EncodeWriter;
154
- picture.custom_ptr = (void*)out;
155
-
156
- if ((encode_config->crop_w | encode_config->crop_h) > 0){
157
- if (!WebPPictureView(&picture, encode_config->crop_x, encode_config->crop_y, encode_config->crop_w, encode_config->crop_h, &picture)) {
158
- //fprintf(stderr, "Error! Cannot crop picture\n");
159
- return_value = 5;
160
- goto Error;
161
- }
162
- }
208
+ // Crops, resizes and encodes 'picture'. On success '*webp' holds the WebP
209
+ // file (free it with free()).
210
+ static int EncodeToMemory(const FfiWebpEncodeConfig* const encode_config,
211
+ const WebPConfig* const config, WebPPicture* const picture,
212
+ char** const webp, size_t* const webp_size, int* const detail) {
213
+ int result = 0;
214
+ FILE* const out = open_memstream(webp, webp_size);
215
+ if (out == NULL) return ENCODE_OUT_OF_MEMORY;
216
+
217
+ picture->writer = EncodeWriter;
218
+ picture->custom_ptr = (void*)out;
219
+ if ((encode_config->crop_w | encode_config->crop_h) > 0 &&
220
+ !WebPPictureView(picture, encode_config->crop_x, encode_config->crop_y,
221
+ encode_config->crop_w, encode_config->crop_h, picture)) {
222
+ result = ENCODE_CANNOT_CROP;
223
+ } else if ((encode_config->resize_w | encode_config->resize_h) > 0 &&
224
+ !WebPPictureRescale(picture, encode_config->resize_w, encode_config->resize_h)) {
225
+ result = ENCODE_CANNOT_RESIZE;
226
+ } else if (!WebPEncode(config, picture)) {
227
+ result = ENCODE_FAILED;
228
+ }
229
+ if (result != 0) *detail = (int)picture->error_code;
230
+
231
+ // '*webp' and '*webp_size' are final only after fclose
232
+ if (fclose(out) != 0 && result == 0) result = ENCODE_OUT_OF_MEMORY;
233
+ if (result != 0) {
234
+ free(*webp);
235
+ *webp = NULL;
236
+ }
237
+ return result;
238
+ }
163
239
 
164
- if ((encode_config->resize_w | encode_config->resize_h) > 0) {
165
- if (!WebPPictureRescale(&picture, encode_config->resize_w, encode_config->resize_h)) {
166
- //fprintf(stderr, "Error! Cannot resize picture\n");
167
- return_value = 6;
168
- goto Error;
169
- }
240
+ // Reads a picture (and its metadata, when asked for) from 'in' and encodes it.
241
+ // On success '*webp' holds the encoder output (free it with free()).
242
+ static int EncodeInput(FILE* const in, const FfiWebpEncodeConfig* const encode_config,
243
+ const WebPConfig* const config, WebPPicture* const picture,
244
+ Metadata* const metadata, char** const webp,
245
+ size_t* const webp_size, int* const detail) {
246
+ if (!UtilReadPictureFile(in, picture, 1, encode_config->metadata > 0 ? metadata : NULL)) {
247
+ return ENCODE_CANNOT_READ_INPUT;
170
248
  }
249
+ return EncodeToMemory(encode_config, config, picture, webp, webp_size, detail);
250
+ }
171
251
 
172
- if (picture.extra_info_type > 0) {
173
- AllocExtraInfo(&picture);
174
- }
252
+ // Writes the WebP file with the requested metadata to 'out_file'. Opening it
253
+ // only now means a failed encode leaves no file, and the input file can be the
254
+ // output file. When writing fails, a file this call created is removed again.
255
+ static int WriteWebPFile(const char* const out_file, const char* const webp,
256
+ size_t webp_size, const WebPPicture* const picture,
257
+ const Metadata* const metadata, int keep_metadata,
258
+ int* const detail) {
259
+ const int existed = UtilFileExists(out_file);
260
+ FILE* const out = fopen(out_file, "wb");
261
+ int ok;
262
+
263
+ if (out == NULL) return ENCODE_CANNOT_OPEN_OUTPUT;
264
+ ok = MetadataWriteWebP(out, (const uint8_t*)webp, webp_size, picture->width,
265
+ picture->height, metadata, keep_metadata);
266
+ // fclose flushes buffered data, so it can also fail (for example, disk full)
267
+ if (fclose(out) != 0) ok = 0;
268
+ if (ok) return 0;
269
+ if (!existed) remove(out_file);
270
+ *detail = VP8_ENC_ERROR_BAD_WRITE;
271
+ return ENCODE_FAILED;
272
+ }
175
273
 
176
- if (!WebPEncode(&config, &picture)) {
177
- //fprintf(stderr, "Error! Cannot encode picture as WebP\n");
178
- return_value = 7;
179
- goto Error;
274
+ int webp_encode(const char *in_file, const char *out_file,
275
+ const FfiWebpEncodeConfig *encode_config, int* detail) {
276
+ WebPPicture picture;
277
+ WebPConfig config;
278
+ Metadata metadata;
279
+ FILE* in;
280
+ char* webp = NULL;
281
+ size_t webp_size = 0;
282
+ int result;
283
+
284
+ *detail = 0;
285
+ result = SetupEncoder(encode_config, &config, &picture);
286
+ // after a version mismatch 'picture' is not initialized, so it can't be freed
287
+ if (result == ENCODE_VERSION_MISMATCH) return result;
288
+ MetadataInit(&metadata);
289
+ if (result != 0) goto End;
290
+
291
+ in = fopen(in_file, "rb");
292
+ if (in == NULL) {
293
+ result = ENCODE_CANNOT_READ_INPUT;
294
+ goto End;
295
+ }
296
+ // the input is read completely and closed before the output is opened
297
+ result = EncodeInput(in, encode_config, &config, &picture, &metadata,
298
+ &webp, &webp_size, detail);
299
+ fclose(in);
300
+ if (result == 0) {
301
+ result = WriteWebPFile(out_file, webp, webp_size, &picture, &metadata,
302
+ encode_config->metadata, detail);
303
+ }
304
+
305
+ End:
306
+ free(webp);
307
+ MetadataFree(&metadata);
308
+ WebPPictureFree(&picture);
309
+ return result;
310
+ }
311
+
312
+ int webp_encode_data(const uint8_t* data, size_t data_size,
313
+ uint8_t** output, size_t* output_size,
314
+ const FfiWebpEncodeConfig* encode_config, int* detail) {
315
+ WebPPicture picture;
316
+ WebPConfig config;
317
+ Metadata metadata;
318
+ FILE* in;
319
+ FILE* out;
320
+ char* webp = NULL;
321
+ size_t webp_size = 0;
322
+ char* buffer = NULL;
323
+ size_t buffer_size = 0;
324
+ int result;
325
+
326
+ *output = NULL;
327
+ *output_size = 0;
328
+ *detail = 0;
329
+ result = SetupEncoder(encode_config, &config, &picture);
330
+ if (result == ENCODE_VERSION_MISMATCH) return result;
331
+ MetadataInit(&metadata);
332
+ if (result != 0) goto End;
333
+
334
+ if (data == NULL || data_size == 0) {
335
+ result = ENCODE_CANNOT_READ_INPUT;
336
+ goto End;
337
+ }
338
+ // the readers take a FILE*, so wrap the data in one; "rb" never writes to it
339
+ in = fmemopen((void*)data, data_size, "rb");
340
+ if (in == NULL) {
341
+ result = ENCODE_OUT_OF_MEMORY;
342
+ goto End;
343
+ }
344
+ result = EncodeInput(in, encode_config, &config, &picture, &metadata,
345
+ &webp, &webp_size, detail);
346
+ fclose(in);
347
+ if (result != 0) goto End;
348
+
349
+ // a FILE* that collects the WebP file in a growing malloc'ed buffer
350
+ out = open_memstream(&buffer, &buffer_size);
351
+ if (out == NULL) {
352
+ result = ENCODE_OUT_OF_MEMORY;
353
+ goto End;
180
354
  }
181
- return_value = 0;
355
+ if (!MetadataWriteWebP(out, (const uint8_t*)webp, webp_size, picture.width,
356
+ picture.height, &metadata, encode_config->metadata)) {
357
+ result = ENCODE_OUT_OF_MEMORY;
358
+ }
359
+ // 'buffer' and 'buffer_size' are final only after fclose
360
+ if (fclose(out) != 0 && result == 0) result = ENCODE_OUT_OF_MEMORY;
182
361
 
183
- Error:
184
- free(picture.extra_info);
362
+ End:
363
+ free(webp);
364
+ MetadataFree(&metadata);
185
365
  WebPPictureFree(&picture);
186
- if (out != NULL) {
187
- fclose(out);
366
+ if (result == 0) {
367
+ *output = (uint8_t*)buffer;
368
+ *output_size = buffer_size;
369
+ } else {
370
+ free(buffer);
188
371
  }
189
-
190
- return return_value;
372
+ return result;
191
373
  }
192
374
 
375
+ int webp_encode_rgba(const uint8_t* rgba, int width, int height,
376
+ uint8_t** output, size_t* output_size,
377
+ const FfiWebpEncodeConfig* encode_config, int* detail) {
378
+ WebPPicture picture;
379
+ WebPConfig config;
380
+ char* webp = NULL;
381
+ size_t webp_size = 0;
382
+ int stride;
383
+ int result;
384
+
385
+ *output = NULL;
386
+ *output_size = 0;
387
+ *detail = 0;
388
+ result = SetupEncoder(encode_config, &config, &picture);
389
+ if (result == ENCODE_VERSION_MISMATCH) return result;
390
+ if (result != 0) goto End;
391
+
392
+ if (rgba == NULL || width <= 0 || height <= 0 ||
393
+ !UtilCheckImageSize((uint64_t)width, (uint64_t)height, 4, &stride, NULL)) {
394
+ result = ENCODE_CANNOT_READ_INPUT;
395
+ goto End;
396
+ }
397
+ picture.width = width;
398
+ picture.height = height;
399
+ picture.use_argb = 1;
400
+ if (!WebPPictureImportRGBA(&picture, rgba, stride)) {
401
+ result = ENCODE_OUT_OF_MEMORY;
402
+ *detail = (int)picture.error_code;
403
+ goto End;
404
+ }
405
+ // raw pixels have no metadata, so the encoder output is the whole file
406
+ result = EncodeToMemory(encode_config, &config, &picture, &webp, &webp_size, detail);
407
+ if (result == 0) {
408
+ *output = (uint8_t*)webp;
409
+ *output_size = webp_size;
410
+ }
411
+
412
+ End:
413
+ WebPPictureFree(&picture);
414
+ return result;
415
+ }
193
416
 
194
-
195
- int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeConfig *decode_config) {
196
- int return_value = -1;
197
- WebPDecoderConfig config;
198
- WebPDecBuffer* const output_buffer = &config.output;
199
- WebPBitstreamFeatures* const bitstream = &config.input;
200
- OutputFileFormat format = oPNG;
201
-
202
- if (!WebPInitDecoderConfig(&config)) {
203
- //fprintf(stderr, "Library version mismatch!\n");
204
- return 1;
417
+ // Initializes 'config' and applies the options from Ruby.
418
+ static int SetupDecoder(const FfiWebpDecodeConfig* const decode_config,
419
+ WebPDecoderConfig* const config,
420
+ OutputFileFormat* const format) {
421
+ if (!WebPInitDecoderConfig(config)) {
422
+ return DECODE_VERSION_MISMATCH;
205
423
  }
206
424
 
207
- if (decode_config->output_format != format){
208
- format = decode_config->output_format;
209
- }
425
+ *format = decode_config->output_format;
210
426
  if (decode_config->no_fancy_upsampling > 0){
211
- config.options.no_fancy_upsampling = 1;
427
+ config->options.no_fancy_upsampling = 1;
212
428
  }
213
429
  if (decode_config->bypass_filtering > 0){
214
- config.options.bypass_filtering = 1;
430
+ config->options.bypass_filtering = 1;
215
431
  }
216
432
  if (decode_config->use_threads > 0){
217
- config.options.use_threads = 1;
433
+ config->options.use_threads = 1;
218
434
  }
219
435
  if ((decode_config->crop_w | decode_config->crop_h) > 0){
220
- config.options.use_cropping = 1;
221
- config.options.crop_left = decode_config->crop_x;
222
- config.options.crop_top = decode_config->crop_y;
223
- config.options.crop_width = decode_config->crop_w;
224
- config.options.crop_height = decode_config->crop_h;
436
+ config->options.use_cropping = 1;
437
+ config->options.crop_left = decode_config->crop_x;
438
+ config->options.crop_top = decode_config->crop_y;
439
+ config->options.crop_width = decode_config->crop_w;
440
+ config->options.crop_height = decode_config->crop_h;
225
441
  }
226
442
  if ((decode_config->resize_w | decode_config->resize_h) > 0){
227
- config.options.use_scaling = 1;
228
- config.options.scaled_width = decode_config->resize_w;
229
- config.options.scaled_height = decode_config->resize_h;
230
- }
231
-
232
- VP8StatusCode status = VP8_STATUS_OK;
233
- size_t data_size = 0;
234
- const uint8_t* data = NULL;
235
-
236
- if (!UtilReadFile(in_file, &data, &data_size)) return -1;
237
-
238
- status = WebPGetFeatures(data, data_size, bitstream);
239
- if (status != VP8_STATUS_OK) {
240
- //fprintf(stderr, "This is invalid webp image!\n");
241
- return_value = 2;
242
- goto Error;
443
+ config->options.use_scaling = 1;
444
+ config->options.scaled_width = decode_config->resize_w;
445
+ config->options.scaled_height = decode_config->resize_h;
243
446
  }
447
+ return 0;
448
+ }
244
449
 
450
+ // The colorspace each output format is written from.
451
+ static int OutputColorspace(OutputFileFormat format, int has_alpha,
452
+ WEBP_CSP_MODE* const colorspace) {
245
453
  switch (format) {
246
454
  case oPNG:
247
- output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
248
- break;
455
+ *colorspace = has_alpha ? MODE_RGBA : MODE_RGB;
456
+ return 1;
249
457
  case oPAM:
250
- output_buffer->colorspace = MODE_RGBA;
251
- break;
458
+ *colorspace = MODE_RGBA;
459
+ return 1;
252
460
  case oPPM:
253
- output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
254
- break;
461
+ *colorspace = MODE_RGB; // drops alpha for PPM
462
+ return 1;
255
463
  case oBMP:
256
- output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
257
- break;
464
+ *colorspace = has_alpha ? MODE_BGRA : MODE_BGR;
465
+ return 1;
258
466
  case oTIFF_: // note: force pre-multiplied alpha
259
- output_buffer->colorspace =
260
- bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
261
- break;
467
+ *colorspace = has_alpha ? MODE_rgbA : MODE_RGB;
468
+ return 1;
262
469
  case oPGM:
263
470
  case oYUV:
264
- output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
265
- break;
471
+ *colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
472
+ return 1;
266
473
  case ALPHA_PLANE_ONLY:
267
- output_buffer->colorspace = MODE_YUVA;
268
- break;
474
+ *colorspace = MODE_YUVA;
475
+ return 1;
269
476
  default:
270
- free((void*)data);
271
- return 3;
477
+ return 0;
272
478
  }
479
+ }
273
480
 
274
- status = WebPDecode(data, data_size, &config);
481
+ // Decodes 'data' into config->output. 'format' picks the colorspace; a
482
+ // negative 'format' decodes to RGBA.
483
+ static int DecodeData(const uint8_t* const data, size_t data_size, int format,
484
+ WebPDecoderConfig* const config, int* const detail) {
485
+ WebPDecBuffer* const output_buffer = &config->output;
486
+ VP8StatusCode status;
275
487
 
488
+ if (data == NULL || data_size == 0) {
489
+ *detail = VP8_STATUS_NOT_ENOUGH_DATA;
490
+ return DECODE_INVALID_WEBP;
491
+ }
492
+ status = WebPGetFeatures(data, data_size, &config->input);
276
493
  if (status != VP8_STATUS_OK) {
277
- //fprintf(stderr, "Decoding of %s failed.\n", in_file);
278
- return_value = 4;
279
- goto Error;
494
+ *detail = (int)status;
495
+ return DECODE_INVALID_WEBP;
496
+ }
497
+ if (format < 0) {
498
+ output_buffer->colorspace = MODE_RGBA;
499
+ } else if (!OutputColorspace((OutputFileFormat)format, config->input.has_alpha,
500
+ &output_buffer->colorspace)) {
501
+ return DECODE_INVALID_FORMAT;
502
+ }
503
+ status = WebPDecode(data, data_size, config);
504
+ if (status != VP8_STATUS_OK) {
505
+ *detail = (int)status;
506
+ return DECODE_FAILED;
507
+ }
508
+ return 0;
509
+ }
510
+
511
+ int webp_decode(const char *in_file, const char *out_file,
512
+ const FfiWebpDecodeConfig *decode_config, int* detail) {
513
+ WebPDecoderConfig config;
514
+ OutputFileFormat format;
515
+ const uint8_t* data = NULL;
516
+ size_t data_size = 0;
517
+ int result;
518
+
519
+ *detail = 0;
520
+ result = SetupDecoder(decode_config, &config, &format);
521
+ if (result != 0) return result;
522
+ if (!UtilReadFile(in_file, &data, &data_size)) {
523
+ return DECODE_CANNOT_READ_INPUT;
524
+ }
525
+
526
+ result = DecodeData(data, data_size, (int)format, &config, detail);
527
+ if (result == 0 && !UtilSaveOutput(&config.output, format, out_file)) {
528
+ result = DECODE_CANNOT_WRITE_OUTPUT;
280
529
  }
281
- UtilSaveOutput(output_buffer, format, out_file);
282
- return_value = 0;
283
530
 
284
- Error:
285
531
  free((void*)data);
286
- WebPFreeDecBuffer(output_buffer);
287
- return return_value;
532
+ WebPFreeDecBuffer(&config.output);
533
+ return result;
288
534
  }
289
535
 
536
+ int webp_decode_data(const uint8_t* data, size_t data_size,
537
+ uint8_t** output, size_t* output_size,
538
+ const FfiWebpDecodeConfig* decode_config, int* detail) {
539
+ WebPDecoderConfig config;
540
+ OutputFileFormat format;
541
+ FILE* out = NULL;
542
+ char* buffer = NULL;
543
+ size_t buffer_size = 0;
544
+ int result;
545
+
546
+ *output = NULL;
547
+ *output_size = 0;
548
+ *detail = 0;
549
+ result = SetupDecoder(decode_config, &config, &format);
550
+ if (result != 0) return result;
551
+
552
+ result = DecodeData(data, data_size, (int)format, &config, detail);
553
+ if (result == 0) {
554
+ // a FILE* that collects the output in a growing malloc'ed buffer
555
+ out = open_memstream(&buffer, &buffer_size);
556
+ if (out == NULL) {
557
+ result = DECODE_OUT_OF_MEMORY;
558
+ } else {
559
+ const int written = UtilWriteOutput(&config.output, format, out);
560
+ // writing to memory only fails when memory runs out
561
+ if (fclose(out) != 0 || !written) {
562
+ result = DECODE_OUT_OF_MEMORY;
563
+ }
564
+ }
565
+ }
566
+ WebPFreeDecBuffer(&config.output);
290
567
 
291
- // test
292
- int test_c(int n) {
293
- return n + 100;
568
+ if (result == 0) {
569
+ *output = (uint8_t*)buffer;
570
+ *output_size = buffer_size;
571
+ } else {
572
+ free(buffer);
573
+ }
574
+ return result;
294
575
  }
295
576
 
577
+ int webp_decode_rgba(const uint8_t* data, size_t data_size,
578
+ uint8_t** output, size_t* output_size, int* width, int* height,
579
+ const FfiWebpDecodeConfig* decode_config, int* detail) {
580
+ WebPDecoderConfig config;
581
+ OutputFileFormat format;
582
+ int result;
583
+
584
+ *output = NULL;
585
+ *output_size = 0;
586
+ *width = 0;
587
+ *height = 0;
588
+ *detail = 0;
589
+ result = SetupDecoder(decode_config, &config, &format);
590
+ if (result != 0) return result;
591
+
592
+ result = DecodeData(data, data_size, -1, &config, detail);
593
+ if (result == 0) {
594
+ const WebPDecBuffer* const buffer = &config.output;
595
+ const WebPRGBABuffer* const rgba = &buffer->u.RGBA;
596
+ const size_t row_size = (size_t)buffer->width * 4;
597
+ uint8_t* pixels = (uint8_t*)malloc(row_size * (size_t)buffer->height);
598
+ if (pixels == NULL) {
599
+ result = DECODE_OUT_OF_MEMORY;
600
+ } else {
601
+ int y;
602
+ // copy row by row: libwebp's stride may be wider than the image
603
+ for (y = 0; y < buffer->height; ++y) {
604
+ memcpy(pixels + (size_t)y * row_size, rgba->rgba + (size_t)y * rgba->stride, row_size);
605
+ }
606
+ *output = pixels;
607
+ *output_size = row_size * (size_t)buffer->height;
608
+ *width = buffer->width;
609
+ *height = buffer->height;
610
+ }
611
+ }
612
+ WebPFreeDecBuffer(&config.output);
613
+ return result;
614
+ }
296
615
 
297
616
  #if defined(__cplusplus) || defined(c_plusplus)
298
617
  } // extern "C"