diff options
Diffstat (limited to 'venv/Lib/site-packages/blendmodes')
-rw-r--r-- | venv/Lib/site-packages/blendmodes/__init__.py | 2 | ||||
-rw-r--r-- | venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc | bin | 0 -> 285 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc | bin | 0 -> 13302 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc | bin | 0 -> 2850 bytes | |||
-rw-r--r-- | venv/Lib/site-packages/blendmodes/blend.py | 511 | ||||
-rw-r--r-- | venv/Lib/site-packages/blendmodes/blendtype.py | 72 | ||||
-rw-r--r-- | venv/Lib/site-packages/blendmodes/imagetools.py | 48 |
7 files changed, 633 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/blendmodes/__init__.py b/venv/Lib/site-packages/blendmodes/__init__.py new file mode 100644 index 00000000..2b6e91ad --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/__init__.py @@ -0,0 +1,2 @@ +"""Use this module to apply a number of blending modes to a background and foreground image
+"""
diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc Binary files differnew file mode 100644 index 00000000..da1d6a38 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc Binary files differnew file mode 100644 index 00000000..23184600 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc Binary files differnew file mode 100644 index 00000000..c599cfb8 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc diff --git a/venv/Lib/site-packages/blendmodes/blend.py b/venv/Lib/site-packages/blendmodes/blend.py new file mode 100644 index 00000000..4165bfa7 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/blend.py @@ -0,0 +1,511 @@ +"""Provide blending functions and types.
+
+Adapted from https://github.com/addisonElliott/pypdn/blob/master/pypdn/reader.py
+and https://gitlab.com/inklabapp/pyora/-/blob/master/pyora/BlendNonSep.py
+MIT License Copyright (c) 2020 FredHappyface
+
+Credits to:
+
+MIT License Copyright (c) 2019 Paul Jewell
+For implementing blending from the Open Raster Image Spec
+
+MIT License Copyright (c) 2018 Addison Elliott
+For implementing blending from Paint.NET
+
+MIT License Copyright (c) 2017 pashango
+For implementing a number of blending functions used by other popular image
+editors
+"""
+
+from __future__ import annotations
+
+import warnings
+
+import numpy as np
+from PIL import Image
+
+from .blendtype import BlendType
+
+
+def normal(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.NORMAL."""
+ del background # we don't care about this
+ return foreground
+
+
+def multiply(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.MULTIPLY."""
+ return np.clip(foreground * background, 0.0, 1.0)
+
+
+def additive(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.ADDITIVE."""
+ return np.minimum(background + foreground, 1.0)
+
+
+def colourburn(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.COLOURBURN."""
+ with np.errstate(divide="ignore"):
+ return np.where(
+ foreground != 0.0, np.maximum(1.0 - ((1.0 - background) / foreground), 0.0), 0.0
+ )
+
+
+def colourdodge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.COLOURDODGE."""
+ with np.errstate(divide="ignore"):
+ return np.where(foreground != 1.0, np.minimum(background / (1.0 - foreground), 1.0), 1.0)
+
+
+def reflect(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.REFLECT."""
+ with np.errstate(divide="ignore"):
+ return np.where(
+ foreground != 1.0, np.minimum((background ** 2) / (1.0 - foreground), 1.0), 1.0
+ )
+
+
+def glow(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.GLOW."""
+ with np.errstate(divide="ignore"):
+ return np.where(
+ background != 1.0, np.minimum((foreground ** 2) / (1.0 - background), 1.0), 1.0
+ )
+
+
+def overlay(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.OVERLAY."""
+ return np.where(
+ background < 0.5,
+ 2 * background * foreground,
+ 1.0 - (2 * (1.0 - background) * (1.0 - foreground)),
+ )
+
+
+def difference(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.DIFFERENCE."""
+ return np.abs(background - foreground)
+
+
+def negation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.NEGATION."""
+ return np.maximum(background - foreground, 0.0)
+
+
+def lighten(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.LIGHTEN."""
+ return np.maximum(background, foreground)
+
+
+def darken(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.DARKEN."""
+ return np.minimum(background, foreground)
+
+
+def screen(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.SCREEN."""
+ return background + foreground - background * foreground
+
+
+def xor(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.XOR."""
+ # XOR requires int values so convert to uint8
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ return imageIntToFloat(imageFloatToInt(background) ^ imageFloatToInt(foreground))
+
+
+def softlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.SOFTLIGHT."""
+ return (1.0 - background) * background * foreground + background * (
+ 1.0 - (1.0 - background) * (1.0 - foreground)
+ )
+
+
+def hardlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.HARDLIGHT."""
+ return np.where(
+ foreground < 0.5,
+ np.minimum(background * 2 * foreground, 1.0),
+ np.minimum(1.0 - ((1.0 - background) * (1.0 - (foreground - 0.5) * 2.0)), 1.0),
+ )
+
+
+def grainextract(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.GRAINEXTRACT."""
+ return np.clip(background - foreground + 0.5, 0.0, 1.0)
+
+
+def grainmerge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.GRAINMERGE."""
+ return np.clip(background + foreground - 0.5, 0.0, 1.0)
+
+
+def divide(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.DIVIDE."""
+ return np.minimum((256.0 / 255.0 * background) / (1.0 / 255.0 + foreground), 1.0)
+
+
+def pinlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.PINLIGHT."""
+ return np.minimum(background, 2 * foreground) * (foreground < 0.5) + np.maximum(
+ background, 2 * (foreground - 0.5)
+ ) * (foreground >= 0.5)
+
+
+def vividlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.VIVIDLIGHT."""
+ return colourburn(background, foreground * 2) * (foreground < 0.5) + colourdodge(
+ background, 2 * (foreground - 0.5)
+ ) * (foreground >= 0.5)
+
+
+def exclusion(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.EXCLUSION."""
+ return background + foreground - (2.0 * background * foreground)
+
+
+def _lum(colours: np.ndarray) -> np.ndarray:
+ """Luminosity.
+
+ :param colours: x by x by 3 matrix of rgb color components of pixels
+ :return: x by x by 3 matrix of luminosity of pixels
+ """
+ return (colours[:, :, 0] * 0.299) + (colours[:, :, 1] * 0.587) + (colours[:, :, 2] * 0.114)
+
+
+def _setLum(originalColours: np.ndarray, newLuminosity: np.ndarray) -> np.ndarray:
+ """Set a new luminosity value for the matrix of color."""
+ _colours = originalColours.copy()
+ _luminosity = _lum(_colours)
+ deltaLum = newLuminosity - _luminosity
+ _colours[:, :, 0] += deltaLum
+ _colours[:, :, 1] += deltaLum
+ _colours[:, :, 2] += deltaLum
+ _luminosity = _lum(_colours)
+ _minColours = np.min(_colours, axis=2)
+ _MaxColours = np.max(_colours, axis=2)
+ for i in range(_colours.shape[0]):
+ for j in range(_colours.shape[1]):
+ _colour = _colours[i][j]
+ newLuminosity = _luminosity[i, j]
+ minColour = _minColours[i, j]
+ maxColour = _MaxColours[i, j]
+ if minColour < 0:
+ _colours[i][j] = newLuminosity + (
+ ((_colour - newLuminosity) * newLuminosity) / (newLuminosity - minColour)
+ )
+ if maxColour > 1:
+ _colours[i][j] = newLuminosity + (
+ ((_colour - newLuminosity) * (1 - newLuminosity)) / (maxColour - newLuminosity)
+ )
+ return _colours
+
+
+def _sat(colours: np.ndarray) -> np.ndarray:
+ """Saturation.
+
+ :param colours: x by x by 3 matrix of rgb color components of pixels
+ :return: int of saturation of pixels
+ """
+ return np.max(colours, axis=2) - np.min(colours, axis=2)
+
+
+def _setSat(originalColours: np.ndarray, newSaturation: np.ndarray) -> np.ndarray:
+ """Set a new saturation value for the matrix of color.
+
+ The current implementation cannot be vectorized in an efficient manner,
+ so it is very slow,
+ O(m*n) at least. This might be able to be improved with openCL if that is
+ the direction that the lib takes.
+ :param c: x by x by 3 matrix of rgb color components of pixels
+ :param s: int of the new saturation value for the matrix
+ :return: x by x by 3 matrix of luminosity of pixels
+ """
+ _colours = originalColours.copy()
+ for i in range(_colours.shape[0]):
+ for j in range(_colours.shape[1]):
+ _colour = _colours[i][j]
+ minI = 0
+ midI = 1
+ maxI = 2
+ if _colour[midI] < _colour[minI]:
+ minI, midI = midI, minI
+ if _colour[maxI] < _colour[midI]:
+ midI, maxI = maxI, midI
+ if _colour[midI] < _colour[minI]:
+ minI, midI = midI, minI
+ if _colour[maxI] - _colour[minI] > 0.0:
+ _colours[i][j][midI] = ((_colour[midI] - _colour[minI]) * newSaturation[i, j]) / (
+ _colour[maxI] - _colour[minI]
+ )
+ _colours[i][j][maxI] = newSaturation[i, j]
+ else:
+ _colours[i][j][midI] = 0
+ _colours[i][j][maxI] = 0
+ _colours[i][j][minI] = 0
+ return _colours
+
+
+def hue(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.HUE."""
+ return _setLum(_setSat(foreground, _sat(background)), _lum(background))
+
+
+def saturation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.SATURATION."""
+ return _setLum(_setSat(background, _sat(foreground)), _lum(background))
+
+
+def colour(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.COLOUR."""
+ return _setLum(foreground, _lum(background))
+
+
+def luminosity(background: np.ndarray, foreground: np.ndarray) -> np.ndarray:
+ """BlendType.LUMINOSITY."""
+ return _setLum(background, _lum(foreground))
+
+
+def destin(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """'clip' composite mode.
+
+ All parts of 'layer above' which are alpha in 'layer below' will be made
+ also alpha in 'layer above'
+ (to whatever degree of alpha they were)
+
+ Destination which overlaps the source, replaces the source.
+
+ Fa = 0; Fb = αs
+ co = αb x Cb x αs
+ αo = αb x αs
+ """
+ del foregroundColour # Not used by function
+ outAlpha = backgroundAlpha * foregroundAlpha
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+ return outRGB, outAlpha
+
+
+def destout(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """Reverse 'Clip' composite mode.
+
+ All parts of 'layer below' which are alpha in 'layer above' will be made
+ also alpha in 'layer below'
+ (to whatever degree of alpha they were)
+
+ """
+ del foregroundColour # Not used by function
+ outAlpha = backgroundAlpha * (1 - foregroundAlpha)
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+ return outRGB, outAlpha
+
+
+def destatop(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """Place the layer below above the 'layer above' in places where the 'layer above' exists...
+
+ where 'layer below' does not exist, but 'layer above' does, place 'layer-above'
+
+ """
+ outAlpha = (foregroundAlpha * (1 - backgroundAlpha)) + (backgroundAlpha * foregroundAlpha)
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((foregroundAlpha * (1 - backgroundAlpha))[:, :, None], foregroundColour)
+ + np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+ return outRGB, outAlpha
+
+
+def srcatop(
+ backgroundAlpha: np.ndarray,
+ foregroundAlpha: np.ndarray,
+ backgroundColour: np.ndarray,
+ foregroundColour: np.ndarray,
+):
+ """Place the layer below above the 'layer above' in places where the 'layer above' exists."""
+ outAlpha = (foregroundAlpha * backgroundAlpha) + (backgroundAlpha * (1 - foregroundAlpha))
+ with np.errstate(divide="ignore", invalid="ignore"):
+ outRGB = np.divide(
+ np.multiply((foregroundAlpha * backgroundAlpha)[:, :, None], foregroundColour)
+ + np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour),
+ outAlpha[:, :, None],
+ )
+
+ return outRGB, outAlpha
+
+
+def imageIntToFloat(image: np.ndarray) -> np.ndarray:
+ """Convert a numpy array representing an image to an array of floats.
+
+ Args:
+ image (np.ndarray): numpy array of ints
+
+ Returns:
+ np.ndarray: numpy array of floats
+ """
+ return image / 255
+
+
+def imageFloatToInt(image: np.ndarray) -> np.ndarray:
+ """Convert a numpy array representing an image to an array of ints.
+
+ Args:
+ image (np.ndarray): numpy array of floats
+
+ Returns:
+ np.ndarray: numpy array of ints
+ """
+ return (image * 255).astype(np.uint8)
+
+
+def blend(background: np.ndarray, foreground: np.ndarray, blendType: BlendType) -> np.ndarray:
+ """Blend pixels.
+
+ Args:
+ background (np.ndarray): background
+ foreground (np.ndarray): foreground
+ blendType (BlendType): the blend type
+
+ Returns:
+ np.ndarray: new array representing the image
+
+ background: np.ndarray,
+ foreground: np.ndarray and the return are in the form
+
+ [[[0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]
+ ...
+ [0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]]
+
+ ...
+
+ [[0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]
+ ...
+ [0. 0. 0.]
+ [0. 0. 0.]
+ [0. 0. 0.]]]
+ """
+ blendLookup = {
+ BlendType.NORMAL: normal,
+ BlendType.MULTIPLY: multiply,
+ BlendType.COLOURBURN: colourburn,
+ BlendType.COLOURDODGE: colourdodge,
+ BlendType.REFLECT: reflect,
+ BlendType.OVERLAY: overlay,
+ BlendType.DIFFERENCE: difference,
+ BlendType.LIGHTEN: lighten,
+ BlendType.DARKEN: darken,
+ BlendType.SCREEN: screen,
+ BlendType.SOFTLIGHT: softlight,
+ BlendType.HARDLIGHT: hardlight,
+ BlendType.GRAINEXTRACT: grainextract,
+ BlendType.GRAINMERGE: grainmerge,
+ BlendType.DIVIDE: divide,
+ BlendType.HUE: hue,
+ BlendType.SATURATION: saturation,
+ BlendType.COLOUR: colour,
+ BlendType.LUMINOSITY: luminosity,
+ BlendType.XOR: xor,
+ BlendType.NEGATION: negation,
+ BlendType.PINLIGHT: pinlight,
+ BlendType.VIVIDLIGHT: vividlight,
+ BlendType.EXCLUSION: exclusion,
+ }
+
+ if blendType not in blendLookup:
+ return normal(background, foreground)
+ return blendLookup[blendType](background, foreground)
+
+
+def blendLayers(
+ background: Image.Image,
+ foreground: Image.Image,
+ blendType: BlendType | tuple[str, ...],
+ opacity: float = 1.0,
+) -> Image.Image:
+ """Blend layers using numpy array.
+
+ Args:
+ background (Image.Image): background layer
+ foreground (Image.Image): foreground layer (must be same size as background)
+ blendType (BlendType): The blendtype
+ opacity (float): The opacity of the foreground image
+
+ Returns:
+ Image.Image: combined image
+ """
+ # Convert the Image.Image to a numpy array
+ npForeground: np.ndarray = imageIntToFloat(np.array(foreground.convert("RGBA")))
+ npBackground: np.ndarray = imageIntToFloat(np.array(background.convert("RGBA")))
+
+ # Get the alpha from the layers
+ backgroundAlpha = npBackground[:, :, 3]
+ foregroundAlpha = npForeground[:, :, 3] * opacity
+ combinedAlpha = backgroundAlpha * foregroundAlpha
+
+ # Get the colour from the layers
+ backgroundColor = npBackground[:, :, 0:3]
+ foregroundColor = npForeground[:, :, 0:3]
+
+ # Some effects require alpha
+ alphaFunc = {
+ BlendType.DESTIN: destin,
+ BlendType.DESTOUT: destout,
+ BlendType.SRCATOP: srcatop,
+ BlendType.DESTATOP: destatop,
+ }
+
+ if blendType in alphaFunc:
+ return Image.fromarray(
+ imageFloatToInt(
+ np.clip(
+ np.dstack(
+ alphaFunc[blendType](
+ backgroundAlpha, foregroundAlpha, backgroundColor, foregroundColor
+ )
+ ),
+ a_min=0,
+ a_max=1,
+ )
+ )
+ )
+
+ # Get the colours and the alpha for the new image
+ colorComponents = (
+ (backgroundAlpha - combinedAlpha)[:, :, None] * backgroundColor
+ + (foregroundAlpha - combinedAlpha)[:, :, None] * foregroundColor
+ + combinedAlpha[:, :, None] * blend(backgroundColor, foregroundColor, blendType)
+ )
+ alphaComponent = backgroundAlpha + foregroundAlpha - combinedAlpha
+
+ return Image.fromarray(
+ imageFloatToInt(np.clip(np.dstack((colorComponents, alphaComponent)), a_min=0, a_max=1))
+ )
diff --git a/venv/Lib/site-packages/blendmodes/blendtype.py b/venv/Lib/site-packages/blendmodes/blendtype.py new file mode 100644 index 00000000..1bde12a6 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/blendtype.py @@ -0,0 +1,72 @@ +"""Specify supported blend types."""
+
+from __future__ import annotations
+
+from aenum import MultiValueEnum
+
+
+class BlendType(str, MultiValueEnum):
+ """Specify supported blend types.
+
+ NORMAL = "bm:normal", "normal"
+ MULTIPLY = "bm:multiply", "multiply"
+ ADDITIVE = "bm:additive", "additive"
+ COLOURBURN = "bm:colourburn", "colourburn"
+ COLOURDODGE = "bm:colourdodge", "colourdodge"
+ REFLECT = "bm:reflect", "reflect"
+ GLOW = "bm:glow", "glow"
+ OVERLAY = "bm:overlay", "overlay"
+ DIFFERENCE = "bm:difference", "difference"
+ NEGATION = "bm:negation", "negation"
+ LIGHTEN = "bm:lighten", "lighten"
+ DARKEN = "bm:darken", "darken"
+ SCREEN = "bm:screen", "screen"
+ XOR = "bm:xor", "xor"
+ SOFTLIGHT = "bm:softlight", "softlight"
+ HARDLIGHT = "bm:hardlight", "hardlight"
+ GRAINEXTRACT = "bm:grainextract", "grainextract"
+ GRAINMERGE = "bm:grainmerge", "grainmerge"
+ DIVIDE = "bm:divide", "divide"
+ HUE = "bm:hue", "hue"
+ SATURATION = "bm:saturation", "saturation"
+ COLOUR = "bm:colour", "colour"
+ LUMINOSITY = "bm:luminosity", "luminosity"
+ PINLIGHT = "bm:pinlight", "pinlight"
+ VIVIDLIGHT = "bm:vividlight", "vividlight"
+ EXCLUSION = "bm:exclusion", "exclusion"
+ DESTIN = "bm:destin", "destin"
+ DESTOUT = "bm:destout", "destout"
+ SRCATOP = "bm:srcatop", "srcatop"
+ DESTATOP = "bm:destatop", "destatop"
+ """
+
+ NORMAL = "bm:normal", "normal"
+ MULTIPLY = "bm:multiply", "multiply"
+ ADDITIVE = "bm:additive", "additive"
+ COLOURBURN = "bm:colourburn", "colourburn"
+ COLOURDODGE = "bm:colourdodge", "colourdodge"
+ REFLECT = "bm:reflect", "reflect"
+ GLOW = "bm:glow", "glow"
+ OVERLAY = "bm:overlay", "overlay"
+ DIFFERENCE = "bm:difference", "difference"
+ NEGATION = "bm:negation", "negation"
+ LIGHTEN = "bm:lighten", "lighten"
+ DARKEN = "bm:darken", "darken"
+ SCREEN = "bm:screen", "screen"
+ XOR = "bm:xor", "xor"
+ SOFTLIGHT = "bm:softlight", "softlight"
+ HARDLIGHT = "bm:hardlight", "hardlight"
+ GRAINEXTRACT = "bm:grainextract", "grainextract"
+ GRAINMERGE = "bm:grainmerge", "grainmerge"
+ DIVIDE = "bm:divide", "divide"
+ HUE = "bm:hue", "hue"
+ SATURATION = "bm:saturation", "saturation"
+ COLOUR = "bm:colour", "colour"
+ LUMINOSITY = "bm:luminosity", "luminosity"
+ PINLIGHT = "bm:pinlight", "pinlight"
+ VIVIDLIGHT = "bm:vividlight", "vividlight"
+ EXCLUSION = "bm:exclusion", "exclusion"
+ DESTIN = "bm:destin", "destin"
+ DESTOUT = "bm:destout", "destout"
+ SRCATOP = "bm:srcatop", "srcatop"
+ DESTATOP = "bm:destatop", "destatop"
diff --git a/venv/Lib/site-packages/blendmodes/imagetools.py b/venv/Lib/site-packages/blendmodes/imagetools.py new file mode 100644 index 00000000..834b7c86 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/imagetools.py @@ -0,0 +1,48 @@ +"""Do stuff to images to prepare them.
+"""
+from __future__ import annotations
+
+import warnings
+
+from deprecation import deprecated
+from PIL import Image
+
+
+@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset")
+def rasterImageOA( # pylint:disable=missing-function-docstring
+ image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0)
+) -> Image.Image:
+ warnings.warn(
+ "Call to deprecated function rasterImageOA.", category=DeprecationWarning, stacklevel=2
+ )
+ return renderWAlphaOffset(image, size, alpha, offsets)
+
+
+@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset")
+def rasterImageOffset( # pylint:disable=missing-function-docstring
+ image: Image.Image, size: tuple[int, int], offsets: tuple[int, int] = (0, 0)
+) -> Image.Image:
+ warnings.warn(
+ "Call to deprecated function rasterImageOffset.", category=DeprecationWarning, stacklevel=2
+ )
+ return renderWAlphaOffset(image, size, 1, offsets)
+
+
+def renderWAlphaOffset(
+ image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0)
+) -> Image.Image:
+ """Render an image with offset and alpha to a given size.
+
+ Args:
+ image (Image.Image): pil image to draw
+ size (tuple[int, int]): width, height as a tuple
+ alpha (float, optional): alpha transparency. Defaults to 1.0.
+ offsets (tuple[int, int], optional): x, y offsets as a tuple.
+ Defaults to (0, 0).
+
+ Returns:
+ Image.Image: new image
+ """
+ imageOffset = Image.new("RGBA", size)
+ imageOffset.paste(image.convert("RGBA"), offsets, image.convert("RGBA"))
+ return Image.blend(Image.new("RGBA", size), imageOffset, alpha)
|