xref: /PHP-5.6/ext/gd/libgd/gd_webp.c (revision 46df0642)
1 #include <stdio.h>
2 #include <math.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include "gd.h"
6 
7 
8 #ifdef HAVE_LIBVPX
9 #include "webpimg.h"
10 #include "gdhelpers.h"
11 
12 extern void gd_YUV420toRGBA(uint8* Y,
13                   uint8* U,
14                   uint8* V,
15                   gdImagePtr im);
16 
17 extern void gd_RGBAToYUV420(gdImagePtr im2,
18                   uint8* Y,
19                   uint8* U,
20                   uint8* V);
21 
gdWebpGetVersionString()22 const char * gdWebpGetVersionString()
23 {
24 	return "not defined";
25 }
26 
gdImageCreateFromWebp(FILE * inFile)27 gdImagePtr gdImageCreateFromWebp (FILE * inFile)
28 {
29 	gdImagePtr im;
30 	gdIOCtx *in = gdNewFileCtx(inFile);
31 	im = gdImageCreateFromWebpCtx(in);
32 	in->gd_free(in);
33 
34 	return im;
35 }
36 
gdImageCreateFromWebpPtr(int size,void * data)37 gdImagePtr gdImageCreateFromWebpPtr (int size, void *data)
38 {
39 	int    width, height, ret;
40  	unsigned char   *Y = NULL;
41 	unsigned char   *U = NULL;
42 	unsigned char   *V = NULL;
43 	gdImagePtr im;
44 
45 	ret = WebPDecode(data, size, &Y, &U, &V, &width, &height);
46 	if (ret != webp_success) {
47 		if (Y) free(Y);
48 		if (U) free(U);
49 		if (V) free(V);
50 		php_gd_error("WebP decode: fail to decode input data");
51 		return NULL;
52 	}
53 	im = gdImageCreateTrueColor(width, height);
54 	if (!im) {
55 		return NULL;
56 	}
57 	gd_YUV420toRGBA(Y, U, V, im);
58 	return im;
59 }
60 
61 #define GD_WEBP_ALLOC_STEP (4*1024)
62 
gdImageCreateFromWebpCtx(gdIOCtx * infile)63 gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile)
64 {
65 	int    width, height, ret;
66 	unsigned char   *filedata = NULL;
67 	unsigned char   *read, *temp;
68 	unsigned char   *Y = NULL;
69 	unsigned char   *U = NULL;
70 	unsigned char   *V = NULL;
71 	size_t size = 0, n;
72 	gdImagePtr im;
73 
74 	do {
75 		temp = gdRealloc(filedata, size+GD_WEBP_ALLOC_STEP);
76 		if (temp) {
77 			filedata = temp;
78 			read = temp + size;
79 		} else {
80 			if (filedata) {
81 				gdFree(filedata);
82 			}
83 			php_gd_error("WebP decode: realloc failed");
84 			return NULL;
85 		}
86 
87 		n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile);
88 		/* differs from upstream where gdGetBuf return 0 instead of EOF */
89 		if (n>0 && n!=EOF) {
90 			size += n;
91 		}
92 	} while (n>0 && n!=EOF);
93 
94 	ret = WebPDecode(filedata, size, &Y, &U, &V, &width, &height);
95 	gdFree(filedata);
96 	if (ret != webp_success) {
97 		if (Y) free(Y);
98 		if (U) free(U);
99 		if (V) free(V);
100 		php_gd_error("WebP decode: fail to decode input data");
101 		return NULL;
102 	}
103 	im = gdImageCreateTrueColor(width, height);
104 	gd_YUV420toRGBA(Y, U, V, im);
105 	return im;
106 }
107 
gdImageWebpEx(gdImagePtr im,FILE * outFile,int quantization)108 void gdImageWebpEx (gdImagePtr im, FILE * outFile, int quantization)
109 {
110 	gdIOCtx *out = gdNewFileCtx(outFile);
111 	gdImageWebpCtx(im, out, quantization);
112 	out->gd_free(out);
113 }
114 
gdImageWebp(gdImagePtr im,FILE * outFile)115 void gdImageWebp (gdImagePtr im, FILE * outFile)
116 {
117 	gdIOCtx *out = gdNewFileCtx(outFile);
118   	gdImageWebpCtx(im, out, -1);
119 	out->gd_free(out);
120 }
121 
gdImageWebpPtr(gdImagePtr im,int * size)122 void * gdImageWebpPtr (gdImagePtr im, int *size)
123 {
124 	void *rv;
125 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
126 	gdImageWebpCtx(im, out, -1);
127 	rv = gdDPExtractData(out, size);
128 	out->gd_free(out);
129 
130 	return rv;
131 }
132 
gdImageWebpPtrEx(gdImagePtr im,int * size,int quantization)133 void * gdImageWebpPtrEx (gdImagePtr im, int *size, int quantization)
134 {
135 	void *rv;
136 	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
137 	gdImageWebpCtx(im, out, quantization);
138 	rv = gdDPExtractData(out, size);
139 	out->gd_free(out);
140 	return rv;
141 }
142 
143 /*
144  * Maps normalized QP (quality) to VP8 QP
145  */
mapQualityToVP8QP(int quality)146 int mapQualityToVP8QP(int quality) {
147 #define MIN_QUALITY 0
148 #define MAX_QUALITY 100
149 #define MIN_VP8QP 1
150 #define MAX_VP8QP 63
151 	const float scale = MAX_VP8QP - MIN_VP8QP;
152 	const float vp8qp =
153 	scale * (MAX_QUALITY - quality) / (MAX_QUALITY - MIN_QUALITY) + MIN_VP8QP;
154 	if (quality < MIN_QUALITY || quality > MAX_QUALITY) {
155 		php_gd_error("Wrong quality value %d.", quality);
156 		return -1;
157 	}
158 
159 	return (int)(vp8qp + 0.5);
160 }
161 
162 /* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
163  *  and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
164  *  (http://www.cdrom.com/pub/png/pngbook.html).
165  */
gdImageWebpCtx(gdImagePtr im,gdIOCtx * outfile,int quantization)166 void gdImageWebpCtx (gdImagePtr im, gdIOCtx * outfile, int quantization)
167 {
168 	int width = im->sx;
169 	int height = im->sy;
170 	int colors = im->colorsTotal;
171 	int *open = im->open;
172 
173 	int  yuv_width, yuv_height, yuv_nbytes, ret;
174 	int vp8_quality;
175 	unsigned char *Y = NULL,
176 				  *U = NULL,
177 				  *V = NULL;
178 	unsigned char *filedata = NULL;
179 
180 	/* Conversion to Y,U,V buffer */
181     yuv_width = (width + 1) >> 1;
182     yuv_height = (height + 1) >> 1;
183 
184 	if (overflow2(width, height)) {
185 		return;
186 	}
187 	/* simplification possible, because WebP must not be larger than 16384**2 */
188 	if (overflow2(width * height, 2 * sizeof(unsigned char))) {
189 		return;
190 	}
191 
192     yuv_nbytes = width * height + 2 * yuv_width * yuv_height;
193 
194     if ((Y = (unsigned char *)gdCalloc(yuv_nbytes, sizeof(unsigned char))) == NULL) {
195     	php_gd_error("gd-webp error: cannot allocate Y buffer");
196         return;
197     }
198 	vp8_quality = mapQualityToVP8QP(quantization);
199 
200     U = Y + width * height;
201     V = U + yuv_width * yuv_height;
202     gd_RGBAToYUV420(im, Y, U, V);
203 
204 	/* Encode Y,U,V and write data to file */
205     ret = WebPEncode(Y, U, V, width, height, width, yuv_width, yuv_height, yuv_width,
206                      vp8_quality, &filedata, &yuv_nbytes, NULL);
207 	gdFree(Y);
208 
209     if (ret != webp_success) {
210     	if (filedata) {
211     		free(filedata);
212 		}
213 		php_gd_error("gd-webp error: WebP Encoder failed");
214 		return;
215     }
216 
217     gdPutBuf (filedata, yuv_nbytes, outfile);
218     free(filedata);
219 }
220 
221 #endif /* HAVE_LIBVPX */
222