1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2009, 2010 Henrik Tidefelt
22 /* Placing this inclusion first could be a dangerous way of avoiding a relevant warning
23 * about possibly conflicting uses of setjmp.h. See libpng/pngconf.h for for information.
28 #include "drawabletypes.h"
29 #include "rasterloaders.h"
30 #include "statetypes.h"
31 #include "autoonoff.h"
38 using namespace Shapes
;
49 Shapes_pnglib_error_fn( png_structp png_ptr
,
50 png_const_charp error_msg
)
52 throw Exceptions::ExternalError( strrefdup( error_msg
) );
56 Shapes_pnglib_warning_fn( png_structp png_ptr
,
57 png_const_charp warn_msg
)
59 std::ostringstream msg
;
60 msg
<< "libpng says " << warn_msg
;
61 WARN_OR_THROW( Exceptions::ExternalError( strrefdup( msg
), true ) );
65 Shapes_pnglib_read_data( png_structp png_ptr
,
69 std::istream
* iFile
= static_cast< std::istream
* >( png_get_io_ptr( png_ptr
) );
70 iFile
->read( reinterpret_cast< char * >( data
), length
);
78 void separateAlpha_bits( const unsigned char * row
, size_t rowBytes
, size_t bitsPerUnit
, std::ostream
& colorDst
, size_t colorUnits
, std::ostream
& alphaDst
, size_t alphaUnits
, const png_color_16
* transparentColor
, char * buf1
, char * buf2
);
80 void separateAlpha( const unsigned char * row
, size_t rowBytes
, std::ostream
& colorDst
, size_t colorUnts
, std::ostream
& alphaDst
, size_t alphaUnits
, const png_color_16
* transparentColor
, char * buf1
, char * buf2
);
81 void applyPalette( const unsigned char * row
, size_t rowBytes
, size_t index_depth
, const png_colorp palette
, const png_bytep trans
, int num_palette
, std::ostream
& colorDst
, std::ostream
& alphaDst
);
89 RefCountPtr
< const Lang::RasterImage
>
90 Shapes::Helpers::RasterLoader_PNG::load( RefCountPtr
< std::ifstream
> & filePtr
, const std::string
& full_filename
, Concrete::Length resolution
, bool override
, const char * core_title
, const Ast::SourceLocation
& callLoc
) const
93 throw Exceptions::BuildRequirement( Interaction::BUILD_REQ_LIBPNG
, core_title
, callLoc
);
96 std::ifstream
& iFile
= *filePtr
;
98 const size_t SIGNATURE_SIZE
= 8;
100 char header
[ SIGNATURE_SIZE
+ 1 ];
101 iFile
.read( header
, SIGNATURE_SIZE
);
102 bool is_png
= png_sig_cmp( reinterpret_cast< png_bytep
>( header
), 0, SIGNATURE_SIZE
) == 0;
105 throw Exceptions::CoreRequirement( "The imported file does not have the magic signature of a PNG file.", core_title
, callLoc
);
109 png_structp png_ptr
= png_create_read_struct( PNG_LIBPNG_VER_STRING
,
110 static_cast< png_voidp
>( 0 ), /* Pointer to additional information passed to error and warning handlers? */
111 Kernel::Shapes_pnglib_error_fn
,
112 Kernel::Shapes_pnglib_warning_fn
);
114 /* We use two error handling mechanisms in parallel, since I don't understand exactly how to use the longjmp function in the uer-defined
115 * error handler Shapes_pnglib_error_fn. Hence, we both use setjmp in case someone uses longjmp, and use try/catch to handle the errors
116 * thrown by Shapes_pnglib_error_fn.
119 png_set_error_fn( png_ptr
,
120 static_cast< png_voidp
>( 0 ), /* Pointer to additional information passed to error and warning handlers? */
121 Kernel::Shapes_pnglib_error_fn
,
122 Kernel::Shapes_pnglib_warning_fn
);
126 throw Exceptions::ExternalError( "Failed to allocate read structure for reading a PNG file." );
129 png_infop info_ptr
= png_create_info_struct( png_ptr
);
132 png_destroy_read_struct( & png_ptr
,
133 static_cast< png_infopp
>( 0 ), static_cast< png_infopp
>( 0 ) );
134 throw Exceptions::ExternalError( "Failed to allocate info structure for reading a PNG file." );
137 png_infop end_info
= png_create_info_struct( png_ptr
);
140 png_destroy_read_struct( & png_ptr
, & info_ptr
,
141 static_cast< png_infopp
>( 0 ) );
142 throw Exceptions::ExternalError( "Failed to allocate end-info structure for reading a PNG file." );
145 if( setjmp( png_jmpbuf( png_ptr
) ) )
147 png_destroy_read_struct( & png_ptr
, & info_ptr
, & end_info
);
148 throw Exceptions::ExternalError( "Caught a longjmp from libpng while reading a PNG file." );
153 png_set_sig_bytes( png_ptr
, SIGNATURE_SIZE
);
155 png_set_read_fn( png_ptr
,
156 static_cast< png_voidp
>( filePtr
.getPtr( ) ),
157 Kernel::Shapes_pnglib_read_data
);
160 png_read_png( png_ptr
, info_ptr
, PNG_TRANSFORM_IDENTITY
, 0 );
166 int interlace_method
;
167 int compression_method
;
170 png_get_IHDR( png_ptr
, info_ptr
,
171 & size_x
, & size_y
, & size_depth
,
172 & color_type
, & interlace_method
, & compression_method
, & filter_method
);
174 RefCountPtr
< const Lang::ColorSpace
> colorSpace
= Lang::THE_COLOR_SPACE_DEVICE_GRAY
; /* Initialize with anything. */
175 bool hasAlpha
= false;
176 bool hasTransparentColor
= false;
177 bool hasPalette
= false;
182 case PNG_COLOR_TYPE_GRAY
:
183 /* Use the default value set above! */
185 case PNG_COLOR_TYPE_RGB
:
186 colorSpace
= Lang::THE_COLOR_SPACE_DEVICE_RGB
;
188 case PNG_COLOR_TYPE_GRAY_ALPHA
:
189 colorSpace
= Lang::THE_COLOR_SPACE_DEVICE_GRAY
;
192 case PNG_COLOR_TYPE_RGB_ALPHA
:
193 colorSpace
= Lang::THE_COLOR_SPACE_DEVICE_RGB
;
196 case PNG_COLOR_TYPE_PALETTE
:
197 /* The palette colors use 8 bit in each of the three channels. */
198 colorSpace
= Lang::THE_COLOR_SPACE_DEVICE_RGB
;
199 png_get_PLTE( png_ptr
, info_ptr
, & palette
, & num_palette
);
203 throw Exceptions::CoreRequirement( "The PNG file was using an unknown color type value.", core_title
, callLoc
);
206 png_bytep tRNS_trans
;
207 png_color_16p tRNS_trans_values
;
208 if( png_get_valid( png_ptr
, info_ptr
, PNG_INFO_tRNS
) != 0 )
212 png_get_tRNS( png_ptr
, info_ptr
, & tRNS_trans
, & tRNS_num_trans
, & tRNS_trans_values
);
215 if( tRNS_num_trans
!= num_palette
)
217 throw Exceptions::CoreRequirement( "The PNG file has a transparency pallete of different size compared to the color palette.", core_title
, callLoc
);
222 hasTransparentColor
= true;
226 size_t components
= colorSpace
->numberOfComponents( );
227 size_t png_channels
= png_get_channels( png_ptr
, info_ptr
);
229 Concrete::Length resolution_x
= resolution
;
230 Concrete::Length resolution_y
= resolution
;
231 if( ! override
&& png_get_valid( png_ptr
, info_ptr
, PNG_INFO_pHYs
) != 0 )
233 png_uint_32 x_pixels_per_unit
;
234 png_uint_32 y_pixels_per_unit
;
236 png_get_pHYs( png_ptr
, info_ptr
, & x_pixels_per_unit
, & y_pixels_per_unit
, & phys_unit_type
);
237 switch( phys_unit_type
)
239 case PNG_RESOLUTION_UNKNOWN
:/* Only aspect ratio. */
241 Concrete::Length tmpRes
= sqrt( static_cast< double >( x_pixels_per_unit
) * static_cast< double >( y_pixels_per_unit
) ) * resolution
;
242 resolution_x
= ( tmpRes
/ x_pixels_per_unit
);
243 resolution_y
= ( tmpRes
/ y_pixels_per_unit
);
246 case PNG_RESOLUTION_METER
:/* Dots per meter. */
248 resolution_x
= ( Concrete::Length( 100/(2.54/72) ) / x_pixels_per_unit
);
249 resolution_y
= ( Concrete::Length( 100/(2.54/72) ) / y_pixels_per_unit
);
253 throw Exceptions::CoreRequirement( "The PNG file was using an unknown density unit value.", core_title
, callLoc
);
257 Lang::RasterImage
* imagePtr
= Lang::RasterImage::newInstance( size_x
, size_y
, hasPalette
? 8 : size_depth
, resolution_x
, resolution_y
, colorSpace
);
258 RefCountPtr
< const Lang::RasterImage
> image
= RefCountPtr
< const Lang::RasterImage
>( RefCountPtr
< const Lang::RasterImage
>( imagePtr
) );
260 RefCountPtr
< SimplePDF::PDF_Stream_out
> maskStream
= RefCountPtr
< SimplePDF::PDF_Stream_out
>( NullPtr
< SimplePDF::PDF_Stream_out
>( ) );
262 SimplePDF::PDF_Stream_out
& stream
= *(imagePtr
->stream( ));
263 stream
[ "Filter" ] = SimplePDF::newName( "FlateDecode" );
266 Lang::RasterImage
* maskPtr
= Lang::RasterImage::newInstance( size_x
, size_y
, hasPalette
? 8 : size_depth
, resolution_x
, resolution_y
, Lang::THE_COLOR_SPACE_DEVICE_GRAY
);
267 imagePtr
->setSoftMask( RefCountPtr
< const Lang::RasterImage
>( RefCountPtr
< const Lang::RasterImage
>( maskPtr
) ) );
268 maskStream
= maskPtr
->stream( );
269 (*maskStream
)[ "Filter" ] = SimplePDF::newName( "FlateDecode" );
271 const SimplePDF::PDF_Version::Version SOFTMASK_VERSION
= SimplePDF::PDF_Version::PDF_1_4
;
272 if( Kernel::the_PDF_version
.greaterOrEqual( SOFTMASK_VERSION
) )
274 stream
[ "SMask" ] = maskPtr
->getResource( );
278 Kernel::the_PDF_version
.message( SOFTMASK_VERSION
, "A PNG alpha channel (soft mask) was ignored." );
282 size_t rowbytes
= png_get_rowbytes( png_ptr
, info_ptr
);
285 PossiblyDeleteOnExit
< char > buf1Deleter
;
286 PossiblyDeleteOnExit
< char > buf2Deleter
;
289 buf1
= new char[ rowbytes
];
290 buf1Deleter
.activate( buf1
);
291 buf2
= new char[ rowbytes
];
292 buf2Deleter
.activate( buf2
);
294 png_bytep
* rowBegin
= png_get_rows( png_ptr
, info_ptr
);
295 for( png_bytep
* srcRow
= rowBegin
; srcRow
< rowBegin
+ size_y
; ++srcRow
)
297 /* Note that image data in PDF starts each row on a fresh byte boundary.
303 Helpers::applyPalette( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, size_depth
, palette
, tRNS_trans
, num_palette
, stream
.data
, maskStream
->data
);
307 std::ostringstream ignore
;
308 Helpers::applyPalette( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, size_depth
, palette
, 0, num_palette
, stream
.data
, ignore
);
313 if( hasTransparentColor
)
315 /* There may be some kind of filler (byte or bits) in the data, that we need to discard. */
321 Helpers::separateAlpha_bits( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, size_depth
, stream
.data
, components
, maskStream
->data
, png_channels
- components
, tRNS_trans_values
, buf1
, buf2
);
324 Helpers::separateAlpha
< png_byte
>( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, stream
.data
, components
, maskStream
->data
, png_channels
- components
, tRNS_trans_values
, buf1
, buf2
);
327 Helpers::separateAlpha
< png_uint_16
>( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, stream
.data
, components
, maskStream
->data
, png_channels
- components
, tRNS_trans_values
, buf1
, buf2
);
330 throw Exceptions::InternalError( "Unexpected color depth in PNG image." );
340 Helpers::separateAlpha_bits( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, size_depth
, stream
.data
, components
, maskStream
->data
, 1, 0, buf1
, buf2
);
343 Helpers::separateAlpha
< png_byte
>( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, stream
.data
, components
, maskStream
->data
, 1, 0, buf1
, buf2
);
346 Helpers::separateAlpha
< png_uint_16
>( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, stream
.data
, components
, maskStream
->data
, 1, 0, buf1
, buf2
);
349 throw Exceptions::InternalError( "Unexpected color depth in PNG image." );
353 else if( png_channels
> components
)
355 /* There is some kind of filler (byte or bits) in the data, that we need to discard. */
356 std::ostringstream ignore
;
362 Helpers::separateAlpha_bits( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, size_depth
, stream
.data
, components
, ignore
, png_channels
- components
, 0, buf1
, buf2
);
365 Helpers::separateAlpha
< png_byte
>( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, stream
.data
, components
, ignore
, png_channels
- components
, 0, buf1
, buf2
);
368 Helpers::separateAlpha
< png_uint_16
>( reinterpret_cast< unsigned char * >( *srcRow
), rowbytes
, stream
.data
, components
, ignore
, png_channels
- components
, 0, buf1
, buf2
);
371 throw Exceptions::InternalError( "Unexpected color depth in PNG image." );
376 stream
.data
.write( reinterpret_cast< char * >( *srcRow
), rowbytes
);
380 /* If everything was OK, we just free allocated resources and return.
382 png_destroy_read_struct( & png_ptr
, & info_ptr
, & end_info
);
386 catch( const Exceptions::Exception
& ball
)
388 png_destroy_read_struct( & png_ptr
, & info_ptr
, & end_info
);
398 Helpers::separateAlpha_bits( const unsigned char * row
, size_t rowBytes
, size_t bitsPerUnit
, std::ostream
& colorDst
, size_t colorUnits
, std::ostream
& alphaDst
, size_t alphaUnits
, const png_color_16
* transparentColor
, char * bufCo
, char * bufAl
)
400 char * dstCo
= bufCo
;
401 char * dstAl
= bufAl
;
403 png_uint_16 transparentColorArray
[3];
404 if( transparentColor
!= 0 )
409 transparentColorArray
[0] = transparentColor
->gray
;
412 transparentColorArray
[0] = transparentColor
->red
;
413 transparentColorArray
[1] = transparentColor
->green
;
414 transparentColorArray
[2] = transparentColor
->blue
;
417 throw Exceptions::InternalError( "Unexpected number of color channels in PNG image alpha bit separation." );
420 const unsigned char * src
= row
;
421 const unsigned char * srcEnd
= row
+ rowBytes
;
422 unsigned char srcByte
= *src
;
424 unsigned char dstByteCo
= 0; /* Initialize to inhibit compiler warnings. */
425 unsigned char dstByteAl
= 0; /* Initialize to inhibit compiler warnings. */
426 unsigned char srcShift
= 8 - bitsPerUnit
;
427 unsigned char dstAvailCo
= 8;
428 unsigned char dstAvailAl
= 8;
430 switch( bitsPerUnit
)
442 throw Exceptions::InternalError( "Unexpected color depth in PNG image alpha bit separation." );
447 const png_uint_16
* transparentColorSrc
= transparentColorArray
;
448 for( size_t i
= 0; i
< colorUnits
; ++i
, ++transparentColorSrc
)
450 dstByteCo
= ( dstByteCo
<< bitsPerUnit
) | ( ( srcByte
>> srcShift
) & mask
);
451 if( transparentColor
!= 0 && ( ( srcByte
>> srcShift
) & mask
) != *transparentColorSrc
)
455 dstAvailCo
-= bitsPerUnit
;
456 if( dstAvailCo
== 0 )
458 *reinterpret_cast< unsigned char * >( dstCo
) = dstByteCo
;
472 srcShift
-= bitsPerUnit
;
474 if( transparentColor
== 0 )
476 for( size_t i
= 0; i
< alphaUnits
; ++i
)
478 dstByteAl
= ( dstByteAl
<< bitsPerUnit
) | ( ( srcByte
>> srcShift
) & mask
);
479 dstAvailAl
-= bitsPerUnit
;
480 if( dstAvailAl
== 0 )
482 *reinterpret_cast< unsigned char * >( dstAl
) = dstByteAl
;
496 srcShift
-= bitsPerUnit
;
501 dstByteAl
= ( dstByteAl
<< bitsPerUnit
) | ( trans
? mask
: 0 );
502 dstAvailAl
-= bitsPerUnit
;
503 if( dstAvailAl
== 0 )
505 *reinterpret_cast< unsigned char * >( dstAl
) = dstByteAl
;
510 /* Just ignore filler data. */
511 for( size_t i
= 0; i
< alphaUnits
; ++i
)
523 srcShift
-= bitsPerUnit
;
531 *reinterpret_cast< unsigned char * >( dstCo
) = ( dstByteCo
<< dstAvailCo
);
536 *reinterpret_cast< unsigned char * >( dstAl
) = ( dstByteAl
<< dstAvailAl
);
540 colorDst
.write( bufCo
, dstCo
- bufCo
);
541 alphaDst
.write( bufAl
, dstAl
- bufAl
);
546 Helpers::separateAlpha( const unsigned char * row
, size_t rowBytes
, std::ostream
& colorDst
, size_t colorUnits
, std::ostream
& alphaDst
, size_t alphaUnits
, const png_color_16
* transparentColor
, char * bufCo
, char * bufAl
)
548 C
* dstCo
= reinterpret_cast< C
* >( bufCo
);
549 C
* dstAl
= reinterpret_cast< C
* >( bufAl
);
553 png_uint_16 transparentColorArray
[3];
554 if( transparentColor
!= 0 )
559 transparentColorArray
[0] = transparentColor
->gray
;
562 transparentColorArray
[0] = transparentColor
->red
;
563 transparentColorArray
[1] = transparentColor
->green
;
564 transparentColorArray
[2] = transparentColor
->blue
;
567 throw Exceptions::InternalError( "Unexpected number of color channels in PNG image alpha bit separation." );
571 const C
* src
= reinterpret_cast< const C
* >( row
);
572 const C
* srcEnd
= reinterpret_cast< const C
* >( row
+ rowBytes
);
573 const C
* tmpEnd
= reinterpret_cast< const C
* >( row
);
574 for( ; src
< srcEnd
; )
577 const png_uint_16
* transparentColorSrc
= transparentColorArray
;
579 tmpEnd
+= colorUnits
;
580 for( ; src
!= tmpEnd
; ++src
, ++dstCo
, ++transparentColorSrc
)
582 *reinterpret_cast< unsigned char * >( dstCo
) = *src
;
583 if( transparentColor
!= 0 && *src
!= *transparentColorSrc
)
588 tmpEnd
+= alphaUnits
;
589 if( transparentColor
== 0 )
591 for( ; src
!= tmpEnd
; ++src
, ++dstAl
)
593 *reinterpret_cast< unsigned char * >( dstAl
) = *src
;
598 *dstAl
= trans
? (~OPAQUE
) : OPAQUE
;
601 /* Just ignore fill bytes. */
605 colorDst
.write( bufCo
, reinterpret_cast< char * >( dstCo
) - bufCo
);
606 alphaDst
.write( bufAl
, reinterpret_cast< char * >( dstAl
) - bufAl
);
610 Helpers::applyPalette( const unsigned char * row
, size_t rowBytes
, size_t index_depth
, const png_colorp palette
, const png_bytep trans
, int num_palette
, std::ostream
& colorDst
, std::ostream
& alphaDst
)
612 switch( index_depth
)
618 const unsigned char * src
= row
;
619 const unsigned char * srcEnd
= row
+ rowBytes
;
620 unsigned char srcByte
= *src
;
622 unsigned char srcShift
= 8 - index_depth
;
624 switch( index_depth
)
636 throw Exceptions::InternalError( "Switch in PNG palette application was out of range." );
640 unsigned char index
= ( srcByte
>> srcShift
) & mask
;
641 if( index
>= num_palette
)
643 throw Exceptions::ExternalError( "PNG palette index is out of bounds." );
645 unsigned char buf
[3];
646 buf
[0] = palette
[ index
].red
;
647 buf
[1] = palette
[ index
].green
;
648 buf
[2] = palette
[ index
].blue
;
649 colorDst
.write( reinterpret_cast< char * >( buf
), 3 );
652 alphaDst
.write( reinterpret_cast< char * >( & ( trans
[ index
] ) ), 1 );
664 srcShift
-= index_depth
;
670 const unsigned char * srcEnd
= row
+ rowBytes
;
671 for( const unsigned char * src
= row
; src
!= srcEnd
; ++src
)
673 if( *src
>= num_palette
)
675 throw Exceptions::ExternalError( "PNG palette index is out of bounds." );
677 unsigned char buf
[3];
678 buf
[0] = palette
[ *src
].red
;
679 buf
[1] = palette
[ *src
].green
;
680 buf
[2] = palette
[ *src
].blue
;
681 colorDst
.write( reinterpret_cast< char * >( buf
), 3 );
684 alphaDst
.write( reinterpret_cast< char * >( & ( trans
[ *src
] ) ), 1 );
690 throw Exceptions::InternalError( "Unexpected depth in PNG palette image (supported values are { 1, 2, 4, 8 })." );