xref: /PHP-5.5/ext/gd/libgd/gd_rotate.c (revision 88b3cdc4)
1 #if HAVE_GD_BUNDLED
2 # include "gd.h"
3 #else
4 # include <gd.h>
5 #endif
6 
7 #include "gd_intern.h"
8 #include <math.h>
9 
10 /*
11  * Rotate function Added on 2003/12
12  * by Pierre-Alain Joye (pierre@php.net)
13  **/
14 /* Begin rotate function */
15 #ifdef ROTATE_PI
16 #undef ROTATE_PI
17 #endif /* ROTATE_PI */
18 
19 #define ROTATE_DEG2RAD  3.1415926535897932384626433832795/180
gdImageSkewX(gdImagePtr dst,gdImagePtr src,int uRow,int iOffset,double dWeight,int clrBack,int ignoretransparent)20 void gdImageSkewX (gdImagePtr dst, gdImagePtr src, int uRow, int iOffset, double dWeight, int clrBack, int ignoretransparent)
21 {
22 	typedef int (*FuncPtr)(gdImagePtr, int, int);
23 	int i, r, g, b, a, clrBackR, clrBackG, clrBackB, clrBackA;
24 	FuncPtr f;
25 
26 	int pxlOldLeft, pxlLeft=0, pxlSrc;
27 
28 	/* Keep clrBack as color index if required */
29 	if (src->trueColor) {
30 		pxlOldLeft = clrBack;
31 		f = gdImageGetTrueColorPixel;
32 	} else {
33 		pxlOldLeft = clrBack;
34 		clrBackR = gdImageRed(src, clrBack);
35 		clrBackG = gdImageGreen(src, clrBack);
36 		clrBackB = gdImageBlue(src, clrBack);
37 		clrBackA = gdImageAlpha(src, clrBack);
38 		clrBack =  gdTrueColorAlpha(clrBackR, clrBackG, clrBackB, clrBackA);
39 		f = gdImageGetPixel;
40 	}
41 
42 	for (i = 0; i < iOffset; i++) {
43 		gdImageSetPixel (dst, i, uRow, clrBack);
44 	}
45 
46 	if (i < dst->sx) {
47 		gdImageSetPixel (dst, i, uRow, clrBack);
48 	}
49 
50 	for (i = 0; i < src->sx; i++) {
51 		pxlSrc = f (src,i,uRow);
52 
53 		r = (int)(gdImageRed(src,pxlSrc) * dWeight);
54 		g = (int)(gdImageGreen(src,pxlSrc) * dWeight);
55 		b = (int)(gdImageBlue(src,pxlSrc) * dWeight);
56 		a = (int)(gdImageAlpha(src,pxlSrc) * dWeight);
57 
58 		pxlLeft = gdImageColorAllocateAlpha(src, r, g, b, a);
59 
60 		if (pxlLeft == -1) {
61 			pxlLeft = gdImageColorClosestAlpha(src, r, g, b, a);
62 		}
63 
64 		r = gdImageRed(src,pxlSrc) - (gdImageRed(src,pxlLeft) - gdImageRed(src,pxlOldLeft));
65 		g = gdImageGreen(src,pxlSrc) - (gdImageGreen(src,pxlLeft) - gdImageGreen(src,pxlOldLeft));
66 		b = gdImageBlue(src,pxlSrc) - (gdImageBlue(src,pxlLeft) - gdImageBlue(src,pxlOldLeft));
67 		a = gdImageAlpha(src,pxlSrc) - (gdImageAlpha(src,pxlLeft) - gdImageAlpha(src,pxlOldLeft));
68 
69         if (r>255) {
70         	r = 255;
71         }
72 
73 		if (g>255) {
74 			g = 255;
75 		}
76 
77 		if (b>255) {
78 			b = 255;
79 		}
80 
81 		if (a>127) {
82 			a = 127;
83 		}
84 
85 		if (ignoretransparent && pxlSrc == dst->transparent) {
86 			pxlSrc = dst->transparent;
87 		} else {
88 			pxlSrc = gdImageColorAllocateAlpha(dst, r, g, b, a);
89 
90 			if (pxlSrc == -1) {
91 				pxlSrc = gdImageColorClosestAlpha(dst, r, g, b, a);
92 			}
93 		}
94 
95 		if ((i + iOffset >= 0) && (i + iOffset < dst->sx)) {
96 			gdImageSetPixel (dst, i+iOffset, uRow,  pxlSrc);
97 		}
98 
99 		pxlOldLeft = pxlLeft;
100 	}
101 
102 	i += iOffset;
103 
104 	if (i < dst->sx) {
105 		gdImageSetPixel (dst, i, uRow, pxlLeft);
106 	}
107 
108 	gdImageSetPixel (dst, iOffset, uRow, clrBack);
109 
110 	i--;
111 
112 	while (++i < dst->sx) {
113 		gdImageSetPixel (dst, i, uRow, clrBack);
114 	}
115 }
116 
gdImageSkewY(gdImagePtr dst,gdImagePtr src,int uCol,int iOffset,double dWeight,int clrBack,int ignoretransparent)117 void gdImageSkewY (gdImagePtr dst, gdImagePtr src, int uCol, int iOffset, double dWeight, int clrBack, int ignoretransparent)
118 {
119 	typedef int (*FuncPtr)(gdImagePtr, int, int);
120 	int i, iYPos=0, r, g, b, a;
121 	FuncPtr f;
122 	int pxlOldLeft, pxlLeft=0, pxlSrc;
123 
124 	if (src->trueColor) {
125 		f = gdImageGetTrueColorPixel;
126 	} else {
127 		f = gdImageGetPixel;
128 	}
129 
130 	for (i = 0; i<=iOffset; i++) {
131 		gdImageSetPixel (dst, uCol, i, clrBack);
132 	}
133 	r = (int)((double)gdImageRed(src,clrBack) * dWeight);
134 	g = (int)((double)gdImageGreen(src,clrBack) * dWeight);
135 	b = (int)((double)gdImageBlue(src,clrBack) * dWeight);
136 	a = (int)((double)gdImageAlpha(src,clrBack) * dWeight);
137 
138 	pxlOldLeft = gdImageColorAllocateAlpha(dst, r, g, b, a);
139 
140 	for (i = 0; i < src->sy; i++) {
141 		pxlSrc = f (src, uCol, i);
142 		iYPos = i + iOffset;
143 
144 		r = (int)((double)gdImageRed(src,pxlSrc) * dWeight);
145 		g = (int)((double)gdImageGreen(src,pxlSrc) * dWeight);
146 		b = (int)((double)gdImageBlue(src,pxlSrc) * dWeight);
147 		a = (int)((double)gdImageAlpha(src,pxlSrc) * dWeight);
148 
149 		pxlLeft = gdImageColorAllocateAlpha(src, r, g, b, a);
150 
151 		if (pxlLeft == -1) {
152 			pxlLeft = gdImageColorClosestAlpha(src, r, g, b, a);
153 		}
154 
155 		r = gdImageRed(src,pxlSrc) - (gdImageRed(src,pxlLeft) - gdImageRed(src,pxlOldLeft));
156 		g = gdImageGreen(src,pxlSrc) - (gdImageGreen(src,pxlLeft) - gdImageGreen(src,pxlOldLeft));
157 		b = gdImageBlue(src,pxlSrc) - (gdImageBlue(src,pxlLeft) - gdImageBlue(src,pxlOldLeft));
158 		a = gdImageAlpha(src,pxlSrc) - (gdImageAlpha(src,pxlLeft) - gdImageAlpha(src,pxlOldLeft));
159 
160 		if (r>255) {
161         		r = 255;
162 		}
163 
164 		if (g>255) {
165 			g = 255;
166 		}
167 
168 		if (b>255) {
169     			b = 255;
170 		}
171 
172 		if (a>127) {
173 			a = 127;
174 		}
175 
176 		if (ignoretransparent && pxlSrc == dst->transparent) {
177 			pxlSrc = dst->transparent;
178 		} else {
179 			pxlSrc = gdImageColorAllocateAlpha(dst, r, g, b, a);
180 
181 			if (pxlSrc == -1) {
182 				pxlSrc = gdImageColorClosestAlpha(dst, r, g, b, a);
183 			}
184 		}
185 
186 		if ((iYPos >= 0) && (iYPos < dst->sy)) {
187 			gdImageSetPixel (dst, uCol, iYPos, pxlSrc);
188 		}
189 
190 		pxlOldLeft = pxlLeft;
191 	}
192 
193 	i = iYPos;
194 	if (i < dst->sy) {
195 		gdImageSetPixel (dst, uCol, i, pxlLeft);
196 	}
197 
198 	i--;
199 	while (++i < dst->sy) {
200 		gdImageSetPixel (dst, uCol, i, clrBack);
201 	}
202 }
203 
204 /* Rotates an image by 90 degrees (counter clockwise) */
gdImageRotate90(gdImagePtr src,int ignoretransparent)205 gdImagePtr gdImageRotate90 (gdImagePtr src, int ignoretransparent)
206 {
207 	int uY, uX;
208 	int c,r,g,b,a;
209 	gdImagePtr dst;
210 	typedef int (*FuncPtr)(gdImagePtr, int, int);
211 	FuncPtr f;
212 
213 	if (src->trueColor) {
214 		f = gdImageGetTrueColorPixel;
215 	} else {
216 		f = gdImageGetPixel;
217 	}
218 	dst = gdImageCreateTrueColor(src->sy, src->sx);
219 
220 	if (dst != NULL) {
221 		int old_blendmode = dst->alphaBlendingFlag;
222 		dst->alphaBlendingFlag = 0;
223 
224 		dst->transparent = src->transparent;
225 
226 		gdImagePaletteCopy (dst, src);
227 
228 		for (uY = 0; uY<src->sy; uY++) {
229 			for (uX = 0; uX<src->sx; uX++) {
230 				c = f (src, uX, uY);
231 				if (!src->trueColor) {
232 					r = gdImageRed(src,c);
233 					g = gdImageGreen(src,c);
234 					b = gdImageBlue(src,c);
235 					a = gdImageAlpha(src,c);
236 					c = gdTrueColorAlpha(r, g, b, a);
237 				}
238 				if (ignoretransparent && c == dst->transparent) {
239 					gdImageSetPixel(dst, uY, (dst->sy - uX - 1), dst->transparent);
240 				} else {
241 					gdImageSetPixel(dst, uY, (dst->sy - uX - 1), c);
242 				}
243 			}
244 		}
245 		dst->alphaBlendingFlag = old_blendmode;
246 	}
247 
248 	return dst;
249 }
250 
251 /* Rotates an image by 180 degrees (counter clockwise) */
gdImageRotate180(gdImagePtr src,int ignoretransparent)252 gdImagePtr gdImageRotate180 (gdImagePtr src, int ignoretransparent)
253 {
254 	int uY, uX;
255 	int c,r,g,b,a;
256 	gdImagePtr dst;
257 	typedef int (*FuncPtr)(gdImagePtr, int, int);
258 	FuncPtr f;
259 
260 	if (src->trueColor) {
261 		f = gdImageGetTrueColorPixel;
262 	} else {
263 		f = gdImageGetPixel;
264 	}
265 	dst = gdImageCreateTrueColor(src->sx, src->sy);
266 
267 	if (dst != NULL) {
268 		int old_blendmode = dst->alphaBlendingFlag;
269 		dst->alphaBlendingFlag = 0;
270 
271 		dst->transparent = src->transparent;
272 
273 		gdImagePaletteCopy (dst, src);
274 
275 		for (uY = 0; uY<src->sy; uY++) {
276 			for (uX = 0; uX<src->sx; uX++) {
277 				c = f (src, uX, uY);
278 				if (!src->trueColor) {
279 					r = gdImageRed(src,c);
280 					g = gdImageGreen(src,c);
281 					b = gdImageBlue(src,c);
282 					a = gdImageAlpha(src,c);
283 					c = gdTrueColorAlpha(r, g, b, a);
284 				}
285 
286 				if (ignoretransparent && c == dst->transparent) {
287 					gdImageSetPixel(dst, (dst->sx - uX - 1), (dst->sy - uY - 1), dst->transparent);
288 				} else {
289 					gdImageSetPixel(dst, (dst->sx - uX - 1), (dst->sy - uY - 1), c);
290 				}
291 			}
292 		}
293 		dst->alphaBlendingFlag = old_blendmode;
294 	}
295 
296 	return dst;
297 }
298 
299 /* Rotates an image by 270 degrees (counter clockwise) */
gdImageRotate270(gdImagePtr src,int ignoretransparent)300 gdImagePtr gdImageRotate270 (gdImagePtr src, int ignoretransparent)
301 {
302 	int uY, uX;
303 	int c,r,g,b,a;
304 	gdImagePtr dst;
305 	typedef int (*FuncPtr)(gdImagePtr, int, int);
306 	FuncPtr f;
307 
308 	if (src->trueColor) {
309 		f = gdImageGetTrueColorPixel;
310 	} else {
311 		f = gdImageGetPixel;
312 	}
313 	dst = gdImageCreateTrueColor (src->sy, src->sx);
314 
315 	if (dst != NULL) {
316 		int old_blendmode = dst->alphaBlendingFlag;
317 		dst->alphaBlendingFlag = 0;
318 
319 		dst->transparent = src->transparent;
320 
321 		gdImagePaletteCopy (dst, src);
322 
323 		for (uY = 0; uY<src->sy; uY++) {
324 			for (uX = 0; uX<src->sx; uX++) {
325 				c = f (src, uX, uY);
326 				if (!src->trueColor) {
327 					r = gdImageRed(src,c);
328 					g = gdImageGreen(src,c);
329 					b = gdImageBlue(src,c);
330 					a = gdImageAlpha(src,c);
331 					c = gdTrueColorAlpha(r, g, b, a);
332 				}
333 
334 				if (ignoretransparent && c == dst->transparent) {
335 					gdImageSetPixel(dst, (dst->sx - uY - 1), uX, dst->transparent);
336 				} else {
337 					gdImageSetPixel(dst, (dst->sx - uY - 1), uX, c);
338 				}
339 			}
340 		}
341 		dst->alphaBlendingFlag = old_blendmode;
342 	}
343 
344 	return dst;
345 }
346 
gdImageRotate45(gdImagePtr src,double dAngle,int clrBack,int ignoretransparent)347 gdImagePtr gdImageRotate45 (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent)
348 {
349 	typedef int (*FuncPtr)(gdImagePtr, int, int);
350 	gdImagePtr dst1,dst2,dst3;
351 	FuncPtr f;
352 	double dRadAngle, dSinE, dTan, dShear;
353 	double dOffset;     /* Variable skew offset */
354 	int u, iShear, newx, newy;
355 	int clrBackR, clrBackG, clrBackB, clrBackA;
356 
357 	/* See GEMS I for the algorithm details */
358 	dRadAngle = dAngle * ROTATE_DEG2RAD; /* Angle in radians */
359 	dSinE = sin (dRadAngle);
360 	dTan = tan (dRadAngle / 2.0);
361 
362 	newx = (int)(src->sx + src->sy * fabs(dTan));
363 	newy = src->sy;
364 
365 	/* 1st shear */
366 	if (src->trueColor) {
367 		f = gdImageGetTrueColorPixel;
368 	} else {
369 		f = gdImageGetPixel;
370 	}
371 
372 	dst1 = gdImageCreateTrueColor(newx, newy);
373 	/******* Perform 1st shear (horizontal) ******/
374 	if (dst1 == NULL) {
375 		return NULL;
376 	}
377 #ifdef HAVE_GD_BUNDLED
378 	dst1->alphaBlendingFlag = gdEffectReplace;
379 #else
380 	gdImageAlphaBlending(dst1, 0);
381 #endif
382 	if (dAngle == 0.0) {
383 		/* Returns copy of src */
384 		gdImageCopy (dst1, src,0,0,0,0,src->sx,src->sy);
385 		return dst1;
386 	}
387 
388 	gdImagePaletteCopy (dst1, src);
389 
390 	if (ignoretransparent) {
391 		if (gdImageTrueColor(src)) {
392 			dst1->transparent = src->transparent;
393 		} else {
394 
395 			dst1->transparent = gdTrueColorAlpha(gdImageRed(src, src->transparent), gdImageBlue(src, src->transparent), gdImageGreen(src, src->transparent), 127);
396 		}
397 	}
398 
399 	dRadAngle = dAngle * ROTATE_DEG2RAD; /* Angle in radians */
400 	dSinE = sin (dRadAngle);
401 	dTan = tan (dRadAngle / 2.0);
402 
403 	for (u = 0; u < dst1->sy; u++) {
404 		if (dTan >= 0.0) {
405 			dShear = ((double)(u + 0.5)) * dTan;
406 		} else {
407 			dShear = ((double)(u - dst1->sy) + 0.5) * dTan;
408 		}
409 
410 		iShear = (int)floor(dShear);
411 		gdImageSkewX(dst1, src, u, iShear, (dShear - iShear), clrBack, ignoretransparent);
412 	}
413 
414 	/*
415 	The 1st shear may use the original clrBack as color index
416 	Convert it once here
417 	*/
418 	if(!src->trueColor) {
419 		clrBackR = gdImageRed(src, clrBack);
420 		clrBackG = gdImageGreen(src, clrBack);
421 		clrBackB = gdImageBlue(src, clrBack);
422 		clrBackA = gdImageAlpha(src, clrBack);
423 		clrBack =  gdTrueColorAlpha(clrBackR, clrBackG, clrBackB, clrBackA);
424 	}
425 	/* 2nd shear */
426 	newx = dst1->sx;
427 
428 	if (dSinE > 0.0) {
429 		dOffset = (src->sx-1) * dSinE;
430 	} else {
431 		dOffset = -dSinE *  (src->sx - newx);
432 	}
433 
434 	newy = (int) ((double) src->sx * fabs( dSinE ) + (double) src->sy * cos (dRadAngle))+1;
435 
436 	if (src->trueColor) {
437 		f = gdImageGetTrueColorPixel;
438 	} else {
439 		f = gdImageGetPixel;
440 	}
441 	dst2 = gdImageCreateTrueColor(newx, newy);
442 	if (dst2 == NULL) {
443 		gdImageDestroy(dst1);
444 		return NULL;
445 	}
446 
447 #ifdef HAVE_GD_BUNDLED
448 	dst2->alphaBlendingFlag = gdEffectReplace;
449 #else
450 	gdImageAlphaBlending(dst2, 0);
451 #endif
452 
453 	if (ignoretransparent) {
454 		dst2->transparent = dst1->transparent;
455 	}
456 
457 	for (u = 0; u < dst2->sx; u++, dOffset -= dSinE) {
458 		iShear = (int)floor (dOffset);
459 		gdImageSkewY(dst2, dst1, u, iShear, (dOffset - (double)iShear), clrBack, ignoretransparent);
460 	}
461 
462 	/* 3rd shear */
463 	gdImageDestroy(dst1);
464 
465 	newx = (int) ((double)src->sy * fabs (dSinE) + (double)src->sx * cos (dRadAngle)) + 1;
466 	newy = dst2->sy;
467 
468 	if (src->trueColor) {
469 		f = gdImageGetTrueColorPixel;
470 	} else {
471 		f = gdImageGetPixel;
472 	}
473 	dst3 = gdImageCreateTrueColor(newx, newy);
474 	if (dst3 == NULL) {
475 		gdImageDestroy(dst2);
476 		return NULL;
477 	}
478 
479 #ifdef HAVE_GD_BUNDLED
480 	dst3->alphaBlendingFlag = gdEffectReplace;
481 #else
482 	gdImageAlphaBlending(dst3, 0);
483 #endif
484 
485 	if (ignoretransparent) {
486 		dst3->transparent = dst2->transparent;
487 	}
488 
489 	if (dSinE >= 0.0) {
490 		dOffset = (double)(src->sx - 1) * dSinE * -dTan;
491 	} else {
492 		dOffset = dTan * ((double)(src->sx - 1) * -dSinE + (double)(1 - newy));
493 	}
494 
495 	for (u = 0; u < dst3->sy; u++, dOffset += dTan) {
496 		int iShear = (int)floor(dOffset);
497 		gdImageSkewX(dst3, dst2, u, iShear, (dOffset - iShear), clrBack, ignoretransparent);
498 	}
499 
500 	gdImageDestroy(dst2);
501 
502 	return dst3;
503 }
504 
gdImageRotate(gdImagePtr src,double dAngle,int clrBack,int ignoretransparent)505 gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent)
506 {
507 	gdImagePtr pMidImg;
508 	gdImagePtr rotatedImg;
509 
510 	if (src == NULL) {
511 		return NULL;
512 	}
513 
514 	if (!gdImageTrueColor(src) && (clrBack < 0 || clrBack>=gdImageColorsTotal(src))) {
515 		return NULL;
516 	}
517 
518 	while (dAngle >= 360.0) {
519 		dAngle -= 360.0;
520 	}
521 
522 	while (dAngle < 0) {
523 		dAngle += 360.0;
524 	}
525 
526 	if (dAngle == 90.00) {
527 		return gdImageRotate90(src, ignoretransparent);
528 	}
529 	if (dAngle == 180.00) {
530 		return gdImageRotate180(src, ignoretransparent);
531 	}
532 	if(dAngle == 270.00) {
533 		return gdImageRotate270 (src, ignoretransparent);
534 	}
535 
536 	if ((dAngle > 45.0) && (dAngle <= 135.0)) {
537 		pMidImg = gdImageRotate90 (src, ignoretransparent);
538 		dAngle -= 90.0;
539 	} else if ((dAngle > 135.0) && (dAngle <= 225.0)) {
540 		pMidImg = gdImageRotate180 (src, ignoretransparent);
541 		dAngle -= 180.0;
542 	} else if ((dAngle > 225.0) && (dAngle <= 315.0)) {
543 		pMidImg = gdImageRotate270 (src, ignoretransparent);
544 		dAngle -= 270.0;
545 	} else {
546 		return gdImageRotate45 (src, dAngle, clrBack, ignoretransparent);
547 	}
548 
549 	if (pMidImg == NULL) {
550 		return NULL;
551 	}
552 
553 	rotatedImg = gdImageRotate45 (pMidImg, dAngle, clrBack, ignoretransparent);
554 	gdImageDestroy(pMidImg);
555 
556 	return rotatedImg;
557 }
558 /* End Rotate function */
559 
560 
561