add box parameter

This commit is contained in:
Alexander 2019-12-02 04:32:08 +03:00
parent cc30b1e55a
commit 778b5f9fed
5 changed files with 210 additions and 174 deletions

View File

@ -18,7 +18,7 @@ class TestImageReduce(PillowTestCase):
cls.gradients_image = Image.open("Tests/images/radial_gradients.png") cls.gradients_image = Image.open("Tests/images/radial_gradients.png")
cls.gradients_image.load() cls.gradients_image.load()
def test_args(self): def test_args_factor(self):
im = Image.new("L", (10, 10)) im = Image.new("L", (10, 10))
self.assertEqual((4, 4), im.reduce(3).size) self.assertEqual((4, 4), im.reduce(3).size)
@ -62,6 +62,12 @@ class TestImageReduce(PillowTestCase):
assert len(mode_info.bands) == 1 assert len(mode_info.bands) == 1
return self.gradients_image.convert(mode) return self.gradients_image.convert(mode)
def compare_reduce_with_box(self, im, factor):
box = (11, 13, 146, 164)
reduced = im.reduce(factor, box=box)
reference = im.crop(box).reduce(factor)
self.assertEqual(reduced, reference)
def compare_reduce_with_reference(self, im, factor, average_diff=0.4, max_diff=1): def compare_reduce_with_reference(self, im, factor, average_diff=0.4, max_diff=1):
"""Image.reduce() should look very similar to Image.resize(BOX). """Image.reduce() should look very similar to Image.resize(BOX).
@ -136,6 +142,7 @@ class TestImageReduce(PillowTestCase):
im = self.get_image("L") im = self.get_image("L")
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_LA(self): def test_mode_LA(self):
im = self.get_image("LA") im = self.get_image("LA")
@ -146,16 +153,19 @@ class TestImageReduce(PillowTestCase):
im.putalpha(Image.new('L', im.size, 255)) im.putalpha(Image.new('L', im.size, 255))
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_La(self): def test_mode_La(self):
im = self.get_image("La") im = self.get_image("La")
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_RGB(self): def test_mode_RGB(self):
im = self.get_image("RGB") im = self.get_image("RGB")
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_RGBA(self): def test_mode_RGBA(self):
im = self.get_image("RGBA") im = self.get_image("RGBA")
@ -166,18 +176,22 @@ class TestImageReduce(PillowTestCase):
im.putalpha(Image.new('L', im.size, 255)) im.putalpha(Image.new('L', im.size, 255))
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_RGBa(self): def test_mode_RGBa(self):
im = self.get_image("RGBa") im = self.get_image("RGBa")
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_I(self): def test_mode_I(self):
im = self.get_image("I") im = self.get_image("I")
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor) self.compare_reduce_with_reference(im, factor)
self.compare_reduce_with_box(im, factor)
def test_mode_F(self): def test_mode_F(self):
im = self.get_image("F") im = self.get_image("F")
for factor in self.remarkable_factors: for factor in self.remarkable_factors:
self.compare_reduce_with_reference(im, factor, 0, 0) self.compare_reduce_with_reference(im, factor, 0, 0)
self.compare_reduce_with_box(im, factor)

View File

