mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	find * -type f "-(" -name "*.bdf" -o -name "*.c" -o -name "*.h" -o -name "*.py" -o -name "*.rst" -o -name "*.txt" "-)" -exec sed -e "s/[[:space:]]*$//" -i {} \;
		
	
			
		
			
				
	
	
		
			960 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			960 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * The Python Imaging Library
 | 
						|
 * $Id$
 | 
						|
 *
 | 
						|
 * the imaging geometry methods
 | 
						|
 *
 | 
						|
 * history:
 | 
						|
 * 1995-06-15 fl  Created
 | 
						|
 * 1996-04-15 fl  Changed origin
 | 
						|
 * 1996-05-18 fl  Fixed rotate90/270 for rectangular images
 | 
						|
 * 1996-05-27 fl  Added general purpose transform
 | 
						|
 * 1996-11-22 fl  Don't crash when resizing from outside source image
 | 
						|
 * 1997-08-09 fl  Fixed rounding error in resize
 | 
						|
 * 1998-09-21 fl  Incorporated transformation patches (from Zircon #2)
 | 
						|
 * 1998-09-22 fl  Added bounding box to transform engines
 | 
						|
 * 1999-02-03 fl  Fixed bicubic filtering for RGB images
 | 
						|
 * 1999-02-16 fl  Added fixed-point version of affine transform
 | 
						|
 * 2001-03-28 fl  Fixed transform(EXTENT) for xoffset < 0
 | 
						|
 * 2003-03-10 fl  Compiler tweaks
 | 
						|
 * 2004-09-19 fl  Fixed bilinear/bicubic filtering of LA images
 | 
						|
 *
 | 
						|
 * Copyright (c) 1997-2003 by Secret Labs AB
 | 
						|
 * Copyright (c) 1995-1997 by Fredrik Lundh
 | 
						|
 *
 | 
						|
 * See the README file for information on usage and redistribution.
 | 
						|
 */
 | 
						|
 | 
						|
#include "Imaging.h"
 | 
						|
 | 
						|
/* Undef if you don't need resampling filters */
 | 
						|
#define WITH_FILTERS
 | 
						|
 | 
						|
#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();
 | 
						|
 | 
						|
    ImagingCopyInfo(imOut, imIn);
 | 
						|
 | 
						|
#define	FLIP_HORIZ(image)\
 | 
						|
    for (y = 0; y < imIn->ysize; y++) {\
 | 
						|
	xr = imIn->xsize-1;\
 | 
						|
	for (x = 0; x < imIn->xsize; x++, xr--)\
 | 
						|
	    imOut->image[y][x] = imIn->image[y][xr];\
 | 
						|
    }
 | 
						|
 | 
						|
    ImagingSectionEnter(&cookie);
 | 
						|
 | 
						|
    if (imIn->image8)
 | 
						|
	FLIP_HORIZ(image8)
 | 
						|
    else
 | 
						|
	FLIP_HORIZ(image32)
 | 
						|
 | 
						|
    ImagingSectionLeave(&cookie);
 | 
						|
 | 
						|
    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();
 | 
						|
 | 
						|
    ImagingCopyInfo(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, xr;
 | 
						|
 | 
						|
    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();
 | 
						|
 | 
						|
    ImagingCopyInfo(imOut, imIn);
 | 
						|
 | 
						|
#define	ROTATE_90(image)\
 | 
						|
    for (y = 0; y < imIn->ysize; y++) {\
 | 
						|
	xr = imIn->xsize-1;\
 | 
						|
	for (x = 0; x < imIn->xsize; x++, xr--)\
 | 
						|
	    imOut->image[xr][y] = imIn->image[y][x];\
 | 
						|
    }
 | 
						|
 | 
						|
    ImagingSectionEnter(&cookie);
 | 
						|
 | 
						|
    if (imIn->image8)
 | 
						|
	ROTATE_90(image8)
 | 
						|
    else
 | 
						|
	ROTATE_90(image32)
 | 
						|
 | 
						|
    ImagingSectionLeave(&cookie);
 | 
						|
 | 
						|
    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();
 | 
						|
 | 
						|
    ImagingCopyInfo(imOut, imIn);
 | 
						|
 | 
						|
    yr = imIn->ysize-1;
 | 
						|
 | 
						|
#define	ROTATE_180(image)\
 | 
						|
    for (y = 0; y < imIn->ysize; y++, yr--) {\
 | 
						|
	xr = imIn->xsize-1;\
 | 
						|
	for (x = 0; x < imIn->xsize; x++, xr--)\
 | 
						|
	    imOut->image[y][x] = imIn->image[yr][xr];\
 | 
						|
    }
 | 
						|
 | 
						|
    ImagingSectionEnter(&cookie);
 | 
						|
 | 
						|
    if (imIn->image8)
 | 
						|
	ROTATE_180(image8)
 | 
						|
    else
 | 
						|
	ROTATE_180(image32)
 | 
						|
 | 
						|
    ImagingSectionLeave(&cookie);
 | 
						|
 | 
						|
    return imOut;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
Imaging
 | 
						|
ImagingRotate270(Imaging imOut, Imaging imIn)
 | 
						|
{
 | 
						|
    ImagingSectionCookie cookie;
 | 
						|
    int x, y, yr;
 | 
						|
 | 
						|
    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();
 | 
						|
 | 
						|
    ImagingCopyInfo(imOut, imIn);
 | 
						|
 | 
						|
    yr = imIn->ysize - 1;
 | 
						|
 | 
						|
#define	ROTATE_270(image)\
 | 
						|
    for (y = 0; y < imIn->ysize; y++, yr--)\
 | 
						|
	for (x = 0; x < imIn->xsize; x++)\
 | 
						|
	    imOut->image[x][y] = imIn->image[yr][x];
 | 
						|
 | 
						|
    ImagingSectionEnter(&cookie);
 | 
						|
 | 
						|
    if (imIn->image8)
 | 
						|
	ROTATE_270(image8)
 | 
						|
    else
 | 
						|
	ROTATE_270(image32)
 | 
						|
 | 
						|
    ImagingSectionLeave(&cookie);
 | 
						|
 | 
						|
    return imOut;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transforms								*/
 | 
						|
 | 
						|
/* transform primitives (ImagingTransformMap) */
 | 
						|
 | 
						|
static int
 | 
						|
affine_transform(double* xin, double* yin, 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];
 | 
						|
 | 
						|
    xin[0] = a0 + a1*x + a2*y;
 | 
						|
    yin[0] = a3 + a4*x + a5*y;
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
perspective_transform(double* xin, double* yin, 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];
 | 
						|
 | 
						|
    xin[0] = (a0 + a1*x + a2*y) / (a6*x + a7*y + 1);
 | 
						|
    yin[0] = (a3 + a4*x + a5*y) / (a6*x + a7*y + 1);
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
static int
 | 
						|
quadratic_transform(double* xin, double* yin, 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 a8 = a[8]; double a9 = a[9]; double a10 = a[10]; double a11 = a[11];
 | 
						|
 | 
						|
    xin[0] = a0 + a1*x + a2*y + a3*x*x + a4*x*y + a5*y*y;
 | 
						|
    yin[0] = a6 + a7*x + a8*y + a9*x*x + a10*x*y + a11*y*y;
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static int
 | 
						|
quad_transform(double* xin, double* yin, 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];
 | 
						|
 | 
						|
    xin[0] = a0 + a1*x + a2*y + a3*x*y;
 | 
						|
    yin[0] = a4 + a5*x + a6*y + a7*x*y;
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* transform filters (ImagingTransformFilter) */
 | 
						|
 | 
						|
#ifdef WITH_FILTERS
 | 
						|
 | 
						|
static int
 | 
						|
nearest_filter8(void* out, Imaging im, double xin, double yin, void* data)
 | 
						|
{
 | 
						|
    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, void* data)
 | 
						|
{
 | 
						|
    int x = COORD(xin);
 | 
						|
    int y = COORD(yin);
 | 
						|
    if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
 | 
						|
        return 0;
 | 
						|
    ((INT16*)out)[0] = ((INT16*)(im->image8[y]))[x];
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
nearest_filter32(void* out, Imaging im, double xin, double yin, void* data)
 | 
						|
{
 | 
						|
    int x = COORD(xin);
 | 
						|
    int y = COORD(yin);
 | 
						|
    if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
 | 
						|
        return 0;
 | 
						|
    ((INT32*)out)[0] = im->image32[y][x];
 | 
						|
    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, void* data)
 | 
						|
{
 | 
						|
    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, void* data)
 | 
						|
{
 | 
						|
    BILINEAR_HEAD(INT32);
 | 
						|
    BILINEAR_BODY(INT32, im->image32, 1, 0);
 | 
						|
    ((INT32*)out)[0] = (INT32) v1;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
bilinear_filter32F(void* out, Imaging im, double xin, double yin, void* data)
 | 
						|
{
 | 
						|
    BILINEAR_HEAD(FLOAT32);
 | 
						|
    BILINEAR_BODY(FLOAT32, im->image32, 1, 0);
 | 
						|
    ((FLOAT32*)out)[0] = (FLOAT32) v1;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
bilinear_filter32LA(void* out, Imaging im, double xin, double yin, void* data)
 | 
						|
{
 | 
						|
    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, void* data)
 | 
						|
{
 | 
						|
    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;
 | 
						|
}
 | 
						|
 | 
						|
#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, void* data)
 | 
						|
{
 | 
						|
    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, void* data)
 | 
						|
{
 | 
						|
    BICUBIC_HEAD(INT32);
 | 
						|
    BICUBIC_BODY(INT32, im->image32, 1, 0);
 | 
						|
    ((INT32*)out)[0] = (INT32) v1;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
bicubic_filter32F(void* out, Imaging im, double xin, double yin, void* data)
 | 
						|
{
 | 
						|
    BICUBIC_HEAD(FLOAT32);
 | 
						|
    BICUBIC_BODY(FLOAT32, im->image32, 1, 0);
 | 
						|
    ((FLOAT32*)out)[0] = (FLOAT32) v1;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
bicubic_filter32LA(void* out, Imaging im, double xin, double yin, void* data)
 | 
						|
{
 | 
						|
    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, void* data)
 | 
						|
{
 | 
						|
    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;
 | 
						|
}
 | 
						|
 | 
						|
static ImagingTransformFilter
 | 
						|
getfilter(Imaging im, int filterid)
 | 
						|
{
 | 
						|
    switch (filterid) {
 | 
						|
    case IMAGING_TRANSFORM_NEAREST:
 | 
						|
        if (im->image8)
 | 
						|
            switch (im->type) {
 | 
						|
            case IMAGING_TYPE_UINT8:
 | 
						|
                return (ImagingTransformFilter) nearest_filter8;
 | 
						|
            case IMAGING_TYPE_SPECIAL:
 | 
						|
                switch (im->pixelsize) {
 | 
						|
                case 1:
 | 
						|
                    return (ImagingTransformFilter) nearest_filter8;
 | 
						|
                case 2:
 | 
						|
                    return (ImagingTransformFilter) nearest_filter16;
 | 
						|
                case 4:
 | 
						|
                    return (ImagingTransformFilter) nearest_filter32;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        else
 | 
						|
            return (ImagingTransformFilter) nearest_filter32;
 | 
						|
        break;
 | 
						|
    case IMAGING_TRANSFORM_BILINEAR:
 | 
						|
        if (im->image8)
 | 
						|
            return (ImagingTransformFilter) bilinear_filter8;
 | 
						|
        else if (im->image32) {
 | 
						|
            switch (im->type) {
 | 
						|
            case IMAGING_TYPE_UINT8:
 | 
						|
                if (im->bands == 2)
 | 
						|
                    return (ImagingTransformFilter) bilinear_filter32LA;
 | 
						|
                else
 | 
						|
                    return (ImagingTransformFilter) bilinear_filter32RGB;
 | 
						|
            case IMAGING_TYPE_INT32:
 | 
						|
                return (ImagingTransformFilter) bilinear_filter32I;
 | 
						|
            case IMAGING_TYPE_FLOAT32:
 | 
						|
                return (ImagingTransformFilter) bilinear_filter32F;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case IMAGING_TRANSFORM_BICUBIC:
 | 
						|
        if (im->image8)
 | 
						|
            return (ImagingTransformFilter) bicubic_filter8;
 | 
						|
        else if (im->image32) {
 | 
						|
            switch (im->type) {
 | 
						|
            case IMAGING_TYPE_UINT8:
 | 
						|
                if (im->bands == 2)
 | 
						|
                    return (ImagingTransformFilter) bicubic_filter32LA;
 | 
						|
                else
 | 
						|
                    return (ImagingTransformFilter) bicubic_filter32RGB;
 | 
						|
            case IMAGING_TYPE_INT32:
 | 
						|
                return (ImagingTransformFilter) bicubic_filter32I;
 | 
						|
            case IMAGING_TYPE_FLOAT32:
 | 
						|
                return (ImagingTransformFilter) bicubic_filter32F;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    /* no such filter */
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
#define getfilter(im, id) NULL
 | 
						|
#endif
 | 
						|
 | 
						|
/* transformation engines */
 | 
						|
 | 
						|
Imaging
 | 
						|
ImagingTransform(
 | 
						|
    Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
 | 
						|
    ImagingTransformMap transform, void* transform_data,
 | 
						|
    ImagingTransformFilter filter, void* filter_data,
 | 
						|
    int fill)
 | 
						|
{
 | 
						|
    /* slow generic transformation.  use ImagingTransformAffine or
 | 
						|
       ImagingScaleAffine where possible. */
 | 
						|
 | 
						|
    ImagingSectionCookie cookie;
 | 
						|
    int x, y;
 | 
						|
    char *out;
 | 
						|
    double xx, yy;
 | 
						|
 | 
						|
    if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
 | 
						|
	return (Imaging) ImagingError_ModeError();
 | 
						|
 | 
						|
    ImagingCopyInfo(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, filter_data)) {
 | 
						|
                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();
 | 
						|
 | 
						|
    ImagingCopyInfo(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;
 | 
						|
 | 
						|
    xintab = (int*) malloc(imOut->xsize * sizeof(int));
 | 
						|
    if (!xintab) {
 | 
						|
	ImagingDelete(imOut);
 | 
						|
	return (Imaging) ImagingError_MemoryError();
 | 
						|
    }
 | 
						|
 | 
						|
    xo = a[0];
 | 
						|
    yo = a[3];
 | 
						|
 | 
						|
    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[1];
 | 
						|
    }
 | 
						|
 | 
						|
#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[5];\
 | 
						|
    }
 | 
						|
 | 
						|
    ImagingSectionEnter(&cookie);
 | 
						|
 | 
						|
    if (imIn->image8) {
 | 
						|
        AFFINE_SCALE(UINT8, image8);
 | 
						|
    } else {
 | 
						|
        AFFINE_SCALE(INT32, image32);
 | 
						|
    }
 | 
						|
 | 
						|
    ImagingSectionLeave(&cookie);
 | 
						|
 | 
						|
    free(xintab);
 | 
						|
 | 
						|
    return imOut;
 | 
						|
}
 | 
						|
 | 
						|
static inline int
 | 
						|
check_fixed(double a[6], int x, int y)
 | 
						|
{
 | 
						|
    return (fabs(a[0] + x*a[1] + y*a[2]) < 32768.0 &&
 | 
						|
            fabs(a[3] + x*a[4] + y*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 */
 | 
						|
 | 
						|
    int x, y;
 | 
						|
    int xin, yin;
 | 
						|
    int xsize, ysize;
 | 
						|
    int xx, yy;
 | 
						|
    int a0, a1, a2, a3, a4, a5;
 | 
						|
 | 
						|
    ImagingCopyInfo(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]); a2 = FIX(a[2]);
 | 
						|
    a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]);
 | 
						|
 | 
						|
#define	AFFINE_TRANSFORM_FIXED(pixel, image)\
 | 
						|
    for (y = y0; y < y1; y++) {\
 | 
						|
	pixel *out;\
 | 
						|
	xx = a0;\
 | 
						|
	yy = a3;\
 | 
						|
	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 += a1;\
 | 
						|
	    yy += a4;\
 | 
						|
	}\
 | 
						|
	a0 += a2;\
 | 
						|
	a3 += a5;\
 | 
						|
    }
 | 
						|
 | 
						|
    if (imIn->image8)
 | 
						|
	AFFINE_TRANSFORM_FIXED(UINT8, image8)
 | 
						|
    else
 | 
						|
	AFFINE_TRANSFORM_FIXED(INT32, image32)
 | 
						|
 | 
						|
    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) {
 | 
						|
        /* Filtered transform */
 | 
						|
        ImagingTransformFilter filter = getfilter(imIn, filterid);
 | 
						|
        if (!filter)
 | 
						|
            return (Imaging) ImagingError_ValueError("unknown filter");
 | 
						|
        return ImagingTransform(
 | 
						|
            imOut, imIn,
 | 
						|
            x0, y0, x1, y1,
 | 
						|
            affine_transform, a,
 | 
						|
            filter, NULL, fill);
 | 
						|
    }
 | 
						|
 | 
						|
    if (a[2] == 0 && a[4] == 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;
 | 
						|
 | 
						|
    ImagingCopyInfo(imOut, imIn);
 | 
						|
 | 
						|
    /* 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? */
 | 
						|
 | 
						|
    xsize = (int) imIn->xsize;
 | 
						|
    ysize = (int) imIn->ysize;
 | 
						|
 | 
						|
    xo = a[0];
 | 
						|
    yo = a[3];
 | 
						|
 | 
						|
#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[1];\
 | 
						|
	    yy += a[4];\
 | 
						|
	}\
 | 
						|
	xo += a[2];\
 | 
						|
	yo += a[5];\
 | 
						|
    }
 | 
						|
 | 
						|
    ImagingSectionEnter(&cookie);
 | 
						|
 | 
						|
    if (imIn->image8)
 | 
						|
	AFFINE_TRANSFORM(UINT8, image8)
 | 
						|
    else
 | 
						|
	AFFINE_TRANSFORM(INT32, image32)
 | 
						|
 | 
						|
    ImagingSectionLeave(&cookie);
 | 
						|
 | 
						|
    return imOut;
 | 
						|
}
 | 
						|
 | 
						|
Imaging
 | 
						|
ImagingTransformPerspective(Imaging imOut, Imaging imIn,
 | 
						|
                            int x0, int y0, int x1, int y1,
 | 
						|
                            double a[8], int filterid, int fill)
 | 
						|
{
 | 
						|
    ImagingTransformFilter filter = getfilter(imIn, filterid);
 | 
						|
    if (!filter)
 | 
						|
        return (Imaging) ImagingError_ValueError("bad filter number");
 | 
						|
 | 
						|
    return ImagingTransform(
 | 
						|
        imOut, imIn,
 | 
						|
        x0, y0, x1, y1,
 | 
						|
        perspective_transform, a,
 | 
						|
        filter, NULL,
 | 
						|
        fill);
 | 
						|
}
 | 
						|
 | 
						|
Imaging
 | 
						|
ImagingTransformQuad(Imaging imOut, Imaging imIn,
 | 
						|
                     int x0, int y0, int x1, int y1,
 | 
						|
                     double a[8], int filterid, int fill)
 | 
						|
{
 | 
						|
    ImagingTransformFilter filter = getfilter(imIn, filterid);
 | 
						|
    if (!filter)
 | 
						|
        return (Imaging) ImagingError_ValueError("bad filter number");
 | 
						|
 | 
						|
    return ImagingTransform(
 | 
						|
        imOut, imIn,
 | 
						|
        x0, y0, x1, y1,
 | 
						|
        quad_transform, a,
 | 
						|
        filter, NULL,
 | 
						|
        fill);
 | 
						|
}
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Convenience functions */
 | 
						|
 | 
						|
Imaging
 | 
						|
ImagingResize(Imaging imOut, Imaging imIn, int filterid)
 | 
						|
{
 | 
						|
    double a[6];
 | 
						|
 | 
						|
    if (imOut->xsize == imIn->xsize && imOut->ysize == imIn->ysize)
 | 
						|
	return ImagingCopy2(imOut, imIn);
 | 
						|
 | 
						|
    memset(a, 0, sizeof a);
 | 
						|
    a[1] = (double) imIn->xsize / imOut->xsize;
 | 
						|
    a[5] = (double) imIn->ysize / imOut->ysize;
 | 
						|
 | 
						|
    if (!filterid && imIn->type != IMAGING_TYPE_SPECIAL)
 | 
						|
        return ImagingScaleAffine(
 | 
						|
            imOut, imIn,
 | 
						|
            0, 0, imOut->xsize, imOut->ysize,
 | 
						|
            a, 1);
 | 
						|
 | 
						|
    return ImagingTransformAffine(
 | 
						|
        imOut, imIn,
 | 
						|
        0, 0, imOut->xsize, imOut->ysize,
 | 
						|
        a, filterid, 1);
 | 
						|
}
 | 
						|
 | 
						|
Imaging
 | 
						|
ImagingRotate(Imaging imOut, Imaging imIn, double theta, int filterid)
 | 
						|
{
 | 
						|
    int xsize, ysize;
 | 
						|
    double sintheta, costheta;
 | 
						|
    double a[6];
 | 
						|
 | 
						|
    /* Setup an affine transform to rotate around the image center */
 | 
						|
    theta = -theta * M_PI / 180.0;
 | 
						|
    sintheta = sin(theta);
 | 
						|
    costheta = cos(theta);
 | 
						|
 | 
						|
    xsize = imOut->xsize;
 | 
						|
    ysize = imOut->ysize;
 | 
						|
 | 
						|
    a[0] = -costheta * xsize/2 - sintheta * ysize/2 + xsize/2;
 | 
						|
    a[1] = costheta;
 | 
						|
    a[2] = sintheta;
 | 
						|
    a[3] = sintheta * xsize/2 - costheta * ysize/2 + ysize/2;
 | 
						|
    a[4] = -sintheta;
 | 
						|
    a[5] = costheta;
 | 
						|
 | 
						|
    return ImagingTransformAffine(
 | 
						|
        imOut, imIn,
 | 
						|
        0, 0, imOut->xsize, imOut->ysize,
 | 
						|
        a, filterid, 1);
 | 
						|
}
 |