mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Merge pull request #6254 from benrg/affine-transform
Support more affine expression forms in im.point()
This commit is contained in:
		
						commit
						80782bba9b
					
				| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import assert_image_equal, hopper
 | 
					from .helper import assert_image_equal, hopper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,11 +19,24 @@ def test_sanity():
 | 
				
			||||||
        im.point(list(range(256)))
 | 
					        im.point(list(range(256)))
 | 
				
			||||||
    im.point(lambda x: x * 1)
 | 
					    im.point(lambda x: x * 1)
 | 
				
			||||||
    im.point(lambda x: x + 1)
 | 
					    im.point(lambda x: x + 1)
 | 
				
			||||||
 | 
					    im.point(lambda x: x - 1)
 | 
				
			||||||
    im.point(lambda x: x * 1 + 1)
 | 
					    im.point(lambda x: x * 1 + 1)
 | 
				
			||||||
 | 
					    im.point(lambda x: 0.1 + 0.2 * x)
 | 
				
			||||||
 | 
					    im.point(lambda x: -x)
 | 
				
			||||||
 | 
					    im.point(lambda x: x - 0.5)
 | 
				
			||||||
 | 
					    im.point(lambda x: 1 - x / 2)
 | 
				
			||||||
 | 
					    im.point(lambda x: (2 + x) / 3)
 | 
				
			||||||
 | 
					    im.point(lambda x: 0.5)
 | 
				
			||||||
 | 
					    im.point(lambda x: x / 1)
 | 
				
			||||||
 | 
					    im.point(lambda x: x + x)
 | 
				
			||||||
    with pytest.raises(TypeError):
 | 
					    with pytest.raises(TypeError):
 | 
				
			||||||
        im.point(lambda x: x - 1)
 | 
					        im.point(lambda x: x * x)
 | 
				
			||||||
    with pytest.raises(TypeError):
 | 
					    with pytest.raises(TypeError):
 | 
				
			||||||
        im.point(lambda x: x / 1)
 | 
					        im.point(lambda x: x / x)
 | 
				
			||||||
 | 
					    with pytest.raises(TypeError):
 | 
				
			||||||
 | 
					        im.point(lambda x: 1 / x)
 | 
				
			||||||
 | 
					    with pytest.raises(TypeError):
 | 
				
			||||||
 | 
					        im.point(lambda x: x // 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_16bit_lut():
 | 
					def test_16bit_lut():
 | 
				
			||||||
| 
						 | 
					@ -47,3 +62,8 @@ def test_f_mode():
 | 
				
			||||||
    im = hopper("F")
 | 
					    im = hopper("F")
 | 
				
			||||||
    with pytest.raises(ValueError):
 | 
					    with pytest.raises(ValueError):
 | 
				
			||||||
        im.point(None)
 | 
					        im.point(None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_coerce_e_deprecation():
 | 
				
			||||||
 | 
					    with pytest.warns(DeprecationWarning):
 | 
				
			||||||
 | 
					        assert Image.coerce_e(2).data == 2
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -170,6 +170,14 @@ in Pillow 10 (2023-07-01). Upgrade to
 | 
				
			||||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
					`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
				
			||||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
 | 
					`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Image.coerce_e
 | 
				
			||||||
 | 
					~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. deprecated:: 9.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This undocumented method has been deprecated and will be removed in Pillow 10
 | 
				
			||||||
 | 
					(2023-07-01).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Removed features
 | 
					Removed features
 | 
				
			||||||
----------------
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,14 @@ FreeTypeFont.getmask2 fill parameter
 | 
				
			||||||
The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2`
 | 
					The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2`
 | 
				
			||||||
has been deprecated and will be removed in Pillow 10 (2023-07-01).
 | 
					has been deprecated and will be removed in Pillow 10 (2023-07-01).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Image.coerce_e
 | 
				
			||||||
 | 
					~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. deprecated:: 9.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This undocumented method has been deprecated and will be removed in Pillow 10
 | 
				
			||||||
 | 
					(2023-07-01).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
API Changes
 | 
					API Changes
 | 
				
			||||||
===========
 | 
					===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,6 @@ import builtins
 | 
				
			||||||
import io
 | 
					import io
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
import numbers
 | 
					 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import struct
 | 
					import struct
 | 
				
			||||||
| 
						 | 
					@ -432,44 +431,50 @@ def _getencoder(mode, encoder_name, args, extra=()):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def coerce_e(value):
 | 
					def coerce_e(value):
 | 
				
			||||||
    return value if isinstance(value, _E) else _E(value)
 | 
					    deprecate("coerce_e", 10)
 | 
				
			||||||
 | 
					    return value if isinstance(value, _E) else _E(1, value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# _E(scale, offset) represents the affine transformation scale * x + offset.
 | 
				
			||||||
 | 
					# The "data" field is named for compatibility with the old implementation,
 | 
				
			||||||
 | 
					# and should be renamed once coerce_e is removed.
 | 
				
			||||||
class _E:
 | 
					class _E:
 | 
				
			||||||
    def __init__(self, data):
 | 
					    def __init__(self, scale, data):
 | 
				
			||||||
 | 
					        self.scale = scale
 | 
				
			||||||
        self.data = data
 | 
					        self.data = data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __neg__(self):
 | 
				
			||||||
 | 
					        return _E(-self.scale, -self.data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __add__(self, other):
 | 
					    def __add__(self, other):
 | 
				
			||||||
        return _E((self.data, "__add__", coerce_e(other).data))
 | 
					        if isinstance(other, _E):
 | 
				
			||||||
 | 
					            return _E(self.scale + other.scale, self.data + other.data)
 | 
				
			||||||
 | 
					        return _E(self.scale, self.data + other)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __radd__ = __add__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __sub__(self, other):
 | 
				
			||||||
 | 
					        return self + -other
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __rsub__(self, other):
 | 
				
			||||||
 | 
					        return other + -self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __mul__(self, other):
 | 
					    def __mul__(self, other):
 | 
				
			||||||
        return _E((self.data, "__mul__", coerce_e(other).data))
 | 
					        if isinstance(other, _E):
 | 
				
			||||||
 | 
					            return NotImplemented
 | 
				
			||||||
 | 
					        return _E(self.scale * other, self.data * other)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __rmul__ = __mul__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __truediv__(self, other):
 | 
				
			||||||
 | 
					        if isinstance(other, _E):
 | 
				
			||||||
 | 
					            return NotImplemented
 | 
				
			||||||
 | 
					        return _E(self.scale / other, self.data / other)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _getscaleoffset(expr):
 | 
					def _getscaleoffset(expr):
 | 
				
			||||||
    stub = ["stub"]
 | 
					    a = expr(_E(1, 0))
 | 
				
			||||||
    data = expr(_E(stub)).data
 | 
					    return (a.scale, a.data) if isinstance(a, _E) else (0, a)
 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        (a, b, c) = data  # simplified syntax
 | 
					 | 
				
			||||||
        if a is stub and b == "__mul__" and isinstance(c, numbers.Number):
 | 
					 | 
				
			||||||
            return c, 0.0
 | 
					 | 
				
			||||||
        if a is stub and b == "__add__" and isinstance(c, numbers.Number):
 | 
					 | 
				
			||||||
            return 1.0, c
 | 
					 | 
				
			||||||
    except TypeError:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        ((a, b, c), d, e) = data  # full syntax
 | 
					 | 
				
			||||||
        if (
 | 
					 | 
				
			||||||
            a is stub
 | 
					 | 
				
			||||||
            and b == "__mul__"
 | 
					 | 
				
			||||||
            and isinstance(c, numbers.Number)
 | 
					 | 
				
			||||||
            and d == "__add__"
 | 
					 | 
				
			||||||
            and isinstance(e, numbers.Number)
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            return c, e
 | 
					 | 
				
			||||||
    except TypeError:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
    raise ValueError("illegal expression")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# --------------------------------------------------------------------
 | 
					# --------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user