Skip to content

Commit

Permalink
Add alpha blending support
Browse files Browse the repository at this point in the history
  • Loading branch information
akiomik committed Nov 18, 2019
1 parent 50d72e0 commit 2adbca5
Show file tree
Hide file tree
Showing 27 changed files with 707 additions and 161 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ twine = "*"
wheel = "*"
pytest-benchmark = "*"
tox = "*"
pytest-mock = "*"

[packages]
174 changes: 93 additions & 81 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

117 changes: 117 additions & 0 deletions pilgram/css/blending/alpha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# coding:utf-8

# Copyright 2019 Akiomi Kamakura
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from PIL import ImageChops

from pilgram.util import invert


def split_alpha(im):
"""Splits alpha channel
Arguments:
im: An image (RGB or RGBA).
Returns:
A tuple of the RGB and alpha image.
"""

if im.mode == 'RGBA':
# NOTE: `merge` is slower than `convert` when using Vanilla Pillow
a = im.split()[3]
im = im.convert('RGB')
return im, a
elif im.mode == 'RGB':
return im, None
else:
raise ValueError('Unsupported mode: ' + im.mode)


def alpha_to_rgb(im):
"""Converts alpha image to rgb image.
Arguments:
im: An image (L only).
Returns:
A tuple of the RGB and alpha image.
"""
if im.mode == 'L':
# NOTE: `merge` is slower than `convert` when using Vanilla Pillow
im = im.convert('RGB')
return im
else:
raise ValueError('Unsupported mode: ' + im.mode)


def alpha_blend(im1, im2, blending):
"""Simple alpha blending
The formula is defined as:
simple alpha compositing:
co = cs + cb x (1 - αs)
written as non-premultiplied:
αo x Co = αs x Cs + (1 - αs) x αb x Cb
now substitute the result of blending for Cs:
αo x Co = αs x ((1 - αb) x Cs + αb x B(Cb, Cs)) + (1 - αs) x αb x Cb
= αs x (1 - αb) x Cs + αs x αb x B(Cb, Cs) + (1 - αs) x αb x Cb
See the W3C document:
https://www.w3.org/TR/compositing-1/#blending
Arguments:
im1: A backdrop image (RGB or RGBA).
im2: A source image (RGB or RGBA).
blending: The blending method.
Returns:
The output image.
"""

im1, a1 = split_alpha(im1)
if a1 is not None:
a1_rgb = alpha_to_rgb(a1)
a1_invert_rgb = alpha_to_rgb(invert(a1))

im2, a2 = split_alpha(im2)
if a2 is not None:
a2_rgb = alpha_to_rgb(a2)
a2_invert_rgb = alpha_to_rgb(invert(a2))

im_blended = blending(im1, im2)

if a1 is not None and a2 is not None:
im_hard_light_alpha = ImageChops.multiply(a1_rgb, a2_rgb)
im1_alpha = ImageChops.multiply(a1_rgb, a2_invert_rgb)
im2_alpha = ImageChops.multiply(a2_rgb, a1_invert_rgb)
im_blended = ImageChops.add(
ImageChops.multiply(im_hard_light_alpha, im_blended),
ImageChops.add(
ImageChops.multiply(im1_alpha, im1),
ImageChops.multiply(im2_alpha, im2)))
elif a1 is not None:
im_blended = ImageChops.add(
ImageChops.multiply(a1_rgb, im_blended),
ImageChops.multiply(a1_invert_rgb, im2))
elif a2 is not None:
im_blended = ImageChops.add(
ImageChops.multiply(a2_rgb, im_blended),
ImageChops.multiply(a2_invert_rgb, im1))

return im_blended
Loading

0 comments on commit 2adbca5

Please sign in to comment.