2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								/*
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								 *  Copyright  ( c )  2023 ,  Nico  Weber  < thakis @ chromium . org > 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								 * 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								 *  SPDX - License - Identifier :  BSD - 2 - Clause 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								 */ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  <LibCore/ArgsParser.h> 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  <LibCore/File.h> 
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								# include  <LibCore/MappedFile.h> 
 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												ICC+image: Add conversion between color spaces for images :^)
For now, only for color spaces that are supported by Profile::to_pcs()
and Profile::from_pcs(), which currently means that all matrix profiles
(but not LUT profiles) in the source color space work, and that
matrix profiles with parametric curves in the destination color
space work.
This adds Profile::convert_image(Bitmap, source_profile), and
adds a `--convert-to-color-profile file.icc` flag to `image`.
It only takes a file path, so to use it with the built-in
sRGB profile, you have to write it to a file first:
% Build/lagom/icc -n sRGB --reencode-to serenity-sRGB.icc
`image` by default writes the source image's color profile
to the output image, and most image viewers display images
looking at the profile.
For example, take `Seven_Coloured_Pencils_(rg-switch_sRGB).jpg`
from https://commons.wikimedia.org/wiki/User:Colin/BrowserTest.
It looks normal in image viewers because they apply the unusual
profile embedded in the profile. But if you run
% Build/lagom/image -o huh.png --strip-color-profile \
    'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
