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,174 @@
1
+ #include "./jpegdec.h"
2
+ #include "./util.h"
3
+
4
+ #include <stdio.h>
5
+ #include <jpeglib.h>
6
+ #include <setjmp.h>
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+
10
+ struct my_error_mgr {
11
+ struct jpeg_error_mgr pub;
12
+ jmp_buf setjmp_buffer;
13
+ };
14
+
15
+ static void my_error_exit(j_common_ptr dinfo) {
16
+ struct my_error_mgr* myerr = (struct my_error_mgr*) dinfo->err;
17
+ longjmp(myerr->setjmp_buffer, 1);
18
+ }
19
+
20
+ // libjpeg prints errors and warnings to stderr by default. A library should
21
+ // stay quiet.
22
+ static void my_output_message(j_common_ptr dinfo) {
23
+ (void)dinfo;
24
+ }
25
+
26
+ // Based on the metadata code in libwebp's imageio/jpegdec.c.
27
+
28
+ // An ICC profile can be split over several APP2 segments:
29
+ // "ICC_PROFILE\0", the 1-based sequence number, the segment count, then data.
30
+ static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
31
+ static const char kSignature[] = "ICC_PROFILE"; // 12 bytes with the '\0'
32
+ const size_t signature_length = sizeof(kSignature);
33
+ const size_t header_length = signature_length + 2;
34
+ const JOCTET* segments[256];
35
+ size_t lengths[256];
36
+ int expected_count = 0;
37
+ int found = 0;
38
+ size_t total_size = 0;
39
+ size_t offset = 0;
40
+ jpeg_saved_marker_ptr marker;
41
+ int i;
42
+
43
+ memset(lengths, 0, sizeof(lengths));
44
+ for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
45
+ if (marker->marker == JPEG_APP0 + 2 && marker->data_length > header_length &&
46
+ !memcmp(marker->data, kSignature, signature_length)) {
47
+ const int seq = marker->data[signature_length];
48
+ const int count = marker->data[signature_length + 1];
49
+ if (seq == 0 || count == 0 || seq > count) return 0;
50
+ if (expected_count == 0) expected_count = count;
51
+ if (count != expected_count || lengths[seq] != 0) return 0;
52
+ segments[seq] = marker->data + header_length;
53
+ lengths[seq] = marker->data_length - header_length;
54
+ total_size += lengths[seq];
55
+ ++found;
56
+ }
57
+ }
58
+ if (found == 0) return 1; // no profile
59
+ if (found != expected_count) return 0; // missing segments
60
+
61
+ iccp->bytes = (uint8_t*)malloc(total_size);
62
+ if (iccp->bytes == NULL) return 0;
63
+ for (i = 1; i <= expected_count; ++i) { // in sequence order
64
+ memcpy(iccp->bytes + offset, segments[i], lengths[i]);
65
+ offset += lengths[i];
66
+ }
67
+ iccp->size = total_size;
68
+ return 1;
69
+ }
70
+
71
+ // EXIF and XMP live in APP1 segments, after a signature that is not copied.
72
+ static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
73
+ Metadata* const metadata) {
74
+ static const char kExifSignature[] = "Exif\0"; // "Exif\0\0", 6 bytes
75
+ static const char kXMPSignature[] = "http://ns.adobe.com/xap/1.0/"; // 29 bytes
76
+ jpeg_saved_marker_ptr marker;
77
+
78
+ if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
79
+ for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
80
+ if (marker->marker != JPEG_APP0 + 1) continue;
81
+ if (marker->data_length > sizeof(kExifSignature) &&
82
+ !memcmp(marker->data, kExifSignature, sizeof(kExifSignature))) {
83
+ if (!MetadataCopy(marker->data + sizeof(kExifSignature),
84
+ marker->data_length - sizeof(kExifSignature),
85
+ &metadata->exif)) {
86
+ return 0;
87
+ }
88
+ } else if (marker->data_length > sizeof(kXMPSignature) &&
89
+ !memcmp(marker->data, kXMPSignature, sizeof(kXMPSignature))) {
90
+ if (!MetadataCopy(marker->data + sizeof(kXMPSignature),
91
+ marker->data_length - sizeof(kXMPSignature),
92
+ &metadata->xmp)) {
93
+ return 0;
94
+ }
95
+ }
96
+ }
97
+ return 1;
98
+ }
99
+
100
+ int UtilReadJPEG(FILE* in_file, WebPPicture* const pic,
101
+ Metadata* const metadata) {
102
+ int ok = 0;
103
+ int stride, width, height;
104
+ size_t rgb_size;
105
+ // 'rgb' changes after setjmp(), so it must be volatile to keep its value
106
+ // when libjpeg jumps back to the error handler.
107
+ uint8_t* volatile rgb = NULL;
108
+ struct jpeg_decompress_struct dinfo;
109
+ struct my_error_mgr jerr;
110
+ JSAMPROW buffer[1];
111
+
112
+ dinfo.err = jpeg_std_error(&jerr.pub);
113
+ jerr.pub.error_exit = my_error_exit;
114
+ jerr.pub.output_message = my_output_message;
115
+
116
+ if (setjmp(jerr.setjmp_buffer)) {
117
+ Error:
118
+ jpeg_destroy_decompress(&dinfo);
119
+ goto End;
120
+ }
121
+
122
+ jpeg_create_decompress(&dinfo);
123
+ jpeg_stdio_src(&dinfo, in_file);
124
+ if (metadata != NULL) {
125
+ jpeg_save_markers(&dinfo, JPEG_APP0 + 1, 0xffff); // EXIF and XMP
126
+ jpeg_save_markers(&dinfo, JPEG_APP0 + 2, 0xffff); // ICC profile
127
+ }
128
+ jpeg_read_header(&dinfo, TRUE);
129
+ if (metadata != NULL && !ExtractMetadataFromJPEG(&dinfo, metadata)) {
130
+ goto Error;
131
+ }
132
+
133
+ dinfo.out_color_space = JCS_RGB;
134
+ dinfo.do_fancy_upsampling = TRUE;
135
+
136
+ jpeg_start_decompress(&dinfo);
137
+
138
+ if (dinfo.output_components != 3) {
139
+ goto Error;
140
+ }
141
+
142
+ if (!UtilCheckImageSize(dinfo.output_width, dinfo.output_height,
143
+ dinfo.output_components, &stride, &rgb_size)) {
144
+ goto Error;
145
+ }
146
+ width = (int)dinfo.output_width;
147
+ height = (int)dinfo.output_height;
148
+
149
+ rgb = (uint8_t*)malloc(rgb_size);
150
+ if (rgb == NULL) {
151
+ goto Error;
152
+ }
153
+ buffer[0] = (JSAMPLE*)rgb;
154
+
155
+ while (dinfo.output_scanline < dinfo.output_height) {
156
+ if (jpeg_read_scanlines(&dinfo, buffer, 1) != 1) {
157
+ goto Error;
158
+ }
159
+ buffer[0] += stride;
160
+ }
161
+
162
+ jpeg_finish_decompress(&dinfo);
163
+ jpeg_destroy_decompress(&dinfo);
164
+
165
+ // WebP conversion.
166
+ pic->width = width;
167
+ pic->height = height;
168
+ ok = WebPPictureImportRGB(pic, rgb, stride);
169
+
170
+ End:
171
+ free(rgb);
172
+ return ok;
173
+ }
174
+
@@ -0,0 +1,20 @@
1
+ #include <stdio.h>
2
+
3
+ #include "webp/encode.h"
4
+ #include "webp/decode.h"
5
+ #include "./metadata.h"
6
+
7
+ #ifdef __cplusplus
8
+ extern "C" {
9
+ #endif
10
+
11
+ // Reads a JPEG from 'in_file', returning the decoded output in 'pic'.
12
+ // The output is RGB. When 'metadata' is not NULL, the ICC profile, EXIF and
13
+ // XMP data are copied into it.
14
+ // Returns true on success.
15
+ int UtilReadJPEG(FILE* in_file, struct WebPPicture* const pic,
16
+ Metadata* const metadata);
17
+
18
+ #ifdef __cplusplus
19
+ } // extern "C"
20
+ #endif
@@ -0,0 +1,138 @@
1
+ #include <stdlib.h>
2
+ #include <string.h>
3
+
4
+ #include "./metadata.h"
5
+
6
+ #if defined(__cplusplus) || defined(c_plusplus)
7
+ extern "C" {
8
+ #endif
9
+
10
+ // Based on WriteWebPWithMetadata in libwebp's examples/cwebp.c.
11
+
12
+ #define TAG_SIZE 4
13
+ #define CHUNK_HEADER_SIZE 8
14
+ #define RIFF_HEADER_SIZE 12
15
+ #define VP8X_CHUNK_SIZE 18 // chunk header and 10 bytes of payload
16
+ #define MAX_RIFF_SIZE 0xfffffff6ULL // the RIFF size is a 32-bit even number
17
+
18
+ // VP8X flags
19
+ #define FLAG_ICCP 0x20
20
+ #define FLAG_ALPHA 0x10
21
+ #define FLAG_EXIF 0x08
22
+ #define FLAG_XMP 0x04
23
+
24
+ void MetadataInit(Metadata* const metadata) {
25
+ memset(metadata, 0, sizeof(*metadata));
26
+ }
27
+
28
+ static void PayloadFree(MetadataPayload* const payload) {
29
+ free(payload->bytes);
30
+ payload->bytes = NULL;
31
+ payload->size = 0;
32
+ }
33
+
34
+ void MetadataFree(Metadata* const metadata) {
35
+ PayloadFree(&metadata->exif);
36
+ PayloadFree(&metadata->iccp);
37
+ PayloadFree(&metadata->xmp);
38
+ }
39
+
40
+ int MetadataCopy(const void* const data, size_t size,
41
+ MetadataPayload* const payload) {
42
+ if (payload->bytes != NULL || data == NULL || size == 0) return 1;
43
+ payload->bytes = (uint8_t*)malloc(size);
44
+ if (payload->bytes == NULL) return 0;
45
+ memcpy(payload->bytes, data, size);
46
+ payload->size = size;
47
+ return 1;
48
+ }
49
+
50
+ static int WriteLE(FILE* const out, uint32_t value, int bytes) {
51
+ uint8_t buffer[4];
52
+ int i;
53
+ for (i = 0; i < bytes; ++i) {
54
+ buffer[i] = (uint8_t)((value >> (8 * i)) & 0xff);
55
+ }
56
+ return fwrite(buffer, (size_t)bytes, 1, out) == 1;
57
+ }
58
+
59
+ // Chunk payloads have an even size; odd ones get one zero byte of padding.
60
+ static uint64_t ChunkSize(const MetadataPayload* const payload) {
61
+ return CHUNK_HEADER_SIZE + (uint64_t)payload->size + (payload->size & 1);
62
+ }
63
+
64
+ static int WriteChunk(FILE* const out, const char* const tag,
65
+ const MetadataPayload* const payload) {
66
+ const uint8_t zero = 0;
67
+ int ok = (fwrite(tag, TAG_SIZE, 1, out) == 1);
68
+ ok = ok && WriteLE(out, (uint32_t)payload->size, 4);
69
+ ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1);
70
+ if (payload->size & 1) ok = ok && (fwrite(&zero, 1, 1, out) == 1);
71
+ return ok;
72
+ }
73
+
74
+ int MetadataWriteWebP(FILE* const out, const uint8_t* webp, size_t webp_size,
75
+ int width, int height, const Metadata* const metadata,
76
+ int keep) {
77
+ const int write_exif = (keep & METADATA_EXIF) && metadata->exif.size > 0;
78
+ const int write_iccp = (keep & METADATA_ICC) && metadata->iccp.size > 0;
79
+ const int write_xmp = (keep & METADATA_XMP) && metadata->xmp.size > 0;
80
+ uint32_t flags = 0;
81
+ uint64_t metadata_size = 0;
82
+ uint64_t riff_size;
83
+ int has_vp8x;
84
+ int ok;
85
+
86
+ if (webp_size < RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE) return 0;
87
+ if (!write_exif && !write_iccp && !write_xmp) {
88
+ return fwrite(webp, webp_size, 1, out) == 1;
89
+ }
90
+
91
+ if (write_exif) { flags |= FLAG_EXIF; metadata_size += ChunkSize(&metadata->exif); }
92
+ if (write_iccp) { flags |= FLAG_ICCP; metadata_size += ChunkSize(&metadata->iccp); }
93
+ if (write_xmp) { flags |= FLAG_XMP; metadata_size += ChunkSize(&metadata->xmp); }
94
+
95
+ has_vp8x = !memcmp(webp + RIFF_HEADER_SIZE, "VP8X", TAG_SIZE);
96
+ riff_size = (uint64_t)webp_size - CHUNK_HEADER_SIZE +
97
+ (has_vp8x ? 0 : VP8X_CHUNK_SIZE) + metadata_size;
98
+ if (riff_size > MAX_RIFF_SIZE) return 0;
99
+
100
+ // "RIFF", the new size, then "WEBP"
101
+ ok = (fwrite(webp, TAG_SIZE, 1, out) == 1);
102
+ ok = ok && WriteLE(out, (uint32_t)riff_size, 4);
103
+ ok = ok && (fwrite(webp + CHUNK_HEADER_SIZE, TAG_SIZE, 1, out) == 1);
104
+ webp += RIFF_HEADER_SIZE;
105
+ webp_size -= RIFF_HEADER_SIZE;
106
+
107
+ if (has_vp8x) {
108
+ // lossy with alpha: the encoder already wrote a VP8X chunk, add our flags
109
+ uint8_t vp8x[VP8X_CHUNK_SIZE];
110
+ memcpy(vp8x, webp, VP8X_CHUNK_SIZE);
111
+ vp8x[CHUNK_HEADER_SIZE] |= (uint8_t)(flags & 0xff);
112
+ ok = ok && (fwrite(vp8x, VP8X_CHUNK_SIZE, 1, out) == 1);
113
+ webp += VP8X_CHUNK_SIZE;
114
+ webp_size -= VP8X_CHUNK_SIZE;
115
+ } else {
116
+ // lossless images keep their alpha bit in the VP8L header
117
+ if (!memcmp(webp, "VP8L", TAG_SIZE) && webp_size > CHUNK_HEADER_SIZE + 4 &&
118
+ (webp[CHUNK_HEADER_SIZE + 4] & (1 << 4))) {
119
+ flags |= FLAG_ALPHA;
120
+ }
121
+ ok = ok && (fwrite("VP8X", TAG_SIZE, 1, out) == 1);
122
+ ok = ok && WriteLE(out, VP8X_CHUNK_SIZE - CHUNK_HEADER_SIZE, 4);
123
+ ok = ok && WriteLE(out, flags, 4);
124
+ ok = ok && WriteLE(out, (uint32_t)(width - 1), 3);
125
+ ok = ok && WriteLE(out, (uint32_t)(height - 1), 3);
126
+ }
127
+
128
+ // the chunk order is fixed: ICCP, image data, EXIF, XMP
129
+ if (write_iccp) ok = ok && WriteChunk(out, "ICCP", &metadata->iccp);
130
+ ok = ok && (fwrite(webp, webp_size, 1, out) == 1);
131
+ if (write_exif) ok = ok && WriteChunk(out, "EXIF", &metadata->exif);
132
+ if (write_xmp) ok = ok && WriteChunk(out, "XMP ", &metadata->xmp);
133
+ return ok;
134
+ }
135
+
136
+ #if defined(__cplusplus) || defined(c_plusplus)
137
+ } // extern "C"
138
+ #endif
@@ -0,0 +1,49 @@
1
+ #ifndef _WEBP_FFI_METADATA_H_
2
+ #define _WEBP_FFI_METADATA_H_
3
+
4
+ #include <stddef.h>
5
+ #include <stdint.h>
6
+ #include <stdio.h>
7
+
8
+ #if defined(__cplusplus) || defined(c_plusplus)
9
+ extern "C" {
10
+ #endif
11
+
12
+ // Which metadata to copy from the input picture into the WebP file.
13
+ #define METADATA_EXIF 1
14
+ #define METADATA_ICC 2
15
+ #define METADATA_XMP 4
16
+ #define METADATA_ALL (METADATA_EXIF | METADATA_ICC | METADATA_XMP)
17
+
18
+ typedef struct {
19
+ uint8_t* bytes;
20
+ size_t size;
21
+ } MetadataPayload;
22
+
23
+ typedef struct {
24
+ MetadataPayload exif;
25
+ MetadataPayload iccp;
26
+ MetadataPayload xmp;
27
+ } Metadata;
28
+
29
+ void MetadataInit(Metadata* const metadata);
30
+ void MetadataFree(Metadata* const metadata);
31
+
32
+ // Copies 'size' bytes into an empty 'payload'. Returns 1 on success, or when
33
+ // the payload already holds data (the first copy wins). Returns 0 when out of
34
+ // memory.
35
+ int MetadataCopy(const void* const data, size_t size, MetadataPayload* const payload);
36
+
37
+ // Writes a complete WebP file to 'out': the encoder output 'webp' plus the
38
+ // metadata selected by 'keep' (METADATA_* flags), using the extended format
39
+ // (VP8X) when any metadata is written. 'width' and 'height' are the size of the
40
+ // encoded picture. Returns 1 on success.
41
+ int MetadataWriteWebP(FILE* const out, const uint8_t* webp, size_t webp_size,
42
+ int width, int height, const Metadata* const metadata,
43
+ int keep);
44
+
45
+ #if defined(__cplusplus) || defined(c_plusplus)
46
+ } // extern "C"
47
+ #endif
48
+
49
+ #endif /* _WEBP_FFI_METADATA_H_ */