Commits

Mikhail Korobov committed 4343dc3

function for creating PIL image from a layer

Comments (0)

Files changed (8)

 ^build
 \.ipynb$
 ^dist
+^psd_tools.egg-info
 include AUTHORS.rst
 include README.rst
 include CHANGES.rst
-include docs/Makefile
-include docs/make.bat
-include docs/conf.py
-
-recursive-include docs *.rst
-recursive-include benchmarks *.py
 import psd_tools.reader
 import psd_tools.decoder
 
+from psd_tools.decoder.layers import channels_to_PIL
+
 logger = logging.getLogger('psd_tools')
 logger.addHandler(logging.StreamHandler())
 
         for it in decoded:
             pprint.pprint(it)
 
+#        layers = decoded.layer_and_mask_data.layers
+#        layer = layers.layer_records[4]
+#        channel_data = layers.channel_image_data[4]
+#
+#        im = channels_to_PIL(layer, channel_data)
+#        im.save('res.png')

psd_tools/constants.py

     USER_LAYER_MASK = -2
     REAL_USER_LAYER_MASK = -3
 
+    @classmethod
+    def to_PIL(cls, channel_id):
+        BANDS_MAP = {
+            cls.RED: 'R',
+            cls.GREEN: 'G',
+            cls.BLUE: 'B',
+            cls.TRANSPARENCY_MASK: 'A'
+        }
+        return BANDS_MAP.get(channel_id, None)
+
 class ImageResourceID(Enum):
     OBSOLETE1 = 1000
     MAC_PRINT_MANAGER_INFO = 1001

psd_tools/decoder/image_resources.py

 import io
 import warnings
 import collections
-import array
 
-from psd_tools.utils import read_pascal_string, unpack, read_be_array, read_fmt, read_unicode_string, be_array_from_bytes
+from psd_tools.utils import read_pascal_string, unpack, read_fmt, read_unicode_string, be_array_from_bytes
 from psd_tools.constants import ImageResourceID, PrintScaleStyle
 
 PrintScale = collections.namedtuple('PrintScale', 'style, x, y, scale')
 @register(ImageResourceID.PRINT_SCALE)
 def _decode_print_scale(data):
     style, x, y, scale = unpack("H3f", data)
+
     if not PrintScaleStyle.is_known(style):
         warnings.warn("Unknown print scale style (%s)" % style)
+
     return PrintScale(style, x, y, scale)
 
 

psd_tools/decoder/layers.py

+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, unicode_literals
+import warnings
+from psd_tools.constants import Compression, ChannelID
+
+def channels_to_PIL(layer, channels_data):
+    from PIL import Image
+    size = layer.width(), layer.height()
+
+    bands = {}
+
+    for channel, info in zip(channels_data, layer.channels):
+
+        pil_band = ChannelID.to_PIL(info.id)
+        if pil_band is None:
+            warnings.warn("Unsupported channel type (%d)" % info.id)
+            continue
+
+        if channel.compression == Compression.RAW:
+            bands[pil_band] = Image.fromstring("L", size, channel.data, 'L')
+        elif channel.compression == Compression.PACK_BITS:
+            bands[pil_band] = Image.fromstring("L", size, channel.data, "packbits", 'L')
+        elif Compression.is_known(channel.compression):
+            warnings.warn("Compression method is not implemented (%s)" % channel.compression)
+        else:
+            warnings.warn("Unknown compression method (%s)" % channel.compression)
+
+    def as_bands(mode):
+        if set(bands.keys()) == set(list(mode)):
+            return [bands[band] for band in ['R', 'G', 'B', 'A']]
+
+
+    for mode in ['RGBA', 'RGB']:
+        image_bands = as_bands(mode)
+        if image_bands:
+            return Image.merge(mode, image_bands)

psd_tools/reader/layers.py

 
 logger = logging.getLogger(__name__)
 
-LayerRecord = collections.namedtuple('LayerRecord', [
+_LayerRecord = collections.namedtuple('_LayerRecord', [
     'top', 'left', 'bottom', 'right',
     'num_channels', 'channels',
     'blend_mode', 'opacity', 'cilpping', 'flags',
     'tagged_blocks'
 ])
 
+class LayerRecord(_LayerRecord):
+
+    def width(self):
+        return self.right - self.left
+
+    def height(self):
+        return self.bottom - self.top
+
+
+Layers = collections.namedtuple('Layers', 'length, layer_count, layer_records, channel_image_data')
+LayerAndMaskInfo = collections.namedtuple('LayerAndMaskData', 'layers global_mask_info tagged_blocks')
 ChannelInfo = collections.namedtuple('ChannelInfo', 'id length')
-LayerMaskData = collections.namedtuple('LayerMaskData', 'top left bottom right default_color flags real_flags real_background')
+MaskData = collections.namedtuple('MaskData', 'top left bottom right default_color flags real_flags real_background')
 LayerBlendingRanges = collections.namedtuple('LayerBlendingRanges', 'composite_ranges channel_ranges')
 
 class ChannelData(collections.namedtuple('ChannelData', 'compression data')):
     def __repr__(self):
-        return "ChannelData(compression=%r, size(data)=%r" % (
+        return "ChannelData(compression=%r, len(data)=%r" % (
             self.compression, len(self.data) if self.data is not None else None
         )
 
     length = read_fmt("I", fp)[0]
     start_position = fp.tell()
 
-    layer_info = _read_layer(fp, encoding)
+    layers = _read_layers(fp, encoding)
 
     # XXX: are tagged blocks really after the layers?
     # XXX: does global mask reading really work?
     consumed_bytes = fp.tell() - start_position
     fp.seek(length-consumed_bytes, 1)
 
-    return layer_info, global_mask_info, tagged_blocks
+    return LayerAndMaskInfo(layers, global_mask_info, tagged_blocks)
 
-def _read_layer(fp, encoding):
+def _read_layers(fp, encoding):
     """
     Reads info about layers.
     """
     length = read_fmt("I", fp)[0]
     layer_count = read_fmt("h", fp)[0]
 
-    layers = []
+    layer_records = []
     for idx in range(abs(layer_count)):
         layer = _read_layer_record(fp, encoding)
-        layers.append(layer)
+        layer_records.append(layer)
 
     channel_image_data = []
-    for layer in layers:
+    for layer in layer_records:
 
         data = _read_channel_image_data(fp, layer)
         channel_image_data.append(data)
 
-    return length, layer_count, layers, channel_image_data
+    return Layers(length, layer_count, layer_records, channel_image_data)
 
 def _read_layer_record(fp, encoding):
     """
     """
     Reads image data for all channels in a layer.
     """
-    w, h = (layer.right - layer.left), (layer.bottom - layer.top)
+    w, h = layer.width(), layer.height()
 
     channel_data = []
 

psd_tools/reader/reader.py

 
 logger = logging.getLogger(__name__)
 
-ParseResult = collections.namedtuple('ParseResult', 'header, color_data, image_resource_blocks, layer_and_mask_info, image_data')
+ParseResult = collections.namedtuple('ParseResult', 'header, color_data, image_resource_blocks, layer_and_mask_data, image_data')
 
 def parse(fp, encoding='latin1'):