12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157 |
- #include "Imaging.h"
- /* For large images rotation is an inefficient operation in terms of CPU cache.
- One row in the source image affects each column in destination.
- Rotating in chunks that fit in the cache can speed up rotation
- 8x on a modern CPU. A chunk size of 128 requires only 65k and is large enough
- that the overhead from the extra loops are not apparent. */
- #define ROTATE_CHUNK 512
- #define ROTATE_SMALL_CHUNK 8
- #define COORD(v) ((v) < 0.0 ? -1 : ((int)(v)))
- #define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v)))
- /* -------------------------------------------------------------------- */
- /* Transpose operations */
- Imaging
- ImagingFlipLeftRight(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int x, y, xr;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- #define FLIP_LEFT_RIGHT(INT, image) \
- for (y = 0; y < imIn->ysize; y++) { \
- INT *in = (INT *)imIn->image[y]; \
- INT *out = (INT *)imOut->image[y]; \
- xr = imIn->xsize - 1; \
- for (x = 0; x < imIn->xsize; x++, xr--) { \
- out[xr] = in[x]; \
- } \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
- FLIP_LEFT_RIGHT(UINT16, image8)
- } else {
- FLIP_LEFT_RIGHT(UINT8, image8)
- }
- } else {
- FLIP_LEFT_RIGHT(INT32, image32)
- }
- ImagingSectionLeave(&cookie);
- #undef FLIP_LEFT_RIGHT
- return imOut;
- }
- Imaging
- ImagingFlipTopBottom(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int y, yr;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- ImagingSectionEnter(&cookie);
- yr = imIn->ysize - 1;
- for (y = 0; y < imIn->ysize; y++, yr--) {
- memcpy(imOut->image[yr], imIn->image[y], imIn->linesize);
- }
- ImagingSectionLeave(&cookie);
- return imOut;
- }
- Imaging
- ImagingRotate90(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int x, y, xx, yy, xr, xxsize, yysize;
- int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- #define ROTATE_90(INT, image) \
- for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
- for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
- yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
- xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
- for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
- for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
- yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
- ? yy + ROTATE_SMALL_CHUNK \
- : imIn->ysize; \
- xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
- ? xx + ROTATE_SMALL_CHUNK \
- : imIn->xsize; \
- for (yyy = yy; yyy < yyysize; yyy++) { \
- INT *in = (INT *)imIn->image[yyy]; \
- xr = imIn->xsize - 1 - xx; \
- for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
- INT *out = (INT *)imOut->image[xr]; \
- out[yyy] = in[xxx]; \
- } \
- } \
- } \
- } \
- } \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
- ROTATE_90(UINT16, image8);
- } else {
- ROTATE_90(UINT8, image8);
- }
- } else {
- ROTATE_90(INT32, image32);
- }
- ImagingSectionLeave(&cookie);
- #undef ROTATE_90
- return imOut;
- }
- Imaging
- ImagingTranspose(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int x, y, xx, yy, xxsize, yysize;
- int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- #define TRANSPOSE(INT, image) \
- for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
- for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
- yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
- xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
- for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
- for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
- yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
- ? yy + ROTATE_SMALL_CHUNK \
- : imIn->ysize; \
- xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
- ? xx + ROTATE_SMALL_CHUNK \
- : imIn->xsize; \
- for (yyy = yy; yyy < yyysize; yyy++) { \
- INT *in = (INT *)imIn->image[yyy]; \
- for (xxx = xx; xxx < xxxsize; xxx++) { \
- INT *out = (INT *)imOut->image[xxx]; \
- out[yyy] = in[xxx]; \
- } \
- } \
- } \
- } \
- } \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
- TRANSPOSE(UINT16, image8);
- } else {
- TRANSPOSE(UINT8, image8);
- }
- } else {
- TRANSPOSE(INT32, image32);
- }
- ImagingSectionLeave(&cookie);
- #undef TRANSPOSE
- return imOut;
- }
- Imaging
- ImagingTransverse(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int x, y, xr, yr, xx, yy, xxsize, yysize;
- int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- #define TRANSVERSE(INT, image) \
- for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
- for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
- yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
- xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
- for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
- for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
- yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
- ? yy + ROTATE_SMALL_CHUNK \
- : imIn->ysize; \
- xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
- ? xx + ROTATE_SMALL_CHUNK \
- : imIn->xsize; \
- yr = imIn->ysize - 1 - yy; \
- for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
- INT *in = (INT *)imIn->image[yyy]; \
- xr = imIn->xsize - 1 - xx; \
- for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
- INT *out = (INT *)imOut->image[xr]; \
- out[yr] = in[xxx]; \
- } \
- } \
- } \
- } \
- } \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
- TRANSVERSE(UINT16, image8);
- } else {
- TRANSVERSE(UINT8, image8);
- }
- } else {
- TRANSVERSE(INT32, image32);
- }
- ImagingSectionLeave(&cookie);
- #undef TRANSVERSE
- return imOut;
- }
- Imaging
- ImagingRotate180(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int x, y, xr, yr;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- #define ROTATE_180(INT, image) \
- for (y = 0; y < imIn->ysize; y++, yr--) { \
- INT *in = (INT *)imIn->image[y]; \
- INT *out = (INT *)imOut->image[yr]; \
- xr = imIn->xsize - 1; \
- for (x = 0; x < imIn->xsize; x++, xr--) { \
- out[xr] = in[x]; \
- } \
- }
- ImagingSectionEnter(&cookie);
- yr = imIn->ysize - 1;
- if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
- ROTATE_180(UINT16, image8)
- } else {
- ROTATE_180(UINT8, image8)
- }
- } else {
- ROTATE_180(INT32, image32)
- }
- ImagingSectionLeave(&cookie);
- #undef ROTATE_180
- return imOut;
- }
- Imaging
- ImagingRotate270(Imaging imOut, Imaging imIn) {
- ImagingSectionCookie cookie;
- int x, y, xx, yy, yr, xxsize, yysize;
- int xxx, yyy, xxxsize, yyysize;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) {
- return (Imaging)ImagingError_Mismatch();
- }
- ImagingCopyPalette(imOut, imIn);
- #define ROTATE_270(INT, image) \
- for (y = 0; y < imIn->ysize; y += ROTATE_CHUNK) { \
- for (x = 0; x < imIn->xsize; x += ROTATE_CHUNK) { \
- yysize = y + ROTATE_CHUNK < imIn->ysize ? y + ROTATE_CHUNK : imIn->ysize; \
- xxsize = x + ROTATE_CHUNK < imIn->xsize ? x + ROTATE_CHUNK : imIn->xsize; \
- for (yy = y; yy < yysize; yy += ROTATE_SMALL_CHUNK) { \
- for (xx = x; xx < xxsize; xx += ROTATE_SMALL_CHUNK) { \
- yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize \
- ? yy + ROTATE_SMALL_CHUNK \
- : imIn->ysize; \
- xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize \
- ? xx + ROTATE_SMALL_CHUNK \
- : imIn->xsize; \
- yr = imIn->ysize - 1 - yy; \
- for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
- INT *in = (INT *)imIn->image[yyy]; \
- for (xxx = xx; xxx < xxxsize; xxx++) { \
- INT *out = (INT *)imOut->image[xxx]; \
- out[yr] = in[xxx]; \
- } \
- } \
- } \
- } \
- } \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- if (strncmp(imIn->mode, "I;16", 4) == 0) {
- ROTATE_270(UINT16, image8);
- } else {
- ROTATE_270(UINT8, image8);
- }
- } else {
- ROTATE_270(INT32, image32);
- }
- ImagingSectionLeave(&cookie);
- #undef ROTATE_270
- return imOut;
- }
- /* -------------------------------------------------------------------- */
- /* Transforms */
- /* transform primitives (ImagingTransformMap) */
- static int
- affine_transform(double *xout, double *yout, int x, int y, void *data) {
- /* full moon tonight. your compiler will generate bogus code
- for simple expressions, unless you reorganize the code, or
- install Service Pack 3 */
- double *a = (double *)data;
- double a0 = a[0];
- double a1 = a[1];
- double a2 = a[2];
- double a3 = a[3];
- double a4 = a[4];
- double a5 = a[5];
- double xin = x + 0.5;
- double yin = y + 0.5;
- xout[0] = a0 * xin + a1 * yin + a2;
- yout[0] = a3 * xin + a4 * yin + a5;
- return 1;
- }
- static int
- perspective_transform(double *xout, double *yout, int x, int y, void *data) {
- double *a = (double *)data;
- double a0 = a[0];
- double a1 = a[1];
- double a2 = a[2];
- double a3 = a[3];
- double a4 = a[4];
- double a5 = a[5];
- double a6 = a[6];
- double a7 = a[7];
- double xin = x + 0.5;
- double yin = y + 0.5;
- xout[0] = (a0 * xin + a1 * yin + a2) / (a6 * xin + a7 * yin + 1);
- yout[0] = (a3 * xin + a4 * yin + a5) / (a6 * xin + a7 * yin + 1);
- return 1;
- }
- static int
- quad_transform(double *xout, double *yout, int x, int y, void *data) {
- /* quad warp: map quadrilateral to rectangle */
- double *a = (double *)data;
- double a0 = a[0];
- double a1 = a[1];
- double a2 = a[2];
- double a3 = a[3];
- double a4 = a[4];
- double a5 = a[5];
- double a6 = a[6];
- double a7 = a[7];
- double xin = x + 0.5;
- double yin = y + 0.5;
- xout[0] = a0 + a1 * xin + a2 * yin + a3 * xin * yin;
- yout[0] = a4 + a5 * xin + a6 * yin + a7 * xin * yin;
- return 1;
- }
- /* transform filters (ImagingTransformFilter) */
- static int
- nearest_filter8(void *out, Imaging im, double xin, double yin) {
- int x = COORD(xin);
- int y = COORD(yin);
- if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
- return 0;
- }
- ((UINT8 *)out)[0] = im->image8[y][x];
- return 1;
- }
- static int
- nearest_filter16(void *out, Imaging im, double xin, double yin) {
- int x = COORD(xin);
- int y = COORD(yin);
- if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
- return 0;
- }
- memcpy(out, im->image8[y] + x * sizeof(INT16), sizeof(INT16));
- return 1;
- }
- static int
- nearest_filter32(void *out, Imaging im, double xin, double yin) {
- int x = COORD(xin);
- int y = COORD(yin);
- if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
- return 0;
- }
- memcpy(out, &im->image32[y][x], sizeof(INT32));
- return 1;
- }
- #define XCLIP(im, x) (((x) < 0) ? 0 : ((x) < im->xsize) ? (x) : im->xsize - 1)
- #define YCLIP(im, y) (((y) < 0) ? 0 : ((y) < im->ysize) ? (y) : im->ysize - 1)
- #define BILINEAR(v, a, b, d) (v = (a) + ((b) - (a)) * (d))
- #define BILINEAR_HEAD(type) \
- int x, y; \
- int x0, x1; \
- double v1, v2; \
- double dx, dy; \
- type *in; \
- if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize) { \
- return 0; \
- } \
- xin -= 0.5; \
- yin -= 0.5; \
- x = FLOOR(xin); \
- y = FLOOR(yin); \
- dx = xin - x; \
- dy = yin - y;
- #define BILINEAR_BODY(type, image, step, offset) \
- { \
- in = (type *)((image)[YCLIP(im, y)] + offset); \
- x0 = XCLIP(im, x + 0) * step; \
- x1 = XCLIP(im, x + 1) * step; \
- BILINEAR(v1, in[x0], in[x1], dx); \
- if (y + 1 >= 0 && y + 1 < im->ysize) { \
- in = (type *)((image)[y + 1] + offset); \
- BILINEAR(v2, in[x0], in[x1], dx); \
- } else { \
- v2 = v1; \
- } \
- BILINEAR(v1, v1, v2, dy); \
- }
- static int
- bilinear_filter8(void *out, Imaging im, double xin, double yin) {
- BILINEAR_HEAD(UINT8);
- BILINEAR_BODY(UINT8, im->image8, 1, 0);
- ((UINT8 *)out)[0] = (UINT8)v1;
- return 1;
- }
- static int
- bilinear_filter32I(void *out, Imaging im, double xin, double yin) {
- INT32 k;
- BILINEAR_HEAD(INT32);
- BILINEAR_BODY(INT32, im->image32, 1, 0);
- k = v1;
- memcpy(out, &k, sizeof(k));
- return 1;
- }
- static int
- bilinear_filter32F(void *out, Imaging im, double xin, double yin) {
- FLOAT32 k;
- BILINEAR_HEAD(FLOAT32);
- BILINEAR_BODY(FLOAT32, im->image32, 1, 0);
- k = v1;
- memcpy(out, &k, sizeof(k));
- return 1;
- }
- static int
- bilinear_filter32LA(void *out, Imaging im, double xin, double yin) {
- BILINEAR_HEAD(UINT8);
- BILINEAR_BODY(UINT8, im->image, 4, 0);
- ((UINT8 *)out)[0] = (UINT8)v1;
- ((UINT8 *)out)[1] = (UINT8)v1;
- ((UINT8 *)out)[2] = (UINT8)v1;
- BILINEAR_BODY(UINT8, im->image, 4, 3);
- ((UINT8 *)out)[3] = (UINT8)v1;
- return 1;
- }
- static int
- bilinear_filter32RGB(void *out, Imaging im, double xin, double yin) {
- int b;
- BILINEAR_HEAD(UINT8);
- for (b = 0; b < im->bands; b++) {
- BILINEAR_BODY(UINT8, im->image, 4, b);
- ((UINT8 *)out)[b] = (UINT8)v1;
- }
- return 1;
- }
- #undef BILINEAR
- #undef BILINEAR_HEAD
- #undef BILINEAR_BODY
- #define BICUBIC(v, v1, v2, v3, v4, d) \
- { \
- double p1 = v2; \
- double p2 = -v1 + v3; \
- double p3 = 2 * (v1 - v2) + v3 - v4; \
- double p4 = -v1 + v2 - v3 + v4; \
- v = p1 + (d) * (p2 + (d) * (p3 + (d)*p4)); \
- }
- #define BICUBIC_HEAD(type) \
- int x = FLOOR(xin); \
- int y = FLOOR(yin); \
- int x0, x1, x2, x3; \
- double v1, v2, v3, v4; \
- double dx, dy; \
- type *in; \
- if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize) { \
- return 0; \
- } \
- xin -= 0.5; \
- yin -= 0.5; \
- x = FLOOR(xin); \
- y = FLOOR(yin); \
- dx = xin - x; \
- dy = yin - y; \
- x--; \
- y--;
- #define BICUBIC_BODY(type, image, step, offset) \
- { \
- in = (type *)((image)[YCLIP(im, y)] + offset); \
- x0 = XCLIP(im, x + 0) * step; \
- x1 = XCLIP(im, x + 1) * step; \
- x2 = XCLIP(im, x + 2) * step; \
- x3 = XCLIP(im, x + 3) * step; \
- BICUBIC(v1, in[x0], in[x1], in[x2], in[x3], dx); \
- if (y + 1 >= 0 && y + 1 < im->ysize) { \
- in = (type *)((image)[y + 1] + offset); \
- BICUBIC(v2, in[x0], in[x1], in[x2], in[x3], dx); \
- } else { \
- v2 = v1; \
- } \
- if (y + 2 >= 0 && y + 2 < im->ysize) { \
- in = (type *)((image)[y + 2] + offset); \
- BICUBIC(v3, in[x0], in[x1], in[x2], in[x3], dx); \
- } else { \
- v3 = v2; \
- } \
- if (y + 3 >= 0 && y + 3 < im->ysize) { \
- in = (type *)((image)[y + 3] + offset); \
- BICUBIC(v4, in[x0], in[x1], in[x2], in[x3], dx); \
- } else { \
- v4 = v3; \
- } \
- BICUBIC(v1, v1, v2, v3, v4, dy); \
- }
- static int
- bicubic_filter8(void *out, Imaging im, double xin, double yin) {
- BICUBIC_HEAD(UINT8);
- BICUBIC_BODY(UINT8, im->image8, 1, 0);
- if (v1 <= 0.0) {
- ((UINT8 *)out)[0] = 0;
- } else if (v1 >= 255.0) {
- ((UINT8 *)out)[0] = 255;
- } else {
- ((UINT8 *)out)[0] = (UINT8)v1;
- }
- return 1;
- }
- static int
- bicubic_filter32I(void *out, Imaging im, double xin, double yin) {
- INT32 k;
- BICUBIC_HEAD(INT32);
- BICUBIC_BODY(INT32, im->image32, 1, 0);
- k = v1;
- memcpy(out, &k, sizeof(k));
- return 1;
- }
- static int
- bicubic_filter32F(void *out, Imaging im, double xin, double yin) {
- FLOAT32 k;
- BICUBIC_HEAD(FLOAT32);
- BICUBIC_BODY(FLOAT32, im->image32, 1, 0);
- k = v1;
- memcpy(out, &k, sizeof(k));
- return 1;
- }
- static int
- bicubic_filter32LA(void *out, Imaging im, double xin, double yin) {
- BICUBIC_HEAD(UINT8);
- BICUBIC_BODY(UINT8, im->image, 4, 0);
- if (v1 <= 0.0) {
- ((UINT8 *)out)[0] = 0;
- ((UINT8 *)out)[1] = 0;
- ((UINT8 *)out)[2] = 0;
- } else if (v1 >= 255.0) {
- ((UINT8 *)out)[0] = 255;
- ((UINT8 *)out)[1] = 255;
- ((UINT8 *)out)[2] = 255;
- } else {
- ((UINT8 *)out)[0] = (UINT8)v1;
- ((UINT8 *)out)[1] = (UINT8)v1;
- ((UINT8 *)out)[2] = (UINT8)v1;
- }
- BICUBIC_BODY(UINT8, im->image, 4, 3);
- if (v1 <= 0.0) {
- ((UINT8 *)out)[3] = 0;
- } else if (v1 >= 255.0) {
- ((UINT8 *)out)[3] = 255;
- } else {
- ((UINT8 *)out)[3] = (UINT8)v1;
- }
- return 1;
- }
- static int
- bicubic_filter32RGB(void *out, Imaging im, double xin, double yin) {
- int b;
- BICUBIC_HEAD(UINT8);
- for (b = 0; b < im->bands; b++) {
- BICUBIC_BODY(UINT8, im->image, 4, b);
- if (v1 <= 0.0) {
- ((UINT8 *)out)[b] = 0;
- } else if (v1 >= 255.0) {
- ((UINT8 *)out)[b] = 255;
- } else {
- ((UINT8 *)out)[b] = (UINT8)v1;
- }
- }
- return 1;
- }
- #undef BICUBIC
- #undef BICUBIC_HEAD
- #undef BICUBIC_BODY
- static ImagingTransformFilter
- getfilter(Imaging im, int filterid) {
- switch (filterid) {
- case IMAGING_TRANSFORM_NEAREST:
- if (im->image8) {
- switch (im->type) {
- case IMAGING_TYPE_UINT8:
- return nearest_filter8;
- case IMAGING_TYPE_SPECIAL:
- switch (im->pixelsize) {
- case 1:
- return nearest_filter8;
- case 2:
- return nearest_filter16;
- case 4:
- return nearest_filter32;
- }
- }
- } else {
- return nearest_filter32;
- }
- break;
- case IMAGING_TRANSFORM_BILINEAR:
- if (im->image8) {
- return bilinear_filter8;
- } else if (im->image32) {
- switch (im->type) {
- case IMAGING_TYPE_UINT8:
- if (im->bands == 2) {
- return bilinear_filter32LA;
- } else {
- return bilinear_filter32RGB;
- }
- case IMAGING_TYPE_INT32:
- return bilinear_filter32I;
- case IMAGING_TYPE_FLOAT32:
- return bilinear_filter32F;
- }
- }
- break;
- case IMAGING_TRANSFORM_BICUBIC:
- if (im->image8) {
- return bicubic_filter8;
- } else if (im->image32) {
- switch (im->type) {
- case IMAGING_TYPE_UINT8:
- if (im->bands == 2) {
- return bicubic_filter32LA;
- } else {
- return bicubic_filter32RGB;
- }
- case IMAGING_TYPE_INT32:
- return bicubic_filter32I;
- case IMAGING_TYPE_FLOAT32:
- return bicubic_filter32F;
- }
- }
- break;
- }
- /* no such filter */
- return NULL;
- }
- /* transformation engines */
- Imaging
- ImagingGenericTransform(
- Imaging imOut,
- Imaging imIn,
- int x0,
- int y0,
- int x1,
- int y1,
- ImagingTransformMap transform,
- void *transform_data,
- int filterid,
- int fill) {
- /* slow generic transformation. use ImagingTransformAffine or
- ImagingScaleAffine where possible. */
- ImagingSectionCookie cookie;
- int x, y;
- char *out;
- double xx, yy;
- ImagingTransformFilter filter = getfilter(imIn, filterid);
- if (!filter) {
- return (Imaging)ImagingError_ValueError("bad filter number");
- }
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- ImagingCopyPalette(imOut, imIn);
- ImagingSectionEnter(&cookie);
- if (x0 < 0) {
- x0 = 0;
- }
- if (y0 < 0) {
- y0 = 0;
- }
- if (x1 > imOut->xsize) {
- x1 = imOut->xsize;
- }
- if (y1 > imOut->ysize) {
- y1 = imOut->ysize;
- }
- for (y = y0; y < y1; y++) {
- out = imOut->image[y] + x0 * imOut->pixelsize;
- for (x = x0; x < x1; x++) {
- if (!transform(&xx, &yy, x - x0, y - y0, transform_data) ||
- !filter(out, imIn, xx, yy)) {
- if (fill) {
- memset(out, 0, imOut->pixelsize);
- }
- }
- out += imOut->pixelsize;
- }
- }
- ImagingSectionLeave(&cookie);
- return imOut;
- }
- static Imaging
- ImagingScaleAffine(
- Imaging imOut,
- Imaging imIn,
- int x0,
- int y0,
- int x1,
- int y1,
- double a[6],
- int fill) {
- /* scale, nearest neighbour resampling */
- ImagingSectionCookie cookie;
- int x, y;
- int xin;
- double xo, yo;
- int xmin, xmax;
- int *xintab;
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- ImagingCopyPalette(imOut, imIn);
- if (x0 < 0) {
- x0 = 0;
- }
- if (y0 < 0) {
- y0 = 0;
- }
- if (x1 > imOut->xsize) {
- x1 = imOut->xsize;
- }
- if (y1 > imOut->ysize) {
- y1 = imOut->ysize;
- }
- /* malloc check ok, uses calloc for overflow */
- xintab = (int *)calloc(imOut->xsize, sizeof(int));
- if (!xintab) {
- ImagingDelete(imOut);
- return (Imaging)ImagingError_MemoryError();
- }
- xo = a[2] + a[0] * 0.5;
- yo = a[5] + a[4] * 0.5;
- xmin = x1;
- xmax = x0;
- /* Pretabulate horizontal pixel positions */
- for (x = x0; x < x1; x++) {
- xin = COORD(xo);
- if (xin >= 0 && xin < (int)imIn->xsize) {
- xmax = x + 1;
- if (x < xmin) {
- xmin = x;
- }
- xintab[x] = xin;
- }
- xo += a[0];
- }
- #define AFFINE_SCALE(pixel, image) \
- for (y = y0; y < y1; y++) { \
- int yi = COORD(yo); \
- pixel *in, *out; \
- out = imOut->image[y]; \
- if (fill && x1 > x0) { \
- memset(out + x0, 0, (x1 - x0) * sizeof(pixel)); \
- } \
- if (yi >= 0 && yi < imIn->ysize) { \
- in = imIn->image[yi]; \
- for (x = xmin; x < xmax; x++) { \
- out[x] = in[xintab[x]]; \
- } \
- } \
- yo += a[4]; \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- AFFINE_SCALE(UINT8, image8);
- } else {
- AFFINE_SCALE(INT32, image32);
- }
- ImagingSectionLeave(&cookie);
- #undef AFFINE_SCALE
- free(xintab);
- return imOut;
- }
- static inline int
- check_fixed(double a[6], int x, int y) {
- return (
- fabs(x * a[0] + y * a[1] + a[2]) < 32768.0 &&
- fabs(x * a[3] + y * a[4] + a[5]) < 32768.0);
- }
- static inline Imaging
- affine_fixed(
- Imaging imOut,
- Imaging imIn,
- int x0,
- int y0,
- int x1,
- int y1,
- double a[6],
- int filterid,
- int fill) {
- /* affine transform, nearest neighbour resampling, fixed point
- arithmetics */
- ImagingSectionCookie cookie;
- int x, y;
- int xin, yin;
- int xsize, ysize;
- int xx, yy;
- int a0, a1, a2, a3, a4, a5;
- ImagingCopyPalette(imOut, imIn);
- xsize = (int)imIn->xsize;
- ysize = (int)imIn->ysize;
- /* use 16.16 fixed point arithmetics */
- #define FIX(v) FLOOR((v)*65536.0 + 0.5)
- a0 = FIX(a[0]);
- a1 = FIX(a[1]);
- a3 = FIX(a[3]);
- a4 = FIX(a[4]);
- a2 = FIX(a[2] + a[0] * 0.5 + a[1] * 0.5);
- a5 = FIX(a[5] + a[3] * 0.5 + a[4] * 0.5);
- #undef FIX
- #define AFFINE_TRANSFORM_FIXED(pixel, image) \
- for (y = y0; y < y1; y++) { \
- pixel *out; \
- xx = a2; \
- yy = a5; \
- out = imOut->image[y]; \
- if (fill && x1 > x0) { \
- memset(out + x0, 0, (x1 - x0) * sizeof(pixel)); \
- } \
- for (x = x0; x < x1; x++, out++) { \
- xin = xx >> 16; \
- if (xin >= 0 && xin < xsize) { \
- yin = yy >> 16; \
- if (yin >= 0 && yin < ysize) { \
- *out = imIn->image[yin][xin]; \
- } \
- } \
- xx += a0; \
- yy += a3; \
- } \
- a2 += a1; \
- a5 += a4; \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- AFFINE_TRANSFORM_FIXED(UINT8, image8)
- } else {
- AFFINE_TRANSFORM_FIXED(INT32, image32)
- }
- ImagingSectionLeave(&cookie);
- #undef AFFINE_TRANSFORM_FIXED
- return imOut;
- }
- Imaging
- ImagingTransformAffine(
- Imaging imOut,
- Imaging imIn,
- int x0,
- int y0,
- int x1,
- int y1,
- double a[6],
- int filterid,
- int fill) {
- /* affine transform, nearest neighbour resampling, floating point
- arithmetics*/
- ImagingSectionCookie cookie;
- int x, y;
- int xin, yin;
- int xsize, ysize;
- double xx, yy;
- double xo, yo;
- if (filterid || imIn->type == IMAGING_TYPE_SPECIAL) {
- return ImagingGenericTransform(
- imOut, imIn, x0, y0, x1, y1, affine_transform, a, filterid, fill);
- }
- if (a[1] == 0 && a[3] == 0) {
- /* Scaling */
- return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);
- }
- if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) {
- return (Imaging)ImagingError_ModeError();
- }
- if (x0 < 0) {
- x0 = 0;
- }
- if (y0 < 0) {
- y0 = 0;
- }
- if (x1 > imOut->xsize) {
- x1 = imOut->xsize;
- }
- if (y1 > imOut->ysize) {
- y1 = imOut->ysize;
- }
- /* translate all four corners to check if they are within the
- range that can be represented by the fixed point arithmetics */
- if (check_fixed(a, 0, 0) && check_fixed(a, x1 - x0, y1 - y0) &&
- check_fixed(a, 0, y1 - y0) && check_fixed(a, x1 - x0, 0)) {
- return affine_fixed(imOut, imIn, x0, y0, x1, y1, a, filterid, fill);
- }
- /* FIXME: cannot really think of any reasonable case when the
- following code is used. maybe we should fall back on the slow
- generic transform engine in this case? */
- ImagingCopyPalette(imOut, imIn);
- xsize = (int)imIn->xsize;
- ysize = (int)imIn->ysize;
- xo = a[2] + a[1] * 0.5 + a[0] * 0.5;
- yo = a[5] + a[4] * 0.5 + a[3] * 0.5;
- #define AFFINE_TRANSFORM(pixel, image) \
- for (y = y0; y < y1; y++) { \
- pixel *out; \
- xx = xo; \
- yy = yo; \
- out = imOut->image[y]; \
- if (fill && x1 > x0) { \
- memset(out + x0, 0, (x1 - x0) * sizeof(pixel)); \
- } \
- for (x = x0; x < x1; x++, out++) { \
- xin = COORD(xx); \
- if (xin >= 0 && xin < xsize) { \
- yin = COORD(yy); \
- if (yin >= 0 && yin < ysize) { \
- *out = imIn->image[yin][xin]; \
- } \
- } \
- xx += a[0]; \
- yy += a[3]; \
- } \
- xo += a[1]; \
- yo += a[4]; \
- }
- ImagingSectionEnter(&cookie);
- if (imIn->image8) {
- AFFINE_TRANSFORM(UINT8, image8)
- } else {
- AFFINE_TRANSFORM(INT32, image32)
- }
- ImagingSectionLeave(&cookie);
- #undef AFFINE_TRANSFORM
- return imOut;
- }
- Imaging
- ImagingTransform(
- Imaging imOut,
- Imaging imIn,
- int method,
- int x0,
- int y0,
- int x1,
- int y1,
- double a[8],
- int filterid,
- int fill) {
- ImagingTransformMap transform;
- switch (method) {
- case IMAGING_TRANSFORM_AFFINE:
- return ImagingTransformAffine(
- imOut, imIn, x0, y0, x1, y1, a, filterid, fill);
- break;
- case IMAGING_TRANSFORM_PERSPECTIVE:
- transform = perspective_transform;
- break;
- case IMAGING_TRANSFORM_QUAD:
- transform = quad_transform;
- break;
- default:
- return (Imaging)ImagingError_ValueError("bad transform method");
- }
- return ImagingGenericTransform(
- imOut, imIn, x0, y0, x1, y1, transform, a, filterid, fill);
- }
|