From 7472f22ae3d3124d62c6122748d98451ba4d40fa Mon Sep 17 00:00:00 2001 From: Paul Friederichsen Date: Sat, 22 Jul 2023 18:58:48 -0500 Subject: [PATCH] Handle ICC profiles for PSDs --- hydrus/client/ClientFiles.py | 30 ++++++++++++++------ hydrus/client/importing/ClientImportFiles.py | 12 ++++++-- hydrus/core/HydrusConstants.py | 2 +- hydrus/core/HydrusImageHandling.py | 2 ++ hydrus/core/HydrusPSDHandling.py | 23 +++++++++++++-- 5 files changed, 54 insertions(+), 15 deletions(-) diff --git a/hydrus/client/ClientFiles.py b/hydrus/client/ClientFiles.py index d3b2727c..39d7f711 100644 --- a/hydrus/client/ClientFiles.py +++ b/hydrus/client/ClientFiles.py @@ -6,7 +6,7 @@ import threading import time import typing -from hydrus.core import HydrusConstants as HC +from hydrus.core import HydrusConstants as HC, HydrusPSDHandling from hydrus.core import HydrusData from hydrus.core import HydrusExceptions from hydrus.core import HydrusFileHandling @@ -2006,22 +2006,34 @@ class FilesMaintenanceManager( object ): if mime not in HC.FILES_THAT_CAN_HAVE_ICC_PROFILE: return False - try: path = self._controller.client_files_manager.GetFilePath( hash, mime ) - try: + if mime == HC.APPLICATION_PSD: + + try: + + has_icc_profile = HydrusPSDHandling.PSDHasICCProfile(path) + + except: + + return None - pil_image = HydrusImageHandling.RawOpenPILImage( path ) - - except: - - return None + else: + + try: + + pil_image = HydrusImageHandling.RawOpenPILImage( path ) + + except: + + return None - has_icc_profile = HydrusImageHandling.HasICCProfile( pil_image ) + has_icc_profile = HydrusImageHandling.HasICCProfile( pil_image ) + additional_data = has_icc_profile diff --git a/hydrus/client/importing/ClientImportFiles.py b/hydrus/client/importing/ClientImportFiles.py index 3afb57d6..c9bdd06a 100644 --- a/hydrus/client/importing/ClientImportFiles.py +++ b/hydrus/client/importing/ClientImportFiles.py @@ -1,6 +1,6 @@ import typing -from hydrus.core import HydrusConstants as HC +from hydrus.core import HydrusConstants as HC, HydrusPSDHandling from hydrus.core import HydrusData from hydrus.core import HydrusExceptions from hydrus.core import HydrusFileHandling @@ -435,9 +435,15 @@ class FileImportJob( object ): try: - pil_image = HydrusImageHandling.RawOpenPILImage( self._temp_path ) + if mime == HC.APPLICATION_PSD: + + has_icc_profile = HydrusPSDHandling.PSDHasICCProfile( self._temp_path ) + + else: - has_icc_profile = HydrusImageHandling.HasICCProfile( pil_image ) + pil_image = HydrusImageHandling.RawOpenPILImage( self._temp_path ) + + has_icc_profile = HydrusImageHandling.HasICCProfile( pil_image ) except: diff --git a/hydrus/core/HydrusConstants.py b/hydrus/core/HydrusConstants.py index 126d28e3..9f59bf38 100644 --- a/hydrus/core/HydrusConstants.py +++ b/hydrus/core/HydrusConstants.py @@ -770,7 +770,7 @@ APPLICATIONS_WITH_THUMBNAILS = set({ IMAGE_SVG, APPLICATION_FLASH, APPLICATION_C MIMES_WITH_THUMBNAILS = set( IMAGES ).union( ANIMATIONS ).union( VIDEO ).union( APPLICATIONS_WITH_THUMBNAILS ) -FILES_THAT_CAN_HAVE_ICC_PROFILE = { IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_TIFF } +FILES_THAT_CAN_HAVE_ICC_PROFILE = { IMAGE_JPEG, IMAGE_PNG, IMAGE_GIF, IMAGE_TIFF, APPLICATION_PSD } FILES_THAT_CAN_HAVE_EXIF = { IMAGE_JPEG, IMAGE_TIFF, IMAGE_PNG, IMAGE_WEBP } # images and animations that PIL can handle diff --git a/hydrus/core/HydrusImageHandling.py b/hydrus/core/HydrusImageHandling.py index 7ddd4efb..61aa3657 100644 --- a/hydrus/core/HydrusImageHandling.py +++ b/hydrus/core/HydrusImageHandling.py @@ -302,6 +302,8 @@ def GenerateNumPyImage( path, mime, force_pil = False ) -> numpy.array: HydrusData.ShowText( 'Loading PSD' ) pil_image = HydrusPSDHandling.MergedPILImageFromPSD( path ) + + pil_image = DequantizePILImage( pil_image ) numpy_image = GenerateNumPyImageFromPILImage( pil_image ) diff --git a/hydrus/core/HydrusPSDHandling.py b/hydrus/core/HydrusPSDHandling.py index 7d3c7cf0..f8d82c82 100644 --- a/hydrus/core/HydrusPSDHandling.py +++ b/hydrus/core/HydrusPSDHandling.py @@ -8,6 +8,7 @@ from hydrus.core import HydrusImageHandling try: from psd_tools import PSDImage + from psd_tools.constants import Resource PSD_TOOLS_OK = True @@ -15,6 +16,16 @@ except: PSD_TOOLS_OK = False +def PSDHasICCProfile(path: str): + + if not PSD_TOOLS_OK: + + raise Exception( 'psd_tools unavailable' ) + + psd = PSDImage.open(path) + + return Resource.ICC_PROFILE in psd.image_resources + def MergedPILImageFromPSD(path: str) -> PILImage: @@ -24,11 +35,12 @@ def MergedPILImageFromPSD(path: str) -> PILImage: psd = PSDImage.open(path) - pil_image = psd.topil() + + pil_image = psd.topil(apply_icc = False) 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(HydrusImageHandling.PILImageHasTransparency(pil_image) and no_alpha): + if HydrusImageHandling.PILImageHasTransparency(pil_image) and no_alpha: # merged image from psd-tools has transparency when it shouldn't # see https://github.com/psd-tools/psd-tools/issues/369 # and https://github.com/psd-tools/psd-tools/pull/370 @@ -37,6 +49,13 @@ def MergedPILImageFromPSD(path: str) -> PILImage: # that has to happen for the thumbnail anyway. pil_image = pil_image.convert("RGB") + + 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