Example #1
0
def compose_layer(layer, force=False, **kwargs):
    """Compose a single layer with pixels."""
    from PIL import Image, ImageChops
    assert layer.bbox != (0, 0, 0, 0), 'Layer bbox is (0, 0, 0, 0)'

    image = layer.topil(**kwargs)
    if image is None or force:
        texture = create_fill(layer)
        if texture is not None:
            image = texture
    if image is None:
        return image

    # TODO: Group should have the following too.

    # Apply vector mask.
    if layer.has_vector_mask() and (force or not layer.has_pixels()):
        vector_mask = draw_vector_mask(layer)
        if image.mode.endswith('A'):
            offset = vector_mask.info['offset']
            vector_mask = ImageChops.darker(image.getchannel('A'), vector_mask)
            vector_mask.info['offset'] = offset
        image.putalpha(vector_mask)

        # Apply stroke.
        if layer.has_stroke() and layer.stroke.enabled:
            image = draw_stroke(image, layer, vector_mask)

    # Apply mask.
    image = apply_mask(layer, image)

    # Apply layer fill effects.
    effect_base = image.copy()
    apply_opacity(image,
                  layer.tagged_blocks.get_data(Tag.BLEND_FILL_OPACITY, 255))
    image = apply_effect(layer, image, effect_base)

    # Clip layers.
    if layer.has_clip_layers():
        clip_box = Group.extract_bbox(layer.clip_layers)
        offset = image.info.get('offset', layer.offset)
        bbox = offset + (offset[0] + image.width, offset[1] + image.height)
        if intersect(bbox, clip_box) != (0, 0, 0, 0):
            clip_image = compose(layer.clip_layers,
                                 bbox=bbox,
                                 context=image.copy())
            if image.mode.endswith('A'):
                mask = image.getchannel('A')
            else:
                mask = Image.new('L', image.size, 255)
            if clip_image.mode.endswith('A'):
                mask = ImageChops.darker(clip_image.getchannel('A'), mask)
            clip_image.putalpha(mask)
            image = blend(image, clip_image, (0, 0))

    # Apply opacity.
    apply_opacity(image, layer.opacity)

    return image
Example #2
0
def _apply_layer_ops(layer, image, force=False, bbox=None):
    """Apply layer masks, effects, and clipping."""
    from PIL import Image, ImageChops
    # Apply vector mask.
    if layer.has_vector_mask() and (force or not layer.has_pixels()):
        offset = image.info.get('offset', layer.offset)
        mask_box = offset + (offset[0] + image.width, offset[1] + image.height)
        vector_mask = draw_vector_mask(layer, mask_box)
        if image.mode.endswith('A'):
            offset = vector_mask.info['offset']
            vector_mask = ImageChops.darker(image.getchannel('A'), vector_mask)
            vector_mask.info['offset'] = offset
        image.putalpha(vector_mask)

        # Apply stroke.
        if layer.has_stroke() and layer.stroke.enabled:
            image = draw_stroke(image, layer, vector_mask)

    # Apply mask.
    image = apply_mask(layer, image, bbox=bbox)

    # Apply layer fill effects.
    apply_opacity(
        image, layer.tagged_blocks.get_data(Tag.BLEND_FILL_OPACITY, 255)
    )
    if layer.effects.enabled:
        image = apply_effect(layer, image, image.copy())

    # Clip layers.
    if layer.has_clip_layers():
        clip_box = Group.extract_bbox(layer.clip_layers)
        offset = image.info.get('offset', layer.offset)
        bbox = offset + (offset[0] + image.width, offset[1] + image.height)
        if intersect(bbox, clip_box) != (0, 0, 0, 0):
            clip_image = compose(
                layer.clip_layers,
                force=force,
                bbox=bbox,
                context=image.copy()
            )
            if image.mode.endswith('A'):
                mask = image.getchannel('A')
            else:
                mask = Image.new('L', image.size, 255)
            if clip_image.mode.endswith('A'):
                mask = ImageChops.darker(clip_image.getchannel('A'), mask)
            clip_image.putalpha(mask)
            image = blend(image, clip_image, (0, 0))

    # Apply opacity.
    apply_opacity(image, layer.opacity)

    return image
Example #3
0
def apply_effect(layer, backdrop, base_image):
    """Apply effect to the image.

    ..note: Correct effect order is the following. All the effects are first
        applied to the original image then blended together.

        * dropshadow
        * outerglow
        * (original)
        * patternoverlay
        * gradientoverlay
        * coloroverlay
        * innershadow
        * innerglow
        * bevelemboss
        * satin
        * stroke
    """
    from PIL import ImageChops
    for effect in layer.effects:
        if effect.__class__.__name__ == 'PatternOverlay':
            image = draw_pattern_fill(base_image.size, layer._psd,
                                      effect.value)
            if base_image.mode.endswith('A'):
                alpha = base_image.getchannel('A')
                if image.mode.endswith('A'):
                    alpha = ImageChops.darker(alpha, image.getchannel('A'))
                image.putalpha(alpha)
            backdrop = blend(backdrop, image, (0, 0), effect.blend_mode)

    for effect in layer.effects:
        if effect.__class__.__name__ == 'GradientOverlay':
            image = draw_gradient_fill(base_image.size, effect.value)
            if base_image.mode.endswith('A'):
                alpha = base_image.getchannel('A')
                if image.mode.endswith('A'):
                    alpha = ImageChops.darker(alpha, image.getchannel('A'))
                image.putalpha(alpha)
            backdrop = blend(backdrop, image, (0, 0), effect.blend_mode)

    for effect in layer.effects:
        if effect.__class__.__name__ == 'ColorOverlay':
            image = draw_solid_color_fill(base_image.size, effect.value)
            if base_image.mode.endswith('A'):
                alpha = base_image.getchannel('A')
                if image.mode.endswith('A'):
                    alpha = ImageChops.darker(alpha, image.getchannel('A'))
                image.putalpha(alpha)
            backdrop = blend(backdrop, image, (0, 0), effect.blend_mode)

    for effect in layer.effects:
        if effect.__class__.__name__ == 'Stroke':
            from PIL import ImageOps

            if layer.has_vector_mask():
                alpha = draw_vector_mask(layer)
            elif base_image.mode.endswith('A'):
                alpha = base_image.getchannel('A')
            else:
                alpha = base_image.convert('L')
            alpha.info['offset'] = base_image.info['offset']
            flat = alpha.getextrema()[0] < 255

            # Expand the image size
            setting = effect.value
            size = int(setting.get(Key.SizeKey))
            offset = backdrop.info['offset']
            backdrop = ImageOps.expand(backdrop, size)
            backdrop.info['offset'] = tuple(x - size for x in offset)
            offset = alpha.info['offset']
            alpha = ImageOps.expand(alpha, size)
            alpha.info['offset'] = tuple(x - size for x in offset)

            if not layer.has_vector_mask() and setting.get(
                    Key.Style).enum == Enum.InsetFrame and flat:
                image = create_stroke_effect(alpha, setting, layer._psd, True)
                backdrop.paste(image)
            else:
                image = create_stroke_effect(alpha, setting, layer._psd)
                backdrop = blend(backdrop, image, (0, 0), effect.blend_mode)

    return backdrop