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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -0
- data/LICENSE.txt +1 -1
- data/README.md +137 -25
- data/ext/webp_ffi/Rakefile +2 -18
- data/ext/webp_ffi/build_config.rb +119 -0
- data/ext/webp_ffi/jpegdec.c +104 -10
- data/ext/webp_ffi/jpegdec.h +5 -2
- data/ext/webp_ffi/metadata.c +138 -0
- data/ext/webp_ffi/metadata.h +49 -0
- data/ext/webp_ffi/pngdec.c +197 -20
- data/ext/webp_ffi/pngdec.h +7 -2
- data/ext/webp_ffi/tiffdec.c +184 -20
- data/ext/webp_ffi/tiffdec.h +12 -4
- data/ext/webp_ffi/util.c +82 -35
- data/ext/webp_ffi/util.h +28 -3
- data/ext/webp_ffi/webp_ffi.c +484 -165
- data/ext/webp_ffi/webp_ffi.h +47 -5
- data/lib/webp/c.rb +41 -7
- data/lib/webp/error.rb +65 -15
- data/lib/webp/info.rb +13 -0
- data/lib/webp/options.rb +129 -22
- data/lib/webp/version.rb +1 -1
- data/lib/webp/webp.rb +150 -32
- data/lib/webp_ffi.rb +1 -1
- data/sig/webp.rbs +74 -0
- metadata +31 -48
- data/.github/workflows/build.yml +0 -45
- data/.gitignore +0 -23
- data/.tool-versions +0 -1
- data/Gemfile +0 -4
- data/Rakefile +0 -38
- data/lib/webp/libc.rb +0 -20
- data/spec/build.sh +0 -6
- data/spec/factories/1.png +0 -0
- data/spec/factories/1.webp +0 -0
- data/spec/factories/2.png +0 -0
- data/spec/factories/2.webp +0 -0
- data/spec/factories/3.jpg +0 -0
- data/spec/factories/3.webp +0 -0
- data/spec/factories/4.jpg +0 -0
- data/spec/factories/4.webp +0 -0
- data/spec/factories/5.tif +0 -0
- data/spec/factories/5.webp +0 -0
- data/spec/spec_helper.rb +0 -9
- data/spec/webp_ffi_spec.rb +0 -191
- data/webp-ffi.gemspec +0 -28
|
@@ -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_ */
|
data/ext/webp_ffi/pngdec.c
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "./pngdec.h"
|
|
2
|
+
#include "./util.h"
|
|
2
3
|
|
|
3
4
|
#include <assert.h>
|
|
4
5
|
#include <stdio.h>
|
|
@@ -12,10 +13,146 @@ static void PNGAPI error_function(png_structp png, png_const_charp dummy) {
|
|
|
12
13
|
longjmp(png_jmpbuf(png), 1);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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;
|
|
19
156
|
int color_type, bit_depth, interlaced;
|
|
20
157
|
int has_alpha;
|
|
21
158
|
int num_passes;
|
|
@@ -23,17 +160,19 @@ int UtilReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
|
|
|
23
160
|
int ok = 0;
|
|
24
161
|
png_uint_32 width, height, y;
|
|
25
162
|
int stride;
|
|
26
|
-
|
|
163
|
+
size_t rgb_size;
|
|
164
|
+
uint8_t* volatile rgb = NULL;
|
|
27
165
|
|
|
28
|
-
png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
|
166
|
+
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
29
167
|
if (png == NULL) {
|
|
30
168
|
goto End;
|
|
31
169
|
}
|
|
32
170
|
|
|
33
|
-
png_set_error_fn(png,
|
|
171
|
+
png_set_error_fn(png, NULL, error_function, warning_function);
|
|
34
172
|
if (setjmp(png_jmpbuf(png))) {
|
|
35
173
|
Error:
|
|
36
|
-
png_destroy_read_struct(&png, &info,
|
|
174
|
+
png_destroy_read_struct((png_structpp)&png, (png_infopp)&info,
|
|
175
|
+
(png_infopp)&end_info);
|
|
37
176
|
goto End;
|
|
38
177
|
}
|
|
39
178
|
|
|
@@ -72,28 +211,31 @@ int UtilReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
|
|
|
72
211
|
|
|
73
212
|
num_passes = png_set_interlace_handling(png);
|
|
74
213
|
png_read_update_info(png, info);
|
|
75
|
-
|
|
76
|
-
|
|
214
|
+
if (!UtilCheckImageSize(width, height, has_alpha ? 4 : 3,
|
|
215
|
+
&stride, &rgb_size)) {
|
|
216
|
+
goto Error;
|
|
217
|
+
}
|
|
218
|
+
rgb = (uint8_t*)malloc(rgb_size);
|
|
77
219
|
if (rgb == NULL) goto Error;
|
|
78
220
|
for (p = 0; p < num_passes; ++p) {
|
|
79
221
|
for (y = 0; y < height; ++y) {
|
|
80
|
-
png_bytep row = rgb + y * stride;
|
|
222
|
+
png_bytep row = rgb + (size_t)y * stride;
|
|
81
223
|
png_read_rows(png, &row, NULL, 1);
|
|
82
224
|
}
|
|
83
225
|
}
|
|
84
226
|
png_read_end(png, end_info);
|
|
85
|
-
|
|
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);
|
|
86
232
|
|
|
87
|
-
pic->width = width;
|
|
88
|
-
pic->height = height;
|
|
233
|
+
pic->width = (int)width;
|
|
234
|
+
pic->height = (int)height;
|
|
89
235
|
pic->use_argb = 1;
|
|
90
236
|
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride)
|
|
91
237
|
: WebPPictureImportRGB(pic, rgb, stride);
|
|
92
238
|
|
|
93
|
-
if (!ok) {
|
|
94
|
-
goto Error;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
239
|
End:
|
|
98
240
|
free(rgb);
|
|
99
241
|
return ok;
|
|
@@ -114,7 +256,7 @@ int UtilWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
|
|
114
256
|
png_uint_32 y;
|
|
115
257
|
|
|
116
258
|
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
117
|
-
NULL, error_function,
|
|
259
|
+
NULL, error_function, warning_function);
|
|
118
260
|
if (png == NULL) {
|
|
119
261
|
return 0;
|
|
120
262
|
}
|
|
@@ -209,4 +351,39 @@ int UtilWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
|
209
351
|
if (width & 1) fputc(0, fout); // padding byte
|
|
210
352
|
}
|
|
211
353
|
return ok;
|
|
212
|
-
}
|
|
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
|
+
}
|
data/ext/webp_ffi/pngdec.h
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
#include "webp/encode.h"
|
|
5
5
|
#include "webp/decode.h"
|
|
6
|
+
#include "./metadata.h"
|
|
6
7
|
|
|
7
8
|
#ifdef __cplusplus
|
|
8
9
|
extern "C" {
|
|
@@ -10,14 +11,18 @@ extern "C" {
|
|
|
10
11
|
|
|
11
12
|
// Reads a PNG from 'in_file', returning the decoded output in 'pic'.
|
|
12
13
|
// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
|
|
13
|
-
// otherwise it will be RGB.
|
|
14
|
+
// otherwise it will be RGB. When 'metadata' is not NULL, the ICC profile, EXIF
|
|
15
|
+
// and XMP data are copied into it.
|
|
14
16
|
// Returns true on success.
|
|
15
|
-
int UtilReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha
|
|
17
|
+
int UtilReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha,
|
|
18
|
+
Metadata* const metadata);
|
|
16
19
|
|
|
17
20
|
int UtilWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
|
|
18
21
|
int UtilWritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha);
|
|
19
22
|
int UtilWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer);
|
|
20
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);
|
|
21
26
|
|
|
22
27
|
#ifdef __cplusplus
|
|
23
28
|
} // extern "C"
|