and then look at huh.png, you can see how the image's colors
look like when interpreted as sRGB (which is the color space
PNG data is in if the PNG doesn't store an embedded profile).
If you now run
% Build/lagom/image -o wow.png \
    --convert-to-color-profile serenity-sRGB.icc --strip-color-profile \
    'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
this will convert that image to sRGB, but then not write
the profile to the output image (verify with `Build/lagom/icc wow.png`).
It will look correct in image viewers, since they display PNGs without
an embedded color profile as sRGB.
(This works because 'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
contains a matrix profile, and Serenity's built-in sRGB profile
uses a matrix profile with a parametric curve.)
											 
										 
										
											2023-05-01 15:42:29 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ICC/Profile.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-21 14:58:06 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/BMPWriter.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-03 09:59:02 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/GIFWriter.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-21 14:58:06 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/ImageDecoder.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-06-20 16:33:51 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/JPEGWriter.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-21 14:58:06 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/PNGWriter.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/WebPSharedLossless.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-03 15:05:09 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								# include  <LibGfx/ImageFormats/WebPWriter.h> 
 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								using  AnyBitmap  =  Variant < RefPtr < Gfx : : Bitmap > ,  RefPtr < Gfx : : CMYKBitmap > > ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:51:20 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								struct  LoadedImage  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    Gfx : : NaturalFrameFormat  internal_format ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    AnyBitmap  bitmap ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:51:20 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    Optional < ReadonlyBytes >  icc_data ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:05:20 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < LoadedImage >  load_image ( RefPtr < Gfx : : ImageDecoder >  const &  decoder ,  int  frame_index ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto  internal_format  =  decoder - > natural_frame_format ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  bitmap  =  TRY ( [ & ] ( )  - >  ErrorOr < AnyBitmap >  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        switch  ( internal_format )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        case  Gfx : : NaturalFrameFormat : : RGB : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        case  Gfx : : NaturalFrameFormat : : Grayscale : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        case  Gfx : : NaturalFrameFormat : : Vector : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  TRY ( decoder - > frame ( frame_index ) ) . image ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        case  Gfx : : NaturalFrameFormat : : CMYK : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            return  RefPtr ( TRY ( decoder - > cmyk_frame ( ) ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        VERIFY_NOT_REACHED ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } ( ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  LoadedImage  {  internal_format ,  move ( bitmap ) ,  TRY ( decoder - > icc_data ( ) )  } ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:05:20 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-02 19:41:08 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < void >  invert_cmyk ( LoadedImage &  image ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! image . bitmap . has < RefPtr < Gfx : : CMYKBitmap > > ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can't --invert-cmyk with RGB bitmaps " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-02 19:41:08 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto &  frame  =  image . bitmap . get < RefPtr < Gfx : : CMYKBitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  ( auto &  pixel  :  * frame )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        pixel . c  =  ~ pixel . c ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        pixel . m  =  ~ pixel . m ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        pixel . y  =  ~ pixel . y ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        pixel . k  =  ~ pixel . k ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < void >  crop_image ( LoadedImage &  image ,  Gfx : : IntRect  const &  rect ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! image . bitmap . has < RefPtr < Gfx : : Bitmap > > ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can't --crop CMYK bitmaps yet " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto &  frame  =  image . bitmap . get < RefPtr < Gfx : : Bitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    frame  =  TRY ( frame - > cropped ( rect ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:40:14 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < void >  move_alpha_to_rgb ( LoadedImage &  image ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( ! image . bitmap . has < RefPtr < Gfx : : Bitmap > > ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can't --move-alpha-to-rgb with CMYK bitmaps " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto &  frame  =  image . bitmap . get < RefPtr < Gfx : : Bitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    switch  ( frame - > format ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : Invalid : 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can't --move-alpha-to-rgb with invalid bitmaps " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : RGBA8888 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // No image decoder currently produces bitmaps with this format.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // If that ever changes, preferrably fix the image decoder to use BGRA8888 instead :)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // If there's a good reason for not doing that, implement support for this, I suppose.
 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " --move-alpha-to-rgb not implemented for RGBA8888 " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : BGRA8888 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : BGRx8888 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // FIXME: If BitmapFormat::Gray8 existed (and image encoders made use of it to write grayscale images), we could use it here.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        for  ( auto &  pixel  :  * frame )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            u8  alpha  =  pixel  > >  24 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            pixel  =  0xff000000  |  ( alpha  < <  16 )  |  ( alpha  < <  8 )  |  alpha ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:40:14 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < void >  strip_alpha ( LoadedImage &  image ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( ! image . bitmap . has < RefPtr < Gfx : : Bitmap > > ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can't --strip-alpha with CMYK bitmaps " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto &  frame  =  image . bitmap . get < RefPtr < Gfx : : Bitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    switch  ( frame - > format ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : Invalid : 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can't --strip-alpha with invalid bitmaps " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : RGBA8888 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // No image decoder currently produces bitmaps with this format.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // If that ever changes, preferrably fix the image decoder to use BGRA8888 instead :)
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // If there's a good reason for not doing that, implement support for this, I suppose.
 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " --strip-alpha not implemented for RGBA8888 " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:28:17 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : BGRA8888 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    case  Gfx : : BitmapFormat : : BGRx8888 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        frame - > strip_alpha_channel ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:57:16 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < OwnPtr < Core : : MappedFile > >  convert_image_profile ( LoadedImage &  image ,  StringView  convert_color_profile_path ,  OwnPtr < Core : : MappedFile >  maybe_source_icc_file ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( ! image . icc_data . has_value ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " No source color space embedded in image. Pass one with --assign-color-profile. " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:57:16 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  source_icc_file  =  move ( maybe_source_icc_file ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  source_icc_data  =  image . icc_data . value ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  icc_file  =  TRY ( Core : : MappedFile : : map ( convert_color_profile_path ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    image . icc_data  =  icc_file - > bytes ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  source_profile  =  TRY ( Gfx : : ICC : : Profile : : try_load_from_externally_owned_memory ( source_icc_data ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  destination_profile  =  TRY ( Gfx : : ICC : : Profile : : try_load_from_externally_owned_memory ( icc_file - > bytes ( ) ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 11:45:32 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( destination_profile - > data_color_space ( )  ! =  Gfx : : ICC : : ColorSpace : : RGB ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Can only convert to RGB at the moment, but destination color space is not RGB " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 11:45:32 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( image . bitmap . has < RefPtr < Gfx : : CMYKBitmap > > ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( source_profile - > data_color_space ( )  ! =  Gfx : : ICC : : ColorSpace : : CMYK ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            return  Error : : from_string_literal ( " Source image data is CMYK but source color space is not CMYK " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 11:45:32 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        auto &  cmyk_frame  =  image . bitmap . get < RefPtr < Gfx : : CMYKBitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        auto  rgb_frame  =  TRY ( Gfx : : Bitmap : : create ( Gfx : : BitmapFormat : : BGRx8888 ,  cmyk_frame - > size ( ) ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        TRY ( destination_profile - > convert_cmyk_image ( * rgb_frame ,  * cmyk_frame ,  * source_profile ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        image . bitmap  =  RefPtr ( move ( rgb_frame ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        image . internal_format  =  Gfx : : NaturalFrameFormat : : RGB ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    }  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        // FIXME: This likely wrong for grayscale images because they've been converted to
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        //        RGB at this point, but their embedded color profile is still for grayscale.
 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        auto &  frame  =  image . bitmap . get < RefPtr < Gfx : : Bitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        TRY ( destination_profile - > convert_image ( * frame ,  * source_profile ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:57:16 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  icc_file ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-06-17 19:42:11 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < void >  save_image ( LoadedImage &  image ,  StringView  out_path ,  u8  jpeg_quality ,  Optional < unsigned >  webp_allowed_transforms ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 07:30:23 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto  stream  =  [ out_path ] ( )  - >  ErrorOr < NonnullOwnPtr < Core : : OutputBufferedFile > >  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        auto  output_stream  =  TRY ( Core : : File : : open ( out_path ,  Core : : File : : OpenMode : : Write ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  Core : : OutputBufferedFile : : create ( move ( output_stream ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-31 20:55:51 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto &  frame  =  image . bitmap . get < RefPtr < Gfx : : Bitmap > > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-05-03 09:59:02 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( out_path . ends_with ( " .gif " sv ,  CaseSensitivity : : CaseInsensitive ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        TRY ( Gfx : : GIFWriter : : encode ( * TRY ( stream ( ) ) ,  * frame ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:07:31 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( out_path . ends_with ( " .jpg " sv ,  CaseSensitivity : : CaseInsensitive )  | |  out_path . ends_with ( " .jpeg " sv ,  CaseSensitivity : : CaseInsensitive ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 07:30:23 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        TRY ( Gfx : : JPEGWriter : : encode ( * TRY ( stream ( ) ) ,  * frame ,  {  . icc_data  =  image . icc_data ,  . quality  =  jpeg_quality  } ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:07:31 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-03 15:05:09 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( out_path . ends_with ( " .webp " sv ,  CaseSensitivity : : CaseInsensitive ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        Gfx : : WebPWriter : : Options  options ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        options . icc_data  =  image . icc_data ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( webp_allowed_transforms . has_value ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            options . vp8l_options . allowed_transforms  =  webp_allowed_transforms . value ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        TRY ( Gfx : : WebPWriter : : encode ( * TRY ( stream ( ) ) ,  * frame ,  options ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-03 15:05:09 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:07:31 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    ByteBuffer  bytes ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( out_path . ends_with ( " .bmp " sv ,  CaseSensitivity : : CaseInsensitive ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        bytes  =  TRY ( Gfx : : BMPWriter : : encode ( * frame ,  {  . icc_data  =  image . icc_data  } ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    }  else  if  ( out_path . ends_with ( " .png " sv ,  CaseSensitivity : : CaseInsensitive ) )  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:31:56 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        bytes  =  TRY ( Gfx : : PNGWriter : : encode ( * frame ,  {  . icc_data  =  image . icc_data  } ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    }  else  { 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " can only write .bmp, .gif, .jpg, .png, and .webp " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 07:30:23 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    TRY ( TRY ( stream ( ) ) - > write_until_depleted ( bytes ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:07:31 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 10:00:37 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    return  { } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								struct  Options  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    StringView  in_path ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    StringView  out_path ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-05-24 09:00:50 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    bool  no_output  =  false ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-05-06 07:09:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    int  frame_index  =  0 ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-02 19:41:08 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    bool  invert_cmyk  =  false ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    Optional < Gfx : : IntRect >  crop_rect ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-06-13 19:33:26 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    bool  move_alpha_to_rgb  =  false ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-06-13 19:26:01 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    bool  strip_alpha  =  false ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-16 09:10:48 +01:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    StringView  assign_color_profile_path ; 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												ICC+image: Add conversion between color spaces for images :^)
For now, only for color spaces that are supported by Profile::to_pcs()
and Profile::from_pcs(), which currently means that all matrix profiles
(but not LUT profiles) in the source color space work, and that
matrix profiles with parametric curves in the destination color
space work.
This adds Profile::convert_image(Bitmap, source_profile), and
adds a `--convert-to-color-profile file.icc` flag to `image`.
It only takes a file path, so to use it with the built-in
sRGB profile, you have to write it to a file first:
% Build/lagom/icc -n sRGB --reencode-to serenity-sRGB.icc
`image` by default writes the source image's color profile
to the output image, and most image viewers display images
looking at the profile.
For example, take `Seven_Coloured_Pencils_(rg-switch_sRGB).jpg`
from https://commons.wikimedia.org/wiki/User:Colin/BrowserTest.
It looks normal in image viewers because they apply the unusual
profile embedded in the profile. But if you run
% Build/lagom/image -o huh.png --strip-color-profile \
    'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
and then look at huh.png, you can see how the image's colors
look like when interpreted as sRGB (which is the color space
PNG data is in if the PNG doesn't store an embedded profile).
If you now run
% Build/lagom/image -o wow.png \
    --convert-to-color-profile serenity-sRGB.icc --strip-color-profile \
    'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
this will convert that image to sRGB, but then not write
the profile to the output image (verify with `Build/lagom/icc wow.png`).
It will look correct in image viewers, since they display PNGs without
an embedded color profile as sRGB.
(This works because 'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
contains a matrix profile, and Serenity's built-in sRGB profile
uses a matrix profile with a parametric curve.)
											 
										 
										
											2023-05-01 15:42:29 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    StringView  convert_color_profile_path ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-04-30 17:29:48 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    bool  strip_color_profile  =  false ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-07-02 00:18:52 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    u8  quality  =  75 ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    Optional < unsigned >  webp_allowed_transforms ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								} ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-07-02 00:18:52 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								template < class  T > 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  ErrorOr < Vector < T > >  parse_comma_separated_numbers ( StringView  rect_string ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  parts  =  rect_string . split_view ( ' , ' ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Vector < T >  part_numbers ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  ( size_t  i  =  0 ;  i  <  parts . size ( ) ;  + + i )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        auto  part  =  parts [ i ] . to_number < T > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( ! part . has_value ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            return  Error : : from_string_literal ( " comma-separated parts must be numbers " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        TRY ( part_numbers . try_append ( part . value ( ) ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  part_numbers ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								static  ErrorOr < Gfx : : IntRect >  parse_rect_string ( StringView  rect_string ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  numbers  =  TRY ( parse_comma_separated_numbers < i32 > ( rect_string ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    if  ( numbers . size ( )  ! =  4 ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " rect must have 4 comma-separated parts " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    return  Gfx : : IntRect  {  numbers [ 0 ] ,  numbers [ 1 ] ,  numbers [ 2 ] ,  numbers [ 3 ]  } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < unsigned >  parse_webp_allowed_transforms_string ( StringView  string ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    unsigned  allowed_transforms  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    for  ( StringView  part  :  string . split_view ( ' , ' ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        if  ( part  = =  " predictor "  | |  part  = =  " p " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            allowed_transforms  | =  1  < <  Gfx : : PREDICTOR_TRANSFORM ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        else  if  ( part  = =  " color "  | |  part  = =  " c " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            allowed_transforms  | =  1  < <  Gfx : : COLOR_TRANSFORM ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        else  if  ( part  = =  " subtract-green "  | |  part  = =  " sg " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            allowed_transforms  | =  1  < <  Gfx : : SUBTRACT_GREEN_TRANSFORM ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        else  if  ( part  = =  " color-indexing "  | |  part  = =  " ci " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								            allowed_transforms  | =  1  < <  Gfx : : COLOR_INDEXING_TRANSFORM ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        else 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								            return  Error : : from_string_literal ( " unknown WebP transform; valid values: predictor, p, color, c, subtract-green, sg, color-indexing, ci " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  allowed_transforms ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								static  ErrorOr < Options >  parse_options ( Main : : Arguments  arguments ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Options  options ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    Core : : ArgsParser  args_parser ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_positional_argument ( options . in_path ,  " Path to input image file " ,  " FILE " ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . out_path ,  " Path to output image file " ,  " output " ,  ' o ' ,  " FILE " ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . no_output ,  " Do not write output (only useful for benchmarking image decoding) " ,  " no-output " ,  { } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . frame_index ,  " Which frame of a multi-frame input image (0-based) " ,  " frame-index " ,  { } ,  " INDEX " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-02 19:41:08 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . invert_cmyk ,  " Invert CMYK channels " ,  " invert-cmyk " ,  { } ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    StringView  crop_rect_string ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( crop_rect_string ,  " Crop to a rectangle " ,  " crop " ,  { } ,  " x,y,w,h " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . move_alpha_to_rgb ,  " Copy alpha channel to rgb, clear alpha " ,  " move-alpha-to-rgb " ,  { } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . strip_alpha ,  " Remove alpha channel " ,  " strip-alpha " ,  { } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . assign_color_profile_path ,  " Load color profile from file and assign it to output image " ,  " assign-color-profile " ,  { } ,  " FILE " ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . convert_color_profile_path ,  " Load color profile from file and convert output image from current profile to loaded profile " ,  " convert-to-color-profile " ,  { } ,  " FILE " ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . strip_color_profile ,  " Do not write color profile to output " ,  " strip-color-profile " ,  { } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( options . quality ,  " Quality used for the JPEG encoder, the default value is 75 on a scale from 0 to 100 " ,  " quality " ,  { } ,  { } ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    StringView  webp_allowed_transforms  =  " default " sv ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . add_option ( webp_allowed_transforms ,  " Comma-separated list of allowed transforms (predictor,p,color,c,subtract-green,sg,color-indexing,ci) for WebP output (default: all allowed) " ,  " webp-allowed-transforms " ,  { } ,  { } ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								    args_parser . parse ( arguments ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . out_path . is_empty ( )  ^  options . no_output ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " exactly one of -o or --no-output is required " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( ! crop_rect_string . is_empty ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        options . crop_rect  =  TRY ( parse_rect_string ( crop_rect_string ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-05-29 18:24:41 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( webp_allowed_transforms  ! =  " default " sv ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        options . webp_allowed_transforms  =  TRY ( parse_webp_allowed_transforms_string ( webp_allowed_transforms ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    return  options ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								ErrorOr < int >  serenity_main ( Main : : Arguments  arguments ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								{ 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:40:14 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    Options  options  =  TRY ( parse_options ( arguments ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    auto  file  =  TRY ( Core : : MappedFile : : map ( options . in_path ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-03-04 18:07:43 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    auto  decoder  =  TRY ( Gfx : : ImageDecoder : : try_create_for_raw_bytes ( file - > bytes ( ) ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:23:38 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( ! decoder ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-09-05 15:06:15 +04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  Error : : from_string_literal ( " Could not find decoder for input file " ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    LoadedImage  image  =  TRY ( load_image ( * decoder ,  options . frame_index ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-06-13 19:26:01 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-02 19:41:08 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . invert_cmyk ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        TRY ( invert_cmyk ( image ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-03-20 08:14:22 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . crop_rect . has_value ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        TRY ( crop_image ( image ,  options . crop_rect . value ( ) ) ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . move_alpha_to_rgb ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:40:14 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        TRY ( move_alpha_to_rgb ( image ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-06-13 19:33:26 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . strip_alpha ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:40:14 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        TRY ( strip_alpha ( image ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-09-26 00:54:34 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    OwnPtr < Core : : MappedFile >  icc_file ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( ! options . assign_color_profile_path . is_empty ( ) )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        icc_file  =  TRY ( Core : : MappedFile : : map ( options . assign_color_profile_path ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:51:20 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        image . icc_data  =  icc_file - > bytes ( ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-16 09:10:48 +01:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( ! options . convert_color_profile_path . is_empty ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								        icc_file  =  TRY ( convert_image_profile ( image ,  options . convert_color_profile_path ,  move ( icc_file ) ) ) ; 
							 
						 
					
						
							
								
									
										
											 
										 
										
											
												ICC+image: Add conversion between color spaces for images :^)
For now, only for color spaces that are supported by Profile::to_pcs()
and Profile::from_pcs(), which currently means that all matrix profiles
(but not LUT profiles) in the source color space work, and that
matrix profiles with parametric curves in the destination color
space work.
This adds Profile::convert_image(Bitmap, source_profile), and
adds a `--convert-to-color-profile file.icc` flag to `image`.
It only takes a file path, so to use it with the built-in
sRGB profile, you have to write it to a file first:
% Build/lagom/icc -n sRGB --reencode-to serenity-sRGB.icc
`image` by default writes the source image's color profile
to the output image, and most image viewers display images
looking at the profile.
For example, take `Seven_Coloured_Pencils_(rg-switch_sRGB).jpg`
from https://commons.wikimedia.org/wiki/User:Colin/BrowserTest.
It looks normal in image viewers because they apply the unusual
profile embedded in the profile. But if you run
% Build/lagom/image -o huh.png --strip-color-profile \
    'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
and then look at huh.png, you can see how the image's colors
look like when interpreted as sRGB (which is the color space
PNG data is in if the PNG doesn't store an embedded profile).
If you now run
% Build/lagom/image -o wow.png \
    --convert-to-color-profile serenity-sRGB.icc --strip-color-profile \
    'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
this will convert that image to sRGB, but then not write
the profile to the output image (verify with `Build/lagom/icc wow.png`).
It will look correct in image viewers, since they display PNGs without
an embedded color profile as sRGB.
(This works because 'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
contains a matrix profile, and Serenity's built-in sRGB profile
uses a matrix profile with a parametric curve.)
											 
										 
										
											2023-05-01 15:42:29 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . strip_color_profile ) 
							 
						 
					
						
							
								
									
										
										
										
											2024-01-24 09:51:20 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        image . icc_data . clear ( ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-16 09:02:31 +01:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-02-01 21:38:50 -05:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    if  ( options . no_output ) 
							 
						 
					
						
							
								
									
										
										
										
											2023-05-24 09:00:50 -04:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								        return  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2024-06-17 19:42:11 +02:00 
										
									 
								 
							 
							
								
									
										 
									 
								
							 
							
								 
							 
							
							
								    TRY ( save_image ( image ,  options . out_path ,  options . quality ,  options . webp_allowed_transforms ) ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-11 19:33:17 -05:00 
										
									 
								 
							 
							
								
							 
							
								 
							 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								    return  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							 
							
							
								}