@ -1887,7 +1887,7 @@ class Image(object):
return self._new(self.im.resize(size, resample, box)) return self._new(self.im.resize(size, resample, box))
def reduce(self, factor): def reduce(self, factor, box=None):
""" """
Returns reduced in `factor` times copy of the image. Returns reduced in `factor` times copy of the image.
If the size of the image is not dividable by the `factor`, If the size of the image is not dividable by the `factor`,
@ -1895,21 +1895,30 @@ class Image(object):
:param factor: A greater than 0 integer or tuple of two integers :param factor: A greater than 0 integer or tuple of two integers
for width and height separately. for width and height separately.
:param box: An optional 4-tuple of ints giving the region
of the source image which should be reduced.
The values should be within (0, 0, width, height) rectangle.
If omitted or None, the entire source is used.
""" """
if not isinstance(factor, (list, tuple)): if not isinstance(factor, (list, tuple)):
factor = (factor, factor) factor = (factor, factor)
if factor == (1, 1): if box is None:
box = (0, 0) + self.size
else:
box = tuple(box)
if factor == (1, 1) and box == (0, 0) + self.size:
return self.copy() return self.copy()
if self.mode in ["LA", "RGBA"]: if self.mode in ["LA", "RGBA"]:
im = self.convert(self.mode[:-1] + "a") im = self.convert(self.mode[:-1] + "a")
im = im.reduce(factor) im = im.reduce(factor, box)
return im.convert(self.mode) return im.convert(self.mode)
self.load() self.load()
return self._new(self.im.reduce(factor)) return self._new(self.im.reduce(factor, box))
def rotate( def rotate(
self, self,

View File

@ -1836,20 +1836,39 @@ _reduce(ImagingObject* self, PyObject* args)
Imaging imOut; Imaging imOut;
int xscale, yscale; int xscale, yscale;
int box[4] = {0, 0, 0, 0};
imIn = self->image; imIn = self->image;
box[2] = imIn->xsize;
box[3] = imIn->ysize;
if (!PyArg_ParseTuple(args, "(ii)", &xscale, &yscale)) if (!PyArg_ParseTuple(args, "(ii)|(iiii)", &xscale, &yscale,
&box[0], &box[1], &box[2], &box[3]))
return NULL; return NULL;
if (xscale < 1 || yscale < 1) { if (xscale < 1 || yscale < 1) {
return ImagingError_ValueError("scale must be > 0"); return ImagingError_ValueError("scale must be > 0");
} }
if (box[0] < 0 || box[1] < 0) {
return ImagingError_ValueError("box offset can't be negative");
}
if (box[2] > imIn->xsize || box[3] > imIn->ysize) {
return ImagingError_ValueError("box can't exceed original image size");
}
if (box[2] < box[0] || box[3] < box[1]) {
return ImagingError_ValueError("box can't be empty");
}
if (xscale == 1 && yscale == 1) { if (xscale == 1 && yscale == 1) {
imOut = ImagingCopy(imIn); imOut = ImagingCrop(imIn, box[0], box[1], box[2], box[3]);
} else { } else {
imOut = ImagingReduce(imIn, xscale, yscale); // Change box format: (left, top, width, height)
box[2] -= box[0];
box[3] -= box[1];
imOut = ImagingReduce(imIn, xscale, yscale, box);
} }
return PyImagingNew(imOut); return PyImagingNew(imOut);

View File

@ -313,7 +313,7 @@ extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn); extern Imaging ImagingTranspose(Imaging imOut, Imaging imIn);
extern Imaging ImagingTransverse(Imaging imOut, Imaging imIn); extern Imaging ImagingTransverse(Imaging imOut, Imaging imIn);
extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]); extern Imaging ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]);
extern Imaging ImagingReduce(Imaging imIn, int xscale, int yscale); extern Imaging ImagingReduce(Imaging imIn, int xscale, int yscale, int box[4]);
extern Imaging ImagingTransform( extern Imaging ImagingTransform(
Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1, Imaging imOut, Imaging imIn, int method, int x0, int y0, int x1, int y1,
double *a, int filter, int fill); double *a, int filter, int fill);

View File

