2016-02-17 10:52:48 +00:00
/* png.c - Handles output to PNG file */
/*
libzint - the open source barcode library
2021-02-26 20:45:08 +00:00
Copyright ( C ) 2009 - 2021 Robin Stuart < rstuart114 @ gmail . com >
2016-02-17 10:52:48 +00:00
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
2017-10-23 21:37:52 +02:00
1. Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2016-02-17 10:52:48 +00:00
2. Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
2017-10-23 21:37:52 +02:00
documentation and / or other materials provided with the distribution .
2016-02-17 10:52:48 +00:00
3. Neither the name of the project nor the names of its contributors
may be used to endorse or promote products derived from this software
2017-10-23 21:37:52 +02:00
without specific prior written permission .
2016-02-17 10:52:48 +00:00
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND
ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
2017-10-23 21:37:52 +02:00
OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
2016-02-17 10:52:48 +00:00
SUCH DAMAGE .
2016-02-20 10:50:15 +00:00
*/
2019-12-19 00:37:55 +00:00
/* vim: set ts=4 sw=4 et : */
2016-02-17 10:52:48 +00:00
2021-06-10 11:15:39 +01:00
# ifndef NO_PNG
# include <errno.h>
2016-02-17 10:52:48 +00:00
# include <stdio.h>
# ifdef _MSC_VER
# include <fcntl.h>
# include <io.h>
2016-11-05 21:06:58 +00:00
# include <malloc.h>
2016-02-17 10:52:48 +00:00
# endif
# include "common.h"
# include <png.h>
# include <zlib.h>
# include <setjmp.h>
2016-07-18 20:14:56 +01:00
2021-07-26 15:29:05 +01:00
/* Note if change this need to change "backend/tests/test_png.c" definition also */
struct wpng_error_type {
struct zint_symbol * symbol ;
2016-02-17 10:52:48 +00:00
jmp_buf jmpbuf ;
} ;
2021-07-26 15:29:05 +01:00
STATIC_UNLESS_ZINT_TEST void wpng_error_handler ( png_structp png_ptr , png_const_charp msg ) {
struct wpng_error_type * wpng_error_ptr ;
2016-02-17 10:52:48 +00:00
2021-07-26 15:29:05 +01:00
wpng_error_ptr = ( struct wpng_error_type * ) png_get_error_ptr ( png_ptr ) ;
if ( wpng_error_ptr = = NULL ) {
2016-02-20 10:50:15 +00:00
/* we are completely hosed now */
2021-07-26 15:29:05 +01:00
fprintf ( stderr , " Error 636: libpng error: %s \n " , msg ? msg : " <NULL> " ) ;
fprintf ( stderr , " Error 637: jmpbuf not recoverable, terminating \n " ) ;
2016-02-17 10:52:48 +00:00
fflush ( stderr ) ;
2021-07-26 15:29:05 +01:00
return ; /* libpng will call abort() */
2016-02-17 10:52:48 +00:00
}
2021-07-26 15:29:05 +01:00
sprintf ( wpng_error_ptr - > symbol - > errtxt , " 635: libpng error: %.60s " , msg ? msg : " <NULL> " ) ;
longjmp ( wpng_error_ptr - > jmpbuf , 1 ) ;
2016-02-17 10:52:48 +00:00
}
2021-02-26 20:45:08 +00:00
/* Guestimate best compression strategy */
static int guess_compression_strategy ( struct zint_symbol * symbol , unsigned char * pixelbuf ) {
( void ) pixelbuf ;
/* TODO: Do properly */
/* It seems the best choice for typical barcode pngs is one of Z_DEFAULT_STRATEGY and Z_FILTERED */
/* Some guesses */
if ( symbol - > symbology = = BARCODE_MAXICODE ) {
return Z_DEFAULT_STRATEGY ;
}
if ( symbol - > symbology = = BARCODE_AZTEC & & symbol - > bitmap_width < = 30 ) {
return Z_DEFAULT_STRATEGY ;
}
/* Z_FILTERED seems to work better for slightly more barcodes+data so default to that */
return Z_FILTERED ;
}
2020-11-01 18:32:55 +00:00
INTERNAL int png_pixel_plot ( struct zint_symbol * symbol , unsigned char * pixelbuf ) {
2021-07-26 15:29:05 +01:00
struct wpng_error_type wpng_error ;
FILE * outfile ;
2016-02-20 10:50:15 +00:00
png_structp png_ptr ;
png_infop info_ptr ;
2021-02-26 20:45:08 +00:00
int i ;
2020-11-01 18:32:55 +00:00
int row , column ;
2021-02-26 20:45:08 +00:00
png_color bg , fg ;
unsigned char bg_alpha , fg_alpha ;
unsigned char map [ 128 ] ;
png_color palette [ 32 ] ;
2021-03-11 11:59:30 +00:00
int num_palette ;
2021-02-26 20:45:08 +00:00
unsigned char trans_alpha [ 32 ] ;
2021-03-11 11:59:30 +00:00
int num_trans ;
2021-02-26 20:45:08 +00:00
int bit_depth ;
int compression_strategy ;
2021-03-05 17:27:11 +00:00
unsigned char * pb ;
2021-07-26 15:29:05 +01:00
const int output_to_stdout = symbol - > output_options & BARCODE_STDOUT ;
2016-02-20 10:50:15 +00:00
2016-02-17 10:52:48 +00:00
# ifndef _MSC_VER
2021-02-26 20:45:08 +00:00
unsigned char outdata [ symbol - > bitmap_width ] ;
2016-02-17 10:52:48 +00:00
# else
2021-08-10 12:04:25 +01:00
unsigned char * outdata = ( unsigned char * ) _alloca ( symbol - > bitmap_width ) ;
2016-02-17 10:52:48 +00:00
# endif
2021-07-26 15:29:05 +01:00
wpng_error . symbol = symbol ;
2016-02-20 10:50:15 +00:00
2021-02-26 20:45:08 +00:00
fg . red = ( 16 * ctoi ( symbol - > fgcolour [ 0 ] ) ) + ctoi ( symbol - > fgcolour [ 1 ] ) ;
fg . green = ( 16 * ctoi ( symbol - > fgcolour [ 2 ] ) ) + ctoi ( symbol - > fgcolour [ 3 ] ) ;
fg . blue = ( 16 * ctoi ( symbol - > fgcolour [ 4 ] ) ) + ctoi ( symbol - > fgcolour [ 5 ] ) ;
bg . red = ( 16 * ctoi ( symbol - > bgcolour [ 0 ] ) ) + ctoi ( symbol - > bgcolour [ 1 ] ) ;
bg . green = ( 16 * ctoi ( symbol - > bgcolour [ 2 ] ) ) + ctoi ( symbol - > bgcolour [ 3 ] ) ;
bg . blue = ( 16 * ctoi ( symbol - > bgcolour [ 4 ] ) ) + ctoi ( symbol - > bgcolour [ 5 ] ) ;
2016-02-20 10:50:15 +00:00
2020-08-02 22:26:39 +01:00
if ( strlen ( symbol - > fgcolour ) > 6 ) {
2021-02-26 20:45:08 +00:00
fg_alpha = ( 16 * ctoi ( symbol - > fgcolour [ 6 ] ) ) + ctoi ( symbol - > fgcolour [ 7 ] ) ;
2020-08-02 22:26:39 +01:00
} else {
2021-02-26 20:45:08 +00:00
fg_alpha = 0xff ;
2020-08-02 22:26:39 +01:00
}
2021-02-26 20:45:08 +00:00
2020-08-02 22:26:39 +01:00
if ( strlen ( symbol - > bgcolour ) > 6 ) {
2021-02-26 20:45:08 +00:00
bg_alpha = ( 16 * ctoi ( symbol - > bgcolour [ 6 ] ) ) + ctoi ( symbol - > bgcolour [ 7 ] ) ;
2020-08-02 22:26:39 +01:00
} else {
2021-02-26 20:45:08 +00:00
bg_alpha = 0xff ;
}
2021-03-16 23:38:47 +00:00
num_trans = 0 ;
2021-02-26 20:45:08 +00:00
if ( symbol - > symbology = = BARCODE_ULTRA ) {
static const int ultra_chars [ 8 ] = { ' W ' , ' C ' , ' B ' , ' M ' , ' R ' , ' Y ' , ' G ' , ' K ' } ;
2021-07-26 15:29:05 +01:00
static const png_color ultra_colours [ 8 ] = {
2021-02-26 20:45:08 +00:00
{ 0xff , 0xff , 0xff , } , /* White */
{ 0 , 0xff , 0xff , } , /* Cyan */
{ 0 , 0 , 0xff , } , /* Blue */
{ 0xff , 0 , 0xff , } , /* Magenta */
{ 0xff , 0 , 0 , } , /* Red */
{ 0xff , 0xff , 0 , } , /* Yellow */
{ 0 , 0xff , 0 , } , /* Green */
{ 0 , 0 , 0 , } , /* Black */
} ;
for ( i = 0 ; i < 8 ; i + + ) {
map [ ultra_chars [ i ] ] = i ;
palette [ i ] = ultra_colours [ i ] ;
if ( fg_alpha ! = 0xff ) {
trans_alpha [ i ] = fg_alpha ;
}
}
num_palette = 8 ;
if ( fg_alpha ! = 0xff ) {
num_trans = 8 ;
}
2021-05-28 14:37:57 +01:00
/* For Ultracode, have foreground only if have bind/box */
if ( symbol - > border_width > 0 & & ( symbol - > output_options & ( BARCODE_BIND | BARCODE_BOX ) ) ) {
/* Check whether can re-use black */
if ( fg . red = = 0 & & fg . green = = 0 & & fg . blue = = 0 ) {
map [ ' 1 ' ] = 7 ; /* Re-use black */
} else {
map [ ' 1 ' ] = num_palette ;
palette [ num_palette + + ] = fg ;
if ( fg_alpha ! = 0xff ) {
trans_alpha [ num_trans + + ] = fg_alpha ;
}
}
}
2021-02-26 20:45:08 +00:00
/* For Ultracode, have background only if have whitespace/quiet zones */
2021-09-24 13:21:24 +01:00
if ( symbol - > whitespace_width > 0 | | symbol - > whitespace_height > 0
| | ( ( symbol - > output_options & BARCODE_QUIET_ZONES )
& & ! ( symbol - > output_options & BARCODE_NO_QUIET_ZONES ) ) ) {
2021-02-26 20:45:08 +00:00
/* Check whether can re-use white */
if ( bg . red = = 0xff & & bg . green = = 0xff & & bg . blue = = 0xff & & bg_alpha = = fg_alpha ) {
map [ ' 0 ' ] = 0 ; /* Re-use white */
} else {
if ( bg_alpha = = 0xff | | fg_alpha ! = 0xff ) {
/* No alpha or have foreground alpha - add to end */
map [ ' 0 ' ] = num_palette ;
palette [ num_palette + + ] = bg ;
} else {
/* Alpha and no foreground alpha - add to front & move white to end */
map [ ' 0 ' ] = 0 ;
palette [ 0 ] = bg ;
map [ ' W ' ] = num_palette ;
palette [ num_palette + + ] = ultra_colours [ 0 ] ;
}
if ( bg_alpha ! = 0xff ) {
trans_alpha [ num_trans + + ] = bg_alpha ;
}
}
}
} else {
int bg_idx = 0 , fg_idx = 1 ;
/* Do alphas first so can swop indexes if background not alpha */
if ( bg_alpha ! = 0xff ) {
trans_alpha [ num_trans + + ] = bg_alpha ;
}
if ( fg_alpha ! = 0xff ) {
trans_alpha [ num_trans + + ] = fg_alpha ;
if ( num_trans = = 1 ) {
/* Only foreground has alpha so swop indexes - saves a byte! */
bg_idx = 1 ;
fg_idx = 0 ;
}
}
map [ ' 0 ' ] = bg_idx ;
palette [ bg_idx ] = bg ;
map [ ' 1 ' ] = fg_idx ;
palette [ fg_idx ] = fg ;
num_palette = 2 ;
}
if ( num_palette < = 2 ) {
bit_depth = 1 ;
} else {
2021-07-26 15:29:05 +01:00
bit_depth = 4 ;
2020-08-02 22:26:39 +01:00
}
2016-02-20 10:50:15 +00:00
/* Open output file in binary mode */
2021-07-26 15:29:05 +01:00
if ( output_to_stdout ) {
2016-02-17 10:52:48 +00:00
# ifdef _MSC_VER
2016-02-20 10:50:15 +00:00
if ( - 1 = = _setmode ( _fileno ( stdout ) , _O_BINARY ) ) {
2021-07-26 15:29:05 +01:00
sprintf ( symbol - > errtxt , " 631: Could not set stdout to binary (%d: %.30s) " , errno , strerror ( errno ) ) ;
2016-02-20 10:50:15 +00:00
return ZINT_ERROR_FILE_ACCESS ;
}
2016-02-17 10:52:48 +00:00
# endif
2021-07-26 15:29:05 +01:00
outfile = stdout ;
2016-02-20 10:50:15 +00:00
} else {
2021-07-26 15:29:05 +01:00
if ( ! ( outfile = fopen ( symbol - > outfile , " wb " ) ) ) {
sprintf ( symbol - > errtxt , " 632: Could not open output file (%d: %.30s) " , errno , strerror ( errno ) ) ;
2016-02-20 10:50:15 +00:00
return ZINT_ERROR_FILE_ACCESS ;
}
}
/* Set up error handling routine as proc() above */
2021-07-26 15:29:05 +01:00
png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING , & wpng_error , wpng_error_handler , NULL ) ;
2016-02-20 10:50:15 +00:00
if ( ! png_ptr ) {
2021-07-26 15:29:05 +01:00
strcpy ( symbol - > errtxt , " 633: Insufficient memory for PNG write structure buffer " ) ;
if ( ! output_to_stdout ) {
fclose ( outfile ) ;
}
2016-02-20 10:50:15 +00:00
return ZINT_ERROR_MEMORY ;
}
info_ptr = png_create_info_struct ( png_ptr ) ;
if ( ! info_ptr ) {
png_destroy_write_struct ( & png_ptr , NULL ) ;
2021-07-26 15:29:05 +01:00
strcpy ( symbol - > errtxt , " 634: Insufficient memory for PNG info structure buffer " ) ;
if ( ! output_to_stdout ) {
fclose ( outfile ) ;
}
2016-02-20 10:50:15 +00:00
return ZINT_ERROR_MEMORY ;
}
/* catch jumping here */
2021-07-26 15:29:05 +01:00
if ( setjmp ( wpng_error . jmpbuf ) ) {
2016-02-20 10:50:15 +00:00
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
2021-07-26 15:29:05 +01:00
if ( ! output_to_stdout ) {
fclose ( outfile ) ;
}
2016-02-20 10:50:15 +00:00
return ZINT_ERROR_MEMORY ;
}
/* open output file with libpng */
2021-07-26 15:29:05 +01:00
png_init_io ( png_ptr , outfile ) ;
2016-02-20 10:50:15 +00:00
/* set compression */
png_set_compression_level ( png_ptr , 9 ) ;
2021-02-26 20:45:08 +00:00
/* Compression strategy can make a difference */
compression_strategy = guess_compression_strategy ( symbol , pixelbuf ) ;
if ( compression_strategy ! = Z_DEFAULT_STRATEGY ) {
png_set_compression_strategy ( png_ptr , compression_strategy ) ;
}
2016-02-20 10:50:15 +00:00
/* set Header block */
2021-07-26 15:29:05 +01:00
png_set_IHDR ( png_ptr , info_ptr , symbol - > bitmap_width , symbol - > bitmap_height ,
2021-02-26 20:45:08 +00:00
bit_depth , PNG_COLOR_TYPE_PALETTE , PNG_INTERLACE_NONE ,
PNG_COMPRESSION_TYPE_DEFAULT , PNG_FILTER_TYPE_DEFAULT ) ;
png_set_PLTE ( png_ptr , info_ptr , palette , num_palette ) ;
if ( num_trans ) {
png_set_tRNS ( png_ptr , info_ptr , trans_alpha , num_trans , NULL ) ;
}
2016-02-20 10:50:15 +00:00
/* write all chunks up to (but not including) first IDAT */
png_write_info ( png_ptr , info_ptr ) ;
/* Pixel Plotting */
2021-03-05 17:27:11 +00:00
pb = pixelbuf ;
2021-02-26 20:45:08 +00:00
if ( bit_depth = = 1 ) {
for ( row = 0 ; row < symbol - > bitmap_height ; row + + ) {
2021-10-20 23:05:30 +01:00
if ( row & & memcmp ( pb , pb - symbol - > bitmap_width , symbol - > bitmap_width ) = = 0 ) {
pb + = symbol - > bitmap_width ;
} else {
unsigned char * image_data = outdata ;
for ( column = 0 ; column < symbol - > bitmap_width ; column + = 8 , image_data + + ) {
unsigned char byte = 0 ;
for ( i = 0 ; i < 8 & & column + i < symbol - > bitmap_width ; i + + , pb + + ) {
byte | = map [ * pb ] < < ( 7 - i ) ;
}
* image_data = byte ;
2021-02-26 20:45:08 +00:00
}
}
/* write row contents to file */
png_write_row ( png_ptr , outdata ) ;
}
2021-07-26 15:29:05 +01:00
} else { /* Bit depth 4 */
2021-02-26 20:45:08 +00:00
for ( row = 0 ; row < symbol - > bitmap_height ; row + + ) {
2021-10-20 23:05:30 +01:00
if ( row & & memcmp ( pb , pb - symbol - > bitmap_width , symbol - > bitmap_width ) = = 0 ) {
pb + = symbol - > bitmap_width ;
} else {
unsigned char * image_data = outdata ;
for ( column = 0 ; column < symbol - > bitmap_width ; column + = 2 , image_data + + ) {
unsigned char byte = map [ * pb + + ] < < 4 ;
if ( column + 1 < symbol - > bitmap_width ) {
byte | = map [ * pb + + ] ;
}
* image_data = byte ;
2021-02-26 20:45:08 +00:00
}
}
/* write row contents to file */
png_write_row ( png_ptr , outdata ) ;
}
2016-02-20 10:50:15 +00:00
}
/* End the file */
png_write_end ( png_ptr , NULL ) ;
/* make sure we have disengaged */
2021-07-26 15:29:05 +01:00
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
if ( output_to_stdout ) {
fflush ( outfile ) ;
2016-02-20 10:50:15 +00:00
} else {
2021-07-26 15:29:05 +01:00
fclose ( outfile ) ;
2016-02-20 10:50:15 +00:00
}
2021-02-26 20:45:08 +00:00
2016-02-20 10:50:15 +00:00
return 0 ;
2016-02-17 10:52:48 +00:00
}
2021-06-10 11:15:39 +01:00
# else
Add Structured Append support for AZTEC, CODEONE, DATAMATRIX, DOTCODE,
GRIDMATRIX, MAXICODE, MICROPDF417, PDF417, QRCODE, ULTRA
DOTCODE: use pre-calculated generator poly coeffs in Reed-Solomon for
performance improvement
PDF417/MICROPDF417: use common routine pdf417_initial()
GUI: code lines <= 118, shorthand widget_obj(),
shorten calling upcean_addon_gap(), upcean_guard_descent()
various backend: var name debug -> debug_print
2021-09-28 21:42:44 +01:00
/* https://stackoverflow.com/a/26541331 Suppresses gcc warning ISO C forbids an empty translation unit */
2021-06-10 11:15:39 +01:00
typedef int make_iso_compilers_happy ;
2016-02-17 10:52:48 +00:00
# endif /* NO_PNG */