/* Copyright (C) 2017-2021, Dirk Krause SPDX-License-Identifier: BSD-3-Clause */ /* WARNING: This file was generated by the dkct program (see http://dktools.sourceforge.net/ for details). Changes you make here will be lost if dkct is run again! You should modify the original source and run dkct on it. Original source: dk4bifn.ctr */ /** @file dk4bifn.c The dk4bifn module. */ #include "dk4conf.h" #if DK4_HAVE_SYS_TYPES_H #ifndef SYS_TYPES_H_INCLUDED #include #define SYS_TYPES_H_INCLUDED1 #endif #endif #if DK4_HAVE_STDINT_H #ifndef STDINT_H_INCLUDED #include #define STDINT_H_INCLUDED 1 #endif #endif #if DK4_HAVE_LIMITS_H #ifndef LIMITS_H_INCLUDED #include #define LIMITS_H_INCLUDED 1 #endif #endif #ifndef DK4TYPES_H_INCLUDED #include #endif #ifndef DK4MEM_H_INCLUDED #include #endif #ifndef DK4MAASZ_H_INCLUDED #include #endif #ifndef DK4STR8_H_INCLUDED #include #endif #ifndef DK4BIFTY_H_INCLUDED #include #endif #ifndef DK4BIF_H_INCLUDED #include #endif #ifndef BIF_H_INCLUDED #include #endif #ifndef DK4MAI8DSZ_H_INCLUDED #include #endif #if DK4_HAVE_ASSERT_H #ifndef ASSERT_H_INCLUDED #include #define ASSERT_H_INCLUDED 1 #endif #endif /** Data to save between applying bytes. */ typedef struct { dk4_bif_t *bif; /**< The result. */ size_t nums[4]; /**< Width, height, maxval. */ int magnum; /**< Magic number. */ int tupt; /**< Tuple type for DK4_NETPBM_P7. */ } dk4_bif_netpbm_builder_t; /** Pointer to unsigned character. */ typedef unsigned char *dk4_puchar_t; static const char * const dk4netpbm_p7_kw[] = { /* 0 */ "WIDTH", /* 1 */ "HEIGHT", /* 2 */ "DEPTH", /* 3 */ "MAXVAL", /* 4 */ "TUPLTYPE", /* 5 */ "ENDHDR", NULL }; /** Tuple types. */ static const char * const dk4netpbm_p7_tut[] = { /* 0 */ "BLACKANDWHITE", /* 1 */ "GRAYSCALE", /* 2 */ "RGB", /* 3 */ "BLACKANDWHITE_ALPHA", /* 4 */ "GRAYSCALE_ALPHA", /* 5 */ "RGB_ALPHA", NULL }; void dk4bifnetpbm_nc_release_per_frame_data( void *tsdata ) { dk4_bif_netpbm_per_frame_t *pfd; /* Per-frame data */ size_t i; /* Traverse all rows */ #if DK4_USE_ASSERT assert(NULL != tsdata); #endif if (NULL != tsdata) { pfd = (dk4_bif_netpbm_per_frame_t *)tsdata; if (NULL != pfd->rows) { for (i = 0; i < (size_t)(pfd->h); i++) { dk4mem_release((pfd->rows)[i]); } dk4mem_free(pfd->rows); } pfd->rows = NULL; pfd->w = 0; pfd->h = 0; pfd->ch = 0; pfd->bpr = 0; pfd->tupt = 0; pfd->maxval = 0; pfd->bpc = 0; pfd->magnum = 0; dk4mem_free(tsdata); } } static int dk4_bif_netpbm_check_input( size_t w, size_t h, size_t maxval, dk4_er_t *erp ) { int back = 0; if ((0 < w) && (0 < h) && (0 < maxval)) { if ((dk4_um_t)(LONG_MAX) >= (dk4_um_t)w) { if ((dk4_um_t)(LONG_MAX) >= (dk4_um_t)h) { if ((dk4_um_t)(DK4_U16_MAX) >= (dk4_um_t)maxval) { back = 1; } } } } if (0 == back) { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } return back; } static dk4_bif_netpbm_per_frame_t * dk4bifnetpbm_tsdata_new( int magnum, int tupt, size_t ch, size_t w, size_t h, size_t maxval, dk4_er_t *erp ) { dk4_er_t er; /* Error report for math op */ dk4_bif_netpbm_per_frame_t *back = NULL; size_t i = 0; /* Traverse all rows */ int ok = 0; /* Flag: Success, no errors */ if (0 != dk4_bif_netpbm_check_input(w, h, maxval, erp)) { back = dk4mem_new(dk4_bif_netpbm_per_frame_t,1,erp); if (NULL != back) { dk4error_init(&er); back->rows = NULL; back->w = (dk4_bif_dim_t)w; back->h = (dk4_bif_dim_t)h; back->ch = 0; /* Number of channels */ back->vpr = 0; /* Values per row */ back->bpr = 0; /* Bytes per row */ back->magnum = magnum; back->tupt = tupt; back->maxval = (dk4_px_t)maxval; back->bpc = (dk4_px_bit_depth_t)dk4pxbit_get_bitno((dk4_px_t)maxval); switch (magnum) { case DK4_NETPBM_P1 : case DK4_NETPBM_P4 : { back->ch = 1; back->vpr = w; back->bpr = w / 8; if (0 != (w % 8)) { back->bpr += 1; } } break; case DK4_NETPBM_P2 : case DK4_NETPBM_P5 : { back->ch = 1; back->vpr = w; back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; case DK4_NETPBM_P3 : case DK4_NETPBM_P6 : { back->ch = 3; back->vpr = dk4ma_size_t_mul(3, w, &er); back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; case DK4_NETPBM_P7 : { switch (tupt) { case DK4_NETPBM_TT_BLACKANDWHITE : { back->ch = 1; back->vpr = w; back->bpr = w; } break; case DK4_NETPBM_TT_GRAYSCALE : { back->ch = 1; back->vpr = w; back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; case DK4_NETPBM_TT_RGB : { back->ch = 3; back->vpr = dk4ma_size_t_mul(3, w, &er); back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; case DK4_NETPBM_TT_BLACKANDWHITE_ALPHA : { back->ch = 2; back->vpr = dk4ma_size_t_mul(2, w, &er); back->bpr = back->vpr; } break; case DK4_NETPBM_TT_GRAYSCALE_ALPHA : { back->ch = 2; back->vpr = dk4ma_size_t_mul(2, w, &er); back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; case DK4_NETPBM_TT_RGB_ALPHA : { back->ch = 4; back->vpr = dk4ma_size_t_mul(4, w, &er); back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; default : { back->ch = ch; back->vpr = dk4ma_size_t_mul(ch, w, &er); back->bpr = back->vpr; if (255 < maxval) { back->bpr = dk4ma_size_t_mul(2, back->vpr, &er); } } break; } } break; } (void)dk4ma_size_t_mul(h,sizeof(dk4_puchar_t),&er); if (DK4_E_NONE == er.ec) { if (0 < back->bpr) { back->rows = dk4mem_new(dk4_puchar_t,h,erp); if (NULL != back->rows) { for (i = 0; i < h; i++) { (back->rows)[i] = NULL; } ok = 1; for (i = 0; i < h; i++) { (back->rows)[i] = dk4mem_new(unsigned char,back->bpr,erp); if (NULL != (back->rows)[i]) { DK4_MEMRES((back->rows)[i],back->bpr); } else { ok = 0; } } } } } else { dk4error_copy(erp, &er); } if (0 == ok) { dk4bifnetpbm_nc_release_per_frame_data(back); back = NULL; } #if TRACE_DEBUG else { } #endif } #if TRACE_DEBUG else { } #endif } #if TRACE_DEBUG else { } #endif return back; } /** Copy original data to frame information. @param cf Frame data to fill. @param pfd Original per frame data. */ static void dk4bifnetpbm_fill_frame( dk4_bif_frame_t *cf, void const *voptr ) { dk4_bif_netpbm_per_frame_t const *pfd; #if DK4_USE_ASSERT assert(NULL != cf); #endif pfd = (dk4_bif_netpbm_per_frame_t *)voptr; cf->n_comp = pfd->ch; /* 2021-07-29: Bugfix, right side was missing. */ cf->dim_x = pfd->w; cf->dim_y = pfd->h; switch (pfd->magnum) { case DK4_NETPBM_P1 : { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = 1; } break; case DK4_NETPBM_P2 : { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_P3 : { cf->l_cs = cf->f_cs = DK4_CS_RGB; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_P4 : { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_P5 : { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_P6 : { cf->l_cs = cf->f_cs = DK4_CS_RGB; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_P7 : { switch (pfd->tupt) { case DK4_NETPBM_TT_BLACKANDWHITE : { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = 1; } break; case DK4_NETPBM_TT_GRAYSCALE : { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_TT_RGB : { cf->l_cs = cf->f_cs = DK4_CS_RGB; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_TT_BLACKANDWHITE_ALPHA : { cf->l_cs = cf->f_cs = DK4_CS_GRAY_ALPHA; cf->l_bdepth = cf->r_bdepth = 1; } break; case DK4_NETPBM_TT_GRAYSCALE_ALPHA : { cf->l_cs = cf->f_cs = DK4_CS_GRAY_ALPHA; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case DK4_NETPBM_TT_RGB_ALPHA : { cf->l_cs = cf->f_cs = DK4_CS_RGB_ALPHA; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; default : { switch (pfd->ch) { case 1: { cf->l_cs = cf->f_cs = DK4_CS_GRAY; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case 2: { cf->l_cs = cf->f_cs = DK4_CS_GRAY_ALPHA; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case 3: { cf->l_cs = cf->f_cs = DK4_CS_RGB; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; case 4: { cf->l_cs = cf->f_cs = DK4_CS_RGB_ALPHA; cf->l_bdepth = cf->r_bdepth = pfd->bpc; } break; } } break; } } break; } cf->bglbd[0] = pfd->maxval; cf->bglbd[1] = pfd->maxval; cf->bglbd[2] = pfd->maxval; cf->bglbd[3] = pfd->maxval; if (pfd->maxval != dk4pxbit_get_max(pfd->bpc)) { cf->fmres = 1; cf->fmrmv = pfd->maxval; dk4pxres_setup_from_maxval(&(cf->resampler), pfd->maxval, pfd->bpc); cf->bg[0] = dk4pxbit_get_max(pfd->bpc); cf->bg[1] = cf->bg[2] = cf->bg[3] = cf->bg[0]; } else { dk4pxres_setup(&(cf->resampler), cf->l_bdepth, cf->l_bdepth); cf->bg[0] = pfd->maxval; cf->bg[1] = pfd->maxval; cf->bg[2] = pfd->maxval; cf->bg[3] = pfd->maxval; } #if TRACE_DEBUG #endif } /** Initialize builder before applying any data. @param pbuilder Builder to initialize. @return 1 on success, 0 on error (memory). */ static int dk4_bif_netpbm_builder_initialize( dk4_bif_netpbm_builder_t *pbuilder, const dkChar *fn, int honly, dk4_cs_conv_ctx_t const *pcsctx, dk4_er_t *erp ) { int back = 0; #if DK4_USE_ASSERT assert(NULL != pbuilder); #endif pbuilder->bif = NULL; pbuilder->magnum = 0; pbuilder->bif = dk4biftool_nc_new_for_one_frame( fn, DK4_BIF_TYPE_NETPBM, honly, pcsctx, erp ); if (NULL != pbuilder->bif) { back = 1; } return back; } /** Clean up builder. @param pbuilder Builder to clean up. */ static void dk4_bif_netpbm_builder_cleanup(dk4_bif_netpbm_builder_t *pbuilder) { #if DK4_USE_ASSERT assert(NULL != pbuilder); #endif if (NULL != pbuilder->bif) { dk4bif_close(pbuilder->bif); pbuilder->bif = NULL; } } /** Attempt to read magic number and one whitespace from file. For a sequence of frames (sequence of NetPBM images) we might have white spaces between frame data and the magic number of the following frame, so we ignore leading white spaces if the havefr flag is set. @param pbuilder Image builder structure. @param fipo Input file, opened for binary reading. @param havefr Flag: Already have previous frame. @param erp Error report, may be NULL. @return 1 on success, 0 on end of file, -1 on error. */ static int dk4bifnetpbm_read_magic_number( dk4_bif_netpbm_builder_t *pbuilder, FILE *fipo, int havefr, dk4_er_t *erp ) { int cc = 1; /* Flag: Can continue */ int c = 0; /* Input character */ int inco = 0; /* Flag: In comment */ int back = -1; #if DK4_USE_ASSERT assert(NULL != pbuilder); assert(NULL != fipo); #endif do { c = fgetc(fipo); switch (c) { case EOF : { cc = -1; back = 0; } break; case 'P' : { cc = 0; } break; case '#' : { inco = 1; do { c = fgetc(fipo); switch (c) { case EOF : { inco = 0; cc = -1; back = 0; } break; case '\n' : { inco = 0; } break; } } while (1 == inco); } break; case ' ' : case '\t' : case '\r' : case '\n' : case '\v' : case '\f' : { if (0 == havefr) { cc = -1; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } break; default : { cc = -1; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } break; } } while (1 == cc); switch (cc) { case 0 : { /* P was read */ back = 1; c = fgetc(fipo); switch (c) { case '1' : { pbuilder->magnum = DK4_NETPBM_P1; } break; case '2' : { pbuilder->magnum = DK4_NETPBM_P2; } break; case '3' : { pbuilder->magnum = DK4_NETPBM_P3; } break; case '4' : { pbuilder->magnum = DK4_NETPBM_P4; } break; case '5' : { pbuilder->magnum = DK4_NETPBM_P5; } break; case '6' : { pbuilder->magnum = DK4_NETPBM_P6; } break; case '7' : { pbuilder->magnum = DK4_NETPBM_P7; } break; default : { back = -1; dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } break; } if (1 == back) { c = fgetc(fipo); back = -1; switch (c) { case ' ' : case '\t' : case '\r' : case '\n' : case '\v' : case '\f' : { back = 1; } break; } } } break; default : { /* Failed */ /* Empty by intent. */ } break; } return back; } static size_t dk4bifnetpbm_digit(char c) { size_t back = 0; switch (c) { case '1' : { back = 1; } break; case '2' : { back = 2; } break; case '3' : { back = 3; } break; case '4' : { back = 4; } break; case '5' : { back = 5; } break; case '6' : { back = 6; } break; case '7' : { back = 7; } break; case '8' : { back = 8; } break; case '9' : { back = 9; } break; } return back; } /** Read NetPBM header (2 or 3 decimal numbers), optionally containing a comment. We must have - white space(s), - 2 or 3 decimal numbers separated by white space(s), - one white space at end as separator between header and data. @param fipo Input file, opened for binary reading. @param numbuf Destination buffer for decimal numbers. @param sznb Buffer size (number of numbers). @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_header_16( FILE *fipo, size_t *numbuf, size_t sznb, dk4_er_t *erp ) { dk4_er_t er; /* Error report */ size_t i; /* Traverse all numbers */ size_t val = 0; /* Value to store in current number */ size_t found = 0; /* Number of completed numbers */ int back = 0; int inco = 0; /* Flag: In comment */ int st = 0; /* State: 0=in space, 1=in number */ int c = 0; /* Current character to process */ int cc = 1; /* Flag: 1=can continue, 0=finished, -1=error */ #if DK4_USE_ASSERT assert(NULL != fipo); assert(NULL != numbuf); assert(0 < sznb); #endif for (i = 0; i < sznb; i++) { numbuf[i] = 0; } dk4error_init(&er); do { c = fgetc(fipo); switch (c) { case EOF : { cc = -1; if (0 != st) { numbuf[found++] = val; val = 0; } } break; case '#' : { if (0 != st) { numbuf[found++] = val; val = 0; st = 0; } inco = 1; do { c = fgetc(fipo); switch (c) { case EOF : { inco = 0; cc = -1; } break; case '\n' : { inco = 0; } break; } } while (1 == inco); } break; case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : { if (0 != st) { val = dk4ma_size_t_add( dk4ma_size_t_mul(val, 10, &er), dk4bifnetpbm_digit((char)c), &er ); } else { val = dk4bifnetpbm_digit((char)c); st = 1; } } break; case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': { if (0 != st) { numbuf[found++] = val; val = 0; if (found == sznb) { cc = 0; back = 1; } } else { if (found == sznb) { cc = 0; back = 1; } } st = 0; } break; default : { cc = -1; } break; } } while (1 == cc); if (DK4_E_NONE != er.ec) { back = 0; } #if TRACE_DEBUG for (i = 0; i < found; i++) { } #endif if (1 == back) { for (i = 0; i < found; i++) { if (0 == numbuf[i]) { back = 0; } } } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } return back; } /** Check configuration data from P7 lines. @param w Image width. @param h Image height. @param ch Number of channels in image. @param maxval Maximum component value. @param tuptp Address of tuple type variable. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_check_p7( size_t w, size_t h, size_t ch, size_t maxval, int *tuptp, dk4_er_t *erp ) { int back = 0; if ((0 == ch) && (0 <= *tuptp)) { switch (*tuptp) { case DK4_NETPBM_TT_BLACKANDWHITE : { ch = 1; } break; case DK4_NETPBM_TT_GRAYSCALE : { ch = 1; } break; case DK4_NETPBM_TT_RGB : { ch = 3; } break; case DK4_NETPBM_TT_BLACKANDWHITE_ALPHA : { ch = 2; } break; case DK4_NETPBM_TT_GRAYSCALE_ALPHA : { ch = 2; } break; case DK4_NETPBM_TT_RGB_ALPHA : { ch = 4; } break; #if TRACE_DEBUG default : { } break; #endif } } else { if ((0 < ch) && (0 > *tuptp)) { switch (ch) { case 1 : { *tuptp = DK4_NETPBM_TT_GRAYSCALE; } break; case 2 : { *tuptp = DK4_NETPBM_TT_GRAYSCALE_ALPHA; } break; case 3 : { *tuptp = DK4_NETPBM_TT_RGB; } break; case 4 : { *tuptp = DK4_NETPBM_TT_RGB_ALPHA; } break; } } } if (0 != dk4_bif_netpbm_check_input(w, h, maxval, erp)) { if ((0 != ch) && (0 <= *tuptp)) { switch (*tuptp) { case DK4_NETPBM_TT_BLACKANDWHITE : { if (1 == ch) { back = 1; } } break; case DK4_NETPBM_TT_GRAYSCALE : { if (1 == ch) { back = 1; } } break; case DK4_NETPBM_TT_RGB : { if (3 == ch) { back = 1; } } break; case DK4_NETPBM_TT_BLACKANDWHITE_ALPHA : { if (2 == ch) { back = 1; } } break; case DK4_NETPBM_TT_GRAYSCALE_ALPHA : { if (2 == ch) { back = 1; } } break; case DK4_NETPBM_TT_RGB_ALPHA : { if (4 == ch) { back = 1; } } break; #if TRACE_DEBUG default : { } break; #endif } } } return back; } /** Read binary data for one row. @param row Address of row buffer to fill. @param fipo Input file, opened for binary reading. @param nbytes Number of bytes for row. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_row( unsigned char *row, FILE *fipo, size_t nbytes, dk4_er_t *erp ) { size_t rdbytes = 0; /* Number of bytes read */ int back = 1; #if DK4_USE_ASSERT assert(NULL != row); assert(NULL != fipo); #endif /* Normally one fread attempt should be sufficient. But sometimes multiple attempts are needed if an fread returns a small portion of data only. Reading 0 bytes means either end of file or read error. */ do { rdbytes = fread(row, 1, nbytes, fipo); if (0 < rdbytes) { if (rdbytes >= nbytes) { nbytes = 0; } else { nbytes -= rdbytes; row = &(row[rdbytes]); } } else { back = 0; } } while ((0 < nbytes) && (1 == back)); if (0 == back) { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } return back; } /** Read binary raster data. @param tsdata Per-frame NetPBM data. @param fipo Input file, opened for binary reading. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_binary( void *tsdata, FILE *fipo, dk4_er_t *erp ) { dk4_bif_netpbm_per_frame_t *pfd = NULL; /* Per-frame data */ unsigned char **row = NULL; /* Array of row pointers */ size_t i; /* Traverse all rows */ int back = 0; #if DK4_USE_ASSERT assert(NULL != tsdata); assert(NULL != fipo); #endif pfd = (dk4_bif_netpbm_per_frame_t *)tsdata; row = pfd->rows; back = 1; for (i = 0; i < (size_t)(pfd->h); i++) { if (0 == dk4bifnetpbm_read_row(*(row++), fipo, pfd->bpr, erp)) { back = 0; i = (size_t)(pfd->h); } } return back; } /** Read frame contents (NetPBM image) after magic number P7. @param pbuilder Image builder structure. @param fipo Input file, opened for binary reading. @param havefr Flag: Already have frame. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_p7( dk4_bif_netpbm_builder_t *pbuilder, FILE *fipo, int havefr, dk4_er_t *erp ) { dk4_er_t er; /* Error report */ char *tok[256]; /* Tokens in input line */ char il[256]; /* Input line */ const char *ep = NULL; /* End pointer */ size_t ilp = 0; /* Current position in input line */ size_t ntok = 0; /* Number of tokens in line */ size_t w = 0; /* Image width */ size_t h = 0; /* Image height */ size_t ch = 0; /* Number of channels */ size_t maxval = 0; /* Maximum component value */ int back = 0; /* Function result */ int cc = 1; /* Can continue (1=yes, 0=no, -1=error) */ int hdt = 0; /* Header line type */ int res = 0; /* Operation result */ int tupt = -1; /* Tuple type */ int c; #if DK4_USE_ASSERT assert(NULL != pbuilder); assert(NULL != fipo); #endif DK4_MEMRES(il,sizeof(il)); dk4error_init(&er); do { c = fgetc(fipo); switch (c) { case EOF : { cc = -1; } break; case '\n' : { il[ilp] = '\0'; ntok = dk4str8_tokenize(tok, 256, il, NULL, erp); if (0 < ntok) { hdt = dk4str8_array_index(dk4netpbm_p7_kw, tok[0], 0); switch (hdt) { case 0: { /* WIDTH */ if (1 < ntok) { ep = NULL; res = dk4ma_input_c8_dec_size_t_simple( &w, tok[1], &ep, 1, erp ); if (0 == res) { cc = -1; } } else { cc = -1; } } break; case 1: { /* HEIGHT */ if (1 < ntok) { ep = NULL; res = dk4ma_input_c8_dec_size_t_simple( &h, tok[1], &ep, 1, erp ); if (0 == res) { cc = -1; } } else { cc = -1; } } break; case 2: { /* DEPTH */ if (1 < ntok) { ep = NULL; res = dk4ma_input_c8_dec_size_t_simple( &ch, tok[1], &ep, 1, erp ); if (0 == res) { cc = -1; } } else { cc = -1; } } break; case 3: { /* MAXVAL */ if (1 < ntok) { ep = NULL; res = dk4ma_input_c8_dec_size_t_simple( &maxval, tok[1], &ep, 1, erp ); if (0 == res) { cc = -1; } } else { cc = -1; } } break; case 4: { /* TUPLTYPE */ if (1 < ntok) { tupt = dk4str8_array_index( dk4netpbm_p7_tut, tok[1], 0 ); if (0 > tupt) { /* 2018-03-14 Simply ignore unkown tuple types. */ } } else { cc = -1; } } break; case 5: { /* ENDHDR */ cc = -1; res = dk4bifnetpbm_check_p7( w, h, ch, maxval, &tupt, erp ); if (0 != res) { back = 1; if (0 != havefr) { back = dk4biftool_nc_add_frame( pbuilder->bif, erp ); } if (1 == back) { back = 0; pbuilder->bif->cf->tsdata = dk4bifnetpbm_tsdata_new( DK4_NETPBM_P7,tupt,ch,w,h,maxval,erp ); if (NULL != pbuilder->bif->cf->tsdata) { cc = 0; dk4bifnetpbm_fill_frame( pbuilder->bif->cf, pbuilder->bif->cf->tsdata ); } } } } break; default : { cc = -1; } break; } } else { /* Empty line is ok */ } DK4_MEMRES(il,sizeof(il)); ilp = 0; } break; default : { il[ilp++] = (char)c; if (sizeof(il) <= ilp) { cc = -1; } } break; } } while (1 == cc); if (0 == cc) { /* Read raster data */ back = dk4bifnetpbm_read_binary( pbuilder->bif->cf->tsdata, fipo, erp ); } return back; } /** Read bit values as text. @param tsdata Per-frame NetPBM data. @param fipo Input file, opened for binary reading. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_bits_text( void *tsdata, FILE *fipo, dk4_er_t *erp ) { dk4_bif_netpbm_per_frame_t *pfd; /* Per-frame data */ size_t rowno = 0; /* Current row number */ size_t valno = 0; /* Current value number */ size_t byteno = 0; /* Byte number within row */ size_t bitno = 0; /* Bit number within byte */ int back = 1; int cc = 1; /* Flag: Can continue */ int c; /* Current input character */ unsigned char uc; /* Byte to save */ #if DK4_USE_ASSERT assert(NULL != tsdata); assert(NULL != fipo); #endif pfd = (dk4_bif_netpbm_per_frame_t *)tsdata; do { c = fgetc(fipo); switch (c) { case EOF : { cc = -1; back = 0; } break; case '0' : case '1' : { if ('1' == c) { byteno = valno / 8; bitno = valno % 8; uc = (unsigned char)dk4pxbit_get_bit(7 - bitno); ((pfd->rows)[rowno])[byteno] |= uc; } valno++; if (valno >= pfd->vpr) { valno = 0; rowno++; if (rowno >= (size_t)(pfd->h)) { cc = 0; } } } break; case ' ' : case '\t' : case '\r' : case '\n' : case '\v' : case '\f' : { } break; default : { cc = -1; } break; } } while (1 == cc); if (0 == cc) { do { c = fgetc(fipo); } while ((EOF != c) && (0 == feof(fipo)) && (0 == ferror(fipo))); } if (0 == back) { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } return back; } /** Read pixel component values as text. @param tsdata Per-frame NetPBM data. @param fipo Input file, opened for binary reading. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_values_from_text( void *tsdata, FILE *fipo, dk4_er_t *erp ) { dk4_er_t er; /* Error report for math */ dk4_bif_netpbm_per_frame_t *pfd = NULL; /* Per-frame data */ size_t rowno = 0; /* Current row number */ size_t valno = 0; /* Current value number */ size_t value = 0; /* Sample value */ int back = 1; int st = 0; /* State: 1=in number */ int cc = 1; /* Flag: Can continue */ int c; /* Current character */ unsigned char uc; /* Byte to save */ #if DK4_USE_ASSERT assert(NULL != tsdata); assert(NULL != fipo); #endif pfd = (dk4_bif_netpbm_per_frame_t *)tsdata; dk4error_init(&er); do { c = fgetc(fipo); switch (c) { case EOF : { if (0 != st) { cc = -1; back = 0; if (value <= pfd->maxval) { if (256 > pfd->maxval) { uc = (unsigned char)value; ((pfd->rows)[rowno])[valno] = uc; } else { uc = (value >> 8) & 0x00FFU; ((pfd->rows)[rowno])[2 * valno] = uc; uc = value & 0x00FFU; ((pfd->rows)[rowno])[2 * valno + 1] = uc; } valno++; if (valno >= pfd->vpr) { valno = 0; rowno++; if (rowno >= (size_t)(pfd->h)) { back = 1; } } } } else { cc = -1; back = 0; } } break; case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : { if (0 != st) { value = dk4ma_size_t_add( dk4ma_size_t_mul(value, 10, &er), dk4bifnetpbm_digit((char)c), &er ); } else { value = dk4bifnetpbm_digit((char)c); } st = 1; } break; case ' ' : case '\t' : case '\r' : case '\n' : case '\v' : case '\f' : { if (0 != st) { if (value <= pfd->maxval) { if (256 > pfd->maxval) { uc = (unsigned char)value; ((pfd->rows)[rowno])[valno] = uc; } else { uc = (value >> 8) & 0x00FFU; ((pfd->rows)[rowno])[2 * valno] = uc; uc = value & 0x00FFU; ((pfd->rows)[rowno])[2 * valno + 1] = uc; } valno++; if (valno >= pfd->vpr) { valno = 0; rowno++; if (rowno >= (size_t)(pfd->h)) { back = 1; cc = 0; } } } else { cc = -1; back = 0; } } st = 0; } break; default : { cc = -1; back = 0; } break; } } while (1 == cc); if (0 == cc) { do { c = fgetc(fipo); } while ((EOF != c) && (0 == feof(fipo)) && (0 == ferror(fipo))); } if (DK4_E_NONE != er.ec) { back = 0; } if (0 == back) { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } return back; } /** Read frame contents (NetPBM image) after magic number. @param pbuilder Image builder structure. @param fipo Input file, opened for binary reading. @param havefr Flag: Already have a frame. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ static int dk4bifnetpbm_read_frame_contents( dk4_bif_netpbm_builder_t *pbuilder, FILE *fipo, int havefr, dk4_er_t *erp ) { int back = 0; #if DK4_USE_ASSERT assert(NULL != pbuilder); assert(NULL != fipo); #endif switch (pbuilder->magnum) { case DK4_NETPBM_P1 : case DK4_NETPBM_P4 : { back = dk4bifnetpbm_read_header_16( fipo, &(pbuilder->nums[0]), 2, erp ); if (1 == back) { back = 0; if (0 != havefr) { if (0 != dk4biftool_nc_add_frame(pbuilder->bif, erp)) { back = 1; } } else { back = 1; } if (0 != back) { back = 0; pbuilder->bif->cf->tsdata = dk4bifnetpbm_tsdata_new( pbuilder->magnum, pbuilder->tupt, 1, pbuilder->nums[0], pbuilder->nums[1], 1, erp ); if (NULL != pbuilder->bif->cf->tsdata) { dk4bifnetpbm_fill_frame( pbuilder->bif->cf, pbuilder->bif->cf->tsdata ); if (DK4_NETPBM_P4 == pbuilder->magnum) { back = dk4bifnetpbm_read_binary( pbuilder->bif->cf->tsdata, fipo, erp ); } else { back = dk4bifnetpbm_read_bits_text( pbuilder->bif->cf->tsdata, fipo, erp ); } } } } } break; case DK4_NETPBM_P2 : case DK4_NETPBM_P3 : case DK4_NETPBM_P5 : case DK4_NETPBM_P6 : { back = dk4bifnetpbm_read_header_16( fipo, &(pbuilder->nums[0]), 3, erp ); if (1 == back) { back = 0; if (0 != havefr) { if (0 != dk4biftool_nc_add_frame(pbuilder->bif, erp)) { back = 1; } } else { back = 1; } if (0 != back) { back = 0; switch (pbuilder->magnum) { case DK4_NETPBM_P2 : case DK4_NETPBM_P5 : { pbuilder->bif->cf->tsdata = dk4bifnetpbm_tsdata_new( pbuilder->magnum, pbuilder->tupt, 1, pbuilder->nums[0], pbuilder->nums[1], pbuilder->nums[2], erp ); } break; default : { pbuilder->bif->cf->tsdata = dk4bifnetpbm_tsdata_new( pbuilder->magnum, pbuilder->tupt, 3, pbuilder->nums[0], pbuilder->nums[1], pbuilder->nums[2], erp ); } break; } if (NULL != pbuilder->bif->cf->tsdata) { dk4bifnetpbm_fill_frame( pbuilder->bif->cf, pbuilder->bif->cf->tsdata ); switch (pbuilder->magnum) { case DK4_NETPBM_P5 : case DK4_NETPBM_P6 : { back = dk4bifnetpbm_read_binary( pbuilder->bif->cf->tsdata, fipo, erp ); } break; default : { back = dk4bifnetpbm_read_values_from_text( pbuilder->bif->cf->tsdata, fipo, erp ); } break; } } } } } break; case DK4_NETPBM_P7 : { back = dk4bifnetpbm_read_p7(pbuilder, fipo, havefr, erp); } break; default : { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } break; } return back; } /** Read one frame (one image) from the input file and add it to builder. @param pbuilder Image builder structure. @param fipo Input file, opened for binary reading. @param havefr Flag: Already have previous frame. @param erp Error report, may be NULL. @return 1 on success, 0 on end of file, -1 on error. */ static int dk4bifnetpbm_read_frame( dk4_bif_netpbm_builder_t *pbuilder, FILE *fipo, int havefr, dk4_er_t *erp ) { int back = -1; #if DK4_USE_ASSERT assert(NULL != pbuilder); assert(NULL != fipo); #endif back = dk4bifnetpbm_read_magic_number(pbuilder, fipo, havefr, erp); if (1 == back) { back = dk4bifnetpbm_read_frame_contents(pbuilder, fipo, havefr, erp); if (1 != back) { back = -1; } } return back; } dk4_bif_t * dk4bifnetpbm_nc_open_file( FILE *fipo, dkChar const *fn, int honly, dk4_cs_conv_ctx_t const *pcsctx, dk4_er_t *erp ) { dk4_bif_t *back = NULL; int res = 0; /* Operation result */ int havefr = 0; /* Flag: frames found */ dk4_bif_netpbm_builder_t builder; /* Builder structure */ #if DK4_USE_ASSERT assert(NULL != fipo); #endif /* Initialize the builder */ if (0 != dk4_bif_netpbm_builder_initialize(&builder,fn,honly,pcsctx,erp)) { /* Attempt to read all frames from input */ do { res = dk4bifnetpbm_read_frame(&builder, fipo, havefr, erp); switch (res) { case 1: { havefr = 1; } break; } } while (0 < res); /* On success (at least one frame was read, end of file was found after last frame) return the builders collected data */ if ((-1 != res) && (0 != havefr)) { back = builder.bif; builder.bif = NULL; } /* Finally clean up the builder */ dk4_bif_netpbm_builder_cleanup(&builder); } return back; } /** Shift and mask a value. @param u Original value. @param w Shift width, 0 allowed. @param m Mask value. @return Operation result. */ static dk4_px_t dk4bifnetpbm_shift_mask(unsigned u, unsigned w, unsigned m) { if (0 < w) { u = u << w; } return ((dk4_px_t)(u & m)); } /** Combine two bytes into one 16 bit unsigned integer for pixel component. @param msb Most significant byte. @param lsb Least significant byte. @return Pixel component value. */ static dk4_px_t dk4bifnetpbm_combine_bytes(unsigned char msb, unsigned char lsb) { dk4_px_t back; #if VERSION_BEFORE_20210729 back = ((((dk4_px_t)msb) << 8) & ((dk4_px_t)0xFF00U)) | (((dk4_px_t)lsb) & ((dk4_px_t)0x00FFU)); #endif back = dk4bifnetpbm_shift_mask(msb, 8, 0xFF00U) | dk4bifnetpbm_shift_mask(lsb, 0, 0x00FFU); return back; } int dk4bifnetpbm_nc_get_original_pixel( dk4_px_t *dptr, dk4_bif_t const *bif, dk4_bif_dim_t rowno, dk4_bif_dim_t colno ) { dk4_bif_netpbm_per_frame_t *pfd = NULL; /* Per-frame data */ unsigned char *ucptr = NULL; /* Row data */ size_t byteno; /* Byte number to retrieve */ size_t bitno; /* Bit number within byte */ size_t i; /* Traverse components */ int back = 0; unsigned char uc; /* Byte read from buffer */ #if DK4_USE_ASSERT assert(NULL != dptr); assert(NULL != bif); #endif if (NULL == bif->cf) { goto finished; } if (NULL == bif->cf->tsdata) { goto finished; } pfd = (dk4_bif_netpbm_per_frame_t *)(bif->cf->tsdata); if (NULL == pfd->rows) { goto finished; } switch (pfd->magnum) { case DK4_NETPBM_P1 : case DK4_NETPBM_P4 : { byteno = (size_t)colno / 8; bitno = (size_t)colno % 8; uc = ((pfd->rows)[rowno])[byteno]; if (0 != (dk4pxbit_get_bit(7 - bitno) & uc)) { *dptr = 0; } else { *dptr = 1; } back = 1; } break; case DK4_NETPBM_P2 : case DK4_NETPBM_P5 : { if (256 > pfd->maxval) { *dptr = (dk4_px_t)(((pfd->rows)[rowno])[colno]); } else { *dptr = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[2 * colno], ((pfd->rows)[rowno])[2 * colno + 1] ); } back = 1; } break; case DK4_NETPBM_P3 : case DK4_NETPBM_P6 : { if (256 > pfd->maxval) { dptr[0] = (dk4_px_t)(((pfd->rows)[rowno])[3 * colno]); dptr[1] = (dk4_px_t)(((pfd->rows)[rowno])[3 * colno + 1]); dptr[2] = (dk4_px_t)(((pfd->rows)[rowno])[3 * colno + 2]); } else { dptr[0] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[6 * colno], ((pfd->rows)[rowno])[6 * colno + 1] ); dptr[1] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[6 * colno + 2], ((pfd->rows)[rowno])[6 * colno + 3] ); dptr[2] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[6 * colno + 4], ((pfd->rows)[rowno])[6 * colno + 5] ); } back = 1; } break; case DK4_NETPBM_P7 : { switch (pfd->tupt) { case DK4_NETPBM_TT_BLACKANDWHITE : { if (0 != ((pfd->rows)[rowno])[colno]) { *dptr = 1; } else { *dptr = 0; } back = 1; } break; case DK4_NETPBM_TT_GRAYSCALE : { if (256 > pfd->maxval) { *dptr = (dk4_px_t)(((pfd->rows)[rowno])[colno]); } else { *dptr = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[2 * colno], ((pfd->rows)[rowno])[2 * colno + 1] ); } back = 1; } break; case DK4_NETPBM_TT_RGB : { if (256 > pfd->maxval) { dptr[0] = (dk4_px_t)(((pfd->rows)[rowno])[3*colno]); dptr[1] = (dk4_px_t)(((pfd->rows)[rowno])[3*colno+1]); dptr[2] = (dk4_px_t)(((pfd->rows)[rowno])[3*colno+2]); } else { dptr[0] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[6 * colno], ((pfd->rows)[rowno])[6 * colno + 1] ); dptr[1] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[6 * colno + 2], ((pfd->rows)[rowno])[6 * colno + 3] ); dptr[2] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[6 * colno + 4], ((pfd->rows)[rowno])[6 * colno + 5] ); } back = 1; } break; case DK4_NETPBM_TT_BLACKANDWHITE_ALPHA : { if (0 != ((pfd->rows)[rowno])[2 * colno]) { dptr[0] = 1; } else { dptr[0] = 0; } if (0 != ((pfd->rows)[rowno])[2 * colno + 1]) { dptr[1] = 1; } else { dptr[1] = 0; } back = 1; } break; case DK4_NETPBM_TT_GRAYSCALE_ALPHA : { if (256 > pfd->maxval) { dptr[0] = (dk4_px_t)(((pfd->rows)[rowno])[2*colno]); dptr[1] = (dk4_px_t)(((pfd->rows)[rowno])[2*colno+1]); } else { dptr[0] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[4 * colno], ((pfd->rows)[rowno])[4 * colno + 1] ); dptr[1] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[4 * colno + 2], ((pfd->rows)[rowno])[4 * colno + 3] ); } back = 1; } break; case DK4_NETPBM_TT_RGB_ALPHA : { if (256 > pfd->maxval) { dptr[0] = (dk4_px_t)(((pfd->rows)[rowno])[4*colno]); dptr[1] = (dk4_px_t)(((pfd->rows)[rowno])[4*colno+1]); dptr[2] = (dk4_px_t)(((pfd->rows)[rowno])[4*colno+2]); dptr[3] = (dk4_px_t)(((pfd->rows)[rowno])[4*colno+3]); } else { dptr[0] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[8 * colno], ((pfd->rows)[rowno])[8 * colno + 1] ); dptr[1] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[8 * colno + 2], ((pfd->rows)[rowno])[8 * colno + 3] ); dptr[2] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[8 * colno + 4], ((pfd->rows)[rowno])[8 * colno + 5] ); dptr[3] = dk4bifnetpbm_combine_bytes( ((pfd->rows)[rowno])[8 * colno + 6], ((pfd->rows)[rowno])[8 * colno + 7] ); } back = 1; } break; default : { if (256 > pfd->maxval) { ucptr = (pfd->rows)[(size_t)rowno]; for (i = 0; i < pfd->ch; i++) { dptr[i] = (dk4_px_t)(ucptr[pfd->ch * (size_t)colno + i]); } } else { ucptr = (pfd->rows)[(size_t)rowno]; for (i = 0; i < pfd->ch; i++) { dptr[i] = dk4bifnetpbm_combine_bytes( ucptr[2*(pfd->ch * (size_t)colno + i)], ucptr[2*(pfd->ch * (size_t)colno + i)+1] ); } } back = 1; } break; } } break; } finished: return back; } /* vim: set ai sw=4 ts=4 : */