1 /**
2  * File: WebP IO
3  *
4  * Read and write WebP images.
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif /* HAVE_CONFIG_H */
10 
11 
12 #include <stdio.h>
13 #include <math.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include "gd.h"
17 #include "gd_errors.h"
18 #include "gdhelpers.h"
19 
20 #ifdef HAVE_LIBWEBP
21 #include "webp/decode.h"
22 #include "webp/encode.h"
23 
24 #define GD_WEBP_ALLOC_STEP (4*1024)
25 
26 /*
27   Function: gdImageCreateFromWebp
28 
29     <gdImageCreateFromWebp> is called to load truecolor images from
30     WebP format files. Invoke <gdImageCreateFromWebp> with an
31     already opened pointer to a file containing the desired
32     image. <gdImageCreateFromWebp> returns a <gdImagePtr> to the new
33     truecolor image, or NULL if unable to load the image (most often
34     because the file is corrupt or does not contain a WebP
35     image). <gdImageCreateFromWebp> does not close the file.
36 
37     You can inspect the sx and sy members of the image to determine
38     its size. The image must eventually be destroyed using
39     <gdImageDestroy>.
40 
41     *The returned image is always a truecolor image.*
42 
43   Variants:
44 
45     <gdImageCreateFromJpegPtr> creates an image from WebP data
46     already in memory.
47 
48     <gdImageCreateFromJpegCtx> reads its data via the function
49     pointers in a <gdIOCtx> structure.
50 
51   Parameters:
52 
53     infile - The input FILE pointer.
54 
55   Returns:
56 
57     A pointer to the new *truecolor* image.  This will need to be
58     destroyed with <gdImageDestroy> once it is no longer needed.
59 
60     On error, returns NULL.
61 */
gdImageCreateFromWebp(FILE * inFile)62 BGD_DECLARE(gdImagePtr) gdImageCreateFromWebp (FILE * inFile)
63 {
64 	gdImagePtr im;
65 	gdIOCtx *in = gdNewFileCtx(inFile);
66 	if (!in) {
67 		return 0;
68 	}
69 	im = gdImageCreateFromWebpCtx(in);
70 	in->gd_free(in);
71 
72 	return im;
73 }
74 
75 
76 /*
77   Function: gdImageCreateFromWebpPtr
78 
79     See <gdImageCreateFromWebp>.
80 
81   Parameters:
82 
83     size            - size of WebP data in bytes.
84     data            - pointer to WebP data.
85 */
gdImageCreateFromWebpPtr(int size,void * data)86 BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpPtr (int size, void *data)
87 {
88 	gdImagePtr im;
89 	gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
90 	if (!in)
91 		return 0;
92 	im = gdImageCreateFromWebpCtx(in);
93 	in->gd_free(in);
94 	return im;
95 }
96 
97 /*
98   Function: gdImageCreateFromWebpCtx
99 
100     See <gdImageCreateFromWebp>.
101 */
gdImageCreateFromWebpCtx(gdIOCtx * infile)102 BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpCtx (gdIOCtx * infile)
103 {
104 	int    width, height;
105 	uint8_t   *filedata = NULL;
106 	uint8_t    *argb = NULL;
107 	unsigned char   *read, *temp;
108 	size_t size = 0, n;
109 	gdImagePtr im;
110 	int x, y;
111 	uint8_t *p;
112 
113 	do {
114 		temp = gdRealloc(filedata, size+GD_WEBP_ALLOC_STEP);
115 		if (temp) {
116 			filedata = temp;
117 			read = temp + size;
118 		} else {
119 			if (filedata) {
120 				gdFree(filedata);
121 			}
122 			gd_error("WebP decode: realloc failed");
123 			return NULL;
124 		}
125 
126 		n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile);
127 		if (n>0 && n!=EOF) {
128 			size += n;
129 		}
130 	} while (n>0 && n!=EOF);
131 
132 	if (WebPGetInfo(filedata,size, &width, &height) == 0) {
133 		gd_error("gd-webp cannot get webp info");
134 		gdFree(temp);
135 		return NULL;
136 	}
137 
138 	im = gdImageCreateTrueColor(width, height);
139 	if (!im) {
140 		gdFree(temp);
141 		return NULL;
142 	}
143 	argb = WebPDecodeARGB(filedata, size, &width, &height);
144 	if (!argb) {
145 		gd_error("gd-webp cannot allocate temporary buffer");
146 		gdFree(temp);
147 		gdImageDestroy(im);
148 		return NULL;
149 	}
150 	for (y = 0, p = argb;  y < height; y++) {
151 		for (x = 0; x < width; x++) {
152 			register uint8_t a = gdAlphaMax - (*(p++) >> 1);
153 			register uint8_t r = *(p++);
154 			register uint8_t g = *(p++);
155 			register uint8_t b = *(p++);
156 			im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a);
157 		}
158 	}
159 	/* do not use gdFree here, in case gdFree/alloc is mapped to something else than libc */
160 	free(argb);
161 	gdFree(temp);
162 	im->saveAlphaFlag = 1;
163 	return im;
164 }
165 
166 
167 /* returns 0 on success, 1 on failure */
_gdImageWebpCtx(gdImagePtr im,gdIOCtx * outfile,int quality)168 static int _gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
169 {
170 	uint8_t *argb;
171 	int x, y;
172 	uint8_t *p;
173 	uint8_t *out;
174 	size_t out_size;
175     int ret = 0;
176 
177 	if (im == NULL) {
178 		return 1;
179 	}
180 
181 	if (!gdImageTrueColor(im)) {
182 		gd_error("Palette image not supported by webp");
183 		return 1;
184 	}
185 
186 	if (quality == -1) {
187 		quality = 80;
188 	}
189 
190 	if (overflow2(gdImageSX(im), 4)) {
191 		return 1;
192 	}
193 
194 	if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) {
195 		return 1;
196 	}
197 
198 	argb = (uint8_t *)gdMalloc(gdImageSX(im) * 4 * gdImageSY(im));
199 	if (!argb) {
200 		return 1;
201 	}
202 	p = argb;
203 	for (y = 0; y < gdImageSY(im); y++) {
204 		for (x = 0; x < gdImageSX(im); x++) {
205 			register int c;
206 			register char a;
207 			c = im->tpixels[y][x];
208 			a = gdTrueColorGetAlpha(c);
209 			if (a == 127) {
210 				a = 0;
211 			} else {
212 				a = 255 - ((a << 1) + (a >> 6));
213 			}
214 			*(p++) = gdTrueColorGetRed(c);
215 			*(p++) = gdTrueColorGetGreen(c);
216 			*(p++) = gdTrueColorGetBlue(c);
217 			*(p++) = a;
218 		}
219 	}
220 	out_size = WebPEncodeRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, quality, &out);
221 	if (out_size == 0) {
222 		gd_error("gd-webp encoding failed");
223         ret = 1;
224 		goto freeargb;
225 	}
226 	gdPutBuf(out, out_size, outfile);
227 	free(out);
228 
229 freeargb:
230 	gdFree(argb);
231 
232     return ret;
233 }
234 
235 
236 /*
237   Function: gdImageWebpCtx
238 
239     Write the image as WebP data via a <gdIOCtx>. See <gdImageWebpEx>
240     for more details.
241 
242   Parameters:
243 
244     im      - The image to write.
245     outfile - The output sink.
246     quality - Image quality.
247 
248   Returns:
249 
250     Nothing.
251 */
gdImageWebpCtx(gdImagePtr im,gdIOCtx * outfile,int quality)252 BGD_DECLARE(void) gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
253 {
254 	_gdImageWebpCtx(im, outfile, quality);
255 }
256 
257 /*
258   Function: gdImageWebpEx
259 
260     <gdImageWebpEx> outputs the specified image to the specified file in
261     WebP format. The file must be open for writing. Under MSDOS and
262     all versions of Windows, it is important to use "wb" as opposed to
263     simply "w" as the mode when opening the file, and under Unix there
264     is no penalty for doing so. <gdImageWebpEx> does not close the file;
265     your code must do so.
266 
267 	If _quality_ is -1, a reasonable quality value (which should yield a
268 	good general quality / size tradeoff for most situations) is used. Otherwise
269 	_quality_ should be a value in the range 0-100, higher quality values
270 	usually implying both higher quality and larger image sizes.
271 
272   Variants:
273 
274     <gdImageWebpCtx> stores the image using a <gdIOCtx> struct.
275 
276     <gdImageWebpPtrEx> stores the image to RAM.
277 
278   Parameters:
279 
280     im      - The image to save.
281     outFile - The FILE pointer to write to.
282     quality - Compression quality (0-100).
283 
284   Returns:
285 
286     Nothing.
287 */
gdImageWebpEx(gdImagePtr im,FILE * outFile,int quality)288 BGD_DECLARE(void) gdImageWebpEx (gdImagePtr im, FILE * outFile, int quality)
289 {
290 	gdIOCtx *out = gdNewFileCtx(outFile);
291 	if (out == NULL) {
292 		return;
293 	}
294 	_gdImageWebpCtx(im, out, quality);
295 	out->gd_free(out);
296 }
297 
298 /*
299   Function: gdImageWebp
300 
301     Variant of <gdImageWebpEx> which uses the default quality (-1).
302 
303   Parameters:
304 
305     im      - The image to save
306     outFile - The FILE pointer to write to.
307 
308   Returns:
309 
310     Nothing.
311 */
gdImageWebp(gdImagePtr im,FILE * outFile)312 BGD_DECLARE(void) gdImageWebp (gdImagePtr im, FILE * outFile)
313 {
314 	gdIOCtx *out = gdNewFileCtx(outFile);
315 	if (out == NULL) {
316 		return;
317 	}
318 	_gdImageWebpCtx(im, out, -1);
319 	out->gd_free(out);
320 }
321 
322 /*
323   Function: gdImageWebpPtr
324 
325     See <gdImageWebpEx>.
326 */
gdImageWebpPtr(gdImagePtr im,int * size)327 BGD_DECLARE(void *) gdImageWebpPtr (gdImagePtr im, int *size)
328 {
329 	void *rv;
330 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
331 	if (out == NULL) {
332 		return NULL;
333 	}
334 	if (_gdImageWebpCtx(im, out, -1)) {
335 		rv = NULL;
336 	} else {
337 		rv = gdDPExtractData(out, size);
338 	}
339 	out->gd_free(out);
340 
341 	return rv;
342 }
343 
344 /*
345   Function: gdImageWebpPtrEx
346 
347     See <gdImageWebpEx>.
348 */
gdImageWebpPtrEx(gdImagePtr im,int * size,int quality)349 BGD_DECLARE(void *) gdImageWebpPtrEx (gdImagePtr im, int *size, int quality)
350 {
351 	void *rv;
352 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
353 	if (out == NULL) {
354 		return NULL;
355 	}
356 	if (_gdImageWebpCtx(im, out, quality)) {
357         rv = NULL;
358     } else {
359         rv = gdDPExtractData(out, size);
360     }
361 	out->gd_free(out);
362 	return rv;
363 }
364 
365 #else /* !HAVE_LIBWEBP */
366 
_noWebpError(void)367 static void _noWebpError(void)
368 {
369 	gd_error("WEBP image support has been disabled\n");
370 }
371 
gdImageCreateFromWebp(FILE * inFile)372 BGD_DECLARE(gdImagePtr) gdImageCreateFromWebp (FILE * inFile)
373 {
374 	_noWebpError();
375 	return NULL;
376 }
377 
gdImageCreateFromWebpPtr(int size,void * data)378 BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpPtr (int size, void *data)
379 {
380 	_noWebpError();
381 	return NULL;
382 }
383 
gdImageCreateFromWebpCtx(gdIOCtx * infile)384 BGD_DECLARE(gdImagePtr) gdImageCreateFromWebpCtx (gdIOCtx * infile)
385 {
386 	_noWebpError();
387 	return NULL;
388 }
389 
gdImageWebpCtx(gdImagePtr im,gdIOCtx * outfile,int quality)390 BGD_DECLARE(void) gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
391 {
392 	_noWebpError();
393 }
394 
gdImageWebpEx(gdImagePtr im,FILE * outFile,int quality)395 BGD_DECLARE(void) gdImageWebpEx (gdImagePtr im, FILE * outFile, int quality)
396 {
397 	_noWebpError();
398 }
399 
gdImageWebp(gdImagePtr im,FILE * outFile)400 BGD_DECLARE(void) gdImageWebp (gdImagePtr im, FILE * outFile)
401 {
402 	_noWebpError();
403 }
404 
gdImageWebpPtr(gdImagePtr im,int * size)405 BGD_DECLARE(void *) gdImageWebpPtr (gdImagePtr im, int *size)
406 {
407 	_noWebpError();
408 	return NULL;
409 }
410 
gdImageWebpPtrEx(gdImagePtr im,int * size,int quality)411 BGD_DECLARE(void *) gdImageWebpPtrEx (gdImagePtr im, int *size, int quality)
412 {
413 	_noWebpError();
414 	return NULL;
415 }
416 
417 #endif /* HAVE_LIBWEBP */
418