@ -15,39 +15,37 @@ division_UINT32(int divider, int result_bits)
void void
ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale) ImagingReduceNxN(Imaging imOut, Imaging imIn, int box[4], int xscale, int yscale)
{ {
/* The most general implementation for any xscale and yscale /* The most general implementation for any xscale and yscale
*/ */
int x, y, xx, yy, xi; int x, y, xx, yy;
UINT32 multiplier = division_UINT32(yscale * xscale, 8); UINT32 multiplier = division_UINT32(yscale * xscale, 8);
UINT32 amend = yscale * xscale / 2; UINT32 amend = yscale * xscale / 2;
if (imIn->image8) { if (imIn->image8) {
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
for (x = 0; x < imIn->xsize / xscale; x++) { int yy_from = box[1] + y*yscale;
for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
UINT32 ss = amend; UINT32 ss = amend;
for (yy = y*yscale; yy < y*yscale + yscale - 1; yy += 2) { for (yy = yy_from; yy < yy_from + yscale - 1; yy += 2) {
UINT8 *line0 = (UINT8 *)imIn->image8[yy]; UINT8 *line0 = (UINT8 *)imIn->image8[yy];
UINT8 *line1 = (UINT8 *)imIn->image8[yy + 1]; UINT8 *line1 = (UINT8 *)imIn->image8[yy + 1];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss += line0[xx + 0] + line0[xx + 1] + ss += line0[xx + 0] + line0[xx + 1] +
line1[xx + 0] + line1[xx + 1]; line1[xx + 0] + line1[xx + 1];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss += line0[xx + 0] + line1[xx + 0]; ss += line0[xx + 0] + line1[xx + 0];
} }
} }
if (yscale & 0x01) { if (yscale & 0x01) {
UINT8 *line = (UINT8 *)imIn->image8[yy]; UINT8 *line = (UINT8 *)imIn->image8[yy];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss += line[xx + 0] + line[xx + 1]; ss += line[xx + 0] + line[xx + 1];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
@ -55,36 +53,34 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
} }
} }
} else { } else {
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
int yy_from = box[1] + y*yscale;
if (imIn->bands == 2) { if (imIn->bands == 2) {
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
UINT32 v; UINT32 v;
UINT32 ss0 = amend, ss3 = amend; UINT32 ss0 = amend, ss3 = amend;
for (yy = y*yscale; yy < y*yscale + yscale - 1; yy += 2) { for (yy = yy_from; yy < yy_from + yscale - 1; yy += 2) {
UINT8 *line0 = (UINT8 *)imIn->image[yy]; UINT8 *line0 = (UINT8 *)imIn->image[yy];
UINT8 *line1 = (UINT8 *)imIn->image[yy + 1]; UINT8 *line1 = (UINT8 *)imIn->image[yy + 1];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss0 += line0[xx*4 + 0] + line0[xx*4 + 4] + ss0 += line0[xx*4 + 0] + line0[xx*4 + 4] +
line1[xx*4 + 0] + line1[xx*4 + 4]; line1[xx*4 + 0] + line1[xx*4 + 4];
ss3 += line0[xx*4 + 3] + line0[xx*4 + 7] + ss3 += line0[xx*4 + 3] + line0[xx*4 + 7] +
line1[xx*4 + 3] + line1[xx*4 + 7]; line1[xx*4 + 3] + line1[xx*4 + 7];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss0 += line0[xx*4 + 0] + line1[xx*4 + 0]; ss0 += line0[xx*4 + 0] + line1[xx*4 + 0];
ss3 += line0[xx*4 + 3] + line1[xx*4 + 3]; ss3 += line0[xx*4 + 3] + line1[xx*4 + 3];
} }
} }
if (yscale & 0x01) { if (yscale & 0x01) {
UINT8 *line = (UINT8 *)imIn->image[yy]; UINT8 *line = (UINT8 *)imIn->image[yy];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss0 += line[xx*4 + 0] + line[xx*4 + 4]; ss0 += line[xx*4 + 0] + line[xx*4 + 4];
ss3 += line[xx*4 + 3] + line[xx*4 + 7]; ss3 += line[xx*4 + 3] + line[xx*4 + 7];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss0 += line[xx*4 + 0]; ss0 += line[xx*4 + 0];
ss3 += line[xx*4 + 3]; ss3 += line[xx*4 + 3];
} }
@ -95,14 +91,14 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v)); memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v));
} }
} else if (imIn->bands == 3) { } else if (imIn->bands == 3) {
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
UINT32 v; UINT32 v;
UINT32 ss0 = amend, ss1 = amend, ss2 = amend; UINT32 ss0 = amend, ss1 = amend, ss2 = amend;
for (yy = y*yscale; yy < y*yscale + yscale - 1; yy += 2) { for (yy = yy_from; yy < yy_from + yscale - 1; yy += 2) {
UINT8 *line0 = (UINT8 *)imIn->image[yy]; UINT8 *line0 = (UINT8 *)imIn->image[yy];
UINT8 *line1 = (UINT8 *)imIn->image[yy + 1]; UINT8 *line1 = (UINT8 *)imIn->image[yy + 1];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss0 += line0[xx*4 + 0] + line0[xx*4 + 4] + ss0 += line0[xx*4 + 0] + line0[xx*4 + 4] +
line1[xx*4 + 0] + line1[xx*4 + 4]; line1[xx*4 + 0] + line1[xx*4 + 4];
ss1 += line0[xx*4 + 1] + line0[xx*4 + 5] + ss1 += line0[xx*4 + 1] + line0[xx*4 + 5] +
@ -111,7 +107,6 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
line1[xx*4 + 2] + line1[xx*4 + 6]; line1[xx*4 + 2] + line1[xx*4 + 6];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss0 += line0[xx*4 + 0] + line1[xx*4 + 0]; ss0 += line0[xx*4 + 0] + line1[xx*4 + 0];
ss1 += line0[xx*4 + 1] + line1[xx*4 + 1]; ss1 += line0[xx*4 + 1] + line1[xx*4 + 1];
ss2 += line0[xx*4 + 2] + line1[xx*4 + 2]; ss2 += line0[xx*4 + 2] + line1[xx*4 + 2];
@ -119,14 +114,12 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
} }
if (yscale & 0x01) { if (yscale & 0x01) {
UINT8 *line = (UINT8 *)imIn->image[yy]; UINT8 *line = (UINT8 *)imIn->image[yy];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss0 += line[xx*4 + 0] + line[xx*4 + 4]; ss0 += line[xx*4 + 0] + line[xx*4 + 4];
ss1 += line[xx*4 + 1] + line[xx*4 + 5]; ss1 += line[xx*4 + 1] + line[xx*4 + 5];
ss2 += line[xx*4 + 2] + line[xx*4 + 6]; ss2 += line[xx*4 + 2] + line[xx*4 + 6];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss0 += line[xx*4 + 0]; ss0 += line[xx*4 + 0];
ss1 += line[xx*4 + 1]; ss1 += line[xx*4 + 1];
ss2 += line[xx*4 + 2]; ss2 += line[xx*4 + 2];
@ -138,14 +131,14 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v)); memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v));
} }
} else { // bands == 4 } else { // bands == 4
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
UINT32 v; UINT32 v;
UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend; UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend;
for (yy = y*yscale; yy < y*yscale + yscale - 1; yy += 2) { for (yy = yy_from; yy < yy_from + yscale - 1; yy += 2) {
UINT8 *line0 = (UINT8 *)imIn->image[yy]; UINT8 *line0 = (UINT8 *)imIn->image[yy];
UINT8 *line1 = (UINT8 *)imIn->image[yy + 1]; UINT8 *line1 = (UINT8 *)imIn->image[yy + 1];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss0 += line0[xx*4 + 0] + line0[xx*4 + 4] + ss0 += line0[xx*4 + 0] + line0[xx*4 + 4] +
line1[xx*4 + 0] + line1[xx*4 + 4]; line1[xx*4 + 0] + line1[xx*4 + 4];
ss1 += line0[xx*4 + 1] + line0[xx*4 + 5] + ss1 += line0[xx*4 + 1] + line0[xx*4 + 5] +
@ -156,7 +149,6 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
line1[xx*4 + 3] + line1[xx*4 + 7]; line1[xx*4 + 3] + line1[xx*4 + 7];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss0 += line0[xx*4 + 0] + line1[xx*4 + 0]; ss0 += line0[xx*4 + 0] + line1[xx*4 + 0];
ss1 += line0[xx*4 + 1] + line1[xx*4 + 1]; ss1 += line0[xx*4 + 1] + line1[xx*4 + 1];
ss2 += line0[xx*4 + 2] + line1[xx*4 + 2]; ss2 += line0[xx*4 + 2] + line1[xx*4 + 2];
@ -165,15 +157,13 @@ ImagingReduceNxN(Imaging imOut, Imaging imIn, int xscale, int yscale)
} }
if (yscale & 0x01) { if (yscale & 0x01) {
UINT8 *line = (UINT8 *)imIn->image[yy]; UINT8 *line = (UINT8 *)imIn->image[yy];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss0 += line[xx*4 + 0] + line[xx*4 + 4]; ss0 += line[xx*4 + 0] + line[xx*4 + 4];
ss1 += line[xx*4 + 1] + line[xx*4 + 5]; ss1 += line[xx*4 + 1] + line[xx*4 + 5];
ss2 += line[xx*4 + 2] + line[xx*4 + 6]; ss2 += line[xx*4 + 2] + line[xx*4 + 6];
ss3 += line[xx*4 + 3] + line[xx*4 + 7]; ss3 += line[xx*4 + 3] + line[xx*4 + 7];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss0 += line[xx*4 + 0]; ss0 += line[xx*4 + 0];
ss1 += line[xx*4 + 1]; ss1 += line[xx*4 + 1];
ss2 += line[xx*4 + 2]; ss2 += line[xx*4 + 2];
@ -1025,74 +1015,77 @@ ImagingReduce5x5(Imaging imOut, Imaging imIn)
void void
ImagingReduceCorners(Imaging imOut, Imaging imIn, int xscale, int yscale) ImagingReduceCorners(Imaging imOut, Imaging imIn, int box[4], int xscale, int yscale)
{ {
/* Fill the last row and the last column for any xscale and yscale. /* Fill the last row and the last column for any xscale and yscale.
*/ */
int x, y, xx, yy; int x, y, xx, yy;
if (imIn->image8) { if (imIn->image8) {
if (imIn->xsize % xscale) { if (box[2] % xscale) {
int scale = (imIn->xsize % xscale) * yscale; int scale = (box[2] % xscale) * yscale;
UINT32 multiplier = division_UINT32(scale, 8); UINT32 multiplier = division_UINT32(scale, 8);
UINT32 amend = scale / 2; UINT32 amend = scale / 2;
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
int yy_from = box[1] + y*yscale;
UINT32 ss = amend; UINT32 ss = amend;
x = imIn->xsize / xscale; x = box[2] / xscale;
for (yy = y*yscale; yy < y*yscale + yscale; yy++) { for (yy = yy_from; yy < yy_from + yscale; yy++) {
UINT8 *line = (UINT8 *)imIn->image8[yy]; UINT8 *line = (UINT8 *)imIn->image8[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
imOut->image8[y][x] = (ss * multiplier) >> 24; imOut->image8[y][x] = (ss * multiplier) >> 24;
} }
} }
if (imIn->ysize % yscale) { if (box[3] % yscale) {
int scale = xscale * (imIn->ysize % yscale); int scale = xscale * (box[3] % yscale);
UINT32 multiplier = division_UINT32(scale, 8); UINT32 multiplier = division_UINT32(scale, 8);
UINT32 amend = scale / 2; UINT32 amend = scale / 2;
y = imIn->ysize / yscale; y = box[3] / yscale;
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
UINT32 ss = amend; UINT32 ss = amend;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
UINT8 *line = (UINT8 *)imIn->image8[yy]; UINT8 *line = (UINT8 *)imIn->image8[yy];
for (xx = x*xscale; xx < x*xscale + xscale; xx++) { for (xx = xx_from; xx < xx_from + xscale; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
imOut->image8[y][x] = (ss * multiplier) >> 24; imOut->image8[y][x] = (ss * multiplier) >> 24;
} }
} }
if (imIn->xsize % xscale && imIn->ysize % yscale) { if (box[2] % xscale && box[3] % yscale) {
int scale = (imIn->xsize % xscale) * (imIn->ysize % yscale); int scale = (box[2] % xscale) * (box[3] % yscale);
UINT32 multiplier = division_UINT32(scale, 8); UINT32 multiplier = division_UINT32(scale, 8);
UINT32 amend = scale / 2; UINT32 amend = scale / 2;
UINT32 ss = amend; UINT32 ss = amend;
x = imIn->xsize / xscale; x = box[2] / xscale;
y = imIn->ysize / yscale; y = box[3] / yscale;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
UINT8 *line = (UINT8 *)imIn->image8[yy]; UINT8 *line = (UINT8 *)imIn->image8[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
imOut->image8[y][x] = (ss * multiplier) >> 24; imOut->image8[y][x] = (ss * multiplier) >> 24;
} }
} else { } else {
if (imIn->xsize % xscale) { if (box[2] % xscale) {
int scale = (imIn->xsize % xscale) * yscale; int scale = (box[2] % xscale) * yscale;
UINT32 multiplier = division_UINT32(scale, 8); UINT32 multiplier = division_UINT32(scale, 8);
UINT32 amend = scale / 2; UINT32 amend = scale / 2;
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
int yy_from = box[1] + y*yscale;
UINT32 v; UINT32 v;
UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend; UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend;
x = imIn->xsize / xscale; x = box[2] / xscale;
for (yy = y*yscale; yy < y*yscale + yscale; yy++) { for (yy = yy_from; yy < yy_from + yscale; yy++) {
UINT8 *line = (UINT8 *)imIn->image[yy]; UINT8 *line = (UINT8 *)imIn->image[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss0 += line[xx*4 + 0]; ss0 += line[xx*4 + 0];
ss1 += line[xx*4 + 1]; ss1 += line[xx*4 + 1];
ss2 += line[xx*4 + 2]; ss2 += line[xx*4 + 2];
@ -1105,17 +1098,18 @@ ImagingReduceCorners(Imaging imOut, Imaging imIn, int xscale, int yscale)
memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v)); memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v));
} }
} }
if (imIn->ysize % yscale) { if (box[3] % yscale) {
int scale = xscale * (imIn->ysize % yscale); int scale = xscale * (box[3] % yscale);
UINT32 multiplier = division_UINT32(scale, 8); UINT32 multiplier = division_UINT32(scale, 8);
UINT32 amend = scale / 2; UINT32 amend = scale / 2;
y = imIn->ysize / yscale; y = box[3] / yscale;
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
UINT32 v; UINT32 v;
UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend; UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
UINT8 *line = (UINT8 *)imIn->image[yy]; UINT8 *line = (UINT8 *)imIn->image[yy];
for (xx = x*xscale; xx < x*xscale + xscale; xx++) { for (xx = xx_from; xx < xx_from + xscale; xx++) {
ss0 += line[xx*4 + 0]; ss0 += line[xx*4 + 0];
ss1 += line[xx*4 + 1]; ss1 += line[xx*4 + 1];
ss2 += line[xx*4 + 2]; ss2 += line[xx*4 + 2];
@ -1128,17 +1122,17 @@ ImagingReduceCorners(Imaging imOut, Imaging imIn, int xscale, int yscale)
memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v)); memcpy(imOut->image[y] + x * sizeof(v), &v, sizeof(v));
} }
} }
if (imIn->xsize % xscale && imIn->ysize % yscale) { if (box[2] % xscale && box[3] % yscale) {
int scale = (imIn->xsize % xscale) * (imIn->ysize % yscale); int scale = (box[2] % xscale) * (box[3] % yscale);
UINT32 multiplier = division_UINT32(scale, 8); UINT32 multiplier = division_UINT32(scale, 8);
UINT32 amend = scale / 2; UINT32 amend = scale / 2;
UINT32 v; UINT32 v;
UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend; UINT32 ss0 = amend, ss1 = amend, ss2 = amend, ss3 = amend;
x = imIn->xsize / xscale; x = box[2] / xscale;
y = imIn->ysize / yscale; y = box[3] / yscale;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
UINT8 *line = (UINT8 *)imIn->image[yy]; UINT8 *line = (UINT8 *)imIn->image[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss0 += line[xx*4 + 0]; ss0 += line[xx*4 + 0];
ss1 += line[xx*4 + 1]; ss1 += line[xx*4 + 1];
ss2 += line[xx*4 + 2]; ss2 += line[xx*4 + 2];
@ -1155,39 +1149,37 @@ ImagingReduceCorners(Imaging imOut, Imaging imIn, int xscale, int yscale)
void void
ImagingReduceNxN_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale) ImagingReduceNxN_32bpc(Imaging imOut, Imaging imIn, int box[4], int xscale, int yscale)
{ {
/* The most general implementation for any xscale and yscale /* The most general implementation for any xscale and yscale
*/ */
int x, y, xx, yy, xi; int x, y, xx, yy;
double multiplier = 1.0 / (yscale * xscale); double multiplier = 1.0 / (yscale * xscale);
switch(imIn->type) { switch(imIn->type) {
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
for (x = 0; x < imIn->xsize / xscale; x++) { int yy_from = box[1] + y*yscale;
for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
double ss = 0; double ss = 0;
for (yy = y*yscale; yy < y*yscale + yscale - 1; yy += 2) { for (yy = yy_from; yy < yy_from + yscale - 1; yy += 2) {
INT32 *line0 = (INT32 *)imIn->image32[yy]; INT32 *line0 = (INT32 *)imIn->image32[yy];
INT32 *line1 = (INT32 *)imIn->image32[yy + 1]; INT32 *line1 = (INT32 *)imIn->image32[yy + 1];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss += line0[xx + 0] + line0[xx + 1] + ss += line0[xx + 0] + line0[xx + 1] +
line1[xx + 0] + line1[xx + 1]; line1[xx + 0] + line1[xx + 1];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss += line0[xx + 0] + line1[xx + 0]; ss += line0[xx + 0] + line1[xx + 0];
} }
} }
if (yscale & 0x01) { if (yscale & 0x01) {
INT32 *line = (INT32 *)imIn->image32[yy]; INT32 *line = (INT32 *)imIn->image32[yy];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss += line[xx + 0] + line[xx + 1]; ss += line[xx + 0] + line[xx + 1];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
@ -1197,30 +1189,28 @@ ImagingReduceNxN_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale)
break; break;
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
for (x = 0; x < imIn->xsize / xscale; x++) { int yy_from = box[1] + y*yscale;
for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
double ss = 0; double ss = 0;
for (yy = y*yscale; yy < y*yscale + yscale - 1; yy += 2) { for (yy = yy_from; yy < yy_from + yscale - 1; yy += 2) {
FLOAT32 *line0 = (FLOAT32 *)imIn->image32[yy]; FLOAT32 *line0 = (FLOAT32 *)imIn->image32[yy];
FLOAT32 *line1 = (FLOAT32 *)imIn->image32[yy + 1]; FLOAT32 *line1 = (FLOAT32 *)imIn->image32[yy + 1];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss += line0[xx + 0] + line0[xx + 1] + ss += line0[xx + 0] + line0[xx + 1] +
line1[xx + 0] + line1[xx + 1]; line1[xx + 0] + line1[xx + 1];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss += line0[xx + 0] + line1[xx + 0]; ss += line0[xx + 0] + line1[xx + 0];
} }
} }
if (yscale & 0x01) { if (yscale & 0x01) {
FLOAT32 *line = (FLOAT32 *)imIn->image32[yy]; FLOAT32 *line = (FLOAT32 *)imIn->image32[yy];
for (xi = 0; xi < xscale - 1; xi += 2) { for (xx = xx_from; xx < xx_from + xscale - 1; xx += 2) {
xx = x*xscale + xi;
ss += line[xx + 0] + line[xx + 1]; ss += line[xx + 0] + line[xx + 1];
} }
if (xscale & 0x01) { if (xscale & 0x01) {
xx = x*xscale + xi;
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
@ -1233,7 +1223,7 @@ ImagingReduceNxN_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale)
void void
ImagingReduceCorners_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale) ImagingReduceCorners_32bpc(Imaging imOut, Imaging imIn, int box[4], int xscale, int yscale)
{ {
/* Fill the last row and the last column for any xscale and yscale. /* Fill the last row and the last column for any xscale and yscale.
*/ */
@ -1241,42 +1231,44 @@ ImagingReduceCorners_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale)
switch(imIn->type) { switch(imIn->type) {
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
if (imIn->xsize % xscale) { if (box[2] % xscale) {
double multiplier = 1.0 / ((imIn->xsize % xscale) * yscale); double multiplier = 1.0 / ((box[2] % xscale) * yscale);
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
int yy_from = box[1] + y*yscale;
double ss = 0; double ss = 0;
x = imIn->xsize / xscale; x = box[2] / xscale;
for (yy = y*yscale; yy < y*yscale + yscale; yy++) { for (yy = yy_from; yy < yy_from + yscale; yy++) {
INT32 *line = (INT32 *)imIn->image32[yy]; INT32 *line = (INT32 *)imIn->image32[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
IMAGING_PIXEL_I(imOut, x, y) = ROUND_UP(ss * multiplier); IMAGING_PIXEL_I(imOut, x, y) = ROUND_UP(ss * multiplier);
} }
} }
if (imIn->ysize % yscale) { if (box[3] % yscale) {
double multiplier = 1.0 / (xscale * (imIn->ysize % yscale)); double multiplier = 1.0 / (xscale * (box[3] % yscale));
y = imIn->ysize / yscale; y = box[3] / yscale;
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
double ss = 0; double ss = 0;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
INT32 *line = (INT32 *)imIn->image32[yy]; INT32 *line = (INT32 *)imIn->image32[yy];
for (xx = x*xscale; xx < x*xscale + xscale; xx++) { for (xx = xx_from; xx < xx_from + xscale; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
IMAGING_PIXEL_I(imOut, x, y) = ROUND_UP(ss * multiplier); IMAGING_PIXEL_I(imOut, x, y) = ROUND_UP(ss * multiplier);
} }
} }
if (imIn->xsize % xscale && imIn->ysize % yscale) { if (box[2] % xscale && box[3] % yscale) {
double multiplier = 1.0 / ((imIn->xsize % xscale) * (imIn->ysize % yscale)); double multiplier = 1.0 / ((box[2] % xscale) * (box[3] % yscale));
double ss = 0; double ss = 0;
x = imIn->xsize / xscale; x = box[2] / xscale;
y = imIn->ysize / yscale; y = box[3] / yscale;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
INT32 *line = (INT32 *)imIn->image32[yy]; INT32 *line = (INT32 *)imIn->image32[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
@ -1285,42 +1277,44 @@ ImagingReduceCorners_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale)
break; break;
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
if (imIn->xsize % xscale) { if (box[2] % xscale) {
double multiplier = 1.0 / ((imIn->xsize % xscale) * yscale); double multiplier = 1.0 / ((box[2] % xscale) * yscale);
for (y = 0; y < imIn->ysize / yscale; y++) { for (y = 0; y < box[3] / yscale; y++) {
int yy_from = box[1] + y*yscale;
double ss = 0; double ss = 0;
x = imIn->xsize / xscale; x = box[2] / xscale;
for (yy = y*yscale; yy < y*yscale + yscale; yy++) { for (yy = yy_from; yy < yy_from + yscale; yy++) {
FLOAT32 *line = (FLOAT32 *)imIn->image32[yy]; FLOAT32 *line = (FLOAT32 *)imIn->image32[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
IMAGING_PIXEL_F(imOut, x, y) = ss * multiplier; IMAGING_PIXEL_F(imOut, x, y) = ss * multiplier;
} }
} }
if (imIn->ysize % yscale) { if (box[3] % yscale) {
double multiplier = 1.0 / (xscale * (imIn->ysize % yscale)); double multiplier = 1.0 / (xscale * (box[3] % yscale));
y = imIn->ysize / yscale; y = box[3] / yscale;
for (x = 0; x < imIn->xsize / xscale; x++) { for (x = 0; x < box[2] / xscale; x++) {
int xx_from = box[0] + x*xscale;
double ss = 0; double ss = 0;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
FLOAT32 *line = (FLOAT32 *)imIn->image32[yy]; FLOAT32 *line = (FLOAT32 *)imIn->image32[yy];
for (xx = x*xscale; xx < x*xscale + xscale; xx++) { for (xx = xx_from; xx < xx_from + xscale; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
IMAGING_PIXEL_F(imOut, x, y) = ss * multiplier; IMAGING_PIXEL_F(imOut, x, y) = ss * multiplier;
} }
} }
if (imIn->xsize % xscale && imIn->ysize % yscale) { if (box[2] % xscale && box[3] % yscale) {
double multiplier = 1.0 / ((imIn->xsize % xscale) * (imIn->ysize % yscale)); double multiplier = 1.0 / ((box[2] % xscale) * (box[3] % yscale));
double ss = 0; double ss = 0;
x = imIn->xsize / xscale; x = box[2] / xscale;
y = imIn->ysize / yscale; y = box[3] / yscale;
for (yy = y*yscale; yy < imIn->ysize; yy++) { for (yy = box[1] + y*yscale; yy < box[1] + box[3]; yy++) {
FLOAT32 *line = (FLOAT32 *)imIn->image32[yy]; FLOAT32 *line = (FLOAT32 *)imIn->image32[yy];
for (xx = x*xscale; xx < imIn->xsize; xx++) { for (xx = box[0] + x*xscale; xx < box[0] + box[2]; xx++) {
ss += line[xx + 0]; ss += line[xx + 0];
} }
} }
@ -1332,7 +1326,7 @@ ImagingReduceCorners_32bpc(Imaging imOut, Imaging imIn, int xscale, int yscale)
Imaging Imaging
ImagingReduce(Imaging imIn, int xscale, int yscale) ImagingReduce(Imaging imIn, int xscale, int yscale, int box[4])
{ {
ImagingSectionCookie cookie; ImagingSectionCookie cookie;
Imaging imOut = NULL; Imaging imOut = NULL;
@ -1344,8 +1338,8 @@ ImagingReduce(Imaging imIn, int xscale, int yscale)
return (Imaging) ImagingError_ModeError(); return (Imaging) ImagingError_ModeError();
imOut = ImagingNewDirty(imIn->mode, imOut = ImagingNewDirty(imIn->mode,
(imIn->xsize + xscale - 1) / xscale, (box[2] + xscale - 1) / xscale,
(imIn->ysize + yscale - 1) / yscale); (box[3] + yscale - 1) / yscale);
if ( ! imOut) { if ( ! imOut) {
return NULL; return NULL;
} }
@ -1354,44 +1348,44 @@ ImagingReduce(Imaging imIn, int xscale, int yscale)
switch(imIn->type) { switch(imIn->type) {
case IMAGING_TYPE_UINT8: case IMAGING_TYPE_UINT8:
if (xscale == 1) { // if (xscale == 1) {
if (yscale == 2) { // if (yscale == 2) {
ImagingReduce1x2(imOut, imIn); // ImagingReduce1x2(imOut, imIn);
} else if (yscale == 3) { // } else if (yscale == 3) {
ImagingReduce1x3(imOut, imIn); // ImagingReduce1x3(imOut, imIn);
} else { // } else {
ImagingReduce1xN(imOut, imIn, yscale); // ImagingReduce1xN(imOut, imIn, yscale);
} // }
} else if (yscale == 1) { // } else if (yscale == 1) {
if (xscale == 2) { // if (xscale == 2) {
ImagingReduce2x1(imOut, imIn); // ImagingReduce2x1(imOut, imIn);
} else if (xscale == 3) { // } else if (xscale == 3) {
ImagingReduce3x1(imOut, imIn); // ImagingReduce3x1(imOut, imIn);
} else { // } else {
ImagingReduceNx1(imOut, imIn, xscale); // ImagingReduceNx1(imOut, imIn, xscale);
} // }
} else if (xscale == yscale && xscale <= 5) { // } else if (xscale == yscale && xscale <= 5) {
if (xscale == 2) { // if (xscale == 2) {
ImagingReduce2x2(imOut, imIn); // ImagingReduce2x2(imOut, imIn);
} else if (xscale == 3) { // } else if (xscale == 3) {
ImagingReduce3x3(imOut, imIn); // ImagingReduce3x3(imOut, imIn);
} else if (xscale == 4) { // } else if (xscale == 4) {
ImagingReduce4x4(imOut, imIn); // ImagingReduce4x4(imOut, imIn);
} else { // } else {
ImagingReduce5x5(imOut, imIn); // ImagingReduce5x5(imOut, imIn);
} // }
} else { // } else {
ImagingReduceNxN(imOut, imIn, xscale, yscale); ImagingReduceNxN(imOut, imIn, box, xscale, yscale);
} // }
ImagingReduceCorners(imOut, imIn, xscale, yscale); ImagingReduceCorners(imOut, imIn, box, xscale, yscale);
break; break;
case IMAGING_TYPE_INT32: case IMAGING_TYPE_INT32:
case IMAGING_TYPE_FLOAT32: case IMAGING_TYPE_FLOAT32:
ImagingReduceNxN_32bpc(imOut, imIn, xscale, yscale); ImagingReduceNxN_32bpc(imOut, imIn, box, xscale, yscale);
ImagingReduceCorners_32bpc(imOut, imIn, xscale, yscale); ImagingReduceCorners_32bpc(imOut, imIn, box, xscale, yscale);
break; break;
} }