Skip to content

Commit

Permalink
Split psd-tools code into separate file to fix issue with using types (
Browse files Browse the repository at this point in the history
  • Loading branch information
floogulinc authored Sep 16, 2023
1 parent b5e011f commit 1018be5
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 78 deletions.
84 changes: 6 additions & 78 deletions hydrus/core/HydrusPSDHandling.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@

try:

from psd_tools import PSDImage
from psd_tools.constants import Resource, ColorMode, Resource
from psd_tools.api.numpy_io import has_transparency, get_transparency_index
from psd_tools.api.pil_io import get_pil_mode, get_pil_channels, _create_image

from hydrus.core import HydrusPSDTools

PSD_TOOLS_OK = True

except:
Expand All @@ -26,9 +23,7 @@ def PSDHasICCProfile( path: str ):
raise HydrusExceptions.UnsupportedFileException( 'psd_tools unavailable' )


psd = PSDImage.open( path )

return Resource.ICC_PROFILE in psd.image_resources
return HydrusPSDTools.PSDHasICCProfile( path )


def MergedPILImageFromPSD( path: str ) -> PILImage:
Expand All @@ -38,27 +33,7 @@ def MergedPILImageFromPSD( path: str ) -> PILImage:
raise HydrusExceptions.UnsupportedFileException( 'psd_tools unavailable' )


psd = PSDImage.open( path )

#pil_image = psd.topil( apply_icc = False )

if psd.has_preview():

pil_image = convert_image_data_to_pil(psd)

else:

raise HydrusExceptions.UnsupportedFileException('PSD file has no embedded preview!')


if Resource.ICC_PROFILE in psd.image_resources:

icc = psd.image_resources.get_data( Resource.ICC_PROFILE )

pil_image.info[ 'icc_profile' ] = icc


return pil_image
return HydrusPSDTools.MergedPILImageFromPSD( path )


def GenerateThumbnailBytesFromPSDPath( path: str, target_resolution: typing.Tuple[int, int], clip_rect = None ) -> bytes:
Expand All @@ -83,9 +58,7 @@ def GetPSDResolution( path: str ):

raise HydrusExceptions.UnsupportedFileException( 'psd_tools unavailable' )

psd = PSDImage.open( path )

return ( psd.width, psd.height )
return HydrusPSDTools.GetPSDResolution( path )


def GetPSDResolutionFallback( path: str ):
Expand All @@ -102,49 +75,4 @@ def GetPSDResolutionFallback( path: str ):
width: int = struct.unpack( '>L', width_bytes )[0]

return ( width, height )


# modified from psd-tools source:
# https://github.com/psd-tools/psd-tools/blob/main/src/psd_tools/api/pil_io.py

def convert_image_data_to_pil(psd: PSDImage):
alpha = None

channel_data = psd._record.image_data.get_data(psd._record.header)
size = (psd.width, psd.height)

channels = [_create_image(size, c, psd.depth) for c in channel_data]

# has_transparency not quite correct
# see https://github.com/psd-tools/psd-tools/issues/369
# and https://github.com/psd-tools/psd-tools/pull/370
no_alpha = psd._record.layer_and_mask_information.layer_info is not None and psd._record.layer_and_mask_information.layer_info.layer_count > 0

if has_transparency(psd) and not no_alpha:
alpha = channels[get_transparency_index(psd)]

if psd.color_mode == ColorMode.INDEXED:
image = channels[0]
image.putpalette(psd._record.color_mode_data.interleave())
elif psd.color_mode == ColorMode.MULTICHANNEL:
image = channels[0] # Multi-channel mode is a collection of alpha.
else:
mode = get_pil_mode(psd.color_mode)
image = PILImage.merge(mode, channels[:get_pil_channels(mode)])

if not image:
return None

return post_process(image, alpha)


def post_process(image, alpha):
# Fix inverted CMYK.
if image.mode == 'CMYK':
from PIL import ImageChops
image = ImageChops.invert(image)

# In Pillow, alpha channel is only available in RGB or L.
if alpha and image.mode in ('RGB', 'L'):
image.putalpha(alpha)
return image

96 changes: 96 additions & 0 deletions hydrus/core/HydrusPSDTools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from PIL import Image as PILImage

from hydrus.core import HydrusExceptions


from psd_tools import PSDImage
from psd_tools.constants import Resource, ColorMode, Resource
from psd_tools.api.numpy_io import has_transparency, get_transparency_index
from psd_tools.api.pil_io import get_pil_mode, get_pil_channels, _create_image



def PSDHasICCProfile( path: str ):

psd = PSDImage.open( path )

return Resource.ICC_PROFILE in psd.image_resources


def MergedPILImageFromPSD( path: str ) -> PILImage:

psd = PSDImage.open( path )

#pil_image = psd.topil( apply_icc = False )

if psd.has_preview():

pil_image = convert_image_data_to_pil(psd)

else:

raise HydrusExceptions.UnsupportedFileException('PSD file has no embedded preview!')


if Resource.ICC_PROFILE in psd.image_resources:

icc = psd.image_resources.get_data( Resource.ICC_PROFILE )

pil_image.info[ 'icc_profile' ] = icc


return pil_image



def GetPSDResolution( path: str ):

psd = PSDImage.open( path )

return ( psd.width, psd.height )


# modified from psd-tools source:
# https://github.com/psd-tools/psd-tools/blob/main/src/psd_tools/api/pil_io.py

def convert_image_data_to_pil(psd: PSDImage):
alpha = None

channel_data = psd._record.image_data.get_data(psd._record.header)
size = (psd.width, psd.height)

channels = [_create_image(size, c, psd.depth) for c in channel_data]

# has_transparency not quite correct
# see https://github.com/psd-tools/psd-tools/issues/369
# and https://github.com/psd-tools/psd-tools/pull/370
no_alpha = psd._record.layer_and_mask_information.layer_info is not None and psd._record.layer_and_mask_information.layer_info.layer_count > 0

if has_transparency(psd) and not no_alpha:
alpha = channels[get_transparency_index(psd)]

if psd.color_mode == ColorMode.INDEXED:
image = channels[0]
image.putpalette(psd._record.color_mode_data.interleave())
elif psd.color_mode == ColorMode.MULTICHANNEL:
image = channels[0] # Multi-channel mode is a collection of alpha.
else:
mode = get_pil_mode(psd.color_mode)
image = PILImage.merge(mode, channels[:get_pil_channels(mode)])

if not image:
return None

return post_process(image, alpha)


def post_process(image, alpha):
# Fix inverted CMYK.
if image.mode == 'CMYK':
from PIL import ImageChops
image = ImageChops.invert(image)

# In Pillow, alpha channel is only available in RGB or L.
if alpha and image.mode in ('RGB', 'L'):
image.putalpha(alpha)
return image

0 comments on commit 1018be5

Please sign in to comment.