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