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,48 +1,212 @@
1
1
  #include "./tiffdec.h"
2
+ #include "./util.h"
2
3
 
4
+ #include <stdarg.h>
5
+ #include <sys/types.h>
3
6
  #include <tiffio.h>
7
+ #include <tiffvers.h>
4
8
 
5
- int UtilReadTIFF(const char* const filename,
6
- WebPPicture* const pic, int keep_alpha) {
7
- TIFF* const tif = TIFFOpen(filename, "r");
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);
8
169
  uint32_t width, height;
9
170
  uint32_t* raster;
171
+ int stride;
172
+ size_t raster_size;
10
173
  int ok = 0;
11
- int dircount = 1;
12
174
 
13
175
  if (tif == NULL) {
14
- //fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename);
15
176
  return 0;
16
177
  }
17
178
 
18
- while (TIFFReadDirectory(tif)) ++dircount;
19
-
20
- if (dircount > 1) {
21
- fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
22
- "Only the first will be used, %d will be ignored.\n",
23
- dircount - 1);
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;
24
187
  }
25
188
 
26
- TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
27
- TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
28
- raster = (uint32_t*)_TIFFmalloc(width * height * sizeof(*raster));
189
+ raster = (uint32_t*)_TIFFmalloc(raster_size);
29
190
  if (raster != NULL) {
30
191
  if (TIFFReadRGBAImageOriented(tif, width, height, raster,
31
192
  ORIENTATION_TOPLEFT, 1)) {
32
- const int stride = width * sizeof(*raster);
33
- pic->width = width;
34
- pic->height = height;
193
+ pic->width = (int)width;
194
+ pic->height = (int)height;
35
195
  // TIFF data is ABGR
36
196
  #ifdef __BIG_ENDIAN__
37
- TIFFSwabArrayOfLong(raster, width * height);
197
+ TIFFSwabArrayOfLong(raster, raster_size / sizeof(*raster));
38
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
+ }
39
205
  ok = keep_alpha
40
206
  ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
41
207
  : WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
42
208
  }
43
209
  _TIFFfree(raster);
44
- } else {
45
- //fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
46
210
  }
47
211
 
48
212
  if (ok && keep_alpha == 2) {
@@ -3,21 +3,29 @@
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" {
9
10
  #endif
10
11
 
11
- // Reads a TIFF from 'filename', returning the decoded output in 'pic'.
12
+ // Reads a TIFF from 'in_file', returning the decoded output in 'pic'.
12
13
  // If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
13
- // otherwise it will be RGB.
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.
14
17
  // Returns true on success.
15
- int UtilReadTIFF(const char* const filename,
16
- struct WebPPicture* const pic, int keep_alpha);
18
+ int UtilReadTIFF(FILE* const in_file,
19
+ struct WebPPicture* const pic, int keep_alpha,
20
+ Metadata* const metadata);
17
21
 
18
22
  int UtilWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer);
19
23
  int UtilWriteBMP(FILE* fout, const WebPDecBuffer* const buffer);
20
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
+
21
29
  #ifdef __cplusplus
22
30
  } // extern "C"
23
31
  #endif
data/ext/webp_ffi/util.c CHANGED
@@ -1,3 +1,5 @@
1
+ #include <limits.h>
2
+ #include <sys/stat.h>
1
3
  #include <stdio.h>
2
4
  #include <stdlib.h>
3
5
  #include <string.h>
