示例#1
0
def create_fill(layer):
    from PIL import Image
    mode = get_pil_mode(layer._psd.color_mode, True)
    size = (layer.width, layer.height)
    fill_image = None
    stroke = layer.tagged_blocks.get_data(Tag.VECTOR_STROKE_DATA)

    # Apply fill.
    if Tag.VECTOR_STROKE_CONTENT_DATA in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data(Tag.VECTOR_STROKE_CONTENT_DATA)
        if stroke and bool(stroke.get('fillEnabled', True)) is False:
            fill_image = Image.new(mode, size)
        elif Enum.Pattern in setting:
            fill_image = draw_pattern_fill(size, layer._psd, setting)
        elif Key.Gradient in setting:
            fill_image = draw_gradient_fill(size, setting)
        else:
            fill_image = draw_solid_color_fill(size, setting)
    elif Tag.SOLID_COLOR_SHEET_SETTING in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data(Tag.SOLID_COLOR_SHEET_SETTING)
        fill_image = draw_solid_color_fill(size, setting)
    elif Tag.PATTERN_FILL_SETTING in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data(Tag.PATTERN_FILL_SETTING)
        fill_image = draw_pattern_fill(size, layer._psd, setting)
    elif Tag.GRADIENT_FILL_SETTING in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data(Tag.GRADIENT_FILL_SETTING)
        fill_image = draw_gradient_fill(size, setting)

    return fill_image
示例#2
0
def create_fill(layer):
    from PIL import Image
    mode = get_pil_mode(layer._psd.color_mode, True)
    image = Image.new(mode, (layer.width, layer.height))
    if 'SOLID_COLOR_SHEET_SETTING' in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data('SOLID_COLOR_SHEET_SETTING')
        draw_solid_color_fill(image, setting)
    elif 'PATTERN_FILL_SETTING' in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data('PATTERN_FILL_SETTING')
        draw_pattern_fill(image, layer._psd, setting)
    elif 'GRADIENT_FILL_SETTING' in layer.tagged_blocks:
        setting = layer.tagged_blocks.get_data('GRADIENT_FILL_SETTING')
        draw_gradient_fill(image, setting, blend=False)
    return image
示例#3
0
def composite_pil(layer,
                  color,
                  alpha,
                  viewport,
                  layer_filter,
                  force,
                  as_layer=False):
    from PIL import Image
    from psd_tools.api.pil_io import get_pil_mode
    from psd_tools.api.numpy_io import has_transparency

    UNSUPPORTED_MODES = {
        ColorMode.DUOTONE,
        ColorMode.LAB,
    }
    color_mode = getattr(layer, '_psd', layer).color_mode
    if color_mode in UNSUPPORTED_MODES:
        logger.warning('Unsupported blending color space: %s' % (color_mode))

    color, _, alpha = composite(layer,
                                color=color,
                                alpha=alpha,
                                viewport=viewport,
                                layer_filter=layer_filter,
                                force=force,
                                as_layer=as_layer)

    mode = get_pil_mode(color_mode)
    if mode == 'P':
        mode = 'RGB'
    # Skip only when there is a preview image and it has no alpha.
    skip_alpha = not force and (
        color_mode not in (ColorMode.GRAYSCALE, ColorMode.RGB) or
        (layer.kind == 'psdimage' and layer.has_preview()
         and not has_transparency(layer)))
    logger.debug('Skipping alpha: %g' % skip_alpha)
    if not skip_alpha:
        color = np.concatenate((color, alpha), 2)
        mode += 'A'
    if mode in ('1', 'L'):
        color = color[:, :, 0]
    if color.shape[0] == 0 or color.shape[1] == 0:
        return None
    return Image.fromarray((255 * color).astype(np.uint8), mode)
示例#4
0
def test_get_pil_mode(mode, alpha, expected):
    assert pil_io.get_pil_mode(mode, alpha) == expected
