Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
1.8.18 (2019-06-05)
-------------------

- fix broken `psd_tools.composer.vector` module in 1.8.17.

1.8.17 (2019-06-05)
-------------------

Expand Down
20 changes: 11 additions & 9 deletions src/psd_tools/composer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,23 +192,23 @@ def create_fill(layer):
image = Image.new(mode, (layer.width, layer.height), 'white')
setting = layer.tagged_blocks.get_data(Tag.VECTOR_STROKE_CONTENT_DATA)
if b'Ptrn' in setting:
draw_pattern_fill(image, layer._psd, setting, blend=False)
draw_pattern_fill(image, layer._psd, setting)
elif b'Grad' in setting:
draw_gradient_fill(image, setting, blend=False)
draw_gradient_fill(image, setting)
else:
draw_solid_color_fill(image, setting, blend=False)
draw_solid_color_fill(image, setting)
elif Tag.SOLID_COLOR_SHEET_SETTING in layer.tagged_blocks:
image = Image.new(mode, (layer.width, layer.height), 'white')
setting = layer.tagged_blocks.get_data(Tag.SOLID_COLOR_SHEET_SETTING)
draw_solid_color_fill(image, setting, blend=False)
draw_solid_color_fill(image, setting)
elif Tag.PATTERN_FILL_SETTING in layer.tagged_blocks:
image = Image.new(mode, (layer.width, layer.height), 'white')
setting = layer.tagged_blocks.get_data(Tag.PATTERN_FILL_SETTING)
draw_pattern_fill(image, layer._psd, setting, blend=False)
draw_pattern_fill(image, layer._psd, setting)
elif Tag.GRADIENT_FILL_SETTING in layer.tagged_blocks:
image = Image.new(mode, (layer.width, layer.height), 'white')
setting = layer.tagged_blocks.get_data(Tag.GRADIENT_FILL_SETTING)
draw_gradient_fill(image, setting, blend=False)
draw_gradient_fill(image, setting)
return image


Expand Down Expand Up @@ -270,15 +270,17 @@ def apply_effect(layer, image):
"""
for effect in layer.effects:
if effect.__class__.__name__ == 'PatternOverlay':
draw_pattern_fill(image, layer._psd, effect.value)
draw_pattern_fill(
image, layer._psd, effect.value, effect.blend_mode
)

for effect in layer.effects:
if effect.__class__.__name__ == 'GradientOverlay':
draw_gradient_fill(image, effect.value)
draw_gradient_fill(image, effect.value, effect.blend_mode)

for effect in layer.effects:
if effect.__class__.__name__ == 'ColorOverlay':
draw_solid_color_fill(image, effect.value)
draw_solid_color_fill(image, effect.value, effect.blend_mode)


def apply_opacity(layer, image):
Expand Down
23 changes: 12 additions & 11 deletions src/psd_tools/composer/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging

from psd_tools.api.pil_io import convert_pattern_to_pil
from psd_tools.composer.blend import blend

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -79,28 +80,28 @@ def _generate_symbol(path, width, height, command='C'):
yield 'Z'


def draw_solid_color_fill(image, setting, blend=True):
def draw_solid_color_fill(image, setting, mode=None):
from PIL import Image, ImageDraw, ImageChops
color = tuple(int(x) for x in setting.get(b'Clr ').values())
canvas = Image.new(image.mode, image.size)
draw = ImageDraw.Draw(canvas)
draw.rectangle((0, 0, canvas.width, canvas.height), fill=color)
del draw
if blend:
if mode:
canvas.putalpha(image.getchannel('A'))
_blend(image, canvas, (0, 0))
blend(image, canvas, (0, 0), mode=mode)
else:
image.paste(canvas)


def draw_pattern_fill(image, psd, setting, blend=True):
def draw_pattern_fill(image, psd, setting, mode=None):
"""
Draw pattern fill on the image.

