diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 04fd03ae50b..f5237668381 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -42,6 +42,7 @@ def test_sanity(self): ImageChops.softlight(im,im) ImageChops.hardlight(im,im) + ImageChops.overlay(im,im) ImageChops.offset(im, 10) ImageChops.offset(im, 10, 20) @@ -367,6 +368,18 @@ def test_hardlight(self): #Assert self.assertEqual(new.getpixel((64,64)), (144,50,27) ) self.assertEqual(new.getpixel((15,100)), (1,1,2) ) + + def test_overlay(self): + # Arrange + im1 = Image.open("Tests/images/hopper.png") + im2 = Image.open("Tests/images/hopper-XYZ.png") + + # Act + new = ImageChops.overlay(im1, im2) + + #Assert + self.assertEqual(new.getpixel((64,64)), (159,50,27) ) + self.assertEqual(new.getpixel((15,100)), (1,1,2) ) def test_logical(self): def table(op, a, b): diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst index d9b14002db9..31142cc3fd2 100644 --- a/docs/reference/ImageChops.rst +++ b/docs/reference/ImageChops.rst @@ -38,6 +38,7 @@ operations in this module). .. autofunction:: PIL.ImageChops.multiply .. autofunction:: PIL.ImageChops.softlight .. autofunction:: PIL.ImageChops.hardlight +.. autofunction:: PIL.ImageChops.overlay .. py:method:: PIL.ImageChops.offset(image, xoffset, yoffset=None) Returns a copy of the image where data has been offset by the given diff --git a/src/PIL/ImageChops.py b/src/PIL/ImageChops.py index d33186fb2d7..bfc6e80d841 100644 --- a/src/PIL/ImageChops.py +++ b/src/PIL/ImageChops.py @@ -160,6 +160,17 @@ def hardlight(image1, image2): image2.load() return image1._new(image1.im.chop_hardlight(image2.im)) +def overlay(image1, image2): + """ + Superimposes two images on top of each other using the Overlay algorithm + + :rtype: :py:class:`~PIL.Image.Image` + """ + + image1.load() + image2.load() + return image1._new(image1.im.chop_overlay(image2.im)) + def add(image1, image2, scale=1.0, offset=0): """ Adds two images, dividing the result by scale and adding the diff --git a/src/_imaging.c b/src/_imaging.c index f7c61d682ee..aa8f81d26d5 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -2376,6 +2376,17 @@ _chop_hardlight(ImagingObject* self, PyObject* args) return PyImagingNew(ImagingChopHardLight(self->image, imagep->image)); } + +static PyObject* +_chop_overlay(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingOverlay(self->image, imagep->image)); +} #endif @@ -3296,6 +3307,7 @@ static struct PyMethodDef methods[] = { {"chop_xor", (PyCFunction)_chop_xor, 1}, {"chop_softlight", (PyCFunction)_chop_softlight, 1}, {"chop_hardlight", (PyCFunction)_chop_hardlight, 1}, + {"chop_overlay", (PyCFunction)_chop_overlay, 1}, #endif diff --git a/src/libImaging/Chops.c b/src/libImaging/Chops.c index 302a8c2b90d..cbd65b19649 100644 --- a/src/libImaging/Chops.c +++ b/src/libImaging/Chops.c @@ -163,4 +163,12 @@ ImagingChopHardLight(Imaging imIn1, Imaging imIn2) CHOP2( (in2[x]<128) ? ( (in1[x]*in2[x])/127) : 255 - ( ((255-in2[x]) * (255-in1[x])) / 127) , NULL); +} + +Imaging +ImagingOverlay(Imaging imIn1, Imaging imIn2) +{ + CHOP2( (in1[x]<128) ? ( (in1[x]*in2[x])/127) + : 255 - ( ((255-in1[x]) * (255-in2[x])) / 127) + , NULL); } \ No newline at end of file diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 2c9ad1c1a02..b048bd85011 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -340,6 +340,7 @@ extern Imaging ImagingChopAddModulo(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopSoftLight(Imaging imIn1, Imaging imIn2); extern Imaging ImagingChopHardLight(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingOverlay(Imaging imIn1, Imaging imIn2); /* "1" images only */ extern Imaging ImagingChopAnd(Imaging imIn1, Imaging imIn2);