示例#5
0
def compose(layers, bbox=None, layer_filter=None, color=None):
    """
    Compose layers to a single :py:class:`PIL.Image`.
    If the layers do not have visible pixels, the function returns `None`.

    Example::

        image = compose([layer1, layer2])

    In order to skip some layers, pass `layer_filter` function which
    should take `layer` as an argument and return `True` to keep the layer
    or return `False` to skip::

        image = compose(
            layers,
            layer_filter=lambda x: x.is_visible() and x.kind == 'type'
        )

    By default, visible layers are composed.

    .. note:: This function is experimental and does not guarantee
        Photoshop-quality rendering.

        Currently the following are ignored:

         - Adjustments layers
         - Layer effects
         - Blending mode (all blending modes become normal)

        Shape drawing is inaccurate if the PSD file is not saved with
        maximum compatibility.

    :param layers: a layer, or an iterable of layers.
    :param bbox: (left, top, bottom, right) tuple that specifies a region to
        compose. By default, all the visible area is composed. The origin
        is at the top-left corner of the PSD document.
    :param layer_filter: a callable that takes a layer and returns `bool`.
    :param color: background color in `int` or `tuple`.
    :return: :py:class:`PIL.Image` or `None`.
    """
    from PIL import Image

    if not hasattr(layers, '__iter__'):
        layers = [layers]

    def _default_filter(layer):
        return layer.is_visible()

    layer_filter = layer_filter or _default_filter
    valid_layers = [x for x in layers if layer_filter(x)]
    if len(valid_layers) == 0:
        return None

    if bbox is None:
        bbox = extract_bbox(valid_layers)
        if bbox == (0, 0, 0, 0):
            return None

    # Alpha must be forced to correctly blend.
    mode = get_pil_mode(valid_layers[0]._psd.color_mode, True)
    result = Image.new(
        mode,
        (bbox[2] - bbox[0], bbox[3] - bbox[1]),
        color=color,
    )

    initial_layer = True
    for layer in valid_layers:
        if intersect(layer.bbox, bbox) == (0, 0, 0, 0):
            continue

        image = layer.compose()
        if image is None:
            continue

        logger.debug('Composing %s' % layer)
        offset = (layer.left - bbox[0], layer.top - bbox[1])
        if initial_layer:
            result.paste(image, offset)
            initial_layer = False
        else:
            result = _blend(result, image, offset)

    return result
示例#6
0
def compose(layers,
            bbox=None,
            context=None,
            layer_filter=None,
            color=None,
            **kwargs):
    """
    Compose layers to a single :py:class:`PIL.Image`.
    If the layers do not have visible pixels, the function returns `None`.

    Example::

        image = compose([layer1, layer2])

    In order to skip some layers, pass `layer_filter` function which
    should take `layer` as an argument and return `True` to keep the layer
    or return `False` to skip::

        image = compose(
            layers,
            layer_filter=lambda x: x.is_visible() and x.kind == 'type'
        )

    By default, visible layers are composed.

    .. note:: This function is experimental and does not guarantee
        Photoshop-quality rendering.

        Currently the following are ignored:

         - Adjustments layers
         - Layer effects
         - Blending mode (all blending modes become normal)

        Shape drawing is inaccurate if the PSD file is not saved with
        maximum compatibility.

    :param layers: a layer, or an iterable of layers.
    :param bbox: (left, top, bottom, right) tuple that specifies a region to
        compose. By default, all the visible area is composed. The origin
        is at the top-left corner of the PSD document.
    :param context: `PIL.Image` object for the backdrop rendering context. Must
        be used with the correct `bbox` size.
    :param layer_filter: a callable that takes a layer and returns `bool`.
    :param color: background color in `int` or `tuple`.
    :return: :py:class:`PIL.Image` or `None`.
    """
    from PIL import Image

    if not hasattr(layers, '__iter__'):
        layers = [layers]

    def _default_filter(layer):
        return layer.is_visible()

    layer_filter = layer_filter or _default_filter
    valid_layers = [x for x in layers if layer_filter(x)]
    if len(valid_layers) == 0:
        return context

    if bbox is None:
        bbox = Group.extract_bbox(valid_layers)
        if bbox == (0, 0, 0, 0):
            return context

    if context is None:
        mode = get_pil_mode(valid_layers[0]._psd.color_mode, True)
        context = Image.new(
            mode,
            (bbox[2] - bbox[0], bbox[3] - bbox[1]),
            color=color if color is not None else 'white',
        )
        context.putalpha(0)  # Alpha must be forced to correctly blend.
        context.info['offset'] = (bbox[0], bbox[1])

    for layer in valid_layers:
        if intersect(layer.bbox, bbox) == (0, 0, 0, 0):
            continue

        if layer.is_group():
            if layer.blend_mode == BlendMode.PASS_THROUGH:
                context = layer.compose(context=context, bbox=bbox, **kwargs)
                continue
            else:
                image = layer.compose(**kwargs)
        else:
            image = compose_layer(layer, **kwargs)
        if image is None:
            continue

        logger.debug('Composing %s' % layer)
        offset = image.info.get('offset', layer.offset)
        offset = (offset[0] - bbox[0], offset[1] - bbox[1])

        context = blend(context, image, offset, layer.blend_mode)

    return context