Skip to content

Commit

Permalink
Merge pull request #4230 from dwastberg/new_chops
Browse files Browse the repository at this point in the history
Added three new channel operations
  • Loading branch information
hugovk committed Mar 31, 2020
2 parents 75b8c36 + c674918 commit c6115c1
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 8 deletions.
59 changes: 51 additions & 8 deletions Tests/test_imagechops.py
Expand Up @@ -38,6 +38,10 @@ def test_sanity():
ImageChops.blend(im, im, 0.5)
ImageChops.composite(im, im, im)

ImageChops.soft_light(im, im)
ImageChops.hard_light(im, im)
ImageChops.overlay(im, im)

ImageChops.offset(im, 10)
ImageChops.offset(im, 10, 20)

Expand Down Expand Up @@ -209,8 +213,8 @@ def test_lighter_image():
# Act
new = ImageChops.lighter(im1, im2)

# Assert
assert_image_equal(new, im1)
# Assert
assert_image_equal(new, im1)


def test_lighter_pixel():
Expand Down Expand Up @@ -275,13 +279,13 @@ def test_offset():
# Act
new = ImageChops.offset(im, xoffset, yoffset)

# Assert
assert new.getbbox() == (0, 45, 100, 96)
assert new.getpixel((50, 50)) == BLACK
assert new.getpixel((50 + xoffset, 50 + yoffset)) == DARK_GREEN
# Assert
assert new.getbbox() == (0, 45, 100, 96)
assert new.getpixel((50, 50)) == BLACK
assert new.getpixel((50 + xoffset, 50 + yoffset)) == DARK_GREEN

# Test no yoffset
assert ImageChops.offset(im, xoffset) == ImageChops.offset(im, xoffset, xoffset)
# Test no yoffset
assert ImageChops.offset(im, xoffset) == ImageChops.offset(im, xoffset, xoffset)


def test_screen():
Expand Down Expand Up @@ -362,6 +366,45 @@ def test_subtract_modulo_no_clip():
assert new.getpixel((50, 50)) == (241, 167, 127)


def test_soft_light():
# Arrange
im1 = Image.open("Tests/images/hopper.png")
im2 = Image.open("Tests/images/hopper-XYZ.png")

# Act
new = ImageChops.soft_light(im1, im2)

# Assert
assert new.getpixel((64, 64)) == (163, 54, 32)
assert new.getpixel((15, 100)) == (1, 1, 3)


def test_hard_light():
# Arrange
im1 = Image.open("Tests/images/hopper.png")
im2 = Image.open("Tests/images/hopper-XYZ.png")

# Act
new = ImageChops.hard_light(im1, im2)

# Assert
assert new.getpixel((64, 64)) == (144, 50, 27)
assert new.getpixel((15, 100)) == (1, 1, 2)


def test_overlay():
# Arrange
im1 = Image.open("Tests/images/hopper.png")
im2 = Image.open("Tests/images/hopper-XYZ.png")

# Act
new = ImageChops.overlay(im1, im2)

# Assert
assert new.getpixel((64, 64)) == (159, 50, 27)
assert new.getpixel((15, 100)) == (1, 1, 2)


def test_logical():
def table(op, a, b):
out = []
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/ImageChops.rst
Expand Up @@ -36,6 +36,9 @@ operations in this module).
.. autofunction:: PIL.ImageChops.logical_or
.. autofunction:: PIL.ImageChops.logical_xor
.. autofunction:: PIL.ImageChops.multiply
.. autofunction:: PIL.ImageChops.soft_light
.. autofunction:: PIL.ImageChops.hard_light
.. 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
Expand Down
36 changes: 36 additions & 0 deletions src/PIL/ImageChops.py
Expand Up @@ -139,6 +139,42 @@ def screen(image1, image2):
return image1._new(image1.im.chop_screen(image2.im))


def soft_light(image1, image2):
"""
Superimposes two images on top of each other using the Soft Light algorithm

:rtype: :py:class:`~PIL.Image.Image`
"""

image1.load()
image2.load()
return image1._new(image1.im.chop_soft_light(image2.im))


def hard_light(image1, image2):
"""
Superimposes two images on top of each other using the Hard Light algorithm

:rtype: :py:class:`~PIL.Image.Image`
"""

image1.load()
image2.load()
return image1._new(image1.im.chop_hard_light(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
Expand Down
36 changes: 36 additions & 0 deletions src/_imaging.c
Expand Up @@ -2406,6 +2406,38 @@ _chop_subtract_modulo(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image));
}

static PyObject*
_chop_soft_light(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;

if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
return NULL;

return PyImagingNew(ImagingChopSoftLight(self->image, imagep->image));
}

static PyObject*
_chop_hard_light(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;

if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
return NULL;

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


Expand Down Expand Up @@ -3325,6 +3357,10 @@ static struct PyMethodDef methods[] = {
{"chop_and", (PyCFunction)_chop_and, 1},
{"chop_or", (PyCFunction)_chop_or, 1},
{"chop_xor", (PyCFunction)_chop_xor, 1},
{"chop_soft_light", (PyCFunction)_chop_soft_light, 1},
{"chop_hard_light", (PyCFunction)_chop_hard_light, 1},
{"chop_overlay", (PyCFunction)_chop_overlay, 1},

#endif

#ifdef WITH_UNSHARPMASK
Expand Down
24 changes: 24 additions & 0 deletions src/libImaging/Chops.c
Expand Up @@ -146,3 +146,27 @@ ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2)
{
CHOP2(in1[x] - in2[x], NULL);
}

Imaging
ImagingChopSoftLight(Imaging imIn1, Imaging imIn2)
{
CHOP2( (((255-in1[x]) * (in1[x]*in2[x]) ) / 65536) +
(in1[x] * ( 255 - ( (255 - in1[x]) * (255 - in2[x] ) / 255) )) / 255
, NULL );
}

Imaging
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);
}
3 changes: 3 additions & 0 deletions src/libImaging/Imaging.h
Expand Up @@ -339,6 +339,9 @@ extern Imaging ImagingChopSubtract(
Imaging imIn1, Imaging imIn2, float scale, int offset);
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);
Expand Down

0 comments on commit c6115c1

Please sign in to comment.