Source code for psd_tools.psd.effects_layer

"""
Effects layer structure.

Note the structures in this module is obsolete and object-based layer effects
are stored in tagged blocks.
"""
from __future__ import absolute_import, unicode_literals
import attr
import logging

from psd_tools.constants import BlendMode, EffectOSType
from psd_tools.psd.base import BaseElement, DictElement
from psd_tools.psd.color import Color
from psd_tools.validators import in_
from psd_tools.utils import (
    read_fmt, write_fmt, read_length_block, write_length_block, write_padding
)

logger = logging.getLogger(__name__)


[docs]@attr.s(repr=False, slots=True) class CommonStateInfo(BaseElement): """ Effects layer common state info. .. py:attribute:: version .. py:attribute:: visible """ version = attr.ib(default=0, type=int) visible = attr.ib(default=1, type=int) @classmethod def read(cls, fp): return cls(*read_fmt('IB2x', fp)) def write(self, fp): return write_fmt(fp, 'IB2x', *attr.astuple(self))
[docs]@attr.s(repr=False, slots=True) class ShadowInfo(BaseElement): """ Effects layer shadow info. .. py:attribute:: version .. py:attribute:: blur .. py:attribute:: intensity .. py:attribute:: angle .. py:attribute:: distance .. py:attribute:: color .. py:attribute:: blend_mode .. py:attribute:: enabled .. py:attribute:: use_global_angle .. py:attribute:: opacity .. py:attribute:: native_color """ version = attr.ib(default=0, type=int) blur = attr.ib(default=0, type=int) intensity = attr.ib(default=0, type=int) angle = attr.ib(default=0, type=int) distance = attr.ib(default=0, type=int) color = attr.ib(factory=Color) blend_mode = attr.ib( default=BlendMode.NORMAL, converter=BlendMode, validator=in_(BlendMode) ) enabled = attr.ib(default=0, type=int) use_global_angle = attr.ib(default=0, type=int) opacity = attr.ib(default=0, type=int) native_color = attr.ib(factory=Color) @classmethod def read(cls, fp): # TODO: Check 4-byte = 2-byte int + 2-byte fraction? version, blur, intensity, angle, distance = read_fmt('IIIiI', fp) color = Color.read(fp) signature = read_fmt('4s', fp)[0] assert signature == b'8BIM', 'Invalid signature %r' % (signature) blend_mode = BlendMode(read_fmt('4s', fp)[0]) enabled, use_global_angle, opacity = read_fmt('3B', fp) native_color = Color.read(fp) return cls( version, blur, intensity, angle, distance, color, blend_mode, enabled, use_global_angle, opacity, native_color ) def write(self, fp): written = write_fmt( fp, 'IIIiI', self.version, self.blur, self.intensity, self.angle, self.distance ) written += self.color.write(fp) written += write_fmt( fp, '4s4s3B', b'8BIM', self.blend_mode.value, self.enabled, self.use_global_angle, self.opacity ) written += self.native_color.write(fp) return written
class _GlowInfo(object): @classmethod def _read_body(cls, fp): # TODO: Check 4-byte = 2-byte int + 2-byte fraction? version, blur, intensity = read_fmt('III', fp) color = Color.read(fp) signature = read_fmt('4s', fp)[0] assert signature == b'8BIM', 'Invalid signature %r' % (signature) blend_mode = BlendMode(read_fmt('4s', fp)[0]) enabled, opacity = read_fmt('2B', fp) return version, blur, intensity, color, blend_mode, enabled, opacity def _write_body(self, fp): written = write_fmt(fp, 'III', self.version, self.blur, self.intensity) written += self.color.write(fp) written += write_fmt( fp, '4s4s2B', b'8BIM', self.blend_mode.value, self.enabled, self.opacity ) return written
[docs]@attr.s(repr=False, slots=True) class OuterGlowInfo(BaseElement, _GlowInfo): """ Effects layer outer glow info. .. py:attribute:: version .. py:attribute:: blur .. py:attribute:: intensity .. py:attribute:: color .. py:attribute:: blend_mode .. py:attribute:: enabled .. py:attribute:: opacity .. py:attribute:: native_color """ version = attr.ib(default=0, type=int) blur = attr.ib(default=0, type=int) intensity = attr.ib(default=0, type=int) color = attr.ib(factory=Color) blend_mode = attr.ib( default=BlendMode.NORMAL, converter=BlendMode, validator=in_(BlendMode) ) enabled = attr.ib(default=0, type=int) opacity = attr.ib(default=0, type=int) native_color = attr.ib(default=None) @classmethod def read(cls, fp): version, blur, intensity, color, blend_mode, enabled, opacity = ( cls._read_body(fp) ) native_color = None if version >= 2: native_color = Color.read(fp) return cls( version, blur, intensity, color, blend_mode, enabled, opacity, native_color ) def write(self, fp): written = self._write_body(fp) if self.native_color: written += self.native_color.write(fp) return written
[docs]@attr.s(repr=False, slots=True) class InnerGlowInfo(BaseElement, _GlowInfo): """ Effects layer inner glow info. .. py:attribute:: version .. py:attribute:: blur .. py:attribute:: intensity .. py:attribute:: color .. py:attribute:: blend_mode .. py:attribute:: enabled .. py:attribute:: opacity .. py:attribute:: invert .. py:attribute:: native_color """ version = attr.ib(default=0, type=int) blur = attr.ib(default=0, type=int) intensity = attr.ib(default=0, type=int) color = attr.ib(factory=Color) blend_mode = attr.ib( default=BlendMode.NORMAL, converter=BlendMode, validator=in_(BlendMode) ) enabled = attr.ib(default=0, type=int) opacity = attr.ib(default=0, type=int) invert = attr.ib(default=None) native_color = attr.ib(default=None) @classmethod def read(cls, fp): version, blur, intensity, color, blend_mode, enabled, opacity = ( cls._read_body(fp) ) invert, native_color = None, None if version >= 2: invert = read_fmt('B', fp)[0] native_color = Color.read(fp) return cls( version, blur, intensity, color, blend_mode, enabled, opacity, invert, native_color ) def write(self, fp): written = self._write_body(fp) if self.version >= 2: written += write_fmt(fp, 'B', self.invert) written += self.native_color.write(fp) return written
[docs]@attr.s(repr=False, slots=True) class BevelInfo(BaseElement): """ Effects layer bevel info. .. py:attribute:: version .. py:attribute:: angle .. py:attribute:: depth .. py:attribute:: blur .. py:attribute:: highlight_blend_mode .. py:attribute:: shadow_blend_mode .. py:attribute:: highlight_color .. py:attribute:: shadow_color .. py:attribute:: highlight_opacity .. py:attribute:: shadow_opacity .. py:attribute:: enabled .. py:attribute:: use_global_angle .. py:attribute:: direction .. py:attribute:: real_hightlight_color .. py:attribute:: real_shadow_color """ version = attr.ib(default=0, type=int) angle = attr.ib(default=0, type=int) depth = attr.ib(default=0, type=int) blur = attr.ib(default=0, type=int) highlight_blend_mode = attr.ib( default=BlendMode.NORMAL, converter=BlendMode, validator=in_(BlendMode) ) shadow_blend_mode = attr.ib( default=BlendMode.NORMAL, converter=BlendMode, validator=in_(BlendMode) ) highlight_color = attr.ib(factory=Color) shadow_color = attr.ib(factory=Color) bevel_style = attr.ib(default=0, type=int) highlight_opacity = attr.ib(default=0, type=int) shadow_opacity = attr.ib(default=0, type=int) enabled = attr.ib(default=0, type=int) use_global_angle = attr.ib(default=0, type=int) direction = attr.ib(default=0, type=int) real_highlight_color = attr.ib(default=None) real_shadow_color = attr.ib(default=None) @classmethod def read(cls, fp): # TODO: Check 4-byte = 2-byte int + 2-byte fraction? version, angle, depth, blur = read_fmt('Ii2I', fp) signature, highlight_blend_mode = read_fmt('4s4s', fp) assert signature == b'8BIM', 'Invalid signature %r' % (signature) signature, shadow_blend_mode = read_fmt('4s4s', fp) assert signature == b'8BIM', 'Invalid signature %r' % (signature) highlight_color = Color.read(fp) shadow_color = Color.read(fp) bevel_style, highlight_opacity, shadow_opacity = read_fmt('3B', fp) enabled, use_global_angle, direction = read_fmt('3B', fp) real_highlight_color, real_shadow_color = None, None if version == 2: real_highlight_color = Color.read(fp) real_shadow_color = Color.read(fp) return cls( version, angle, depth, blur, highlight_blend_mode, shadow_blend_mode, highlight_color, shadow_color, bevel_style, highlight_opacity, shadow_opacity, enabled, use_global_angle, direction, real_highlight_color, real_shadow_color ) def write(self, fp): written = write_fmt( fp, 'Ii2I', self.version, self.angle, self.depth, self.blur ) written += write_fmt( fp, '4s4s4s4s', b'8BIM', self.highlight_blend_mode.value, b'8BIM', self.shadow_blend_mode.value ) written += self.highlight_color.write(fp) written += self.shadow_color.write(fp) written += write_fmt( fp, '6B', self.bevel_style, self.highlight_opacity, self.shadow_opacity, self.enabled, self.use_global_angle, self.direction ) if self.version >= 2: written += self.highlight_color.write(fp) written += self.shadow_color.write(fp) return written
[docs]@attr.s(repr=False, slots=True) class SolidFillInfo(BaseElement): """ Effects layer inner glow info. .. py:attribute:: version .. py:attribute:: blend_mode .. py:attribute:: color .. py:attribute:: opacity .. py:attribute:: enabled .. py:attribute:: native_color """ version = attr.ib(default=2, type=int) blend_mode = attr.ib( default=BlendMode.NORMAL, converter=BlendMode, validator=in_(BlendMode) ) color = attr.ib(factory=Color) opacity = attr.ib(default=0, type=int) enabled = attr.ib(default=0, type=int) native_color = attr.ib(factory=Color) @classmethod def read(cls, fp): version = read_fmt('I', fp)[0] signature, blend_mode = read_fmt('4s4s', fp) assert signature == b'8BIM', 'Invalid signature %r' % (signature) color = Color.read(fp) opacity, enabled = read_fmt('2B', fp) native_color = Color.read(fp) return cls(version, blend_mode, color, opacity, enabled, native_color) def write(self, fp): written = write_fmt( fp, 'I4s4s', self.version, b'8BIM', self.blend_mode.value ) written += self.color.write(fp) written += write_fmt(fp, '2B', self.opacity, self.enabled) written += self.native_color.write(fp) return written
[docs]@attr.s(repr=False, slots=True) class EffectsLayer(DictElement): """ Dict-like EffectsLayer structure. See :py:class:`psd_tools.constants.EffectOSType` for available keys. .. py:attribute:: version """ version = attr.ib(default=0, type=int) EFFECT_TYPES = { EffectOSType.COMMON_STATE: CommonStateInfo, EffectOSType.DROP_SHADOW: ShadowInfo, EffectOSType.INNER_SHADOW: ShadowInfo, EffectOSType.OUTER_GLOW: OuterGlowInfo, EffectOSType.INNER_GLOW: InnerGlowInfo, EffectOSType.BEVEL: BevelInfo, EffectOSType.SOLID_FILL: SolidFillInfo, } @classmethod def read(cls, fp, **kwargs): version, count = read_fmt('2H', fp) items = [] for _ in range(count): signature = read_fmt('4s', fp)[0] assert signature == b'8BIM', 'Invalid signature %r' % (signature) ostype = EffectOSType(read_fmt('4s', fp)[0]) kls = cls.EFFECT_TYPES.get(ostype) items.append((ostype, kls.frombytes(read_length_block(fp)))) return cls(version=version, items=items) def write(self, fp, **kwargs): written = write_fmt(fp, '2H', self.version, len(self)) for key in self: written += write_fmt(fp, '4s4s', b'8BIM', key.value) written += write_length_block(fp, lambda f: self[key].write(f)) written += write_padding(fp, written, 4) return written