Pillow
1159 строк · 38.9 Кб
1#include "Imaging.h"
2
3/* For large images rotation is an inefficient operation in terms of CPU cache.
4One row in the source image affects each column in destination.
5Rotating in chunks that fit in the cache can speed up rotation
68x on a modern CPU. A chunk size of 128 requires only 65k and is large enough
7that the overhead from the extra loops are not apparent. */
8#define ROTATE_CHUNK 512
9#define ROTATE_SMALL_CHUNK 8
10
11#define COORD(v) ((v) < 0.0 ? -1 : ((int)(v)))
12#define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v)))
13
14/* -------------------------------------------------------------------- */
15/* Transpose operations */
16
17Imaging
18ImagingFlipLeftRight(Imaging imOut, Imaging imIn) {
19ImagingSectionCookie cookie;
20int x, y, xr;
21
22if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
23return (Imaging)ImagingError_ModeError();
24}
25if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
26return (Imaging)ImagingError_Mismatch();
27}
28
29ImagingCopyPalette(imOut, imIn);
30
31#define FLIP_LEFT_RIGHT(INT, image) \
32for (y = 0; y < imIn->ysize; y++) { \
33INT *in = (INT *)imIn->image[y]; \
34INT *out = (INT *)imOut->image[y]; \
35xr = imIn->xsize - 1; \
36for (x = 0; x < imIn->xsize; x++, xr--) { \
37out[xr] = in[x]; \
38} \
39}
40
41ImagingSectionEnter(&cookie);
42
43if (imIn->image8) {
44if (strncmp(imIn->mode, "I;16", 4) == 0) {
45FLIP_LEFT_RIGHT(UINT16, image8)
46} else {
47FLIP_LEFT_RIGHT(UINT8, image8)
48}
49} else {
50FLIP_LEFT_RIGHT(INT32, image32)
51}
52
53ImagingSectionLeave(&cookie);
54
55#undef FLIP_LEFT_RIGHT
56
57return imOut;
58}
59
60Imaging
61ImagingFlipTopBottom(Imaging imOut, Imaging imIn) {
62ImagingSectionCookie cookie;
63int y, yr;
64
65if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
66return (Imaging)ImagingError_ModeError();
67}
68if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
69return (Imaging)ImagingError_Mismatch();
70}
71
72ImagingCopyPalette(imOut, imIn);
73
74ImagingSectionEnter(&cookie);
75
76yr = imIn->ysize - 1;
77for (y = 0; y < imIn->ysize; y++, yr--) {
78memcpy(imOut->image[yr], imIn->image[y], imIn->linesize);
79}
80
81ImagingSectionLeave(&cookie);
82
83return imOut;
84}
85
86Imaging
87ImagingRotate90(Imaging imOut, Imaging imIn) {
88ImagingSectionCookie cookie;
89int x, y, xx, yy, xr, xxsize, yysize;
90int xxx, yyy, xxxsize, yyysize;
91
92if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
93return (Imaging)ImagingError_ModeError();
94}
95if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
96return (Imaging)ImagingError_Mismatch();
97}
98
99ImagingCopyPalette(imOut, imIn);
100
101#define ROTATE_90(INT, image) \
102for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
103for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
104yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
105xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
106for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
107for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
108yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
109? yy + ROTATE_SMALL_CHUNK \
110: imIn->ysize; \
111xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
112? xx + ROTATE_SMALL_CHUNK \
113: imIn->xsize; \
114for (yyy = yy; yyy < yyysize; yyy++) { \
115INT *in = (INT *)imIn->image[yyy]; \
116xr = imIn->xsize - 1 - xx; \
117for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
118INT *out = (INT *)imOut->image[xr]; \
119out[yyy] = in[xxx]; \
120} \
121} \
122} \
123} \
124} \
125}
126
127ImagingSectionEnter(&cookie);
128
129if (imIn->image8) {
130if (strncmp(imIn->mode, "I;16", 4) == 0) {
131ROTATE_90(UINT16, image8);
132} else {
133ROTATE_90(UINT8, image8);
134}
135} else {
136ROTATE_90(INT32, image32);
137}
138
139ImagingSectionLeave(&cookie);
140
141#undef ROTATE_90
142
143return imOut;
144}
145
146Imaging
147ImagingTranspose(Imaging imOut, Imaging imIn) {
148ImagingSectionCookie cookie;
149int x, y, xx, yy, xxsize, yysize;
150int xxx, yyy, xxxsize, yyysize;
151
152if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
153return (Imaging)ImagingError_ModeError();
154}
155if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
156return (Imaging)ImagingError_Mismatch();
157}
158
159ImagingCopyPalette(imOut, imIn);
160
161#define TRANSPOSE(INT, image) \
162for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
163for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
164yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
165xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
166for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
167for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
168yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
169? yy + ROTATE_SMALL_CHUNK \
170: imIn->ysize; \
171xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
172? xx + ROTATE_SMALL_CHUNK \
173: imIn->xsize; \
174for (yyy = yy; yyy < yyysize; yyy++) { \
175INT *in = (INT *)imIn->image[yyy]; \
176for (xxx = xx; xxx < xxxsize; xxx++) { \
177INT *out = (INT *)imOut->image[xxx]; \
178out[yyy] = in[xxx]; \
179} \
180} \
181} \
182} \
183} \
184}
185
186ImagingSectionEnter(&cookie);
187
188if (imIn->image8) {
189if (strncmp(imIn->mode, "I;16", 4) == 0) {
190TRANSPOSE(UINT16, image8);
191} else {
192TRANSPOSE(UINT8, image8);
193}
194} else {
195TRANSPOSE(INT32, image32);
196}
197
198ImagingSectionLeave(&cookie);
199
200#undef TRANSPOSE
201
202return imOut;
203}
204
205Imaging
206ImagingTransverse(Imaging imOut, Imaging imIn) {
207ImagingSectionCookie cookie;
208int x, y, xr, yr, xx, yy, xxsize, yysize;
209int xxx, yyy, xxxsize, yyysize;
210
211if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
212return (Imaging)ImagingError_ModeError();
213}
214if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
215return (Imaging)ImagingError_Mismatch();
216}
217
218ImagingCopyPalette(imOut, imIn);
219
220#define TRANSVERSE(INT, image) \
221for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
222for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
223yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
224xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
225for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
226for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
227yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
228? yy + ROTATE_SMALL_CHUNK \
229: imIn->ysize; \
230xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
231? xx + ROTATE_SMALL_CHUNK \
232: imIn->xsize; \
233yr = imIn->ysize - 1 - yy; \
234for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
235INT *in = (INT *)imIn->image[yyy]; \
236xr = imIn->xsize - 1 - xx; \
237for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
238INT *out = (INT *)imOut->image[xr]; \
239out[yr] = in[xxx]; \
240} \
241} \
242} \
243} \
244} \
245}
246
247ImagingSectionEnter(&cookie);
248
249if (imIn->image8) {
250if (strncmp(imIn->mode, "I;16", 4) == 0) {
251TRANSVERSE(UINT16, image8);
252} else {
253TRANSVERSE(UINT8, image8);
254}
255} else {
256TRANSVERSE(INT32, image32);
257}
258
259ImagingSectionLeave(&cookie);
260
261#undef TRANSVERSE
262
263return imOut;
264}
265
266Imaging
267ImagingRotate180(Imaging imOut, Imaging imIn) {
268ImagingSectionCookie cookie;
269int x, y, xr, yr;
270
271if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
272return (Imaging)ImagingError_ModeError();
273}
274if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
275return (Imaging)ImagingError_Mismatch();
276}
277
278ImagingCopyPalette(imOut, imIn);
279
280#define ROTATE_180(INT, image) \
281for (y = 0; y < imIn->ysize; y++, yr--) { \
282INT *in = (INT *)imIn->image[y]; \
283INT *out = (INT *)imOut->image[yr]; \
284xr = imIn->xsize - 1; \
285for (x = 0; x < imIn->xsize; x++, xr--) { \
286out[xr] = in[x]; \
287} \
288}
289
290ImagingSectionEnter(&cookie);
291
292yr = imIn->ysize - 1;
293if (imIn->image8) {
294if (strncmp(imIn->mode, "I;16", 4) == 0) {
295ROTATE_180(UINT16, image8)
296} else {
297ROTATE_180(UINT8, image8)
298}
299} else {
300ROTATE_180(INT32, image32)
301}
302
303ImagingSectionLeave(&cookie);
304
305#undef ROTATE_180
306
307return imOut;
308}
309
310Imaging
311ImagingRotate270(Imaging imOut, Imaging imIn) {
312ImagingSectionCookie cookie;
313int x, y, xx, yy, yr, xxsize, yysize;
314int xxx, yyy, xxxsize, yyysize;
315
316if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
317return (Imaging)ImagingError_ModeError();
318}
319if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
320return (Imaging)ImagingError_Mismatch();
321}
322
323ImagingCopyPalette(imOut, imIn);
324
325#define ROTATE_270(INT, image) \
326for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
327for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
328yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
329xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
330for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
331for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
332yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
333? yy + ROTATE_SMALL_CHUNK \
334: imIn->ysize; \
335xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
336? xx + ROTATE_SMALL_CHUNK \
337: imIn->xsize; \
338yr = imIn->ysize - 1 - yy; \
339for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
340INT *in = (INT *)imIn->image[yyy]; \
341for (xxx = xx; xxx < xxxsize; xxx++) { \
342INT *out = (INT *)imOut->image[xxx]; \
343out[yr] = in[xxx]; \
344} \
345} \
346} \
347} \
348} \
349}
350
351ImagingSectionEnter(&cookie);
352
353if (imIn->image8) {
354if (strncmp(imIn->mode, "I;16", 4) == 0) {
355ROTATE_270(UINT16, image8);
356} else {
357ROTATE_270(UINT8, image8);
358}
359} else {
360ROTATE_270(INT32, image32);
361}
362
363ImagingSectionLeave(&cookie);
364
365#undef ROTATE_270
366
367return imOut;
368}
369
370/* -------------------------------------------------------------------- */
371/* Transforms */
372
373/* transform primitives (ImagingTransformMap) */
374
375static int
376affine_transform(double *xout, double *yout, int x, int y, void *data) {
377/* full moon tonight. your compiler will generate bogus code
378for simple expressions, unless you reorganize the code, or
379install Service Pack 3 */
380
381double *a = (double *)data;
382double a0 = a[0];
383double a1 = a[1];
384double a2 = a[2];
385double a3 = a[3];
386double a4 = a[4];
387double a5 = a[5];
388
389double xin = x + 0.5;
390double yin = y + 0.5;
391
392xout[0] = a0 * xin + a1 * yin + a2;
393yout[0] = a3 * xin + a4 * yin + a5;
394
395return 1;
396}
397
398static int
399perspective_transform(double *xout, double *yout, int x, int y, void *data) {
400double *a = (double *)data;
401double a0 = a[0];
402double a1 = a[1];
403double a2 = a[2];
404double a3 = a[3];
405double a4 = a[4];
406double a5 = a[5];
407double a6 = a[6];
408double a7 = a[7];
409
410double xin = x + 0.5;
411double yin = y + 0.5;
412
413xout[0] = (a0 * xin + a1 * yin + a2) / (a6 * xin + a7 * yin + 1);
414yout[0] = (a3 * xin + a4 * yin + a5) / (a6 * xin + a7 * yin + 1);
415
416return 1;
417}
418
419static int
420quad_transform(double *xout, double *yout, int x, int y, void *data) {
421/* quad warp: map quadrilateral to rectangle */
422
423double *a = (double *)data;
424double a0 = a[0];
425double a1 = a[1];
426double a2 = a[2];
427double a3 = a[3];
428double a4 = a[4];
429double a5 = a[5];
430double a6 = a[6];
431double a7 = a[7];
432
433double xin = x + 0.5;
434double yin = y + 0.5;
435
436xout[0] = a0 + a1 * xin + a2 * yin + a3 * xin * yin;
437yout[0] = a4 + a5 * xin + a6 * yin + a7 * xin * yin;
438
439return 1;
440}
441
442/* transform filters (ImagingTransformFilter) */
443
444static int
445nearest_filter8(void *out, Imaging im, double xin, double yin) {
446int x = COORD(xin);
447int y = COORD(yin);
448if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
449return 0;
450}
451((UINT8 *)out)[0] = im->image8[y][x];
452return 1;
453}
454
455static int
456nearest_filter16(void *out, Imaging im, double xin, double yin) {
457int x = COORD(xin);
458int y = COORD(yin);
459if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
460return 0;
461}
462memcpy(out, im->image8[y] + x * sizeof(INT16), sizeof(INT16));
463return 1;
464}
465
466static int
467nearest_filter32(void *out, Imaging im, double xin, double yin) {
468int x = COORD(xin);
469int y = COORD(yin);
470if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
471return 0;
472}
473memcpy(out, &im->image32[y][x], sizeof(INT32));
474return 1;
475}
476
477#define XCLIP(im, x) (((x) < 0) ? 0 : ((x) < im->xsize) ? (x) : im->xsize - 1)
478#define YCLIP(im, y) (((y) < 0) ? 0 : ((y) < im->ysize) ? (y) : im->ysize - 1)
479
480#define BILINEAR(v, a, b, d) (v = (a) + ((b) - (a)) * (d))
481
482#define BILINEAR_HEAD(type) \
483int x, y; \
484int x0, x1; \
485double v1, v2; \
486double dx, dy; \
487type *in; \
488if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize) { \
489return 0; \
490} \
491xin -= 0.5; \
492yin -= 0.5; \
493x = FLOOR(xin); \
494y = FLOOR(yin); \
495dx = xin - x; \
496dy = yin - y;
497
498#define BILINEAR_BODY(type, image, step, offset) \
499{ \
500in = (type *)((image)[YCLIP(im, y)] + offset); \
501x0 = XCLIP(im, x + 0) * step; \
502x1 = XCLIP(im, x + 1) * step; \
503BILINEAR(v1, in[x0], in[x1], dx); \
504if (y + 1 >= 0 && y + 1 < im->ysize) { \
505in = (type *)((image)[y + 1] + offset); \
506BILINEAR(v2, in[x0], in[x1], dx); \
507} else { \
508v2 = v1; \
509} \
510BILINEAR(v1, v1, v2, dy); \
511}
512
513static int
514bilinear_filter8(void *out, Imaging im, double xin, double yin) {
515BILINEAR_HEAD(UINT8);
516BILINEAR_BODY(UINT8, im->image8, 1, 0);
517((UINT8 *)out)[0] = (UINT8)v1;
518return 1;
519}
520
521static int
522bilinear_filter32I(void *out, Imaging im, double xin, double yin) {
523INT32 k;
524BILINEAR_HEAD(INT32);
525BILINEAR_BODY(INT32, im->image32, 1, 0);
526k = v1;
527memcpy(out, &k, sizeof(k));
528return 1;
529}
530
531static int
532bilinear_filter32F(void *out, Imaging im, double xin, double yin) {
533FLOAT32 k;
534BILINEAR_HEAD(FLOAT32);
535BILINEAR_BODY(FLOAT32, im->image32, 1, 0);
536k = v1;
537memcpy(out, &k, sizeof(k));
538return 1;
539}
540
541static int
542bilinear_filter32LA(void *out, Imaging im, double xin, double yin) {
543BILINEAR_HEAD(UINT8);
544BILINEAR_BODY(UINT8, im->image, 4, 0);
545((UINT8 *)out)[0] = (UINT8)v1;
546((UINT8 *)out)[1] = (UINT8)v1;
547((UINT8 *)out)[2] = (UINT8)v1;
548BILINEAR_BODY(UINT8, im->image, 4, 3);
549((UINT8 *)out)[3] = (UINT8)v1;
550return 1;
551}
552
553static int
554bilinear_filter32RGB(void *out, Imaging im, double xin, double yin) {
555int b;
556BILINEAR_HEAD(UINT8);
557for (b = 0; b < im->bands; b++) {
558BILINEAR_BODY(UINT8, im->image, 4, b);
559((UINT8 *)out)[b] = (UINT8)v1;
560}
561return 1;
562}
563
564#undef BILINEAR
565#undef BILINEAR_HEAD
566#undef BILINEAR_BODY
567
568#define BICUBIC(v, v1, v2, v3, v4, d) \
569{ \
570double p1 = v2; \
571double p2 = -v1 + v3; \
572double p3 = 2 * (v1 - v2) + v3 - v4; \
573double p4 = -v1 + v2 - v3 + v4; \
574v = p1 + (d) * (p2 + (d) * (p3 + (d) * p4)); \
575}
576
577#define BICUBIC_HEAD(type) \
578int x = FLOOR(xin); \
579int y = FLOOR(yin); \
580int x0, x1, x2, x3; \
581double v1, v2, v3, v4; \
582double dx, dy; \
583type *in; \
584if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize) { \
585return 0; \
586} \
587xin -= 0.5; \
588yin -= 0.5; \
589x = FLOOR(xin); \
590y = FLOOR(yin); \
591dx = xin - x; \
592dy = yin - y; \
593x--; \
594y--;
595
596#define BICUBIC_BODY(type, image, step, offset) \
597{ \
598in = (type *)((image)[YCLIP(im, y)] + offset); \
599x0 = XCLIP(im, x + 0) * step; \
600x1 = XCLIP(im, x + 1) * step; \
601x2 = XCLIP(im, x + 2) * step; \
602x3 = XCLIP(im, x + 3) * step; \
603BICUBIC(v1, in[x0], in[x1], in[x2], in[x3], dx); \
604if (y + 1 >= 0 && y + 1 < im->ysize) { \
605in = (type *)((image)[y + 1] + offset); \
606BICUBIC(v2, in[x0], in[x1], in[x2], in[x3], dx); \
607} else { \
608v2 = v1; \
609} \
610if (y + 2 >= 0 && y + 2 < im->ysize) { \
611in = (type *)((image)[y + 2] + offset); \
612BICUBIC(v3, in[x0], in[x1], in[x2], in[x3], dx); \
613} else { \
614v3 = v2; \
615} \
616if (y + 3 >= 0 && y + 3 < im->ysize) { \
617in = (type *)((image)[y + 3] + offset); \
618BICUBIC(v4, in[x0], in[x1], in[x2], in[x3], dx); \
619} else { \
620v4 = v3; \
621} \
622BICUBIC(v1, v1, v2, v3, v4, dy); \
623}
624
625static int
626bicubic_filter8(void *out, Imaging im, double xin, double yin) {
627BICUBIC_HEAD(UINT8);
628BICUBIC_BODY(UINT8, im->image8, 1, 0);
629if (v1 <= 0.0) {
630((UINT8 *)out)[0] = 0;
631} else if (v1 >= 255.0) {
632((UINT8 *)out)[0] = 255;
633} else {
634((UINT8 *)out)[0] = (UINT8)v1;
635}
636return 1;
637}
638
639static int
640bicubic_filter32I(void *out, Imaging im, double xin, double yin) {
641INT32 k;
642BICUBIC_HEAD(INT32);
643BICUBIC_BODY(INT32, im->image32, 1, 0);
644k = v1;
645memcpy(out, &k, sizeof(k));
646return 1;
647}
648
649static int
650bicubic_filter32F(void *out, Imaging im, double xin, double yin) {
651FLOAT32 k;
652BICUBIC_HEAD(FLOAT32);
653BICUBIC_BODY(FLOAT32, im->image32, 1, 0);
654k = v1;
655memcpy(out, &k, sizeof(k));
656return 1;
657}
658
659static int
660bicubic_filter32LA(void *out, Imaging im, double xin, double yin) {
661BICUBIC_HEAD(UINT8);
662BICUBIC_BODY(UINT8, im->image, 4, 0);
663if (v1 <= 0.0) {
664((UINT8 *)out)[0] = 0;
665((UINT8 *)out)[1] = 0;
666((UINT8 *)out)[2] = 0;
667} else if (v1 >= 255.0) {
668((UINT8 *)out)[0] = 255;
669((UINT8 *)out)[1] = 255;
670((UINT8 *)out)[2] = 255;
671} else {
672((UINT8 *)out)[0] = (UINT8)v1;
673((UINT8 *)out)[1] = (UINT8)v1;
674((UINT8 *)out)[2] = (UINT8)v1;
675}
676BICUBIC_BODY(UINT8, im->image, 4, 3);
677if (v1 <= 0.0) {
678((UINT8 *)out)[3] = 0;
679} else if (v1 >= 255.0) {
680((UINT8 *)out)[3] = 255;
681} else {
682((UINT8 *)out)[3] = (UINT8)v1;
683}
684return 1;
685}
686
687static int
688bicubic_filter32RGB(void *out, Imaging im, double xin, double yin) {
689int b;
690BICUBIC_HEAD(UINT8);
691for (b = 0; b < im->bands; b++) {
692BICUBIC_BODY(UINT8, im->image, 4, b);
693if (v1 <= 0.0) {
694((UINT8 *)out)[b] = 0;
695} else if (v1 >= 255.0) {
696((UINT8 *)out)[b] = 255;
697} else {
698((UINT8 *)out)[b] = (UINT8)v1;
699}
700}
701return 1;
702}
703
704#undef BICUBIC
705#undef BICUBIC_HEAD
706#undef BICUBIC_BODY
707
708static ImagingTransformFilter
709getfilter(Imaging im, int filterid) {
710switch (filterid) {
711case IMAGING_TRANSFORM_NEAREST:
712if (im->image8) {
713switch (im->type) {
714case IMAGING_TYPE_UINT8:
715return nearest_filter8;
716case IMAGING_TYPE_SPECIAL:
717switch (im->pixelsize) {
718case 1:
719return nearest_filter8;
720case 2:
721return nearest_filter16;
722case 4:
723return nearest_filter32;
724}
725}
726} else {
727return nearest_filter32;
728}
729break;
730case IMAGING_TRANSFORM_BILINEAR:
731if (im->image8) {
732return bilinear_filter8;
733} else if (im->image32) {
734switch (im->type) {
735case IMAGING_TYPE_UINT8:
736if (im->bands == 2) {
737return bilinear_filter32LA;
738} else {
739return bilinear_filter32RGB;
740}
741case IMAGING_TYPE_INT32:
742return bilinear_filter32I;
743case IMAGING_TYPE_FLOAT32:
744return bilinear_filter32F;
745}
746}
747break;
748case IMAGING_TRANSFORM_BICUBIC:
749if (im->image8) {
750return bicubic_filter8;
751} else if (im->image32) {
752switch (im->type) {
753case IMAGING_TYPE_UINT8:
754if (im->bands == 2) {
755return bicubic_filter32LA;
756} else {
757return bicubic_filter32RGB;
758}
759case IMAGING_TYPE_INT32:
760return bicubic_filter32I;
761case IMAGING_TYPE_FLOAT32:
762return bicubic_filter32F;
763}
764}
765break;
766}
767/* no such filter */
768return NULL;
769}
770
771/* transformation engines */
772
773Imaging
774ImagingGenericTransform(
775Imaging imOut,
776Imaging imIn,
777int x0,
778int y0,
779int x1,
780int y1,
781ImagingTransformMap transform,
782void *transform_data,
783int filterid,
784int fill
785) {
786/* slow generic transformation. use ImagingTransformAffine or
787ImagingScaleAffine where possible. */
788
789ImagingSectionCookie cookie;
790int x, y;
791char *out;
792double xx, yy;
793
794ImagingTransformFilter filter = getfilter(imIn, filterid);
795if (!filter) {
796return (Imaging)ImagingError_ValueError("bad filter number");
797}
798
799if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
800return (Imaging)ImagingError_ModeError();
801}
802
803ImagingCopyPalette(imOut, imIn);
804
805ImagingSectionEnter(&cookie);
806
807if (x0 < 0) {
808x0 = 0;
809}
810if (y0 < 0) {
811y0 = 0;
812}
813if (x1 > imOut->xsize) {
814x1 = imOut->xsize;
815}
816if (y1 > imOut->ysize) {
817y1 = imOut->ysize;
818}
819
820for (y = y0; y < y1; y++) {
821out = imOut->image[y] + x0 * imOut->pixelsize;
822for (x = x0; x < x1; x++) {
823if (!transform(&xx, &yy, x - x0, y - y0, transform_data) ||
824!filter(out, imIn, xx, yy)) {
825if (fill) {
826memset(out, 0, imOut->pixelsize);
827}
828}
829out += imOut->pixelsize;
830}
831}
832
833ImagingSectionLeave(&cookie);
834
835return imOut;
836}
837
838static Imaging
839ImagingScaleAffine(
840Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[6], int fill
841) {
842/* scale, nearest neighbour resampling */
843
844ImagingSectionCookie cookie;
845int x, y;
846int xin;
847double xo, yo;
848int xmin, xmax;
849int *xintab;
850
851if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
852return (Imaging)ImagingError_ModeError();
853}
854
855ImagingCopyPalette(imOut, imIn);
856
857if (x0 < 0) {
858x0 = 0;
859}
860if (y0 < 0) {
861y0 = 0;
862}
863if (x1 > imOut->xsize) {
864x1 = imOut->xsize;
865}
866if (y1 > imOut->ysize) {
867y1 = imOut->ysize;
868}
869
870/* malloc check ok, uses calloc for overflow */
871xintab = (int *)calloc(imOut->xsize, sizeof(int));
872if (!xintab) {
873ImagingDelete(imOut);
874return (Imaging)ImagingError_MemoryError();
875}
876
877xo = a[2] + a[0] * 0.5;
878yo = a[5] + a[4] * 0.5;
879
880xmin = x1;
881xmax = x0;
882
883/* Pretabulate horizontal pixel positions */
884for (x = x0; x < x1; x++) {
885xin = COORD(xo);
886if (xin >= 0 && xin < (int)imIn->xsize) {
887xmax = x + 1;
888if (x < xmin) {
889xmin = x;
890}
891xintab[x] = xin;
892}
893xo += a[0];
894}
895
896#define AFFINE_SCALE(pixel, image) \
897for (y = y0; y < y1; y++) { \
898int yi = COORD(yo); \
899pixel *in, *out; \
900out = imOut->image[y]; \
901if (fill && x1 > x0) { \
902memset(out + x0, 0, (x1 - x0) * sizeof(pixel)); \
903} \
904if (yi >= 0 && yi < imIn->ysize) { \
905in = imIn->image[yi]; \
906for (x = xmin; x < xmax; x++) { \
907out[x] = in[xintab[x]]; \
908} \
909} \
910yo += a[4]; \
911}
912
913ImagingSectionEnter(&cookie);
914
915if (imIn->image8) {
916AFFINE_SCALE(UINT8, image8);
917} else {
918AFFINE_SCALE(INT32, image32);
919}
920
921ImagingSectionLeave(&cookie);
922
923#undef AFFINE_SCALE
924
925free(xintab);
926
927return imOut;
928}
929
930static inline int
931check_fixed(double a[6], int x, int y) {
932return (
933fabs(x * a[0] + y * a[1] + a[2]) < 32768.0 &&
934fabs(x * a[3] + y * a[4] + a[5]) < 32768.0
935);
936}
937
938static inline Imaging
939affine_fixed(
940Imaging imOut,
941Imaging imIn,
942int x0,
943int y0,
944int x1,
945int y1,
946double a[6],
947int filterid,
948int fill
949) {
950/* affine transform, nearest neighbour resampling, fixed point
951arithmetics */
952
953ImagingSectionCookie cookie;
954int x, y;
955int xin, yin;
956int xsize, ysize;
957int xx, yy;
958int a0, a1, a2, a3, a4, a5;
959
960ImagingCopyPalette(imOut, imIn);
961
962xsize = (int)imIn->xsize;
963ysize = (int)imIn->ysize;
964
965/* use 16.16 fixed point arithmetics */
966#define FIX(v) FLOOR((v) * 65536.0 + 0.5)
967
968a0 = FIX(a[0]);
969a1 = FIX(a[1]);
970a3 = FIX(a[3]);
971a4 = FIX(a[4]);
972a2 = FIX(a[2] + a[0] * 0.5 + a[1] * 0.5);
973a5 = FIX(a[5] + a[3] * 0.5 + a[4] * 0.5);
974
975#undef FIX
976
977#define AFFINE_TRANSFORM_FIXED(pixel, image) \
978for (y = y0; y < y1; y++) { \
979pixel *out; \
980xx = a2; \
981yy = a5; \
982out = imOut->image[y]; \
983if (fill && x1 > x0) { \
984memset(out + x0, 0, (x1 - x0) * sizeof(pixel)); \
985} \
986for (x = x0; x < x1; x++, out++) { \
987xin = xx >> 16; \
988if (xin >= 0 && xin < xsize) { \
989yin = yy >> 16; \
990if (yin >= 0 && yin < ysize) { \
991*out = imIn->image[yin][xin]; \
992} \
993} \
994xx += a0; \
995yy += a3; \
996} \
997a2 += a1; \
998a5 += a4; \
999}
1000
1001ImagingSectionEnter(&cookie);
1002
1003if (imIn->image8) {
1004AFFINE_TRANSFORM_FIXED(UINT8, image8)
1005} else {
1006AFFINE_TRANSFORM_FIXED(INT32, image32)
1007}
1008
1009ImagingSectionLeave(&cookie);
1010
1011#undef AFFINE_TRANSFORM_FIXED
1012
1013return imOut;
1014}
1015
1016Imaging
1017ImagingTransformAffine(
1018Imaging imOut,
1019Imaging imIn,
1020int x0,
1021int y0,
1022int x1,
1023int y1,
1024double a[6],
1025int filterid,
1026int fill
1027) {
1028/* affine transform, nearest neighbour resampling, floating point
1029arithmetics*/
1030
1031ImagingSectionCookie cookie;
1032int x, y;
1033int xin, yin;
1034int xsize, ysize;
1035double xx, yy;
1036double xo, yo;
1037
1038if (filterid || imIn->type == IMAGING_TYPE_SPECIAL) {
1039return ImagingGenericTransform(
1040imOut, imIn, x0, y0, x1, y1, affine_transform, a, filterid, fill
1041);
1042}
1043
1044if (a[1] == 0 && a[3] == 0) {
1045/* Scaling */
1046return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);
1047}
1048
1049if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
1050return (Imaging)ImagingError_ModeError();
1051}
1052
1053if (x0 < 0) {
1054x0 = 0;
1055}
1056if (y0 < 0) {
1057y0 = 0;
1058}
1059if (x1 > imOut->xsize) {
1060x1 = imOut->xsize;
1061}
1062if (y1 > imOut->ysize) {
1063y1 = imOut->ysize;
1064}
1065
1066/* translate all four corners to check if they are within the
1067range that can be represented by the fixed point arithmetics */
1068
1069if (check_fixed(a, 0, 0) && check_fixed(a, x1 - x0, y1 - y0) &&
1070check_fixed(a, 0, y1 - y0) && check_fixed(a, x1 - x0, 0)) {
1071return affine_fixed(imOut, imIn, x0, y0, x1, y1, a, filterid, fill);
1072}
1073
1074/* FIXME: cannot really think of any reasonable case when the
1075following code is used. maybe we should fall back on the slow
1076generic transform engine in this case? */
1077
1078ImagingCopyPalette(imOut, imIn);
1079
1080xsize = (int)imIn->xsize;
1081ysize = (int)imIn->ysize;
1082
1083xo = a[2] + a[1] * 0.5 + a[0] * 0.5;
1084yo = a[5] + a[4] * 0.5 + a[3] * 0.5;
1085
1086#define AFFINE_TRANSFORM(pixel, image) \
1087for (y = y0; y < y1; y++) { \
1088pixel *out; \
1089xx = xo; \
1090yy = yo; \
1091out = imOut->image[y]; \
1092if (fill && x1 > x0) { \
1093memset(out + x0, 0, (x1 - x0) * sizeof(pixel)); \
1094} \
1095for (x = x0; x < x1; x++, out++) { \
1096xin = COORD(xx); \
1097if (xin >= 0 && xin < xsize) { \
1098yin = COORD(yy); \
1099if (yin >= 0 && yin < ysize) { \
1100*out = imIn->image[yin][xin]; \
1101} \
1102} \
1103xx += a[0]; \
1104yy += a[3]; \
1105} \
1106xo += a[1]; \
1107yo += a[4]; \
1108}
1109
1110ImagingSectionEnter(&cookie);
1111
1112if (imIn->image8) {
1113AFFINE_TRANSFORM(UINT8, image8)
1114} else {
1115AFFINE_TRANSFORM(INT32, image32)
1116}
1117
1118ImagingSectionLeave(&cookie);
1119
1120#undef AFFINE_TRANSFORM
1121
1122return imOut;
1123}
1124
1125Imaging
1126ImagingTransform(
1127Imaging imOut,
1128Imaging imIn,
1129int method,
1130int x0,
1131int y0,
1132int x1,
1133int y1,
1134double a[8],
1135int filterid,
1136int fill
1137) {
1138ImagingTransformMap transform;
1139
1140switch (method) {
1141case IMAGING_TRANSFORM_AFFINE:
1142return ImagingTransformAffine(
1143imOut, imIn, x0, y0, x1, y1, a, filterid, fill
1144);
1145break;
1146case IMAGING_TRANSFORM_PERSPECTIVE:
1147transform = perspective_transform;
1148break;
1149case IMAGING_TRANSFORM_QUAD:
1150transform = quad_transform;
1151break;
1152default:
1153return (Imaging)ImagingError_ValueError("bad transform method");
1154}
1155
1156return ImagingGenericTransform(
1157imOut, imIn, x0, y0, x1, y1, transform, a, filterid, fill
1158);
1159}
1160