webp-ffi 1.0.0-x86_64-linux-gnu

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.
@@ -0,0 +1,389 @@
1
+ #include "./pngdec.h"
2
+ #include "./util.h"
3
+
4
+ #include <assert.h>
5
+ #include <stdio.h>
6
+ #include <png.h>
7
+ #include <setjmp.h> // note: this must be included *after* png.h
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+
11
+ static void PNGAPI error_function(png_structp png, png_const_charp dummy) {
12
+ (void)dummy; // remove variable-unused warning
13
+ longjmp(png_jmpbuf(png), 1);
14
+ }
15
+
16
+ // libpng prints warnings to stderr by default. A library should stay quiet.
17
+ static void PNGAPI warning_function(png_structp png, png_const_charp dummy) {
18
+ (void)png;
19
+ (void)dummy;
20
+ }
21
+
22
+ // The metadata code below is based on libwebp's imageio/pngdec.c.
23
+
24
+ // Converts pairs of hex digits, possibly split by newlines, to 'length' bytes.
25
+ // Returns NULL for other characters or when there are fewer bytes.
26
+ static uint8_t* HexStringToBytes(const char* hexstring, size_t length) {
27
+ const char* src = hexstring;
28
+ size_t actual_length = 0;
29
+ uint8_t* const raw_data = (uint8_t*)malloc(length > 0 ? length : 1);
30
+ uint8_t* dst;
31
+
32
+ if (raw_data == NULL) return NULL;
33
+ for (dst = raw_data; actual_length < length && *src != '\0'; ++src) {
34
+ char* end;
35
+ char value[3];
36
+ if (*src == '\n') continue;
37
+ value[0] = *src++;
38
+ value[1] = *src;
39
+ value[2] = '\0';
40
+ *dst++ = (uint8_t)strtol(value, &end, 16);
41
+ if (end != value + 2) break;
42
+ ++actual_length;
43
+ }
44
+ if (actual_length != length) {
45
+ free(raw_data);
46
+ return NULL;
47
+ }
48
+ return raw_data;
49
+ }
50
+
51
+ // ImageMagick and ExifTool store metadata in text chunks as "raw profiles":
52
+ // "\n<name>\n<length>\n<hex data>\n".
53
+ static int ProcessRawProfile(const char* profile, size_t profile_length,
54
+ MetadataPayload* const payload) {
55
+ const char* src = profile;
56
+ char* end;
57
+ long length;
58
+
59
+ if (profile == NULL || profile_length == 0 || *src != '\n') return 0;
60
+ ++src;
61
+ while (*src != '\0' && *src++ != '\n') {} // skip the profile name
62
+ length = strtol(src, &end, 10);
63
+ if (*end != '\n') return 0;
64
+ ++end;
65
+ // two hex digits per byte, so a longer length cannot be right
66
+ if (length < 0 || (size_t)length > profile_length / 2) return 0;
67
+ payload->bytes = HexStringToBytes(end, (size_t)length);
68
+ if (payload->bytes == NULL) return 0;
69
+ payload->size = (size_t)length;
70
+ return 1;
71
+ }
72
+
73
+ static int CopyText(const char* text, size_t length, MetadataPayload* const payload) {
74
+ return MetadataCopy(text, length, payload);
75
+ }
76
+
77
+ static const struct {
78
+ const char* key;
79
+ int (*process)(const char* text, size_t length, MetadataPayload* const payload);
80
+ int kind; // METADATA_* flag
81
+ } kPNGTextMetadata[] = {
82
+ // https://exiftool.org/TagNames/PNG.html#TextualData
83
+ { "Raw profile type exif", ProcessRawProfile, METADATA_EXIF },
84
+ { "Raw profile type xmp", ProcessRawProfile, METADATA_XMP },
85
+ // ExifTool puts EXIF data in an APP1 profile too; ImageMagick in "app1".
86
+ { "Raw profile type APP1", ProcessRawProfile, METADATA_EXIF },
87
+ { "Raw profile type app1", ProcessRawProfile, METADATA_EXIF },
88
+ // XMP Specification Part 3, Section 3 #PNG
89
+ { "XML:com.adobe.xmp", CopyText, METADATA_XMP },
90
+ { NULL, NULL, 0 }
91
+ };
92
+
93
+ // Looks for metadata before and after the image data, preferring what comes
94
+ // first. Like libwebp, an eXIf chunk wins over EXIF in text chunks.
95
+ static int ExtractMetadataFromPNG(png_structp png, png_infop info,
96
+ png_infop end_info, Metadata* const metadata) {
97
+ int p;
98
+ for (p = 0; p < 2; ++p) {
99
+ png_infop const head = (p == 0) ? info : end_info;
100
+ png_textp text = NULL;
101
+ const png_uint_32 count = png_get_text(png, head, &text, NULL);
102
+ png_uint_32 i;
103
+
104
+ #ifdef PNG_eXIf_SUPPORTED
105
+ {
106
+ png_bytep exif = NULL;
107
+ png_uint_32 length = 0;
108
+ if (png_get_eXIf_1(png, head, &length, &exif) == PNG_INFO_eXIf &&
109
+ !MetadataCopy(exif, length, &metadata->exif)) {
110
+ return 0;
111
+ }
112
+ }
113
+ #endif
114
+
115
+ for (i = 0; i < count; ++i, ++text) {
116
+ int j;
117
+ for (j = 0; kPNGTextMetadata[j].key != NULL; ++j) {
118
+ if (text->key != NULL && !strcmp(text->key, kPNGTextMetadata[j].key)) {
119
+ MetadataPayload* const payload =
120
+ (kPNGTextMetadata[j].kind == METADATA_EXIF) ? &metadata->exif : &metadata->xmp;
121
+ // iTXt chunks keep their length in a separate field
122
+ const size_t length = (text->compression == PNG_ITXT_COMPRESSION_NONE ||
123
+ text->compression == PNG_ITXT_COMPRESSION_zTXt)
124
+ ? text->itxt_length : text->text_length;
125
+ // the first one wins; like cwebp, a broken one fails the read
126
+ if (payload->bytes == NULL &&
127
+ !kPNGTextMetadata[j].process(text->text, length, payload)) {
128
+ return 0;
129
+ }
130
+ break;
131
+ }
132
+ }
133
+ }
134
+
135
+ {
136
+ png_charp name;
137
+ int compression_type;
138
+ png_bytep profile;
139
+ png_uint_32 length;
140
+ if (png_get_iCCP(png, head, &name, &compression_type, &profile, &length) == PNG_INFO_iCCP &&
141
+ !MetadataCopy(profile, length, &metadata->iccp)) {
142
+ return 0;
143
+ }
144
+ }
145
+ }
146
+ return 1;
147
+ }
148
+
149
+ int UtilReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha,
150
+ Metadata* const metadata) {
151
+ // Locals that change after setjmp() must be volatile, otherwise their values
152
+ // are undefined when libpng jumps back to the error handler.
153
+ volatile png_structp png = NULL;
154
+ volatile png_infop info = NULL;
155
+ volatile png_infop end_info = NULL;
156
+ int color_type, bit_depth, interlaced;
157
+ int has_alpha;
158
+ int num_passes;
159
+ int p;
160
+ int ok = 0;
161
+ png_uint_32 width, height, y;
162
+ int stride;
163
+ size_t rgb_size;
164
+ uint8_t* volatile rgb = NULL;
165
+
166
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
167
+ if (png == NULL) {
168
+ goto End;
169
+ }
170
+
171
+ png_set_error_fn(png, NULL, error_function, warning_function);
172
+ if (setjmp(png_jmpbuf(png))) {
173
+ Error:
174
+ png_destroy_read_struct((png_structpp)&png, (png_infopp)&info,
175
+ (png_infopp)&end_info);
176
+ goto End;
177
+ }
178
+
179
+ info = png_create_info_struct(png);
180
+ if (info == NULL) goto Error;
181
+ end_info = png_create_info_struct(png);
182
+ if (end_info == NULL) goto Error;
183
+
184
+ png_init_io(png, in_file);
185
+ png_read_info(png, info);
186
+ if (!png_get_IHDR(png, info,
187
+ &width, &height, &bit_depth, &color_type, &interlaced,
188
+ NULL, NULL)) goto Error;
189
+
190
+ png_set_strip_16(png);
191
+ png_set_packing(png);
192
+ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
193
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
194
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
195
+ if (bit_depth < 8) {
196
+ png_set_expand_gray_1_2_4_to_8(png);
197
+ }
198
+ png_set_gray_to_rgb(png);
199
+ }
200
+ if (png_get_valid(png, info, PNG_INFO_tRNS)) {
201
+ png_set_tRNS_to_alpha(png);
202
+ has_alpha = 1;
203
+ } else {
204
+ has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA);
205
+ }
206
+
207
+ if (!keep_alpha) {
208
+ png_set_strip_alpha(png);
209
+ has_alpha = 0;
210
+ }
211
+
212
+ num_passes = png_set_interlace_handling(png);
213
+ png_read_update_info(png, info);
214
+ if (!UtilCheckImageSize(width, height, has_alpha ? 4 : 3,
215
+ &stride, &rgb_size)) {
216
+ goto Error;
217
+ }
218
+ rgb = (uint8_t*)malloc(rgb_size);
219
+ if (rgb == NULL) goto Error;
220
+ for (p = 0; p < num_passes; ++p) {
221
+ for (y = 0; y < height; ++y) {
222
+ png_bytep row = rgb + (size_t)y * stride;
223
+ png_read_rows(png, &row, NULL, 1);
224
+ }
225
+ }
226
+ png_read_end(png, end_info);
227
+ if (metadata != NULL && !ExtractMetadataFromPNG(png, info, end_info, metadata)) {
228
+ goto Error;
229
+ }
230
+ png_destroy_read_struct((png_structpp)&png, (png_infopp)&info,
231
+ (png_infopp)&end_info);
232
+
233
+ pic->width = (int)width;
234
+ pic->height = (int)height;
235
+ pic->use_argb = 1;
236
+ ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride)
237
+ : WebPPictureImportRGB(pic, rgb, stride);
238
+
239
+ End:
240
+ free(rgb);
241
+ return ok;
242
+ }
243
+
244
+
245
+
246
+
247
+
248
+ int UtilWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
249
+ const uint32_t width = buffer->width;
250
+ const uint32_t height = buffer->height;
251
+ unsigned char* const rgb = buffer->u.RGBA.rgba;
252
+ const int stride = buffer->u.RGBA.stride;
253
+ const int has_alpha = (buffer->colorspace == MODE_RGBA);
254
+ png_structp png;
255
+ png_infop info;
256
+ png_uint_32 y;
257
+
258
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
259
+ NULL, error_function, warning_function);
260
+ if (png == NULL) {
261
+ return 0;
262
+ }
263
+ info = png_create_info_struct(png);
264
+ if (info == NULL) {
265
+ png_destroy_write_struct(&png, NULL);
266
+ return 0;
267
+ }
268
+ if (setjmp(png_jmpbuf(png))) {
269
+ png_destroy_write_struct(&png, &info);
270
+ return 0;
271
+ }
272
+ png_init_io(png, out_file);
273
+ png_set_IHDR(png, info, width, height, 8,
274
+ has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
275
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
276
+ PNG_FILTER_TYPE_DEFAULT);
277
+ png_write_info(png, info);
278
+ for (y = 0; y < height; ++y) {
279
+ png_bytep row = rgb + y * stride;
280
+ png_write_rows(png, &row, 1);
281
+ }
282
+ png_write_end(png, info);
283
+ png_destroy_write_struct(&png, &info);
284
+ return 1;
285
+ }
286
+
287
+
288
+ int UtilWritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha) {
289
+ const uint32_t width = buffer->width;
290
+ const uint32_t height = buffer->height;
291
+ const unsigned char* const rgb = buffer->u.RGBA.rgba;
292
+ const int stride = buffer->u.RGBA.stride;
293
+ const size_t bytes_per_px = alpha ? 4 : 3;
294
+ uint32_t y;
295
+
296
+ if (alpha) {
297
+ fprintf(fout, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
298
+ "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
299
+ } else {
300
+ fprintf(fout, "P6\n%d %d\n255\n", width, height);
301
+ }
302
+ for (y = 0; y < height; ++y) {
303
+ if (fwrite(rgb + y * stride, width, bytes_per_px, fout) != bytes_per_px) {
304
+ return 0;
305
+ }
306
+ }
307
+ return 1;
308
+ }
309
+
310
+ int UtilWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
311
+ const uint32_t width = buffer->width;
312
+ const uint32_t height = buffer->height;
313
+ const unsigned char* const a = buffer->u.YUVA.a;
314
+ const int a_stride = buffer->u.YUVA.a_stride;
315
+ uint32_t y;
316
+ assert(a != NULL);
317
+ fprintf(fout, "P5\n%d %d\n255\n", width, height);
318
+ for (y = 0; y < height; ++y) {
319
+ if (fwrite(a + y * a_stride, width, 1, fout) != 1) {
320
+ return 0;
321
+ }
322
+ }
323
+ return 1;
324
+ }
325
+
326
+ int UtilWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
327
+ const int width = buffer->width;
328
+ const int height = buffer->height;
329
+ const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
330
+ // Save a grayscale PGM file using the IMC4 layout
331
+ // (http://www.fourcc.org/yuv.php#IMC4). This is a very
332
+ // convenient format for viewing the samples, esp. for
333
+ // odd dimensions.
334
+ int ok = 1;
335
+ int y;
336
+ const int uv_width = (width + 1) / 2;
337
+ const int uv_height = (height + 1) / 2;
338
+ const int out_stride = (width + 1) & ~1;
339
+ const int a_height = yuv->a ? height : 0;
340
+ fprintf(fout, "P5\n%d %d\n255\n", out_stride, height + uv_height + a_height);
341
+ for (y = 0; ok && y < height; ++y) {
342
+ ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1);
343
+ if (width & 1) fputc(0, fout); // padding byte
344
+ }
345
+ for (y = 0; ok && y < uv_height; ++y) {
346
+ ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
347
+ ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
348
+ }
349
+ for (y = 0; ok && y < a_height; ++y) {
350
+ ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1);
351
+ if (width & 1) fputc(0, fout); // padding byte
352
+ }
353
+ return ok;
354
+ }
355
+
356
+ // Based on WebPWriteYUV in libwebp's imageio/image_enc.c.
357
+ int UtilWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
358
+ const int width = buffer->width;
359
+ const int height = buffer->height;
360
+ const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
361
+ const uint8_t* src_y = yuv->y;
362
+ const uint8_t* src_u = yuv->u;
363
+ const uint8_t* src_v = yuv->v;
364
+ const uint8_t* src_a = yuv->a;
365
+ const int uv_width = (width + 1) / 2;
366
+ const int uv_height = (height + 1) / 2;
367
+ const int a_height = (src_a != NULL) ? height : 0;
368
+ int ok = 1;
369
+ int y;
370
+
371
+ if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
372
+ for (y = 0; ok && y < height; ++y) {
373
+ ok &= (fwrite(src_y, width, 1, fout) == 1);
374
+ src_y += yuv->y_stride;
375
+ }
376
+ for (y = 0; ok && y < uv_height; ++y) {
377
+ ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
378
+ src_u += yuv->u_stride;
379
+ }
380
+ for (y = 0; ok && y < uv_height; ++y) {
381
+ ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
382
+ src_v += yuv->v_stride;
383
+ }
384
+ for (y = 0; ok && y < a_height; ++y) {
385
+ ok &= (fwrite(src_a, width, 1, fout) == 1);
386
+ src_a += yuv->a_stride;
387
+ }
388
+ return ok;
389
+ }
@@ -0,0 +1,29 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+
4
+ #include "webp/encode.h"
5
+ #include "webp/decode.h"
6
+ #include "./metadata.h"
7
+
8
+ #ifdef __cplusplus
9
+ extern "C" {
10
+ #endif
11
+
12
+ // Reads a PNG from 'in_file', returning the decoded output in 'pic'.
13
+ // If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
14
+ // otherwise it will be RGB. When 'metadata' is not NULL, the ICC profile, EXIF
15
+ // and XMP data are copied into it.
16
+ // Returns true on success.
17
+ int UtilReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha,
18
+ Metadata* const metadata);
19
+
20
+ int UtilWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
21
+ int UtilWritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha);
22
+ int UtilWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer);
23
+ int UtilWritePGM(FILE* fout, const WebPDecBuffer* const buffer);
24
+ // Raw planes: Y, then U and V (half size, rounded up), then alpha if any.
25
+ int UtilWriteYUV(FILE* fout, const WebPDecBuffer* const buffer);
26
+
27
+ #ifdef __cplusplus
28
+ } // extern "C"
29
+ #endif