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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +135 -0
- data/LICENSE.txt +22 -0
- data/README.md +308 -0
- data/ext/webp_ffi/Rakefile +3 -0
- data/ext/webp_ffi/build_config.rb +119 -0
- data/ext/webp_ffi/jpegdec.c +174 -0
- data/ext/webp_ffi/jpegdec.h +20 -0
- data/ext/webp_ffi/metadata.c +138 -0
- data/ext/webp_ffi/metadata.h +49 -0
- data/ext/webp_ffi/pngdec.c +389 -0
- data/ext/webp_ffi/pngdec.h +29 -0
- data/ext/webp_ffi/tiffdec.c +361 -0
- data/ext/webp_ffi/tiffdec.h +31 -0
- data/ext/webp_ffi/util.c +216 -0
- data/ext/webp_ffi/util.h +63 -0
- data/ext/webp_ffi/webp_ffi.c +618 -0
- data/ext/webp_ffi/webp_ffi.h +110 -0
- data/ext/webp_ffi/x86_64-linux/libwebp_ffi.so +0 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libjpeg-turbo-LICENSE.md +147 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libjpeg-turbo-README.ijg +260 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libpng-LICENSE +134 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libtiff-LICENSE.md +49 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libwebp-COPYING +30 -0
- data/ext/webp_ffi/x86_64-linux/licenses/libwebp-PATENTS +23 -0
- data/ext/webp_ffi/x86_64-linux/licenses/zlib-LICENSE +22 -0
- data/ext/webp_ffi/x86_64-linux/versions.txt +5 -0
- data/lib/webp/c.rb +101 -0
- data/lib/webp/error.rb +71 -0
- data/lib/webp/info.rb +13 -0
- data/lib/webp/options.rb +171 -0
- data/lib/webp/version.rb +3 -0
- data/lib/webp/webp.rb +179 -0
- data/lib/webp-ffi.rb +1 -0
- data/lib/webp_ffi.rb +16 -0
- data/sig/webp.rbs +74 -0
- metadata +145 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
#include "./tiffdec.h"
|
|
2
|
+
#include "./util.h"
|
|
3
|
+
|
|
4
|
+
#include <stdarg.h>
|
|
5
|
+
#include <sys/types.h>
|
|
6
|
+
#include <tiffio.h>
|
|
7
|
+
#include <tiffvers.h>
|
|
8
|
+
|
|
9
|
+
// libtiff 4.5.0 added error and warning handlers per opened file.
|
|
10
|
+
#if defined(TIFFLIB_AT_LEAST)
|
|
11
|
+
#if TIFFLIB_AT_LEAST(4, 5, 0)
|
|
12
|
+
#define UTIL_TIFF_HAS_OPEN_OPTIONS
|
|
13
|
+
#endif
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#ifdef UTIL_TIFF_HAS_OPEN_OPTIONS
|
|
17
|
+
// libtiff prints errors and warnings to stderr by default. A library should
|
|
18
|
+
// stay quiet. Returning 1 also stops libtiff from calling its global handlers.
|
|
19
|
+
static int SilentTIFFHandler(TIFF* tif, void* user_data, const char* module,
|
|
20
|
+
const char* fmt, va_list ap) {
|
|
21
|
+
(void)tif;
|
|
22
|
+
(void)user_data;
|
|
23
|
+
(void)module;
|
|
24
|
+
(void)fmt;
|
|
25
|
+
(void)ap;
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
// libtiff reads through these callbacks, so the same code works for files and
|
|
31
|
+
// for in-memory data opened with fmemopen.
|
|
32
|
+
static tmsize_t UtilTIFFRead(thandle_t handle, void* buffer, tmsize_t size) {
|
|
33
|
+
if (size <= 0) return 0;
|
|
34
|
+
return (tmsize_t)fread(buffer, 1, (size_t)size, (FILE*)handle);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static tmsize_t UtilTIFFWrite(thandle_t handle, void* buffer, tmsize_t size) {
|
|
38
|
+
(void)handle;
|
|
39
|
+
(void)buffer;
|
|
40
|
+
(void)size;
|
|
41
|
+
return 0; // read only
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static toff_t UtilTIFFSeek(thandle_t handle, toff_t offset, int whence) {
|
|
45
|
+
FILE* const file = (FILE*)handle;
|
|
46
|
+
if (fseeko(file, (off_t)offset, whence) != 0) return (toff_t)-1;
|
|
47
|
+
return (toff_t)ftello(file);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static int UtilTIFFClose(thandle_t handle) {
|
|
51
|
+
(void)handle;
|
|
52
|
+
return 0; // the caller closes the file
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static toff_t UtilTIFFSize(thandle_t handle) {
|
|
56
|
+
FILE* const file = (FILE*)handle;
|
|
57
|
+
const off_t position = ftello(file);
|
|
58
|
+
off_t size;
|
|
59
|
+
if (position < 0 || fseeko(file, 0, SEEK_END) != 0) return 0;
|
|
60
|
+
size = ftello(file);
|
|
61
|
+
fseeko(file, position, SEEK_SET);
|
|
62
|
+
return size < 0 ? 0 : (toff_t)size;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static int UtilTIFFMap(thandle_t handle, void** base, toff_t* size) {
|
|
66
|
+
(void)handle;
|
|
67
|
+
(void)base;
|
|
68
|
+
(void)size;
|
|
69
|
+
return 0; // no memory mapping
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static void UtilTIFFUnmap(thandle_t handle, void* base, toff_t size) {
|
|
73
|
+
(void)handle;
|
|
74
|
+
(void)base;
|
|
75
|
+
(void)size;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// "m" turns off memory mapping, which a FILE* can't provide
|
|
79
|
+
static TIFF* OpenTIFF(FILE* const file) {
|
|
80
|
+
#ifdef UTIL_TIFF_HAS_OPEN_OPTIONS
|
|
81
|
+
TIFF* tif;
|
|
82
|
+
TIFFOpenOptions* const opts = TIFFOpenOptionsAlloc();
|
|
83
|
+
if (opts == NULL) return NULL;
|
|
84
|
+
TIFFOpenOptionsSetErrorHandlerExtR(opts, SilentTIFFHandler, NULL);
|
|
85
|
+
TIFFOpenOptionsSetWarningHandlerExtR(opts, SilentTIFFHandler, NULL);
|
|
86
|
+
tif = TIFFClientOpenExt("webp-ffi", "rm", (thandle_t)file,
|
|
87
|
+
UtilTIFFRead, UtilTIFFWrite, UtilTIFFSeek, UtilTIFFClose,
|
|
88
|
+
UtilTIFFSize, UtilTIFFMap, UtilTIFFUnmap, opts);
|
|
89
|
+
TIFFOpenOptionsFree(opts);
|
|
90
|
+
return tif;
|
|
91
|
+
#else
|
|
92
|
+
// Older libtiff only has process-wide handlers. Changing those would affect
|
|
93
|
+
// other code in the same process, so its messages still reach stderr.
|
|
94
|
+
return TIFFClientOpen("webp-ffi", "rm", (thandle_t)file,
|
|
95
|
+
UtilTIFFRead, UtilTIFFWrite, UtilTIFFSeek, UtilTIFFClose,
|
|
96
|
+
UtilTIFFSize, UtilTIFFMap, UtilTIFFUnmap);
|
|
97
|
+
#endif
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const char* UtilTIFFVersion(void) {
|
|
101
|
+
return TIFFGetVersion();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// libtiff's RGBA interface always returns premultiplied (associated) alpha,
|
|
105
|
+
// whatever kind of alpha the file stores, so undo the multiplication.
|
|
106
|
+
// Based on MultARGBRow in libwebp's imageio/tiffdec.c (taken from libwebp's
|
|
107
|
+
// dsp/alpha_processing).
|
|
108
|
+
#define MFIX 24 // 24bit fixed-point arithmetic
|
|
109
|
+
#define HALF ((1u << MFIX) >> 1)
|
|
110
|
+
|
|
111
|
+
static uint32_t Unmult(uint8_t x, uint32_t mult) {
|
|
112
|
+
const uint32_t v = (x * mult + HALF) >> MFIX;
|
|
113
|
+
return (v > 255u) ? 255u : v;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static uint32_t GetScale(uint32_t a) {
|
|
117
|
+
return (255u << MFIX) / a;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#undef MFIX
|
|
121
|
+
#undef HALF
|
|
122
|
+
|
|
123
|
+
static void UnmultiplyRow(uint8_t* ptr, uint32_t width) {
|
|
124
|
+
uint32_t x;
|
|
125
|
+
for (x = 0; x < width; ++x, ptr += 4) {
|
|
126
|
+
const uint32_t alpha = ptr[3];
|
|
127
|
+
if (alpha < 255) {
|
|
128
|
+
if (alpha == 0) {
|
|
129
|
+
ptr[0] = ptr[1] = ptr[2] = 0;
|
|
130
|
+
} else {
|
|
131
|
+
const uint32_t scale = GetScale(alpha);
|
|
132
|
+
ptr[0] = (uint8_t)Unmult(ptr[0], scale);
|
|
133
|
+
ptr[1] = (uint8_t)Unmult(ptr[1], scale);
|
|
134
|
+
ptr[2] = (uint8_t)Unmult(ptr[2], scale);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// True when the first extra sample is alpha, associated or not.
|
|
141
|
+
static int HasAlphaSample(TIFF* const tif) {
|
|
142
|
+
uint16_t count = 0;
|
|
143
|
+
uint16_t* samples = NULL;
|
|
144
|
+
return TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &count, &samples) && count > 0 &&
|
|
145
|
+
samples != NULL && (samples[0] == EXTRASAMPLE_ASSOCALPHA ||
|
|
146
|
+
samples[0] == EXTRASAMPLE_UNASSALPHA);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
|
|
150
|
+
uint32_t length = 0;
|
|
151
|
+
void* data = NULL;
|
|
152
|
+
if (TIFFGetField(tif, TIFFTAG_ICCPROFILE, &length, &data) &&
|
|
153
|
+
!MetadataCopy(data, length, &metadata->iccp)) {
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
length = 0;
|
|
157
|
+
data = NULL;
|
|
158
|
+
if (TIFFGetField(tif, TIFFTAG_XMLPACKET, &length, &data) &&
|
|
159
|
+
!MetadataCopy(data, length, &metadata->xmp)) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
return 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
int UtilReadTIFF(FILE* const in_file,
|
|
166
|
+
WebPPicture* const pic, int keep_alpha,
|
|
167
|
+
Metadata* const metadata) {
|
|
168
|
+
TIFF* const tif = OpenTIFF(in_file);
|
|
169
|
+
uint32_t width, height;
|
|
170
|
+
uint32_t* raster;
|
|
171
|
+
int stride;
|
|
172
|
+
size_t raster_size;
|
|
173
|
+
int ok = 0;
|
|
174
|
+
|
|
175
|
+
if (tif == NULL) {
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Multi-page TIFF files are not supported: only the first page is used.
|
|
180
|
+
if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) ||
|
|
181
|
+
!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height) ||
|
|
182
|
+
!UtilCheckImageSize(width, height, sizeof(*raster),
|
|
183
|
+
&stride, &raster_size) ||
|
|
184
|
+
(metadata != NULL && !ExtractMetadataFromTIFF(tif, metadata))) {
|
|
185
|
+
TIFFClose(tif);
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
raster = (uint32_t*)_TIFFmalloc(raster_size);
|
|
190
|
+
if (raster != NULL) {
|
|
191
|
+
if (TIFFReadRGBAImageOriented(tif, width, height, raster,
|
|
192
|
+
ORIENTATION_TOPLEFT, 1)) {
|
|
193
|
+
pic->width = (int)width;
|
|
194
|
+
pic->height = (int)height;
|
|
195
|
+
// TIFF data is ABGR
|
|
196
|
+
#ifdef __BIG_ENDIAN__
|
|
197
|
+
TIFFSwabArrayOfLong(raster, raster_size / sizeof(*raster));
|
|
198
|
+
#endif
|
|
199
|
+
if (keep_alpha && HasAlphaSample(tif)) {
|
|
200
|
+
uint32_t y;
|
|
201
|
+
for (y = 0; y < height; ++y) {
|
|
202
|
+
UnmultiplyRow((uint8_t*)raster + (size_t)y * stride, width);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
ok = keep_alpha
|
|
206
|
+
? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
|
|
207
|
+
: WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
|
|
208
|
+
}
|
|
209
|
+
_TIFFfree(raster);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (ok && keep_alpha == 2) {
|
|
213
|
+
WebPCleanupTransparentArea(pic);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
TIFFClose(tif);
|
|
217
|
+
return ok;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
static void PutLE16(uint8_t* const dst, uint32_t value) {
|
|
223
|
+
dst[0] = (value >> 0) & 0xff;
|
|
224
|
+
dst[1] = (value >> 8) & 0xff;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static void PutLE32(uint8_t* const dst, uint32_t value) {
|
|
228
|
+
PutLE16(dst + 0, (value >> 0) & 0xffff);
|
|
229
|
+
PutLE16(dst + 2, (value >> 16) & 0xffff);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
#define NUM_IFD_ENTRIES 15
|
|
234
|
+
#define EXTRA_DATA_SIZE 16
|
|
235
|
+
// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
|
|
236
|
+
#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
|
|
237
|
+
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
|
|
238
|
+
|
|
239
|
+
int UtilWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
240
|
+
const int has_alpha = (buffer->colorspace != MODE_RGB);
|
|
241
|
+
const uint32_t width = buffer->width;
|
|
242
|
+
const uint32_t height = buffer->height;
|
|
243
|
+
const uint8_t* const rgba = buffer->u.RGBA.rgba;
|
|
244
|
+
const int stride = buffer->u.RGBA.stride;
|
|
245
|
+
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
|
|
246
|
+
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
|
|
247
|
+
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
|
|
248
|
+
: NUM_IFD_ENTRIES - 1;
|
|
249
|
+
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
|
|
250
|
+
0x49, 0x49, 0x2a, 0x00, // little endian signature
|
|
251
|
+
8, 0, 0, 0, // offset to the unique IFD that follows
|
|
252
|
+
// IFD (offset = 8). Entries must be written in increasing tag order.
|
|
253
|
+
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
|
|
254
|
+
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
|
|
255
|
+
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
|
|
256
|
+
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
|
|
257
|
+
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
|
|
258
|
+
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
|
|
259
|
+
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
|
|
260
|
+
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
|
|
261
|
+
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
|
|
262
|
+
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
|
|
263
|
+
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
|
|
264
|
+
bytes_per_px, 0, 0, 0,
|
|
265
|
+
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
|
|
266
|
+
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
|
|
267
|
+
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
|
|
268
|
+
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
|
269
|
+
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
|
|
270
|
+
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
|
|
271
|
+
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
|
|
272
|
+
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
|
|
273
|
+
0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
|
|
274
|
+
0, 0, 0, 0, // 190: IFD terminator
|
|
275
|
+
// EXTRA_DATA_OFFSET:
|
|
276
|
+
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
|
|
277
|
+
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
|
|
278
|
+
};
|
|
279
|
+
uint32_t y;
|
|
280
|
+
|
|
281
|
+
// Fill placeholders in IFD:
|
|
282
|
+
PutLE32(tiff_header + 10 + 8, width);
|
|
283
|
+
PutLE32(tiff_header + 22 + 8, height);
|
|
284
|
+
PutLE32(tiff_header + 106 + 8, height);
|
|
285
|
+
PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
|
|
286
|
+
if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator
|
|
287
|
+
|
|
288
|
+
// write header
|
|
289
|
+
if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
292
|
+
// write pixel values
|
|
293
|
+
for (y = 0; y < height; ++y) {
|
|
294
|
+
if (fwrite(rgba + y * stride, bytes_per_px, width, fout) != width) {
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return 1;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
#undef TIFF_HEADER_SIZE
|
|
303
|
+
#undef EXTRA_DATA_OFFSET
|
|
304
|
+
#undef EXTRA_DATA_SIZE
|
|
305
|
+
#undef NUM_IFD_ENTRIES
|
|
306
|
+
|
|
307
|
+
#define BMP_HEADER_SIZE 54
|
|
308
|
+
int UtilWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
|
|
309
|
+
const int has_alpha = (buffer->colorspace != MODE_BGR);
|
|
310
|
+
const uint32_t width = buffer->width;
|
|
311
|
+
const uint32_t height = buffer->height;
|
|
312
|
+
const uint8_t* const rgba = buffer->u.RGBA.rgba;
|
|
313
|
+
const int stride = buffer->u.RGBA.stride;
|
|
314
|
+
const uint32_t bytes_per_px = has_alpha ? 4 : 3;
|
|
315
|
+
uint32_t y;
|
|
316
|
+
const uint32_t line_size = bytes_per_px * width;
|
|
317
|
+
const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
|
|
318
|
+
const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE;
|
|
319
|
+
uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 };
|
|
320
|
+
|
|
321
|
+
// bitmap file header
|
|
322
|
+
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
|
|
323
|
+
PutLE32(bmp_header + 2, total_size); // size including header
|
|
324
|
+
PutLE32(bmp_header + 6, 0); // reserved
|
|
325
|
+
PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array
|
|
326
|
+
// bitmap info header
|
|
327
|
+
PutLE32(bmp_header + 14, 40); // DIB header size
|
|
328
|
+
PutLE32(bmp_header + 18, width); // dimensions
|
|
329
|
+
PutLE32(bmp_header + 22, -(int)height); // vertical flip!
|
|
330
|
+
PutLE16(bmp_header + 26, 1); // number of planes
|
|
331
|
+
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
|
|
332
|
+
PutLE32(bmp_header + 30, 0); // no compression (BI_RGB)
|
|
333
|
+
PutLE32(bmp_header + 34, 0); // image size (dummy)
|
|
334
|
+
PutLE32(bmp_header + 38, 2400); // x pixels/meter
|
|
335
|
+
PutLE32(bmp_header + 42, 2400); // y pixels/meter
|
|
336
|
+
PutLE32(bmp_header + 46, 0); // number of palette colors
|
|
337
|
+
PutLE32(bmp_header + 50, 0); // important color count
|
|
338
|
+
|
|
339
|
+
// TODO(skal): color profile
|
|
340
|
+
|
|
341
|
+
// write header
|
|
342
|
+
if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) {
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// write pixel array
|
|
347
|
+
for (y = 0; y < height; ++y) {
|
|
348
|
+
if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) {
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
351
|
+
// write padding zeroes
|
|
352
|
+
if (bmp_stride != line_size) {
|
|
353
|
+
const uint8_t zeroes[3] = { 0 };
|
|
354
|
+
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return 1;
|
|
360
|
+
}
|
|
361
|
+
#undef BMP_HEADER_SIZE
|
|
@@ -0,0 +1,31 @@
|
|
|
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 TIFF from 'in_file', returning the decoded output in 'pic'.
|
|
13
|
+
// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
|
|
14
|
+
// otherwise it will be RGB. 'in_file' must be seekable and stays open.
|
|
15
|
+
// When 'metadata' is not NULL, the ICC profile and XMP data are copied into it.
|
|
16
|
+
// Like libwebp's own reader, EXIF is not read from TIFF files.
|
|
17
|
+
// Returns true on success.
|
|
18
|
+
int UtilReadTIFF(FILE* const in_file,
|
|
19
|
+
struct WebPPicture* const pic, int keep_alpha,
|
|
20
|
+
Metadata* const metadata);
|
|
21
|
+
|
|
22
|
+
int UtilWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer);
|
|
23
|
+
int UtilWriteBMP(FILE* fout, const WebPDecBuffer* const buffer);
|
|
24
|
+
|
|
25
|
+
// Version string of the libtiff in use. Prebuilt gems keep the bundled
|
|
26
|
+
// libtiff symbols private, so the specs ask through this function.
|
|
27
|
+
const char* UtilTIFFVersion(void);
|
|
28
|
+
|
|
29
|
+
#ifdef __cplusplus
|
|
30
|
+
} // extern "C"
|
|
31
|
+
#endif
|
data/ext/webp_ffi/util.c
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#include <limits.h>
|
|
2
|
+
#include <sys/stat.h>
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#include "./util.h"
|
|
8
|
+
#include "./jpegdec.h"
|
|
9
|
+
#include "./pngdec.h"
|
|
10
|
+
#include "./tiffdec.h"
|
|
11
|
+
|
|
12
|
+
#include "webp/decode.h"
|
|
13
|
+
#include "webp/encode.h"
|
|
14
|
+
|
|
15
|
+
#if defined(__cplusplus) || defined(c_plusplus)
|
|
16
|
+
extern "C" {
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
// Same memory cap as the image readers in libwebp (imageio/imageio_util.c).
|
|
20
|
+
#if SIZE_MAX > (1ULL << 34)
|
|
21
|
+
#define UTIL_MAX_ALLOCABLE_MEMORY (1ULL << 34)
|
|
22
|
+
#else
|
|
23
|
+
#define UTIL_MAX_ALLOCABLE_MEMORY ((1ULL << 31) - (1 << 16))
|
|
24
|
+
#endif
|
|
25
|
+
#define UTIL_MAX_BYTES_PER_PIXEL 8
|
|
26
|
+
|
|
27
|
+
int UtilCheckImageSize(uint64_t width, uint64_t height, uint64_t bytes_per_px,
|
|
28
|
+
int* const stride, size_t* const size) {
|
|
29
|
+
uint64_t row_size, total_size;
|
|
30
|
+
|
|
31
|
+
if (width == 0 || height == 0) return 0;
|
|
32
|
+
if (bytes_per_px == 0 || bytes_per_px > UTIL_MAX_BYTES_PER_PIXEL) return 0;
|
|
33
|
+
// WebPPicture and the row strides passed to libwebp are plain ints.
|
|
34
|
+
if (width > INT_MAX || height > INT_MAX) return 0;
|
|
35
|
+
|
|
36
|
+
// Both products stay far below 2^64 because of the limits checked above.
|
|
37
|
+
row_size = width * bytes_per_px;
|
|
38
|
+
if (row_size > INT_MAX) return 0;
|
|
39
|
+
total_size = row_size * height;
|
|
40
|
+
if (total_size > UTIL_MAX_ALLOCABLE_MEMORY) return 0;
|
|
41
|
+
|
|
42
|
+
if (stride != NULL) *stride = (int)row_size;
|
|
43
|
+
if (size != NULL) *size = (size_t)total_size;
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static int UtilReadYUV(FILE* in_file, WebPPicture* const pic) {
|
|
48
|
+
const int use_argb = pic->use_argb;
|
|
49
|
+
const int uv_width = (pic->width + 1) / 2;
|
|
50
|
+
const int uv_height = (pic->height + 1) / 2;
|
|
51
|
+
int y;
|
|
52
|
+
int ok = 0;
|
|
53
|
+
|
|
54
|
+
pic->use_argb = 0;
|
|
55
|
+
if (!WebPPictureAlloc(pic)) return ok;
|
|
56
|
+
|
|
57
|
+
for (y = 0; y < pic->height; ++y) {
|
|
58
|
+
if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) {
|
|
59
|
+
goto End;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (y = 0; y < uv_height; ++y) {
|
|
63
|
+
if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
|
64
|
+
goto End;
|
|
65
|
+
}
|
|
66
|
+
for (y = 0; y < uv_height; ++y) {
|
|
67
|
+
if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
|
68
|
+
goto End;
|
|
69
|
+
}
|
|
70
|
+
ok = 1;
|
|
71
|
+
if (use_argb) ok = WebPPictureYUVAToARGB(pic);
|
|
72
|
+
|
|
73
|
+
End:
|
|
74
|
+
return ok;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static InputFileFormat GetImageType(FILE* in_file) {
|
|
78
|
+
InputFileFormat format = UNSUPPORTED;
|
|
79
|
+
unsigned int magic;
|
|
80
|
+
unsigned char buf[4];
|
|
81
|
+
|
|
82
|
+
if ((fread(&buf[0], 4, 1, in_file) != 1) ||
|
|
83
|
+
(fseek(in_file, 0, SEEK_SET) != 0)) {
|
|
84
|
+
return format;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// shift unsigned values: buf[0] << 24 as an int overflows for bytes >= 0x80
|
|
88
|
+
magic = ((unsigned int)buf[0] << 24) | ((unsigned int)buf[1] << 16) |
|
|
89
|
+
((unsigned int)buf[2] << 8) | buf[3];
|
|
90
|
+
if (magic == 0x89504E47U) {
|
|
91
|
+
format = iPNG_;
|
|
92
|
+
} else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
|
|
93
|
+
format = iJPEG_;
|
|
94
|
+
} else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
|
|
95
|
+
format = iTIFF_;
|
|
96
|
+
}
|
|
97
|
+
return format;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
int UtilReadPictureFile(FILE* const in_file, WebPPicture* const pic,
|
|
101
|
+
int keep_alpha, Metadata* const metadata) {
|
|
102
|
+
int ok = 0;
|
|
103
|
+
|
|
104
|
+
if (pic->width == 0 || pic->height == 0) {
|
|
105
|
+
// If no size specified, try to decode it as PNG/JPEG/TIFF (as appropriate).
|
|
106
|
+
const InputFileFormat format = GetImageType(in_file);
|
|
107
|
+
if (format == iPNG_) {
|
|
108
|
+
ok = UtilReadPNG(in_file, pic, keep_alpha, metadata);
|
|
109
|
+
} else if (format == iJPEG_) {
|
|
110
|
+
ok = UtilReadJPEG(in_file, pic, metadata);
|
|
111
|
+
} else if (format == iTIFF_) {
|
|
112
|
+
ok = UtilReadTIFF(in_file, pic, keep_alpha, metadata);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
// If image size is specified, infer it as YUV format.
|
|
116
|
+
ok = UtilReadYUV(in_file, pic);
|
|
117
|
+
}
|
|
118
|
+
return ok;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
int UtilFileExists(const char* const path) {
|
|
122
|
+
struct stat info;
|
|
123
|
+
return stat(path, &info) == 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
int UtilSaveOutput(const WebPDecBuffer* const buffer,
|
|
127
|
+
OutputFileFormat format, const char* const out_file) {
|
|
128
|
+
// a failed write removes the file, but only when this call created it
|
|
129
|
+
const int existed = UtilFileExists(out_file);
|
|
130
|
+
int ok;
|
|
131
|
+
FILE* const fout = fopen(out_file, "wb");
|
|
132
|
+
if (!fout) {
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
ok = UtilWriteOutput(buffer, format, fout);
|
|
136
|
+
// fclose flushes buffered data, so it can also fail (for example, disk full)
|
|
137
|
+
if (fclose(fout) != 0) {
|
|
138
|
+
ok = 0;
|
|
139
|
+
}
|
|
140
|
+
if (!ok && !existed) remove(out_file);
|
|
141
|
+
return ok;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
int UtilWriteOutput(const WebPDecBuffer* const buffer,
|
|
145
|
+
OutputFileFormat format, FILE* const fout) {
|
|
146
|
+
int ok = 1;
|
|
147
|
+
|
|
148
|
+
if (format == oPNG) {
|
|
149
|
+
ok &= UtilWritePNG(fout, buffer);
|
|
150
|
+
} else if (format == oPAM) {
|
|
151
|
+
ok &= UtilWritePPM(fout, buffer, 1);
|
|
152
|
+
} else if (format == oPPM) {
|
|
153
|
+
ok &= UtilWritePPM(fout, buffer, 0);
|
|
154
|
+
} else if (format == oBMP) {
|
|
155
|
+
ok &= UtilWriteBMP(fout, buffer);
|
|
156
|
+
} else if (format == oTIFF_) {
|
|
157
|
+
ok &= UtilWriteTIFF(fout, buffer);
|
|
158
|
+
} else if (format == oPGM) {
|
|
159
|
+
ok &= UtilWritePGM(fout, buffer);
|
|
160
|
+
} else if (format == oYUV) {
|
|
161
|
+
ok &= UtilWriteYUV(fout, buffer);
|
|
162
|
+
} else if (format == ALPHA_PLANE_ONLY) {
|
|
163
|
+
ok &= UtilWriteAlphaPlane(fout, buffer);
|
|
164
|
+
} else {
|
|
165
|
+
ok = 0;
|
|
166
|
+
}
|
|
167
|
+
return ok;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// -----------------------------------------------------------------------------
|
|
171
|
+
// File I/O
|
|
172
|
+
|
|
173
|
+
int UtilReadFile(const char* const file_name,
|
|
174
|
+
const uint8_t** data, size_t* data_size) {
|
|
175
|
+
int ok;
|
|
176
|
+
void* file_data;
|
|
177
|
+
long file_size;
|
|
178
|
+
struct stat info;
|
|
179
|
+
FILE* in;
|
|
180
|
+
|
|
181
|
+
if (file_name == NULL || data == NULL || data_size == NULL) return 0;
|
|
182
|
+
*data = NULL;
|
|
183
|
+
*data_size = 0;
|
|
184
|
+
|
|
185
|
+
in = fopen(file_name, "rb");
|
|
186
|
+
if (in == NULL) {
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
// only regular files: on Linux a folder opens and reports a huge size
|
|
190
|
+
if (fstat(fileno(in), &info) != 0 || !S_ISREG(info.st_mode) ||
|
|
191
|
+
fseek(in, 0, SEEK_END) != 0 ||
|
|
192
|
+
(file_size = ftell(in)) <= 0 ||
|
|
193
|
+
fseek(in, 0, SEEK_SET) != 0) {
|
|
194
|
+
fclose(in);
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
file_data = malloc((size_t)file_size);
|
|
198
|
+
if (file_data == NULL) {
|
|
199
|
+
fclose(in);
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
ok = (fread(file_data, (size_t)file_size, 1, in) == 1);
|
|
203
|
+
fclose(in);
|
|
204
|
+
|
|
205
|
+
if (!ok) {
|
|
206
|
+
free(file_data);
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
*data = (uint8_t*)file_data;
|
|
210
|
+
*data_size = (size_t)file_size;
|
|
211
|
+
return 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#if defined(__cplusplus) || defined(c_plusplus)
|
|
215
|
+
} // extern "C"
|
|
216
|
+
#endif
|
data/ext/webp_ffi/util.h
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#ifndef _WEBP_FFI_UTIL_H_
|
|
2
|
+
#define _WEBP_FFI_UTIL_H_
|
|
3
|
+
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
#include <stdio.h>
|
|
7
|
+
|
|
8
|
+
#include "webp/decode.h"
|
|
9
|
+
#include "webp/encode.h"
|
|
10
|
+
#include "./metadata.h"
|
|
11
|
+
|
|
12
|
+
#if defined(__cplusplus) || defined(c_plusplus)
|
|
13
|
+
extern "C" {
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
typedef enum {
|
|
17
|
+
oPNG = 0,
|
|
18
|
+
oPAM,
|
|
19
|
+
oPPM,
|
|
20
|
+
oPGM,
|
|
21
|
+
oBMP,
|
|
22
|
+
oTIFF_,
|
|
23
|
+
oYUV,
|
|
24
|
+
ALPHA_PLANE_ONLY // this is for experimenting only
|
|
25
|
+
} OutputFileFormat;
|
|
26
|
+
|
|
27
|
+
typedef enum {
|
|
28
|
+
iPNG_ = 0,
|
|
29
|
+
iJPEG_,
|
|
30
|
+
iTIFF_, // 'TIFF' clashes with libtiff
|
|
31
|
+
UNSUPPORTED
|
|
32
|
+
} InputFileFormat;
|
|
33
|
+
|
|
34
|
+
// Writes 'buffer' to 'out_file'. When writing fails, a file this call created
|
|
35
|
+
// is removed again. Returns 1 on success.
|
|
36
|
+
int UtilSaveOutput(const WebPDecBuffer* const buffer, OutputFileFormat format, const char* const out_file);
|
|
37
|
+
int UtilFileExists(const char* const path);
|
|
38
|
+
// Writes 'buffer' in 'format' to an open file. Returns 1 on success.
|
|
39
|
+
int UtilWriteOutput(const WebPDecBuffer* const buffer, OutputFileFormat format, FILE* const fout);
|
|
40
|
+
// Allocates storage for entire file 'file_name' and returns contents and size
|
|
41
|
+
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
|
42
|
+
// be deleted using free().
|
|
43
|
+
int UtilReadFile(const char* const file_name,
|
|
44
|
+
const uint8_t** data, size_t* data_size);
|
|
45
|
+
|
|
46
|
+
// Reads a PNG, JPEG or TIFF picture from an open file (or raw YUV when the
|
|
47
|
+
// picture already has a size). When 'metadata' is not NULL, the picture's
|
|
48
|
+
// metadata is copied into it. Returns 1 on success.
|
|
49
|
+
int UtilReadPictureFile(FILE* const in_file, WebPPicture* const pic,
|
|
50
|
+
int keep_alpha, Metadata* const metadata);
|
|
51
|
+
|
|
52
|
+
// Checks that a 'width' x 'height' image with 'bytes_per_px' bytes per pixel
|
|
53
|
+
// fits in memory, using 64-bit math so the size can't wrap around.
|
|
54
|
+
// Returns 1 on success and stores the row size in 'stride' and the buffer size
|
|
55
|
+
// in 'size' (either pointer may be NULL). Returns 0 for empty or oversized images.
|
|
56
|
+
int UtilCheckImageSize(uint64_t width, uint64_t height, uint64_t bytes_per_px,
|
|
57
|
+
int* const stride, size_t* const size);
|
|
58
|
+
|
|
59
|
+
#if defined(__cplusplus) || defined(c_plusplus)
|
|
60
|
+
} // extern "C"
|
|
61
|
+
#endif
|
|
62
|
+
|
|
63
|
+
#endif /* _WEBP_FFI_UTIL_H_ */
|