2013-07-01 02:42:19 +04:00
|
|
|
/*
|
2010-07-31 06:52:47 +04:00
|
|
|
* The Python Imaging Library
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* helpers to bounding boxes, min/max values, number of colors, etc.
|
|
|
|
*
|
|
|
|
* history:
|
|
|
|
* 1996-07-22 fl Created
|
|
|
|
* 1996-12-30 fl Added projection stuff
|
|
|
|
* 1998-07-12 fl Added extrema stuff
|
|
|
|
* 2004-09-17 fl Added colors stuff
|
|
|
|
*
|
|
|
|
* Copyright (c) 1997-2004 by Secret Labs AB.
|
|
|
|
* Copyright (c) 1996-2004 by Fredrik Lundh.
|
|
|
|
*
|
|
|
|
* See the README file for details on usage and redistribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "Imaging.h"
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
ImagingGetBBox(Imaging im, int bbox[4])
|
|
|
|
{
|
|
|
|
/* Get the bounding box for any non-zero data in the image.*/
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
int has_data;
|
|
|
|
|
|
|
|
/* Initialize bounding box to max values */
|
|
|
|
bbox[0] = im->xsize;
|
|
|
|
bbox[1] = -1;
|
|
|
|
bbox[2] = bbox[3] = 0;
|
|
|
|
|
2020-05-01 15:08:57 +03:00
|
|
|
#define GETBBOX(image, mask)\
|
2010-07-31 06:52:47 +04:00
|
|
|
for (y = 0; y < im->ysize; y++) {\
|
2020-05-01 15:08:57 +03:00
|
|
|
has_data = 0;\
|
2020-05-10 12:56:36 +03:00
|
|
|
for (x = 0; x < im->xsize; x++) {\
|
2020-05-01 15:08:57 +03:00
|
|
|
if (im->image[y][x] & mask) {\
|
|
|
|
has_data = 1;\
|
2020-05-11 00:46:12 +03:00
|
|
|
if (x < bbox[0]) {\
|
2020-05-01 15:08:57 +03:00
|
|
|
bbox[0] = x;\
|
2020-05-11 00:46:12 +03:00
|
|
|
}\
|
|
|
|
if (x >= bbox[2]) {\
|
2020-05-01 15:08:57 +03:00
|
|
|
bbox[2] = x+1;\
|
2020-05-11 00:46:12 +03:00
|
|
|
}\
|
2020-05-01 15:08:57 +03:00
|
|
|
}\
|
2020-05-10 12:56:36 +03:00
|
|
|
}\
|
2020-05-01 15:08:57 +03:00
|
|
|
if (has_data) {\
|
2020-05-11 00:46:12 +03:00
|
|
|
if (bbox[1] < 0) {\
|
|
|
|
bbox[1] = y;\
|
|
|
|
}\
|
2020-05-01 15:08:57 +03:00
|
|
|
bbox[3] = y+1;\
|
|
|
|
}\
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (im->image8) {
|
2020-05-01 15:08:57 +03:00
|
|
|
GETBBOX(image8, 0xff);
|
2010-07-31 06:52:47 +04:00
|
|
|
} else {
|
2020-05-01 15:08:57 +03:00
|
|
|
INT32 mask = 0xffffffff;
|
|
|
|
if (im->bands == 3) {
|
|
|
|
((UINT8*) &mask)[3] = 0;
|
|
|
|
} else if (strcmp(im->mode, "RGBa") == 0 ||
|
|
|
|
strcmp(im->mode, "RGBA") == 0 ||
|
|
|
|
strcmp(im->mode, "La") == 0 ||
|
|
|
|
strcmp(im->mode, "LA") == 0 ||
|
|
|
|
strcmp(im->mode, "PA") == 0) {
|
2020-02-28 14:33:02 +03:00
|
|
|
#ifdef WORDS_BIGENDIAN
|
2020-05-01 15:08:57 +03:00
|
|
|
mask = 0x000000ff;
|
2020-02-28 14:33:02 +03:00
|
|
|
#else
|
2020-05-01 15:08:57 +03:00
|
|
|
mask = 0xff000000;
|
2020-02-28 14:33:02 +03:00
|
|
|
#endif
|
2020-05-01 15:08:57 +03:00
|
|
|
}
|
|
|
|
GETBBOX(image32, mask);
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that we got a box */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (bbox[1] < 0) {
|
2020-05-01 15:08:57 +03:00
|
|
|
return 0; /* no data */
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
return 1; /* ok */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj)
|
|
|
|
{
|
|
|
|
/* Get projection arrays for non-zero data in the image.*/
|
|
|
|
|
|
|
|
int x, y;
|
|
|
|
int has_data;
|
|
|
|
|
|
|
|
/* Initialize projection arrays */
|
|
|
|
memset(xproj, 0, im->xsize);
|
|
|
|
memset(yproj, 0, im->ysize);
|
|
|
|
|
2020-05-01 15:08:57 +03:00
|
|
|
#define GETPROJ(image, mask)\
|
|
|
|
for (y = 0; y < im->ysize; y++) {\
|
|
|
|
has_data = 0;\
|
2020-05-10 12:56:36 +03:00
|
|
|
for (x = 0; x < im->xsize; x++) {\
|
2020-05-01 15:08:57 +03:00
|
|
|
if (im->image[y][x] & mask) {\
|
|
|
|
has_data = 1;\
|
|
|
|
xproj[x] = 1;\
|
|
|
|
}\
|
2020-05-10 12:56:36 +03:00
|
|
|
}\
|
|
|
|
if (has_data) {\
|
|
|
|
yproj[y] = 1;\
|
|
|
|
}\
|
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
if (im->image8) {
|
2020-05-01 15:08:57 +03:00
|
|
|
GETPROJ(image8, 0xff);
|
2010-07-31 06:52:47 +04:00
|
|
|
} else {
|
2020-05-01 15:08:57 +03:00
|
|
|
INT32 mask = 0xffffffff;
|
2020-05-10 12:56:36 +03:00
|
|
|
if (im->bands == 3) {
|
2020-05-01 15:08:57 +03:00
|
|
|
((UINT8*) &mask)[3] = 0;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
GETPROJ(image32, mask);
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1; /* ok */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
ImagingGetExtrema(Imaging im, void *extrema)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
INT32 imin, imax;
|
|
|
|
FLOAT32 fmin, fmax;
|
|
|
|
|
|
|
|
if (im->bands != 1) {
|
2020-05-01 15:08:57 +03:00
|
|
|
(void) ImagingError_ModeError();
|
2010-07-31 06:52:47 +04:00
|
|
|
return -1; /* mismatch */
|
|
|
|
}
|
|
|
|
|
2020-05-10 12:56:36 +03:00
|
|
|
if (!im->xsize || !im->ysize) {
|
2010-07-31 06:52:47 +04:00
|
|
|
return 0; /* zero size */
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
switch (im->type) {
|
|
|
|
case IMAGING_TYPE_UINT8:
|
|
|
|
imin = imax = im->image8[0][0];
|
|
|
|
for (y = 0; y < im->ysize; y++) {
|
|
|
|
UINT8* in = im->image8[y];
|
|
|
|
for (x = 0; x < im->xsize; x++) {
|
2020-05-10 12:56:36 +03:00
|
|
|
if (imin > in[x]) {
|
2010-07-31 06:52:47 +04:00
|
|
|
imin = in[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
} else if (imax < in[x]) {
|
2010-07-31 06:52:47 +04:00
|
|
|
imax = in[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
((UINT8*) extrema)[0] = (UINT8) imin;
|
|
|
|
((UINT8*) extrema)[1] = (UINT8) imax;
|
|
|
|
break;
|
|
|
|
case IMAGING_TYPE_INT32:
|
|
|
|
imin = imax = im->image32[0][0];
|
|
|
|
for (y = 0; y < im->ysize; y++) {
|
|
|
|
INT32* in = im->image32[y];
|
|
|
|
for (x = 0; x < im->xsize; x++) {
|
2020-05-10 12:56:36 +03:00
|
|
|
if (imin > in[x]) {
|
2010-07-31 06:52:47 +04:00
|
|
|
imin = in[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
} else if (imax < in[x]) {
|
2010-07-31 06:52:47 +04:00
|
|
|
imax = in[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
}
|
2018-06-29 23:33:08 +03:00
|
|
|
memcpy(extrema, &imin, sizeof(imin));
|
2018-07-01 13:47:59 +03:00
|
|
|
memcpy(((char*)extrema) + sizeof(imin), &imax, sizeof(imax));
|
2010-07-31 06:52:47 +04:00
|
|
|
break;
|
|
|
|
case IMAGING_TYPE_FLOAT32:
|
|
|
|
fmin = fmax = ((FLOAT32*) im->image32[0])[0];
|
|
|
|
for (y = 0; y < im->ysize; y++) {
|
|
|
|
FLOAT32* in = (FLOAT32*) im->image32[y];
|
|
|
|
for (x = 0; x < im->xsize; x++) {
|
2020-05-10 12:56:36 +03:00
|
|
|
if (fmin > in[x]) {
|
2010-07-31 06:52:47 +04:00
|
|
|
fmin = in[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
} else if (fmax < in[x]) {
|
2010-07-31 06:52:47 +04:00
|
|
|
fmax = in[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
}
|
2018-06-29 23:33:08 +03:00
|
|
|
memcpy(extrema, &fmin, sizeof(fmin));
|
2018-07-01 13:47:59 +03:00
|
|
|
memcpy(((char*)extrema) + sizeof(fmin), &fmax, sizeof(fmax));
|
2010-07-31 06:52:47 +04:00
|
|
|
break;
|
|
|
|
case IMAGING_TYPE_SPECIAL:
|
|
|
|
if (strcmp(im->mode, "I;16") == 0) {
|
2018-07-01 13:47:59 +03:00
|
|
|
UINT16 v;
|
2020-02-29 12:29:28 +03:00
|
|
|
UINT8* pixel = *im->image8;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
v = pixel[0] + (pixel[1] << 8);
|
|
|
|
#else
|
|
|
|
memcpy(&v, pixel, sizeof(v));
|
|
|
|
#endif
|
2018-07-01 13:47:59 +03:00
|
|
|
imin = imax = v;
|
2010-07-31 06:52:47 +04:00
|
|
|
for (y = 0; y < im->ysize; y++) {
|
|
|
|
for (x = 0; x < im->xsize; x++) {
|
2020-02-29 12:29:28 +03:00
|
|
|
pixel = im->image[y] + x * sizeof(v);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
v = pixel[0] + (pixel[1] << 8);
|
|
|
|
#else
|
|
|
|
memcpy(&v, pixel, sizeof(v));
|
|
|
|
#endif
|
2020-05-10 12:56:36 +03:00
|
|
|
if (imin > v) {
|
2018-07-01 13:47:59 +03:00
|
|
|
imin = v;
|
2020-05-10 12:56:36 +03:00
|
|
|
} else if (imax < v) {
|
2018-07-01 13:47:59 +03:00
|
|
|
imax = v;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
}
|
2018-07-01 13:47:59 +03:00
|
|
|
v = (UINT16) imin;
|
|
|
|
memcpy(extrema, &v, sizeof(v));
|
|
|
|
v = (UINT16) imax;
|
|
|
|
memcpy(((char*)extrema) + sizeof(v), &v, sizeof(v));
|
2020-05-01 15:08:57 +03:00
|
|
|
break;
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
/* FALL THROUGH */
|
|
|
|
default:
|
2020-05-01 15:08:57 +03:00
|
|
|
(void) ImagingError_ModeError();
|
2010-07-31 06:52:47 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 1; /* ok */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* static ImagingColorItem* getcolors8(Imaging im, int maxcolors, int* size);*/
|
|
|
|
static ImagingColorItem* getcolors32(Imaging im, int maxcolors, int* size);
|
|
|
|
|
|
|
|
ImagingColorItem*
|
|
|
|
ImagingGetColors(Imaging im, int maxcolors, int* size)
|
|
|
|
{
|
|
|
|
/* FIXME: add support for 8-bit images */
|
|
|
|
return getcolors32(im, maxcolors, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ImagingColorItem*
|
|
|
|
getcolors32(Imaging im, int maxcolors, int* size)
|
|
|
|
{
|
|
|
|
unsigned int h;
|
|
|
|
unsigned int i, incr;
|
|
|
|
int colors;
|
|
|
|
INT32 pixel_mask;
|
|
|
|
int x, y;
|
|
|
|
ImagingColorItem* table;
|
|
|
|
ImagingColorItem* v;
|
|
|
|
|
|
|
|
unsigned int code_size;
|
|
|
|
unsigned int code_poly;
|
|
|
|
unsigned int code_mask;
|
|
|
|
|
|
|
|
/* note: the hash algorithm used here is based on the dictionary
|
|
|
|
code in Python 2.1.3; the exact implementation is borrowed from
|
|
|
|
Python's Unicode property database (written by yours truly) /F */
|
|
|
|
|
|
|
|
static int SIZES[] = {
|
|
|
|
4,3, 8,3, 16,3, 32,5, 64,3, 128,3, 256,29, 512,17, 1024,9, 2048,5,
|
|
|
|
4096,83, 8192,27, 16384,43, 32768,3, 65536,45, 131072,9, 262144,39,
|
|
|
|
524288,39, 1048576,9, 2097152,5, 4194304,3, 8388608,33, 16777216,27,
|
|
|
|
33554432,9, 67108864,71, 134217728,39, 268435456,9, 536870912,5,
|
|
|
|
1073741824,83, 0
|
|
|
|
};
|
2013-07-01 02:42:19 +04:00
|
|
|
|
2010-07-31 06:52:47 +04:00
|
|
|
code_size = code_poly = code_mask = 0;
|
|
|
|
|
|
|
|
for (i = 0; SIZES[i]; i += 2) {
|
|
|
|
if (SIZES[i] > maxcolors) {
|
|
|
|
code_size = SIZES[i];
|
|
|
|
code_poly = SIZES[i+1];
|
|
|
|
code_mask = code_size - 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* printf("code_size=%d\n", code_size); */
|
|
|
|
/* printf("code_poly=%d\n", code_poly); */
|
|
|
|
|
2020-05-10 12:56:36 +03:00
|
|
|
if (!code_size) {
|
2020-05-01 15:08:57 +03:00
|
|
|
return ImagingError_MemoryError(); /* just give up */
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-05-10 12:56:36 +03:00
|
|
|
if (!im->image32) {
|
2020-05-01 15:08:57 +03:00
|
|
|
return ImagingError_ModeError();
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
table = calloc(code_size + 1, sizeof(ImagingColorItem));
|
2020-05-10 12:56:36 +03:00
|
|
|
if (!table) {
|
2020-05-01 15:08:57 +03:00
|
|
|
return ImagingError_MemoryError();
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
pixel_mask = 0xffffffff;
|
2020-05-10 12:56:36 +03:00
|
|
|
if (im->bands == 3) {
|
2010-07-31 06:52:47 +04:00
|
|
|
((UINT8*) &pixel_mask)[3] = 0;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
colors = 0;
|
|
|
|
|
|
|
|
for (y = 0; y < im->ysize; y++) {
|
|
|
|
INT32* p = im->image32[y];
|
|
|
|
for (x = 0; x < im->xsize; x++) {
|
|
|
|
INT32 pixel = p[x] & pixel_mask;
|
|
|
|
h = (pixel); /* null hashing */
|
|
|
|
i = (~h) & code_mask;
|
|
|
|
v = &table[i];
|
|
|
|
if (!v->count) {
|
|
|
|
/* add to table */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (colors++ == maxcolors) {
|
2010-07-31 06:52:47 +04:00
|
|
|
goto overflow;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
v->x = x; v->y = y;
|
|
|
|
v->pixel = pixel;
|
|
|
|
v->count = 1;
|
|
|
|
continue;
|
|
|
|
} else if (v->pixel == pixel) {
|
|
|
|
v->count++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
incr = (h ^ (h >> 3)) & code_mask;
|
2020-05-10 12:56:36 +03:00
|
|
|
if (!incr) {
|
2010-07-31 06:52:47 +04:00
|
|
|
incr = code_mask;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
for (;;) {
|
|
|
|
i = (i + incr) & code_mask;
|
|
|
|
v = &table[i];
|
|
|
|
if (!v->count) {
|
|
|
|
/* add to table */
|
2020-05-10 12:56:36 +03:00
|
|
|
if (colors++ == maxcolors) {
|
2010-07-31 06:52:47 +04:00
|
|
|
goto overflow;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
v->x = x; v->y = y;
|
|
|
|
v->pixel = pixel;
|
|
|
|
v->count = 1;
|
|
|
|
break;
|
|
|
|
} else if (v->pixel == pixel) {
|
|
|
|
v->count++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
incr = incr << 1;
|
2020-05-10 12:56:36 +03:00
|
|
|
if (incr > code_mask) {
|
2010-07-31 06:52:47 +04:00
|
|
|
incr = incr ^ code_poly;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
overflow:
|
|
|
|
|
|
|
|
/* pack the table */
|
|
|
|
for (x = y = 0; x < (int) code_size; x++)
|
|
|
|
if (table[x].count) {
|
2020-05-10 12:56:36 +03:00
|
|
|
if (x != y) {
|
2010-07-31 06:52:47 +04:00
|
|
|
table[y] = table[x];
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
y++;
|
|
|
|
}
|
|
|
|
table[y].count = 0; /* mark end of table */
|
|
|
|
|
|
|
|
*size = colors;
|
|
|
|
|
|
|
|
return table;
|
|
|
|
}
|