@@ -14,6 +16,34 @@
14
16
  extern "C" {
15
17
  #endif
16
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
+
17
47
  static int UtilReadYUV(FILE* in_file, WebPPicture* const pic) {
18
48
  const int use_argb = pic->use_argb;
19
49
  const int uv_width = (pic->width + 1) / 2;
@@ -54,7 +84,9 @@ static InputFileFormat GetImageType(FILE* in_file) {
54
84
  return format;
55
85
  }
56
86
 
57
- magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
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];
58
90
  if (magic == 0x89504E47U) {
59
91
  format = iPNG_;
60
92
  } else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
@@ -65,44 +97,53 @@ static InputFileFormat GetImageType(FILE* in_file) {
65
97
  return format;
66
98
  }
67
99
 
68
- int UtilReadPicture(const char* const filename, WebPPicture* const pic,
69
- int keep_alpha) {
100
+ int UtilReadPictureFile(FILE* const in_file, WebPPicture* const pic,
101
+ int keep_alpha, Metadata* const metadata) {
70
102
  int ok = 0;
71
- FILE* in_file = fopen(filename, "rb");
72
- if (in_file == NULL) {
73
- //fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
74
- return ok;
75
- }
76
103
 
77
104
  if (pic->width == 0 || pic->height == 0) {
78
- // If no size specified, try to decode it as PNG/JPEG (as appropriate).
105
+ // If no size specified, try to decode it as PNG/JPEG/TIFF (as appropriate).
79
106
  const InputFileFormat format = GetImageType(in_file);
80
107
  if (format == iPNG_) {
81
- ok = UtilReadPNG(in_file, pic, keep_alpha);
108
+ ok = UtilReadPNG(in_file, pic, keep_alpha, metadata);
82
109
  } else if (format == iJPEG_) {
83
- ok = UtilReadJPEG(in_file, pic);
110
+ ok = UtilReadJPEG(in_file, pic, metadata);
84
111
  } else if (format == iTIFF_) {
85
- ok = UtilReadTIFF(filename, pic, keep_alpha);
112
+ ok = UtilReadTIFF(in_file, pic, keep_alpha, metadata);
86
113
  }
87
114
  } else {
88
115
  // If image size is specified, infer it as YUV format.
89
116
  ok = UtilReadYUV(in_file, pic);
90
117
  }
91
-
92
- fclose(in_file);
93
118
  return ok;
94
119
  }
95
120
 
121
+ int UtilFileExists(const char* const path) {
122
+ struct stat info;
123
+ return stat(path, &info) == 0;
124
+ }
125
+
96
126
  int UtilSaveOutput(const WebPDecBuffer* const buffer,
97
127
  OutputFileFormat format, const char* const out_file) {
98
- FILE* fout = NULL;
99
- int ok = 1;
100
-
101
- fout = fopen(out_file, "wb");
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");
102
132
  if (!fout) {
103
- fprintf(stderr, "Error opening output file %s\n", out_file);
104
133
  return 0;
105
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;
106
147
 
107
148
  if (format == oPNG) {
108
149
  ok &= UtilWritePNG(fout, buffer);
@@ -114,14 +155,14 @@ int UtilSaveOutput(const WebPDecBuffer* const buffer,
114
155
  ok &= UtilWriteBMP(fout, buffer);
115
156
  } else if (format == oTIFF_) {
116
157
  ok &= UtilWriteTIFF(fout, buffer);
117
- } else if (format == oPGM || format == oYUV) {
158
+ } else if (format == oPGM) {
118
159
  ok &= UtilWritePGM(fout, buffer);
160
+ } else if (format == oYUV) {
161
+ ok &= UtilWriteYUV(fout, buffer);
119
162
  } else if (format == ALPHA_PLANE_ONLY) {
120
163
  ok &= UtilWriteAlphaPlane(fout, buffer);
121
- }
122
-
123
- if (fout) {
124
- fclose(fout);
164
+ } else {
165
+ ok = 0;
125
166
  }
126
167
  return ok;
127
168
  }
@@ -133,7 +174,8 @@ int UtilReadFile(const char* const file_name,
133
174
  const uint8_t** data, size_t* data_size) {
134
175
  int ok;
135
176
  void* file_data;
136
- size_t file_size;
177
+ long file_size;
178
+ struct stat info;
137
179
  FILE* in;
138
180
 
139
181
  if (file_name == NULL || data == NULL || data_size == NULL) return 0;
@@ -142,25 +184,30 @@ int UtilReadFile(const char* const file_name,
142
184
 
143
185
  in = fopen(file_name, "rb");
144
186
  if (in == NULL) {
145
- fprintf(stderr, "cannot open input file '%s'\n", file_name);
146
187
  return 0;
147
188
  }
148
- fseek(in, 0, SEEK_END);
149
- file_size = ftell(in);
150
- fseek(in, 0, SEEK_SET);
151
- file_data = malloc(file_size);
152
- if (file_data == NULL) return 0;
153
- ok = (fread(file_data, file_size, 1, in) == 1);
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);
154
203
  fclose(in);
155
204
 
156
205
  if (!ok) {
157
- fprintf(stderr, "Could not read %zu bytes of data from file %s\n",
158
- file_size, file_name);
159
206
  free(file_data);
160
207
  return 0;
161
208
  }
162
209
  *data = (uint8_t*)file_data;
163
- *data_size = file_size;
210
+ *data_size = (size_t)file_size;
164
211
  return 1;
165
212
  }
166
213
 
data/ext/webp_ffi/util.h CHANGED
@@ -1,5 +1,13 @@
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
+
1
8
  #include "webp/decode.h"
2
9
  #include "webp/encode.h"
10
+ #include "./metadata.h"
3
11
 
4
12
  #if defined(__cplusplus) || defined(c_plusplus)
5
13
  extern "C" {
@@ -23,16 +31,33 @@ typedef enum {
23
31
  UNSUPPORTED
24
32
  } InputFileFormat;
25
33
 
34
+ // Writes 'buffer' to 'out_file'. When writing fails, a file this call created
35
+ // is removed again. Returns 1 on success.
26
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);
27
40
  // Allocates storage for entire file 'file_name' and returns contents and size
28
41
  // in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
29
42
  // be deleted using free().
30
43
  int UtilReadFile(const char* const file_name,
31
44
  const uint8_t** data, size_t* data_size);
32
45
 
33
- int UtilReadPicture(const char* const filename, WebPPicture* const pic,
34
- int keep_alpha);
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);
35
58
 
36
59
  #if defined(__cplusplus) || defined(c_plusplus)
37
60
  } // extern "C"
38
- #endif
61
+ #endif
62
+
63
+ #endif /* _WEBP_FFI_UTIL_H_ */