/src/leptonica/src/webpio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file webpio.c |
29 | | * <pre> |
30 | | * |
31 | | * Reading WebP |
32 | | * PIX *pixReadStreamWebP() |
33 | | * PIX *pixReadMemWebP() |
34 | | * |
35 | | * Reading WebP header |
36 | | * l_int32 readHeaderWebP() |
37 | | * l_int32 readHeaderMemWebP() |
38 | | * |
39 | | * Writing WebP |
40 | | * l_int32 pixWriteWebP() [ special top level ] |
41 | | * l_int32 pixWriteStreamWebP() |
42 | | * l_int32 pixWriteMemWebP() |
43 | | * </pre> |
44 | | */ |
45 | | |
46 | | #ifdef HAVE_CONFIG_H |
47 | | #include <config_auto.h> |
48 | | #endif /* HAVE_CONFIG_H */ |
49 | | |
50 | | #include "allheaders.h" |
51 | | |
52 | | /* --------------------------------------------*/ |
53 | | #if HAVE_LIBWEBP /* defined in environ.h */ |
54 | | /* --------------------------------------------*/ |
55 | | #include "webp/decode.h" |
56 | | #include "webp/encode.h" |
57 | | |
58 | | /*---------------------------------------------------------------------* |
59 | | * Reading WebP * |
60 | | *---------------------------------------------------------------------*/ |
61 | | /*! |
62 | | * \brief pixReadStreamWebP() |
63 | | * |
64 | | * \param[in] fp file stream corresponding to WebP image |
65 | | * \return pix 32 bpp, or NULL on error |
66 | | */ |
67 | | PIX * |
68 | | pixReadStreamWebP(FILE *fp) |
69 | 0 | { |
70 | 0 | l_uint8 *filedata; |
71 | 0 | size_t filesize; |
72 | 0 | PIX *pix; |
73 | |
|
74 | 0 | if (!fp) |
75 | 0 | return (PIX *)ERROR_PTR("fp not defined", __func__, NULL); |
76 | | |
77 | | /* Read data from file and decode into Y,U,V arrays */ |
78 | 0 | rewind(fp); |
79 | 0 | if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL) |
80 | 0 | return (PIX *)ERROR_PTR("filedata not read", __func__, NULL); |
81 | | |
82 | 0 | pix = pixReadMemWebP(filedata, filesize); |
83 | 0 | LEPT_FREE(filedata); |
84 | 0 | return pix; |
85 | 0 | } |
86 | | |
87 | | |
88 | | /*! |
89 | | * \brief pixReadMemWebP() |
90 | | * |
91 | | * \param[in] filedata webp compressed data in memory |
92 | | * \param[in] filesize number of bytes in data |
93 | | * \return pix 32 bpp, or NULL on error |
94 | | * |
95 | | * <pre> |
96 | | * Notes: |
97 | | * (1) When the encoded data only has 3 channels (no alpha), |
98 | | * WebPDecodeRGBAInto() generates a raster of 32-bit pixels, with |
99 | | * the alpha channel set to opaque (255). |
100 | | * (2) We don't need to use the gnu runtime functions like fmemopen() |
101 | | * for redirecting data from a stream to memory, because |
102 | | * the webp library has been written with memory-to-memory |
103 | | * functions at the lowest level (which is good!). And, in |
104 | | * any event, fmemopen() doesn't work with l_binaryReadStream(). |
105 | | * </pre> |
106 | | */ |
107 | | PIX * |
108 | | pixReadMemWebP(const l_uint8 *filedata, |
109 | | size_t filesize) |
110 | 0 | { |
111 | 0 | l_uint8 *out = NULL; |
112 | 0 | l_int32 w, h, has_alpha, wpl, stride; |
113 | 0 | l_uint32 *data; |
114 | 0 | size_t size; |
115 | 0 | PIX *pix; |
116 | 0 | WebPBitstreamFeatures features; |
117 | |
|
118 | 0 | if (!filedata) |
119 | 0 | return (PIX *)ERROR_PTR("filedata not defined", __func__, NULL); |
120 | | |
121 | 0 | if (WebPGetFeatures(filedata, filesize, &features)) |
122 | 0 | return (PIX *)ERROR_PTR("Invalid WebP file", __func__, NULL); |
123 | 0 | w = features.width; |
124 | 0 | h = features.height; |
125 | 0 | has_alpha = features.has_alpha; |
126 | | |
127 | | /* Write from compressed Y,U,V arrays to pix raster data */ |
128 | 0 | pix = pixCreate(w, h, 32); |
129 | 0 | pixSetInputFormat(pix, IFF_WEBP); |
130 | 0 | if (has_alpha) pixSetSpp(pix, 4); |
131 | 0 | data = pixGetData(pix); |
132 | 0 | wpl = pixGetWpl(pix); |
133 | 0 | stride = wpl * 4; |
134 | 0 | size = (size_t)stride * h; |
135 | 0 | out = WebPDecodeRGBAInto(filedata, filesize, (uint8_t *)data, size, |
136 | 0 | stride); |
137 | 0 | if (out == NULL) { /* error: out should also point to data */ |
138 | 0 | pixDestroy(&pix); |
139 | 0 | return (PIX *)ERROR_PTR("WebP decode failed", __func__, NULL); |
140 | 0 | } |
141 | | |
142 | | /* The WebP API expects data in RGBA order. The pix stores |
143 | | * in host-dependent order with R as the MSB and A as the LSB. |
144 | | * On little-endian machines, the bytes in the word must |
145 | | * be swapped; e.g., R goes from byte 0 (LSB) to byte 3 (MSB). |
146 | | * No swapping is necessary for big-endians. */ |
147 | 0 | pixEndianByteSwap(pix); |
148 | 0 | return pix; |
149 | 0 | } |
150 | | |
151 | | |
152 | | /*! |
153 | | * \brief readHeaderWebP() |
154 | | * |
155 | | * \param[in] filename |
156 | | * \param[out] pw width |
157 | | * \param[out] ph height |
158 | | * \param[out] pspp spp (3 or 4) |
159 | | * \return 0 if OK, 1 on error |
160 | | */ |
161 | | l_ok |
162 | | readHeaderWebP(const char *filename, |
163 | | l_int32 *pw, |
164 | | l_int32 *ph, |
165 | | l_int32 *pspp) |
166 | 0 | { |
167 | 0 | l_uint8 data[100]; /* expect size info within the first 50 bytes or so */ |
168 | 0 | l_int32 nbytes, bytesread; |
169 | 0 | size_t filesize; |
170 | 0 | FILE *fp; |
171 | |
|
172 | 0 | if (!pw || !ph || !pspp) |
173 | 0 | return ERROR_INT("input ptr(s) not defined", __func__, 1); |
174 | 0 | *pw = *ph = *pspp = 0; |
175 | 0 | if (!filename) |
176 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
177 | | |
178 | | /* Read no more than 100 bytes from the file */ |
179 | 0 | if ((filesize = nbytesInFile(filename)) == 0) |
180 | 0 | return ERROR_INT_1("no file size found", filename, __func__, 1); |
181 | 0 | if (filesize < 100) |
182 | 0 | L_WARNING("very small webp file: %s\n", __func__, filename); |
183 | 0 | nbytes = L_MIN(filesize, 100); |
184 | 0 | if ((fp = fopenReadStream(filename)) == NULL) |
185 | 0 | return ERROR_INT_1("image file not found", filename, __func__, 1); |
186 | 0 | bytesread = fread(data, 1, nbytes, fp); |
187 | 0 | fclose(fp); |
188 | 0 | if (bytesread != nbytes) |
189 | 0 | return ERROR_INT("failed to read requested data", __func__, 1); |
190 | | |
191 | 0 | return readHeaderMemWebP(data, nbytes, pw, ph, pspp); |
192 | 0 | } |
193 | | |
194 | | |
195 | | /*! |
196 | | * \brief readHeaderMemWebP() |
197 | | * |
198 | | * \param[in] data |
199 | | * \param[in] size 100 bytes is sufficient |
200 | | * \param[out] pw width |
201 | | * \param[out] ph height |
202 | | * \param[out] pspp spp (3 or 4) |
203 | | * \return 0 if OK, 1 on error |
204 | | */ |
205 | | l_ok |
206 | | readHeaderMemWebP(const l_uint8 *data, |
207 | | size_t size, |
208 | | l_int32 *pw, |
209 | | l_int32 *ph, |
210 | | l_int32 *pspp) |
211 | 0 | { |
212 | 0 | WebPBitstreamFeatures features; |
213 | |
|
214 | 0 | if (pw) *pw = 0; |
215 | 0 | if (ph) *ph = 0; |
216 | 0 | if (pspp) *pspp = 0; |
217 | 0 | if (!data) |
218 | 0 | return ERROR_INT("data not defined", __func__, 1); |
219 | 0 | if (!pw || !ph || !pspp) |
220 | 0 | return ERROR_INT("input ptr(s) not defined", __func__, 1); |
221 | | |
222 | 0 | if (WebPGetFeatures(data, (l_int32)size, &features)) |
223 | 0 | return ERROR_INT("invalid WebP file", __func__, 1); |
224 | 0 | *pw = features.width; |
225 | 0 | *ph = features.height; |
226 | 0 | *pspp = (features.has_alpha) ? 4 : 3; |
227 | 0 | return 0; |
228 | 0 | } |
229 | | |
230 | | |
231 | | /*---------------------------------------------------------------------* |
232 | | * Writing WebP * |
233 | | *---------------------------------------------------------------------*/ |
234 | | /*! |
235 | | * \brief pixWriteWebP() |
236 | | * |
237 | | * \param[in] filename |
238 | | * \param[in] pixs |
239 | | * \param[in] quality 0 - 100; default ~80 |
240 | | * \param[in] lossless use 1 for lossless; 0 for lossy |
241 | | * \return 0 if OK, 1 on error |
242 | | * |
243 | | * <pre> |
244 | | * Notes: |
245 | | * (1) Special top-level function allowing specification of quality. |
246 | | * </pre> |
247 | | */ |
248 | | l_ok |
249 | | pixWriteWebP(const char *filename, |
250 | | PIX *pixs, |
251 | | l_int32 quality, |
252 | | l_int32 lossless) |
253 | 0 | { |
254 | 0 | l_int32 ret; |
255 | 0 | FILE *fp; |
256 | |
|
257 | 0 | if (!pixs) |
258 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
259 | 0 | if (!filename) |
260 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
261 | | |
262 | 0 | if ((fp = fopenWriteStream(filename, "wb+")) == NULL) |
263 | 0 | return ERROR_INT_1("stream not opened", filename, __func__, 1); |
264 | 0 | ret = pixWriteStreamWebP(fp, pixs, quality, lossless); |
265 | 0 | fclose(fp); |
266 | 0 | if (ret) |
267 | 0 | return ERROR_INT_1("pixs not compressed to stream", filename, __func__, 1); |
268 | 0 | return 0; |
269 | 0 | } |
270 | | |
271 | | |
272 | | /*! |
273 | | * \brief pixWriteStreampWebP() |
274 | | * |
275 | | * \param[in] fp file stream |
276 | | * \param[in] pixs all depths |
277 | | * \param[in] quality 0 - 100; default ~80 |
278 | | * \param[in] lossless use 1 for lossless; 0 for lossy |
279 | | * \return 0 if OK, 1 on error |
280 | | * |
281 | | * <pre> |
282 | | * Notes: |
283 | | * (1) See pixWriteMemWebP() for details. |
284 | | * (2) Use 'free', and not leptonica's 'LEPT_FREE', for all heap data |
285 | | * that is returned from the WebP library. |
286 | | * </pre> |
287 | | */ |
288 | | l_ok |
289 | | pixWriteStreamWebP(FILE *fp, |
290 | | PIX *pixs, |
291 | | l_int32 quality, |
292 | | l_int32 lossless) |
293 | 0 | { |
294 | 0 | l_uint8 *filedata; |
295 | 0 | size_t filebytes, nbytes; |
296 | |
|
297 | 0 | if (!fp) |
298 | 0 | return ERROR_INT("stream not open", __func__, 1); |
299 | 0 | if (!pixs) |
300 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
301 | | |
302 | 0 | pixSetPadBits(pixs, 0); |
303 | 0 | pixWriteMemWebP(&filedata, &filebytes, pixs, quality, lossless); |
304 | 0 | rewind(fp); |
305 | 0 | nbytes = fwrite(filedata, 1, filebytes, fp); |
306 | 0 | free(filedata); |
307 | 0 | if (nbytes != filebytes) |
308 | 0 | return ERROR_INT("Write error", __func__, 1); |
309 | 0 | return 0; |
310 | 0 | } |
311 | | |
312 | | |
313 | | /*! |
314 | | * \brief pixWriteMemWebP() |
315 | | * |
316 | | * \param[out] pencdata webp encoded data of pixs |
317 | | * \param[out] pencsize size of webp encoded data |
318 | | * \param[in] pixs any depth, cmapped OK |
319 | | * \param[in] quality 0 - 100; default ~80 |
320 | | * \param[in] lossless use 1 for lossless; 0 for lossy |
321 | | * \return 0 if OK, 1 on error |
322 | | * |
323 | | * <pre> |
324 | | * Notes: |
325 | | * (1) Lossless and lossy encoding are entirely different in webp. |
326 | | * %quality applies to lossy, and is ignored for lossless. |
327 | | * (2) The input image is converted to RGB if necessary. If spp == 3, |
328 | | * we set the alpha channel to fully opaque (255), and |
329 | | * WebPEncodeRGBA() then removes the alpha chunk when encoding, |
330 | | * setting the internal header field has_alpha to 0. |
331 | | * </pre> |
332 | | */ |
333 | | l_ok |
334 | | pixWriteMemWebP(l_uint8 **pencdata, |
335 | | size_t *pencsize, |
336 | | PIX *pixs, |
337 | | l_int32 quality, |
338 | | l_int32 lossless) |
339 | 0 | { |
340 | 0 | l_int32 w, h, d, wpl, stride; |
341 | 0 | l_uint32 *data; |
342 | 0 | PIX *pix1, *pix2; |
343 | |
|
344 | 0 | if (!pencdata) |
345 | 0 | return ERROR_INT("&encdata not defined", __func__, 1); |
346 | 0 | *pencdata = NULL; |
347 | 0 | if (!pencsize) |
348 | 0 | return ERROR_INT("&encsize not defined", __func__, 1); |
349 | 0 | *pencsize = 0; |
350 | 0 | if (!pixs) |
351 | 0 | return ERROR_INT("&pixs not defined", __func__, 1); |
352 | 0 | if (lossless == 0 && (quality < 0 || quality > 100)) |
353 | 0 | return ERROR_INT("quality not in [0 ... 100]", __func__, 1); |
354 | | |
355 | 0 | if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR)) == NULL) |
356 | 0 | return ERROR_INT("failure to remove color map", __func__, 1); |
357 | | |
358 | | /* Convert to rgb if not 32 bpp; pix2 must not be a clone of pixs. */ |
359 | 0 | if (pixGetDepth(pix1) != 32) |
360 | 0 | pix2 = pixConvertTo32(pix1); |
361 | 0 | else |
362 | 0 | pix2 = pixCopy(NULL, pix1); |
363 | 0 | pixDestroy(&pix1); |
364 | 0 | pixGetDimensions(pix2, &w, &h, &d); |
365 | 0 | if (w <= 0 || h <= 0 || d != 32) { |
366 | 0 | pixDestroy(&pix2); |
367 | 0 | return ERROR_INT("pix2 not 32 bpp or of 0 size", __func__, 1); |
368 | 0 | } |
369 | | |
370 | | /* If spp == 3, need to set alpha layer to opaque (all 1s). */ |
371 | 0 | if (pixGetSpp(pix2) == 3) |
372 | 0 | pixSetComponentArbitrary(pix2, L_ALPHA_CHANNEL, 255); |
373 | | |
374 | | /* The WebP API expects data in RGBA order. The pix stores |
375 | | * in host-dependent order with R as the MSB and A as the LSB. |
376 | | * On little-endian machines, the bytes in the word must |
377 | | * be swapped; e.g., R goes from byte 0 (LSB) to byte 3 (MSB). |
378 | | * No swapping is necessary for big-endians. */ |
379 | 0 | pixEndianByteSwap(pix2); |
380 | 0 | wpl = pixGetWpl(pix2); |
381 | 0 | data = pixGetData(pix2); |
382 | 0 | stride = wpl * 4; |
383 | 0 | if (lossless) { |
384 | 0 | *pencsize = WebPEncodeLosslessRGBA((uint8_t *)data, w, h, |
385 | 0 | stride, pencdata); |
386 | 0 | } else { |
387 | 0 | *pencsize = WebPEncodeRGBA((uint8_t *)data, w, h, stride, |
388 | 0 | quality, pencdata); |
389 | 0 | } |
390 | 0 | pixDestroy(&pix2); |
391 | |
|
392 | 0 | if (*pencsize == 0) { |
393 | 0 | free(*pencdata); |
394 | 0 | *pencdata = NULL; |
395 | 0 | return ERROR_INT("webp encoding failed", __func__, 1); |
396 | 0 | } |
397 | | |
398 | 0 | return 0; |
399 | 0 | } |
400 | | |
401 | | /* --------------------------------------------*/ |
402 | | #endif /* HAVE_LIBWEBP */ |
403 | | /* --------------------------------------------*/ |