:param image: Image to be filled.
:param psd: :py:class:`PSDImage`.
:param setting: Descriptor containing pattern fill.
:param blend: Blend the fill or ignore. Effects blend.
:param mode: Blend the fill or ignore if None.
"""
from PIL import Image
pattern_id = setting[b'Ptrn'][b'Idnt'].value.rstrip('\x00')
Expand All @@ -121,7 +122,7 @@ def draw_pattern_fill(image, psd, setting, blend=True):
panel.putalpha(opacity)

pattern_image = Image.new(image.mode, image.size)
mask = image.getchannel('A') if blend else Image.new('L', image.size, 255)
mask = image.getchannel('A') if mode else Image.new('L', image.size, 255)

for left in range(0, pattern_image.width, panel.width):
for top in range(0, pattern_image.height, panel.height):
Expand All @@ -130,13 +131,13 @@ def draw_pattern_fill(image, psd, setting, blend=True):
)
pattern_image.paste(panel, (left, top), panel_mask)

if blend:
image.paste(_blend(image, pattern_image, (0, 0)))
if mode:
image.paste(blend(image, pattern_image, (0, 0), mode=mode))
else:
image.paste(pattern_image)


def draw_gradient_fill(image, setting, blend=True):
def draw_gradient_fill(image, setting, mode=None):
try:
import numpy as np
from scipy import interpolate
Expand All @@ -153,9 +154,9 @@ def draw_gradient_fill(image, setting, blend=True):
Z = np.ones((image.height, image.width)) * 0.5

gradient_image = _apply_color_map(image.mode, setting.get(b'Grad'), Z)
if blend:
if mode:
gradient_image.putalpha(image.getchannel('A'))
_blend(image, gradient_image, offset=(0, 0))
blend(image, gradient_image, (0, 0), mode=mode)
else:
image.paste(gradient_image)

Expand Down
2 changes: 1 addition & 1 deletion src/psd_tools/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.8.17'
__version__ = '1.8.18'
18 changes: 0 additions & 18 deletions tests/psd_tools/composer/test_composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,6 @@ def test_compose_quality(filename, threshold=0.1):
assert _calculate_hash_error(preview, rendered) <= threshold


@pytest.mark.parametrize(("filename", ), [
('path-operations/combine.psd', ),
('path-operations/exclude-first.psd', ),
('path-operations/exclude.psd', ),
('path-operations/intersect-all.psd', ),
('path-operations/intersect-first.psd', ),
('path-operations/subtract-all.psd', ),
('path-operations/subtract-first.psd', ),
('path-operations/subtract-second.psd', ),
('stroke.psd', ),
])
def test_compose_quality_rgb(filename):
psd = PSDImage.open(full_name(filename))
preview = psd.topil().convert('RGB')
rendered = psd.compose(force=True).convert('RGB')
assert _calculate_hash_error(preview, rendered) <= 0.1


@pytest.mark.parametrize(
'filename', [
'smartobject-layer.psd',
Expand Down
74 changes: 74 additions & 0 deletions tests/psd_tools/composer/test_vector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from __future__ import absolute_import, unicode_literals
import pytest
import logging
from PIL import Image

from psd_tools import PSDImage
from psd_tools.constants import Tag, BlendMode
from psd_tools.composer.vector import (
draw_solid_color_fill, draw_pattern_fill, draw_gradient_fill
)
from psd_tools.psd.descriptor import Double
from psd_tools.terminology import Enum, Key

from ..utils import full_name
from .test_composer import _calculate_hash_error

logger = logging.getLogger(__name__)


@pytest.mark.parametrize(("filename", ), [
('path-operations/combine.psd', ),
('path-operations/exclude-first.psd', ),
('path-operations/exclude.psd', ),
('path-operations/intersect-all.psd', ),
('path-operations/intersect-first.psd', ),
('path-operations/subtract-all.psd', ),
('path-operations/subtract-first.psd', ),
('path-operations/subtract-second.psd', ),
('stroke.psd', ),
])
def test_draw_vector_mask(filename):
psd = PSDImage.open(full_name(filename))
preview = psd.topil().convert('RGB')
rendered = psd.compose(force=True).convert('RGB')
assert _calculate_hash_error(preview, rendered) <= 0.1


def test_draw_solid_color_fill():
psd = PSDImage.open(full_name('layers-minimal/solid-color-fill.psd'))
setting = psd[0].tagged_blocks.get_data(Tag.SOLID_COLOR_SHEET_SETTING)
image = Image.new('RGBA', psd.size)
draw_solid_color_fill(image, setting)
draw_solid_color_fill(image, setting, BlendMode.SCREEN)


def test_draw_pattern_fill():
psd = PSDImage.open(full_name('layers-minimal/pattern-fill.psd'))
setting = psd[0].tagged_blocks.get_data(Tag.PATTERN_FILL_SETTING)
image = Image.new('RGBA', psd.size)
draw_pattern_fill(image, psd, setting)
draw_pattern_fill(image, psd, setting, BlendMode.SCREEN)
setting[b'Scl '] = Double(50.)
setting[b'Opct'] = Double(67.)
draw_pattern_fill(image, psd, setting, BlendMode.NORMAL)


def test_draw_gradient_fill():
psd = PSDImage.open(full_name('layers-minimal/gradient-fill.psd'))
setting = psd[0].tagged_blocks.get_data(Tag.GRADIENT_FILL_SETTING)
image = Image.new('RGBA', psd.size)
draw_gradient_fill(image, setting)
draw_gradient_fill(image, setting, BlendMode.SCREEN)

for angle in (
-90.,
0.,
90.,
180.,
):
setting.get(Key.Angle.value).value = angle
draw_gradient_fill(image, setting)

setting.get(b'Type').enum = Enum.Radial.value
draw_gradient_fill(image, setting)