xref: /php-src/ext/gd/libgd/gd_webp.c (revision 692cea5c)
1 #include <stdio.h>
2 #include <math.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include "gd.h"
6 #include "gdhelpers.h"
7 
8 #ifdef HAVE_LIBWEBP
9 #include "webp/decode.h"
10 #include "webp/encode.h"
11 
12 #define GD_WEBP_ALLOC_STEP (4*1024)
13 
gdImageCreateFromWebp(FILE * inFile)14 gdImagePtr gdImageCreateFromWebp (FILE * inFile)
15 {
16 	gdImagePtr im;
17 	gdIOCtx *in = gdNewFileCtx(inFile);
18 	if (!in)
19 		return 0;
20 	im = gdImageCreateFromWebpCtx(in);
21 	in->gd_free(in);
22 
23 	return im;
24 }
25 
26 
gdImageCreateFromWebpPtr(int size,void * data)27 gdImagePtr gdImageCreateFromWebpPtr (int size, void *data)
28 {
29 	gdImagePtr im;
30 	gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
31 	if (!in)
32 		return 0;
33 	im = gdImageCreateFromWebpCtx(in);
34 	in->gd_free(in);
35 	return im;
36 }
37 
gdImageCreateFromWebpCtx(gdIOCtx * infile)38 gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile)
39 {
40 	int    width, height;
41 	uint8_t   *filedata = NULL;
42 	uint8_t    *argb = NULL;
43 	size_t size = 0, n;
44 	gdImagePtr im;
45 	int x, y;
46 	uint8_t *p;
47 
48 	do {
49 		unsigned char *read, *temp;
50 
51 		temp = gdRealloc(filedata, size+GD_WEBP_ALLOC_STEP);
52 		if (temp) {
53 			filedata = temp;
54 			read = temp + size;
55 		} else {
56 			if (filedata) {
57 				gdFree(filedata);
58 			}
59 			zend_error_noreturn(E_ERROR, "WebP decode: realloc failed");
60 			return NULL;
61 		}
62 
63 		n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile);
64 		if (n>0 && n!=EOF) {
65 			size += n;
66 		}
67 	} while (n>0 && n!=EOF);
68 
69 	if (WebPGetInfo(filedata,size, &width, &height) == 0) {
70 		zend_error_noreturn(E_ERROR, "gd-webp cannot get webp info");
71 		gdFree(filedata);
72 		return NULL;
73 	}
74 
75 	im = gdImageCreateTrueColor(width, height);
76 	if (!im) {
77 		gdFree(filedata);
78 		return NULL;
79 	}
80 	argb = WebPDecodeARGB(filedata, size, &width, &height);
81 	if (!argb) {
82 		zend_error_noreturn(E_ERROR, "gd-webp cannot allocate temporary buffer");
83 		gdFree(filedata);
84 		gdImageDestroy(im);
85 		return NULL;
86 	}
87 	for (y = 0, p = argb;  y < height; y++) {
88 		for (x = 0; x < width; x++) {
89 			register uint8_t a = gdAlphaMax - (*(p++) >> 1);
90 			register uint8_t r = *(p++);
91 			register uint8_t g = *(p++);
92 			register uint8_t b = *(p++);
93 			im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a);
94 		}
95 	}
96 	gdFree(filedata);
97 	/* do not use gdFree here, in case gdFree/alloc is mapped to something else than libc */
98 	free(argb);
99 	im->saveAlphaFlag = 1;
100 	return im;
101 }
102 
gdImageWebpCtx(gdImagePtr im,gdIOCtx * outfile,int quality)103 void gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
104 {
105 	uint8_t *argb;
106 	int x, y;
107 	uint8_t *p;
108 	uint8_t *out;
109 	size_t out_size;
110 
111 	if (im == NULL) {
112 		return;
113 	}
114 
115 	if (!gdImageTrueColor(im)) {
116 		zend_error_noreturn(E_ERROR, "Palette image not supported by webp");
117 		return;
118 	}
119 
120 	if (quality == -1) {
121 		quality = 80;
122 	}
123 
124 	if (overflow2(gdImageSX(im), 4)) {
125 		return;
126 	}
127 
128 	if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) {
129 		return;
130 	}
131 
132 	argb = (uint8_t *)gdMalloc(gdImageSX(im) * 4 * gdImageSY(im));
133 	if (!argb) {
134 		return;
135 	}
136 	p = argb;
137 	for (y = 0; y < gdImageSY(im); y++) {
138 		for (x = 0; x < gdImageSX(im); x++) {
139 			register int c;
140 			register char a;
141 			c = im->tpixels[y][x];
142 			a = gdTrueColorGetAlpha(c);
143 			if (a == 127) {
144 				a = 0;
145 			} else {
146 				a = 255 - ((a << 1) + (a >> 6));
147 			}
148 			*(p++) = gdTrueColorGetRed(c);
149 			*(p++) = gdTrueColorGetGreen(c);
150 			*(p++) = gdTrueColorGetBlue(c);
151 			*(p++) = a;
152 		}
153 	}
154 
155 	if (quality >= gdWebpLossless) {
156 		out_size = WebPEncodeLosslessRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, &out);
157 	} else {
158 		out_size = WebPEncodeRGBA(argb, gdImageSX(im), gdImageSY(im), gdImageSX(im) * 4, quality, &out);
159 	}
160 
161 	if (out_size == 0) {
162 		zend_error_noreturn(E_ERROR, "gd-webp encoding failed");
163 		goto freeargb;
164 	}
165 	gdPutBuf(out, out_size, outfile);
166 	free(out);
167 
168 freeargb:
169 	gdFree(argb);
170 }
171 
gdImageWebpEx(gdImagePtr im,FILE * outFile,int quality)172 void gdImageWebpEx (gdImagePtr im, FILE * outFile, int quality)
173 {
174 	gdIOCtx *out = gdNewFileCtx(outFile);
175 	gdImageWebpCtx(im, out, quality);
176 	out->gd_free(out);
177 }
178 
gdImageWebp(gdImagePtr im,FILE * outFile)179 void gdImageWebp (gdImagePtr im, FILE * outFile)
180 {
181 	gdIOCtx *out = gdNewFileCtx(outFile);
182 	gdImageWebpCtx(im, out, -1);
183 	out->gd_free(out);
184 }
185 
gdImageWebpPtr(gdImagePtr im,int * size)186 void * gdImageWebpPtr (gdImagePtr im, int *size)
187 {
188 	void *rv;
189 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
190 	gdImageWebpCtx(im, out, -1);
191 	rv = gdDPExtractData(out, size);
192 	out->gd_free(out);
193 
194 	return rv;
195 }
196 
gdImageWebpPtrEx(gdImagePtr im,int * size,int quality)197 void * gdImageWebpPtrEx (gdImagePtr im, int *size, int quality)
198 {
199 	void *rv;
200 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
201 	gdImageWebpCtx(im, out, quality);
202 	rv = gdDPExtractData(out, size);
203 	out->gd_free(out);
204 	return rv;
205 }
206 #endif /* HAVE_LIBWEBP */
207