diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2022-12-31 00:32:37 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2022-12-31 00:32:37 +0000 |
commit | a70405848cb06782036364a88e27f452fb0a0b32 (patch) | |
tree | 514c1d8e50b2989b77b4e1958ea1e0e149ec13c3 | |
parent | 1c19660f1cdb62646f324615d4842a46c7681303 (diff) | |
download | netpbm-mirror-a70405848cb06782036364a88e27f452fb0a0b32.tar.gz netpbm-mirror-a70405848cb06782036364a88e27f452fb0a0b32.tar.xz netpbm-mirror-a70405848cb06782036364a88e27f452fb0a0b32.zip |
promote Development to Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@4489 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r-- | converter/other/jpeg2000/libjasper/base/jas_image.c | 33 | ||||
-rw-r--r-- | converter/other/jpeg2000/libjasper/jpc/jpc_enc.c | 1377 | ||||
-rw-r--r-- | converter/other/jpeg2000/libjasper/jpc/jpc_math.c | 2 | ||||
-rw-r--r-- | converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c | 163 | ||||
-rw-r--r-- | converter/other/jpeg2000/libjasper/jpc/jpc_util.c | 71 | ||||
-rw-r--r-- | converter/other/jpeg2000/pamtojpeg2k.c | 11 | ||||
-rw-r--r-- | converter/ppm/ppmtompeg/frame.c | 68 | ||||
-rw-r--r-- | converter/ppm/ppmtompeg/readframe.c | 103 | ||||
-rw-r--r-- | doc/HISTORY | 18 | ||||
-rw-r--r-- | editor/pamcat.c | 214 | ||||
-rw-r--r-- | editor/pamhomography.c | 760 | ||||
-rw-r--r-- | other/pnmcolormap.c | 135 | ||||
-rw-r--r-- | test/Test-Order | 1 | ||||
-rwxr-xr-x | test/cut-cat-roundtrip.test | 18 | ||||
-rw-r--r-- | test/ilbm-roundtrip.ok | 2 | ||||
-rwxr-xr-x | test/ilbm-roundtrip.test | 2 | ||||
-rwxr-xr-x | test/lps-roundtrip.test | 2 | ||||
-rw-r--r-- | test/pamarith.ok | 2 | ||||
-rwxr-xr-x | test/pamarith.test | 2 | ||||
-rwxr-xr-x | test/pamcat2.test | 10 | ||||
-rw-r--r-- | test/pamcat3.ok | 19 | ||||
-rwxr-xr-x | test/pamcat3.test | 97 | ||||
-rwxr-xr-x | test/stdin-pam3.test | 2 | ||||
-rw-r--r-- | version.mk | 4 |
24 files changed, 1809 insertions, 1307 deletions
diff --git a/converter/other/jpeg2000/libjasper/base/jas_image.c b/converter/other/jpeg2000/libjasper/base/jas_image.c index 5ee13a0d..c6f984c1 100644 --- a/converter/other/jpeg2000/libjasper/base/jas_image.c +++ b/converter/other/jpeg2000/libjasper/base/jas_image.c @@ -675,7 +675,7 @@ int jas_image_fmtfromname(char *name) return -1; } ++ext; - /* Try to find a format that uses this extension. */ + /* Try to find a format that uses this extension. */ for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i, ++fmtinfo) { /* Do we have a match? */ @@ -686,25 +686,28 @@ int jas_image_fmtfromname(char *name) return -1; } -/******************************************************************************\ +/*****************************************************************************\ * Miscellaneous operations. -\******************************************************************************/ +\*****************************************************************************/ -uint_fast32_t jas_image_rawsize(jas_image_t *image) -{ - uint_fast32_t rawsize; - uint_fast32_t cmptno; - jas_image_cmpt_t *cmpt; +uint_fast32_t +jas_image_rawsize(jas_image_t * const imageP) { +/*---------------------------------------------------------------------------- + The raw size of the image, i.e. the number of bytes the raster of the image + would take if just represented simply, with no compression. +-----------------------------------------------------------------------------*/ + uint_fast32_t rawsize; + uint_fast32_t cmptno; - rawsize = 0; - for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) { - cmpt = image->cmpts_[cmptno]; - rawsize += (cmpt->width_ * cmpt->height_ * cmpt->prec_ + - 7) / 8; - } - return rawsize; + for (cmptno = 0, rawsize = 0; cmptno < imageP->numcmpts_; ++cmptno) { + jas_image_cmpt_t * const cmptP = imageP->cmpts_[cmptno]; + rawsize += (cmptP->width_ * cmptP->height_ * cmptP->prec_ + 7) / 8; + } + return rawsize; } + + void jas_image_delcmpt(jas_image_t *image, uint_fast16_t cmptno) { if (cmptno >= image->numcmpts_) { diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c index 9db41ca2..0c77a94e 100644 --- a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c +++ b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c @@ -69,7 +69,6 @@ static void prc_destroy(jpc_enc_prc_t *prcs); static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp, jpc_enc_prc_t *prc); static void cblk_destroy(jpc_enc_cblk_t *cblks); -int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size); static void pass_destroy(jpc_enc_pass_t *pass); void jpc_enc_dump(jpc_enc_t *enc); @@ -78,17 +77,8 @@ void jpc_enc_dump(jpc_enc_t *enc); \*****************************************************************************/ void quantize(jas_matrix_t *data, jpc_fix_t stepsize); -static int jpc_enc_encodemainhdr(jpc_enc_t *enc); -static int jpc_enc_encodemainbody(jpc_enc_t *enc); -int jpc_enc_encodetiledata(jpc_enc_t *enc); jpc_enc_t *jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image); void jpc_enc_destroy(jpc_enc_t *enc); -static int jpc_enc_encodemainhdr(jpc_enc_t *enc); -static int jpc_enc_encodemainbody(jpc_enc_t *enc); -int jpc_enc_encodetiledata(jpc_enc_t *enc); -int setins(int numvalues, jpc_flt_t *values, jpc_flt_t value); -static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image); -void jpc_enc_cp_destroy(jpc_enc_cp_t *cp); @@ -143,7 +133,6 @@ typedef enum { OPT_NUMGBITS, OPT_RATE, OPT_ILYRRATES, - OPT_JP2OVERHEAD } optid_t; jas_taginfo_t encopts[] = { @@ -173,7 +162,6 @@ jas_taginfo_t encopts[] = { {OPT_NUMGBITS, "numgbits"}, {OPT_RATE, "rate"}, {OPT_ILYRRATES, "ilyrrates"}, - {OPT_JP2OVERHEAD, "_jp2overhead"}, {-1, 0} }; @@ -230,72 +218,46 @@ trace(const char * const fmt, ...) { -int -jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr) { - - jpc_enc_t *enc; - jpc_enc_cp_t *cp; - - enc = 0; - cp = 0; - - jpc_initluts(); - - if (!(cp = cp_create(optstr, image))) { - fprintf(stderr, "invalid JP encoder options\n"); - goto error; - } - - if (!(enc = jpc_enc_create(cp, out, image))) { - goto error; - } - cp = 0; +/*****************************************************************************\ +* Option parsing code. +\*****************************************************************************/ - /* Encode the main header. */ - if (jpc_enc_encodemainhdr(enc)) { - goto error; - } +static void +ratestrtosize(const char * const s, + uint_fast32_t const rawsize, + uint_fast32_t * const sizeP) { - /* Encode the main body. This constitutes most of the encoding work. */ - if (jpc_enc_encodemainbody(enc)) { - goto error; - } + if (strchr(s, 'B')) { + *sizeP = atoi(s); + } else { + jpc_flt_t const f = atof(s); - /* Write EOC marker segment. */ - if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) { - goto error; - } - if (jpc_putms(enc->out, enc->cstate, enc->mrk)) { - fprintf(stderr, "cannot write EOI marker\n"); - goto error; + if (f < 0) { + *sizeP = 0; + } else if (f > 1.0) { + *sizeP = rawsize + 1; + } else { + *sizeP = f * rawsize; + } } - jpc_ms_destroy(enc->mrk); - enc->mrk = 0; +} - if (jas_stream_flush(enc->out)) { - goto error; - } - jpc_enc_destroy(enc); - return 0; +static void +cp_destroy(jpc_enc_cp_t *cp) { -error: - if (cp) { - jpc_enc_cp_destroy(cp); - } - if (enc) { - jpc_enc_destroy(enc); + if (cp->ccps) { + if (cp->tcp.ilyrrates) { + jas_free(cp->tcp.ilyrrates); + } + jas_free(cp->ccps); } - return -1; + jas_free(cp); } -/*****************************************************************************\ -* Option parsing code. -\*****************************************************************************/ - static jpc_enc_cp_t * cp_create(char *optstr, jas_image_t *image) { @@ -314,7 +276,6 @@ cp_create(char *optstr, jas_image_t *image) { uint_fast16_t prcwidthexpn; uint_fast16_t prcheightexpn; bool enablemct; - uint_fast32_t jp2overhead; uint_fast16_t lyrno; uint_fast32_t hsteplcm; uint_fast32_t vsteplcm; @@ -332,7 +293,6 @@ cp_create(char *optstr, jas_image_t *image) { prcwidthexpn = 15; prcheightexpn = 15; enablemct = true; - jp2overhead = 0; cp->ccps = 0; cp->debug = 0; @@ -380,6 +340,10 @@ cp_create(char *optstr, jas_image_t *image) { cp->rawsize = jas_image_rawsize(image); cp->totalsize = UINT_FAST32_MAX; + /* Set default value, the special value that means size is unlimited + (so lossless coding is called for). To be overridden if user + specified + */ tcp = &cp->tcp; tcp->csty = 0; @@ -492,12 +456,8 @@ cp_create(char *optstr, jas_image_t *image) { cp->tccp.numgbits = atoi(jas_tvparser_getval(tvp)); break; case OPT_RATE: - if (ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize, - &cp->totalsize)) { - fprintf(stderr, - "ignoring bad rate specifier %s\n", - jas_tvparser_getval(tvp)); - } + ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize, + &cp->totalsize); break; case OPT_ILYRRATES: if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates, @@ -505,13 +465,10 @@ cp_create(char *optstr, jas_image_t *image) { fprintf(stderr, "warning: invalid intermediate layer rates specifier " "ignored (%s)\n", - jas_tvparser_getval(tvp)); + jas_tvparser_getval(tvp)); } break; - case OPT_JP2OVERHEAD: - jp2overhead = atoi(jas_tvparser_getval(tvp)); - break; default: fprintf(stderr, "warning: ignoring invalid option %s\n", jas_tvparser_gettag(tvp)); @@ -522,11 +479,6 @@ cp_create(char *optstr, jas_image_t *image) { jas_tvparser_destroy(tvp); tvp = 0; - if (cp->totalsize != UINT_FAST32_MAX) { - cp->totalsize = (cp->totalsize > jp2overhead) ? - (cp->totalsize - jp2overhead) : 0; - } - if (cp->imgareatlx == UINT_FAST32_MAX) { cp->imgareatlx = 0; } else { @@ -697,20 +649,28 @@ cp_create(char *optstr, jas_image_t *image) { /* The intermediate layers rates must increase monotonically. */ for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) { if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) { - fprintf(stderr, - "intermediate layer rates must increase " - "monotonically\n"); + pm_message("Compression rate for Layer %u (%f) " + "is not greater than that for Layer %u (%f). " + "Rates must increase at every layer", + (unsigned)(lyrno+1), + jpc_fixtodbl(tcp->ilyrrates[lyrno + 1]), + (unsigned)lyrno, + jpc_fixtodbl(tcp->ilyrrates[lyrno])); goto error; } } /* The intermediate layer rates must be less than the overall rate. */ if (cp->totalsize != UINT_FAST32_MAX) { for (lyrno = 0; lyrno < tcp->numlyrs - 1; ++lyrno) { - if (jpc_fixtodbl(tcp->ilyrrates[lyrno]) > - ((double) cp->totalsize) / cp->rawsize) { - fprintf(stderr, - "warning: intermediate layer rates must be " - "less than overall rate\n"); + double const thisLyrRate = jpc_fixtodbl(tcp->ilyrrates[lyrno]); + double const completeRate = + ((double) cp->totalsize) / cp->rawsize; + if (thisLyrRate > completeRate) { + pm_message( + "Compression rate for Layer %u is %f, " + "which is greater than the rate for the complete " + "image (%f)", + (unsigned)lyrno, thisLyrRate, completeRate); goto error; } } @@ -732,118 +692,17 @@ error: jas_tvparser_destroy(tvp); } if (cp) { - jpc_enc_cp_destroy(cp); - } - return 0; -} - - - -void -jpc_enc_cp_destroy(jpc_enc_cp_t *cp) { - - if (cp->ccps) { - if (cp->tcp.ilyrrates) { - jas_free(cp->tcp.ilyrrates); - } - jas_free(cp->ccps); - } - jas_free(cp); -} - - - -int -ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size) { - - char *cp; - jpc_flt_t f; - - /* Note: This function must not modify output size on failure. */ - if ((cp = strchr(s, 'B'))) { - *size = atoi(s); - } else { - f = atof(s); - if (f < 0) { - *size = 0; - } else if (f > 1.0) { - *size = rawsize + 1; - } else { - *size = f * rawsize; - } - } - return 0; -} - -/*****************************************************************************\ -* Encoder constructor and destructor. -\*****************************************************************************/ - -jpc_enc_t * -jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image) { - - jpc_enc_t *enc; - - enc = 0; - - if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) { - goto error; - } - - enc->image = image; - enc->out = out; - enc->cp = cp; - enc->cstate = 0; - enc->tmpstream = 0; - enc->mrk = 0; - enc->curtile = 0; - - if (!(enc->cstate = jpc_cstate_create())) { - goto error; - } - enc->len = 0; - enc->mainbodysize = 0; - - return enc; - -error: - - if (enc) { - jpc_enc_destroy(enc); + cp_destroy(cp); } return 0; } -void -jpc_enc_destroy(jpc_enc_t *enc) { - - /* The image object (i.e., enc->image) and output stream object - (i.e., enc->out) are created outside of the encoder. - Therefore, they must not be destroyed here. - */ - - if (enc->curtile) { - jpc_enc_tile_destroy(enc->curtile); - } - if (enc->cp) { - jpc_enc_cp_destroy(enc->cp); - } - if (enc->cstate) { - jpc_cstate_destroy(enc->cstate); - } - if (enc->tmpstream) { - jas_stream_close(enc->tmpstream); - } - - jas_free(enc); -} - - - static int -jpc_enc_encodemainhdr(jpc_enc_t *enc) { +encodemainhdr(jpc_enc_t *enc) { + + uint_fast32_t const maintlrlen = 2; jpc_siz_t *siz; jpc_cod_t *cod; @@ -961,7 +820,7 @@ jpc_enc_encodemainhdr(jpc_enc_t *enc) { (analgain + 1)), bandinfo->synenergywt); } else { absstepsize = jpc_inttofix(1); - } + } cp->ccps[cmptno].stepsizes[bandno] = jpc_abstorelstepsize(absstepsize, cp->ccps[cmptno].prec + analgain); @@ -1038,61 +897,32 @@ jpc_enc_encodemainhdr(jpc_enc_t *enc) { enc->mrk = 0; } -#define MAINTLRLEN 2 mainhdrlen = jas_stream_getrwcount(enc->out) - startoff; enc->len += mainhdrlen; if (enc->cp->totalsize != UINT_FAST32_MAX) { - uint_fast32_t overhead; - overhead = mainhdrlen + MAINTLRLEN; - enc->mainbodysize = (enc->cp->totalsize >= overhead) ? - (enc->cp->totalsize - overhead) : 0; - } else { - enc->mainbodysize = UINT_FAST32_MAX; - } - - return 0; -} - - - -int -jpc_enc_encodetiledata(jpc_enc_t *enc) { - - assert(enc->tmpstream); - if (jpc_enc_encpkts(enc, enc->tmpstream)) { - return -1; - } - return 0; -} - - - -void -quantize(jas_matrix_t *data, jpc_fix_t stepsize) { + uint_fast32_t const overhead = mainhdrlen + maintlrlen; - int i; - int j; - jpc_fix_t t; - - if (stepsize == jpc_inttofix(1)) { - return; - } - - for (i = 0; i < jas_matrix_numrows(data); ++i) { - for (j = 0; j < jas_matrix_numcols(data); ++j) { - t = jas_matrix_get(data, i, j); + if (overhead > enc->cp->totalsize) { + pm_message("Requested limit on image size of %u bytes " + "is not possible because it is less than " + "the image metadata size (%u bytes)", + (unsigned)enc->cp->totalsize, (unsigned)overhead); + return -1; + } + enc->mainbodysize = enc->cp->totalsize - overhead; + /* This has never actually worked. 'totalsize' is supposed to be + the total all-in, so if you request total size 200, you should + get an output file 200 bytes or smaller; but we see 209 bytes. + Furthermore, at 194 bytes, we get a warning that an empty layer + is generated, which probably is actually an error. -{ - if (t < 0) { - t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize)); + We should fix this some day. + */ } else { - t = jpc_fix_div(t, stepsize); + enc->mainbodysize = UINT_FAST32_MAX; } -} - jas_matrix_set(data, i, j, t); - } - } + return 0; } @@ -1166,12 +996,12 @@ calcrdslopes(jpc_enc_cblk_t *cblk) { static void traceLayerSizes(const uint_fast32_t * const lyrSizes, - unsigned int const layerCt) { + uint_fast32_t const layerCt) { if (jas_getdbglevel() > 0) { - unsigned int i; + uint_fast32_t i; for (i = 0; i < layerCt; ++i) { - fprintf(stderr, "Layer %u size = ", i); + fprintf(stderr, "Layer %u size = ", (unsigned)i); if (lyrSizes[i] == UINT_FAST32_MAX) fprintf(stderr, "Unlimited"); @@ -1189,48 +1019,28 @@ computeLayerSizes(jpc_enc_t * const encP, jpc_enc_tile_t * const tileP, jpc_enc_cp_t * const cpP, double const rho, - long const tilehdrlen, - const char ** const errorP) { + long const tilehdrlen) { /* Note that in allowed sizes, UINT_FAST32_MAX is a special value meaning "unlimited". */ - unsigned int const lastLyrno = tileP->numlyrs - 1; + uint_fast32_t const lastLyrno = tileP->numlyrs - 1; - unsigned int lyrno; + uint_fast32_t lyrno; assert(tileP->numlyrs > 0); for (lyrno = 0; lyrno < lastLyrno; ++lyrno) { - tileP->lyrsizes[lyrno] = tileP->rawsize * jpc_fixtodbl( - cpP->tcp.ilyrrates[lyrno]); + tileP->lyrsizes[lyrno] = + MAX(tileP->rawsize * + jpc_fixtodbl(cpP->tcp.ilyrrates[lyrno]), + tilehdrlen + 1) - tilehdrlen; } tileP->lyrsizes[lastLyrno] = - (cpP->totalsize != UINT_FAST32_MAX) ? - (rho * encP->mainbodysize) : UINT_FAST32_MAX; - - - /* Subtract 'tilehdrlen' from every layer. */ - - for (lyrno = 0; lyrno < tileP->numlyrs; ++lyrno) { - if (tileP->lyrsizes[lyrno] != UINT_FAST32_MAX) { - if (tilehdrlen <= tileP->lyrsizes[lyrno]) { - tileP->lyrsizes[lyrno] -= tilehdrlen; - } else { - tileP->lyrsizes[lyrno] = 0; - } - } - } - - if (tileP->lyrsizes[lastLyrno] < 1) - pm_asprintf(errorP, "Cannot make image that small (%u bytes). " - "Even with pixels compressed as far as possible, metadata " - "would exceed the limit", - (unsigned)cpP->totalsize); - else - *errorP = NULL; + (cpP->totalsize == UINT_FAST32_MAX) ? + UINT_FAST32_MAX : (rho * encP->mainbodysize); traceLayerSizes(tileP->lyrsizes, tileP->numlyrs); } @@ -1313,8 +1123,8 @@ trace_layeringinfo(jpc_enc_t * const encP) { static void validateCumlensIncreases(const uint_fast32_t * const cumlens, - unsigned int const numlyrs) { - unsigned int lyrno; + uint_fast32_t const numlyrs) { + uint_fast32_t lyrno; for (lyrno = 1; lyrno < numlyrs - 1; ++lyrno) { if (cumlens[lyrno - 1] > cumlens[lyrno]) { @@ -1404,7 +1214,7 @@ findMinMaxRDSlopeValues(jpc_enc_tile_t * const tileP, static void performTier2CodingOneLayer(jpc_enc_t * const encP, jpc_enc_tile_t * const tileP, - unsigned int const lyrno, + uint_fast32_t const lyrno, jas_stream_t * const outP, const char ** const errorP) { /*---------------------------------------------------------------------------- @@ -1444,9 +1254,9 @@ performTier2CodingOneLayer(jpc_enc_t * const encP, static void assignHighSlopePassesToLayer(jpc_enc_t * const encP, jpc_enc_tile_t * const tileP, - unsigned int const lyrno, - bool const haveThresh, - jpc_flt_t const thresh) { + uint_fast32_t const lyrno, + bool const haveThresh, + jpc_flt_t const thresh) { /*---------------------------------------------------------------------------- Assign all passes with R-D slopes greater than or equal to 'thresh' to layer 'lyrno' and the rest to no layer. @@ -1505,8 +1315,7 @@ assignHighSlopePassesToLayer(jpc_enc_t * const encP, for (; passP != endpassesP; ++passP) { passP->lyrno = -1; } - - } + } } } } @@ -1522,7 +1331,7 @@ assignHighSlopePassesToLayer(jpc_enc_t * const encP, static void doLayer(jpc_enc_t * const encP, jpc_enc_tile_t * const tileP, - unsigned int const lyrno, + uint_fast32_t const lyrno, uint_fast32_t const allowedSize, jpc_flt_t const mnrdslope, jpc_flt_t const mxrdslope, @@ -1550,7 +1359,7 @@ doLayer(jpc_enc_t * const encP, long pos; jpc_flt_t lo; jpc_flt_t hi; - unsigned int numiters; + uint_fast32_t numiters; lo = mnrdslope; /* initial value */ hi = mxrdslope; /* initial value */ @@ -1559,52 +1368,46 @@ doLayer(jpc_enc_t * const encP, goodThresh = 0; /* initial value */ do { - if (allowedSize == UINT_FAST32_MAX) { - /* There's no rate constraint (This can be true of the last - layer, e.g. for lossless coding). */ - goodThresh = -1; - haveGoodThresh = true; - } else { - jpc_flt_t const thresh = (lo + hi) / 2; - - int rc; - long oldpos; - - /* Save the tier 2 coding state. */ - jpc_save_t2state(encP); - oldpos = jas_stream_tell(outP); - assert(oldpos >= 0); - - assignHighSlopePassesToLayer(encP, tileP, lyrno, true, thresh); - - performTier2CodingOneLayer(encP, tileP, lyrno, outP, errorP); - - if (!*errorP) { - pos = jas_stream_tell(outP); - - /* Check the rate constraint. */ - assert(pos >= 0); - if (pos > allowedSize) { - /* The rate is too high. */ - lo = thresh; - } else if (pos <= allowedSize) { - /* The rate is low enough, so try higher. */ - hi = thresh; - if (!haveGoodThresh || thresh < goodThresh) { - goodThresh = thresh; - haveGoodThresh = true; - } + jpc_flt_t const thresh = (lo + hi) / 2; + + int rc; + long oldpos; + + /* Save the tier 2 coding state. */ + jpc_save_t2state(encP); + oldpos = jas_stream_tell(outP); + assert(oldpos >= 0); + + assignHighSlopePassesToLayer(encP, tileP, lyrno, true, thresh); + + performTier2CodingOneLayer(encP, tileP, lyrno, outP, errorP); + + if (!*errorP) { + pos = jas_stream_tell(outP); + + /* Check the rate constraint. */ + assert(pos >= 0); + if (pos > allowedSize) { + /* The rate is too high. */ + lo = thresh; + } else if (pos <= allowedSize) { + /* The rate is low enough, so try higher. */ + hi = thresh; + if (!haveGoodThresh || thresh < goodThresh) { + goodThresh = thresh; + haveGoodThresh = true; } } - /* Restore the tier 2 coding state. */ - jpc_restore_t2state(encP); - rc = jas_stream_seek(outP, oldpos, SEEK_SET); - if (rc < 0) - abort(); - - trace("iter %u: allowedlen=%08ld actuallen=%08ld thresh=%f", - numiters, allowedSize, pos, thresh); } + /* Restore the tier 2 coding state. */ + jpc_restore_t2state(encP); + rc = jas_stream_seek(outP, oldpos, SEEK_SET); + if (rc < 0) + abort(); + + trace("iter %u: allowedlen=%08ld actuallen=%08ld thresh=%f", + numiters, allowedSize, pos, thresh); + ++numiters; } while (lo < hi - 1e-3 && numiters < 32 && !*errorP); } @@ -1625,10 +1428,10 @@ doLayer(jpc_enc_t * const encP, static void -performTier2Coding(jpc_enc_t * const encP, - unsigned int const numlyrs, - uint_fast32_t * const cumlens, - const char ** const errorP) { +performTier2Coding(jpc_enc_t * const encP, + uint_fast32_t const numlyrs, + uint_fast32_t * const cumlens, + const char ** const errorP) { /*---------------------------------------------------------------------------- Encode in 'numlyrs' layers, such that at each layer L, the size is cumlens[L]. @@ -1636,7 +1439,7 @@ performTier2Coding(jpc_enc_t * const encP, jpc_enc_tile_t * const tileP = encP->curtile; jas_stream_t * outP; - unsigned int lyrno; + uint_fast32_t lyrno; jpc_flt_t mnrdslope; jpc_flt_t mxrdslope; @@ -1669,6 +1472,562 @@ performTier2Coding(jpc_enc_t * const encP, + + +static void +encodeTileBody(jpc_enc_t * const encoderP, + long const tilehdrlen, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Encode the body of encoder *encoderP's current tile, writing the encoded + result to the encoder's output stream. + + Assume the tile header is already in that stream, and its length is + 'tilehdrlen'. +-----------------------------------------------------------------------------*/ + jpc_enc_tile_t * const tileP = encoderP->curtile; + jpc_enc_cp_t * const cp = encoderP->cp; + + int rc; + + rc = jpc_enc_enccblks(encoderP); + if (rc != 0) + pm_asprintf(errorP, "jpc_enc_enccblks() failed"); + else { + double const rho = + (double) (tileP->brx - tileP->tlx) * (tileP->bry - tileP->tly) / + ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight - + cp->imgareatly)); + const char * error; + + tileP->rawsize = cp->rawsize * rho; + + computeLayerSizes(encoderP, tileP, cp, rho, tilehdrlen); + + performTier2Coding(encoderP, tileP->numlyrs, tileP->lyrsizes, &error); + + if (error) { + pm_asprintf(errorP, "Tier 2 coding failed. %s", error); + pm_strfree(error); + } else { + int rc; + + rc = jpc_enc_encpkts(encoderP, encoderP->tmpstream); + if (rc != 0) + pm_asprintf(errorP, "jpc_enc_encpkts() failed\n"); + else + *errorP = NULL; + } + } +} + + + +static void +quantizeBand(jpc_enc_band_t * const bandP, + jpc_enc_tile_t * const tileP, + jpc_enc_cp_t * const cp, + int const prec, + int const tccp_numgbits, + int * const numgbitsP) { + + if (bandP->data) { + int actualnumbps; + uint_fast32_t y; + jpc_fix_t mxmag; + + for (y = 0, actualnumbps = 0, mxmag = 0; + y < jas_matrix_numrows(bandP->data); + ++y) { + uint_fast32_t x; + + for (x = 0; x < jas_matrix_numcols(bandP->data); ++x) + mxmag = MAX(mxmag, abs(jas_matrix_get(bandP->data, y, x))); + } + if (tileP->intmode) + actualnumbps = jpc_firstone(mxmag) + 1; + else + actualnumbps = jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS; + + *numgbitsP = actualnumbps - (prec - 1 + bandP->analgain); + + if (!tileP->intmode) { + bandP->absstepsize = + jpc_fix_div( + jpc_inttofix(1 << (bandP->analgain + 1)), + bandP->synweight); + } else { + bandP->absstepsize = jpc_inttofix(1); + } + bandP->stepsize = jpc_abstorelstepsize( + bandP->absstepsize, prec + bandP->analgain); + /* I couldn't figure out what the calculation with tccp_numgbits and + stepsize does (or even what a step size is), but there is an + assertion elsewhere than the number here is at least at large as + the 'numbps' value for every code block, which means + 'actualnumbps'. In practice, we saw that not be true, so we added + the code to make 'actualnumbps' the floor here in hopes that would + fix the problem. But with the change, the image that caused the + assertion failure produces incorrect output. 22.11.07 + */ + bandP->numbps = + MAX(actualnumbps, + tccp_numgbits + JPC_QCX_GETEXPN(bandP->stepsize) - 1); + + if (!tileP->intmode && bandP->data) + quantize(bandP->data, bandP->absstepsize); + } else + *numgbitsP = 0; +} + + + +static int +encodemainbody(jpc_enc_t *enc) { + + int tileno; + int i; + jpc_sot_t *sot; + int rlvlno; + jpc_qcc_t *qcc; + jpc_cod_t *cod; + int adjust; + int j; + int absbandno; + long tilehdrlen; + long tilelen; + jpc_enc_tile_t *tile; + jpc_enc_cp_t *cp; + int samestepsizes; + jpc_enc_ccp_t *ccps; + jpc_enc_tccp_t *tccp; + int mingbits; /* Minimum number of guard bits needed */ + const char * error; + + cp = enc->cp; + + for (tileno = 0; tileno < cp->numtiles; ++tileno) { + uint_fast16_t cmptno; + + enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno); + if (!enc->curtile) + abort(); + + tile = enc->curtile; + + if (jas_getdbglevel() >= 10) { + jpc_enc_dump(enc); + } + + for (cmptno = 0; cmptno < tile->numtcmpts; ++cmptno) { + + jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno]; + + if (!cp->ccps[cmptno].sgnd) { + adjust = 1 << (cp->ccps[cmptno].prec - 1); + for (i = 0; i < jas_matrix_numrows(comp->data); ++i) { + for (j = 0; j < jas_matrix_numcols(comp->data); ++j) { + *jas_matrix_getref(comp->data, i, j) -= adjust; + } + } + } + } + + if (!tile->intmode) { + uint_fast16_t cmptno; + for (cmptno = 0; cmptno < tile->numtcmpts; ++cmptno) { + jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno]; + jas_matrix_asl(comp->data, JPC_FIX_FRACBITS); + } + } + + switch (tile->mctid) { + case JPC_MCT_RCT: + assert(jas_image_numcmpts(enc->image) == 3); + jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data, + tile->tcmpts[2].data); + break; + case JPC_MCT_ICT: + assert(jas_image_numcmpts(enc->image) == 3); + jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data, + tile->tcmpts[2].data); + break; + default: + break; + } + + for (i = 0; i < jas_image_numcmpts(enc->image); ++i) { + jpc_enc_tcmpt_t * const comp = &tile->tcmpts[i]; + jpc_tsfb_analyze(comp->tsfb, + ((comp->qmfbid == JPC_COX_RFT) ? + JPC_TSFB_RITIMODE : 0), comp->data); + + } + + for (cmptno = 0; cmptno < tile->numtcmpts; ++cmptno) { + + jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno]; + + mingbits = 0; + absbandno = 0; + /* All bands must have a corresponding quantizer step size, + even if they contain no samples and are never coded. */ + /* Some bands may not be hit by the loop below, so we must + initialize all of the step sizes to a sane value. */ + memset(comp->stepsizes, 0, sizeof(comp->stepsizes)); + for (rlvlno = 0; rlvlno < comp->numrlvls; ++rlvlno) { + jpc_enc_rlvl_t * const lvl = &comp->rlvls[rlvlno]; + + unsigned int bandno; + + if (!lvl->bands) { + absbandno += rlvlno ? 3 : 1; + continue; + } + for (bandno = 0; bandno < lvl->numbands; ++bandno) { + jpc_enc_band_t * const band = &lvl->bands[bandno]; + + int numgbits; + + quantizeBand(band, tile, cp, + cp->ccps[cmptno].prec, + cp->tccp.numgbits, + &numgbits); + + mingbits = MAX(mingbits, numgbits); + + comp->stepsizes[absbandno] = band->stepsize; + + ++absbandno; + } + } + + assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS); + if (!tile->intmode) { + jas_matrix_divpow2(comp->data, + JPC_FIX_FRACBITS - JPC_NUMEXTRABITS); + } else { + jas_matrix_asl(comp->data, JPC_NUMEXTRABITS); + } + } + + if (mingbits > cp->tccp.numgbits) { + fprintf(stderr, "error: too few guard bits (need at least %d)\n", + mingbits); + return -1; + } + + enc->tmpstream = jas_stream_memopen(0, 0); + if (!enc->tmpstream) { + fprintf(stderr, "cannot open tmp file\n"); + return -1; + } + + /* Write the tile header. */ + enc->mrk = jpc_ms_create(JPC_MS_SOT); + if (!enc->mrk) + return -1; + sot = &enc->mrk->parms.sot; + sot->len = 0; + sot->tileno = tileno; + sot->partno = 0; + sot->numparts = 1; + if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { + fprintf(stderr, "cannot write SOT marker\n"); + return -1; + } + jpc_ms_destroy(enc->mrk); + enc->mrk = 0; + +/************************************************************************/ +/************************************************************************/ +/************************************************************************/ + + tccp = &cp->tccp; + for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) { + jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno]; + jpc_enc_tcmpt_t * const comp0 = &tile->tcmpts[0]; + + if (comp->numrlvls != tccp->maxrlvls) { + if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) { + return -1; + } + /* XXX = this is not really correct. we are using comp #0's + precint sizes and other characteristics */ + cod = &enc->mrk->parms.cod; + cod->compparms.csty = 0; + cod->compparms.numdlvls = comp0->numrlvls - 1; + cod->prg = tile->prg; + cod->numlyrs = tile->numlyrs; + cod->compparms.cblkwidthval = + JPC_COX_CBLKSIZEEXPN(comp0->cblkwidthexpn); + cod->compparms.cblkheightval = + JPC_COX_CBLKSIZEEXPN(comp0->cblkheightexpn); + cod->compparms.cblksty = comp0->cblksty; + cod->compparms.qmfbid = comp0->qmfbid; + cod->mctrans = (tile->mctid != JPC_MCT_NONE); + for (i = 0; i < comp0->numrlvls; ++i) { + cod->compparms.rlvls[i].parwidthval = + comp0->rlvls[i].prcwidthexpn; + cod->compparms.rlvls[i].parheightval = + comp0->rlvls[i].prcheightexpn; + } + if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { + return -1; + } + jpc_ms_destroy(enc->mrk); + enc->mrk = 0; + } + } + + for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) { + jpc_enc_tcmpt_t * const comp = &tile->tcmpts[cmptno]; + + ccps = &cp->ccps[cmptno]; + if (ccps->numstepsizes == comp->numstepsizes) { + unsigned int bandno; + samestepsizes = 1; + for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) { + if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) { + samestepsizes = 0; + break; + } + } + } else { + samestepsizes = 0; + } + if (!samestepsizes) { + if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) { + return -1; + } + qcc = &enc->mrk->parms.qcc; + qcc->compno = cmptno; + qcc->compparms.numguard = cp->tccp.numgbits; + qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ? + JPC_QCX_SEQNT : JPC_QCX_NOQNT; + qcc->compparms.numstepsizes = comp->numstepsizes; + qcc->compparms.stepsizes = comp->stepsizes; + if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { + return -1; + } + qcc->compparms.stepsizes = 0; + jpc_ms_destroy(enc->mrk); + enc->mrk = 0; + } + } + + /* Write a SOD marker to indicate the end of the tile header. */ + if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) { + return -1; + } + if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { + fprintf(stderr, "cannot write SOD marker\n"); + return -1; + } + jpc_ms_destroy(enc->mrk); + enc->mrk = 0; + tilehdrlen = jas_stream_getrwcount(enc->tmpstream); + +/************************************************************************/ +/************************************************************************/ +/************************************************************************/ + + encodeTileBody(enc, tilehdrlen, &error); + /* Encodes current tile; writes to output file */ + + if (error) { + fprintf(stderr, "Failed to encode body of tile %u. %s\n", + tileno, error); + pm_strfree(error); + return -1; + } + tilelen = jas_stream_tell(enc->tmpstream); + + if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) { + return -1; + } + jpc_putuint32(enc->tmpstream, tilelen); + + if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) { + return -1; + } + if (jpc_putdata(enc->out, enc->tmpstream, -1)) { + return -1; + } + enc->len += tilelen; + + jas_stream_close(enc->tmpstream); + enc->tmpstream = 0; + + jpc_enc_tile_destroy(enc->curtile); + enc->curtile = 0; + + } + + return 0; +} + + + +int +jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr) { + + jpc_enc_t *enc; + jpc_enc_cp_t *cp; + + enc = 0; + cp = 0; + + jpc_initluts(); + + if (!(cp = cp_create(optstr, image))) { + fprintf(stderr, "invalid JP encoder options\n"); + goto error; + } + + if (!(enc = jpc_enc_create(cp, out, image))) { + goto error; + } + cp = 0; + + /* Encode the main header. */ + if (encodemainhdr(enc)) { + goto error; + } + + /* Encode the main body. This constitutes most of the encoding work. */ + if (encodemainbody(enc)) { + goto error; + } + + /* Write EOC marker segment. */ + if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) { + goto error; + } + if (jpc_putms(enc->out, enc->cstate, enc->mrk)) { + fprintf(stderr, "cannot write EOI marker\n"); + goto error; + } + jpc_ms_destroy(enc->mrk); + enc->mrk = 0; + + if (jas_stream_flush(enc->out)) { + goto error; + } + + jpc_enc_destroy(enc); + + return 0; + +error: + if (cp) { + cp_destroy(cp); + } + if (enc) { + jpc_enc_destroy(enc); + } + return -1; +} + + + +/*****************************************************************************\ +* Encoder constructor and destructor. +\*****************************************************************************/ + +jpc_enc_t * +jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image) { + + jpc_enc_t *enc; + + enc = 0; + + if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) { + goto error; + } + + enc->image = image; + enc->out = out; + enc->cp = cp; + enc->cstate = 0; + enc->tmpstream = 0; + enc->mrk = 0; + enc->curtile = 0; + + if (!(enc->cstate = jpc_cstate_create())) { + goto error; + } + enc->len = 0; + enc->mainbodysize = 0; + + return enc; + +error: + + if (enc) { + jpc_enc_destroy(enc); + } + return 0; +} + + + +void +jpc_enc_destroy(jpc_enc_t *enc) { + + /* The image object (i.e., enc->image) and output stream object + (i.e., enc->out) are created outside of the encoder. + Therefore, they must not be destroyed here. + */ + + if (enc->curtile) { + jpc_enc_tile_destroy(enc->curtile); + } + if (enc->cp) { + cp_destroy(enc->cp); + } + if (enc->cstate) { + jpc_cstate_destroy(enc->cstate); + } + if (enc->tmpstream) { + jas_stream_close(enc->tmpstream); + } + + jas_free(enc); +} + + + +void +quantize(jas_matrix_t *data, jpc_fix_t stepsize) { + + int i; + int j; + jpc_fix_t t; + + if (stepsize == jpc_inttofix(1)) { + return; + } + + for (i = 0; i < jas_matrix_numrows(data); ++i) { + for (j = 0; j < jas_matrix_numcols(data); ++j) { + t = jas_matrix_get(data, i, j); + +{ + if (t < 0) { + t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize)); + } else { + t = jpc_fix_div(t, stepsize); + } +} + + jas_matrix_set(data, i, j, t); + } + } +} + + + /*****************************************************************************\ * Tile constructors and destructors. \*****************************************************************************/ @@ -2227,7 +2586,7 @@ prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp, jpc_enc_band_t *band) { } prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t)); - + if (!prc->cblks) goto error; for (cblkno = 0, cblk = prc->cblks; @@ -2445,9 +2804,9 @@ jpc_enc_dump(jpc_enc_t *enc) { prcno < rlvl->numprcs; ++prcno, ++prc) { fprintf(stderr, " prc %5d %5d %5d %5d (%5d %5d)\n", - (int)prc->tlx, (int)prc->tly, + (int)prc->tlx, (int)prc->tly, (int)prc->brx, (int)prc->bry, - (int)(prc->brx - prc->tlx), + (int)(prc->brx - prc->tlx), (int)(prc->bry - prc->tly)); if (!prc->cblks) { continue; @@ -2468,352 +2827,6 @@ jpc_enc_dump(jpc_enc_t *enc) { -static int -jpc_enc_encodemainbody(jpc_enc_t *enc) { - - int tileno; - int i; - jpc_sot_t *sot; - jpc_enc_tcmpt_t *comp; - jpc_enc_tcmpt_t *endcomps; - jpc_enc_band_t *band; - jpc_enc_band_t *endbands; - jpc_enc_rlvl_t *lvl; - int rlvlno; - jpc_qcc_t *qcc; - jpc_cod_t *cod; - int adjust; - int j; - int absbandno; - long tilehdrlen; - long tilelen; - jpc_enc_tile_t *tile; - jpc_enc_cp_t *cp; - double rho; - uint_fast16_t cmptno; - int samestepsizes; - jpc_enc_ccp_t *ccps; - jpc_enc_tccp_t *tccp; - int bandno; - uint_fast32_t x; - uint_fast32_t y; - int mingbits; - int actualnumbps; - jpc_fix_t mxmag; - jpc_fix_t mag; - int numgbits; - const char * error; - - cp = enc->cp; - - for (tileno = 0; tileno < cp->numtiles; ++tileno) { - enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno); - if (!enc->curtile) - abort(); - - tile = enc->curtile; - - if (jas_getdbglevel() >= 10) { - jpc_enc_dump(enc); - } - - endcomps = &tile->tcmpts[tile->numtcmpts]; - for (cmptno = 0, comp = tile->tcmpts; - cmptno < tile->numtcmpts; - ++cmptno, ++comp) { - if (!cp->ccps[cmptno].sgnd) { - adjust = 1 << (cp->ccps[cmptno].prec - 1); - for (i = 0; i < jas_matrix_numrows(comp->data); ++i) { - for (j = 0; j < jas_matrix_numcols(comp->data); ++j) { - *jas_matrix_getref(comp->data, i, j) -= adjust; - } - } - } - } - - if (!tile->intmode) { - endcomps = &tile->tcmpts[tile->numtcmpts]; - for (comp = tile->tcmpts; comp != endcomps; ++comp) { - jas_matrix_asl(comp->data, JPC_FIX_FRACBITS); - } - } - - switch (tile->mctid) { - case JPC_MCT_RCT: - assert(jas_image_numcmpts(enc->image) == 3); - jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data, - tile->tcmpts[2].data); - break; - case JPC_MCT_ICT: - assert(jas_image_numcmpts(enc->image) == 3); - jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data, - tile->tcmpts[2].data); - break; - default: - break; - } - - for (i = 0; i < jas_image_numcmpts(enc->image); ++i) { - comp = &tile->tcmpts[i]; - jpc_tsfb_analyze(comp->tsfb, - ((comp->qmfbid == JPC_COX_RFT) ? - JPC_TSFB_RITIMODE : 0), comp->data); - - } - - endcomps = &tile->tcmpts[tile->numtcmpts]; - for (cmptno = 0, comp = tile->tcmpts; - comp != endcomps; - ++cmptno, ++comp) { - mingbits = 0; - absbandno = 0; - /* All bands must have a corresponding quantizer step size, - even if they contain no samples and are never coded. */ - /* Some bands may not be hit by the loop below, so we must - initialize all of the step sizes to a sane value. */ - memset(comp->stepsizes, 0, sizeof(comp->stepsizes)); - for (rlvlno = 0, lvl = comp->rlvls; - rlvlno < comp->numrlvls; - ++rlvlno, ++lvl) { - if (!lvl->bands) { - absbandno += rlvlno ? 3 : 1; - continue; - } - endbands = &lvl->bands[lvl->numbands]; - for (band = lvl->bands; band != endbands; ++band) { - if (!band->data) { - ++absbandno; - continue; - } - actualnumbps = 0; - mxmag = 0; - for (y = 0; y < jas_matrix_numrows(band->data); ++y) { - for (x = 0; x < jas_matrix_numcols(band->data); ++x) { - mag = abs(jas_matrix_get(band->data, y, x)); - if (mag > mxmag) { - mxmag = mag; - } - } - } - if (tile->intmode) { - actualnumbps = - jpc_firstone(mxmag) + 1; - } else { - actualnumbps = - jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS; - } - numgbits = actualnumbps - (cp->ccps[cmptno].prec - 1 + - band->analgain); - if (numgbits > mingbits) { - mingbits = numgbits; - } - if (!tile->intmode) { - band->absstepsize = - jpc_fix_div( - jpc_inttofix(1 << (band->analgain + 1)), - band->synweight); - } else { - band->absstepsize = jpc_inttofix(1); - } - band->stepsize = jpc_abstorelstepsize( - band->absstepsize, cp->ccps[cmptno].prec + - band->analgain); - band->numbps = cp->tccp.numgbits + - JPC_QCX_GETEXPN(band->stepsize) - 1; - - if ((!tile->intmode) && band->data) { - quantize(band->data, band->absstepsize); - } - - comp->stepsizes[absbandno] = band->stepsize; - ++absbandno; - } - } - - assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS); - if (!tile->intmode) { - jas_matrix_divpow2(comp->data, - JPC_FIX_FRACBITS - JPC_NUMEXTRABITS); - } else { - jas_matrix_asl(comp->data, JPC_NUMEXTRABITS); - } - } - - if (mingbits > cp->tccp.numgbits) { - fprintf(stderr, "error: too few guard bits (need at least %d)\n", - mingbits); - return -1; - } - - enc->tmpstream = jas_stream_memopen(0, 0); - if (!enc->tmpstream) { - fprintf(stderr, "cannot open tmp file\n"); - return -1; - } - - /* Write the tile header. */ - enc->mrk = jpc_ms_create(JPC_MS_SOT); - if (!enc->mrk) - return -1; - sot = &enc->mrk->parms.sot; - sot->len = 0; - sot->tileno = tileno; - sot->partno = 0; - sot->numparts = 1; - if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { - fprintf(stderr, "cannot write SOT marker\n"); - return -1; - } - jpc_ms_destroy(enc->mrk); - enc->mrk = 0; - -/************************************************************************/ -/************************************************************************/ -/************************************************************************/ - - tccp = &cp->tccp; - for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) { - comp = &tile->tcmpts[cmptno]; - if (comp->numrlvls != tccp->maxrlvls) { - if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) { - return -1; - } - /* XXX = this is not really correct. we are using comp #0's - precint sizes and other characteristics */ - comp = &tile->tcmpts[0]; - cod = &enc->mrk->parms.cod; - cod->compparms.csty = 0; - cod->compparms.numdlvls = comp->numrlvls - 1; - cod->prg = tile->prg; - cod->numlyrs = tile->numlyrs; - cod->compparms.cblkwidthval = - JPC_COX_CBLKSIZEEXPN(comp->cblkwidthexpn); - cod->compparms.cblkheightval = - JPC_COX_CBLKSIZEEXPN(comp->cblkheightexpn); - cod->compparms.cblksty = comp->cblksty; - cod->compparms.qmfbid = comp->qmfbid; - cod->mctrans = (tile->mctid != JPC_MCT_NONE); - for (i = 0; i < comp->numrlvls; ++i) { - cod->compparms.rlvls[i].parwidthval = - comp->rlvls[i].prcwidthexpn; - cod->compparms.rlvls[i].parheightval = - comp->rlvls[i].prcheightexpn; - } - if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { - return -1; - } - jpc_ms_destroy(enc->mrk); - enc->mrk = 0; - } - } - - for (cmptno = 0, comp = tile->tcmpts; - cmptno < cp->numcmpts; - ++cmptno, ++comp) { - ccps = &cp->ccps[cmptno]; - if (ccps->numstepsizes == comp->numstepsizes) { - samestepsizes = 1; - for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) { - if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) { - samestepsizes = 0; - break; - } - } - } else { - samestepsizes = 0; - } - if (!samestepsizes) { - if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) { - return -1; - } - qcc = &enc->mrk->parms.qcc; - qcc->compno = cmptno; - qcc->compparms.numguard = cp->tccp.numgbits; - qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ? - JPC_QCX_SEQNT : JPC_QCX_NOQNT; - qcc->compparms.numstepsizes = comp->numstepsizes; - qcc->compparms.stepsizes = comp->stepsizes; - if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { - return -1; - } - qcc->compparms.stepsizes = 0; - jpc_ms_destroy(enc->mrk); - enc->mrk = 0; - } - } - - /* Write a SOD marker to indicate the end of the tile header. */ - if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) { - return -1; - } - if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) { - fprintf(stderr, "cannot write SOD marker\n"); - return -1; - } - jpc_ms_destroy(enc->mrk); - enc->mrk = 0; - tilehdrlen = jas_stream_getrwcount(enc->tmpstream); - -/************************************************************************/ -/************************************************************************/ -/************************************************************************/ - - if (jpc_enc_enccblks(enc)) { - abort(); - return -1; - } - - cp = enc->cp; - rho = (double) (tile->brx - tile->tlx) * (tile->bry - tile->tly) / - ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight - - cp->imgareatly)); - tile->rawsize = cp->rawsize * rho; - - computeLayerSizes(enc, tile, cp, rho, tilehdrlen, &error); - - if (!error) { - int rc; - performTier2Coding(enc, tile->numlyrs, tile->lyrsizes, &error); - - rc = jpc_enc_encodetiledata(enc); - if (rc != 0) - pm_asprintf(&error, "jpc_enc_encodetiledata() failed\n"); - } - - if (error) { - fprintf(stderr, "%s\n", error); - pm_strfree(error); - return -1; - } - - tilelen = jas_stream_tell(enc->tmpstream); - - if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) { - return -1; - } - jpc_putuint32(enc->tmpstream, tilelen); - - if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) { - return -1; - } - if (jpc_putdata(enc->out, enc->tmpstream, -1)) { - return -1; - } - enc->len += tilelen; - - jas_stream_close(enc->tmpstream); - enc->tmpstream = 0; - - jpc_enc_tile_destroy(enc->curtile); - enc->curtile = 0; - - } - - return 0; -} - - - /* * Copyright (c) 1999-2000 Image Power, Inc. and the University of * British Columbia. diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c index 01e3611e..a884ed4d 100644 --- a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c +++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c @@ -50,6 +50,8 @@ jpc_firstone(int_fast32_t const arg) { /*---------------------------------------------------------------------------- Calculate the bit position of the first leading one in a nonnegative integer. + + LSB is bit position 0. Iff there are no ones, return -1. -----------------------------------------------------------------------------*/ int n; int x; diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c index e1af0f61..34b9738c 100644 --- a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c +++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c @@ -124,6 +124,8 @@ #include <stdlib.h> #include <assert.h> +#include "netpbm/nstring.h" + #include "jasper/jas_fix.h" #include "jasper/jas_malloc.h" #include "jasper/jas_math.h" @@ -149,74 +151,111 @@ static int jpc_encrawsigpass(jpc_bitstream_t *out, int bitpos, int, static int jpc_encrawrefpass(jpc_bitstream_t *out, int bitpos, int, jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec); -/******************************************************************************\ +/*****************************************************************************\ * Code for encoding code blocks. -\******************************************************************************/ +\*****************************************************************************/ -/* Encode all of the code blocks associated with the current tile. */ -int jpc_enc_enccblks(jpc_enc_t *enc) -{ - jpc_enc_tcmpt_t *tcmpt; - jpc_enc_tcmpt_t *endcomps; - jpc_enc_rlvl_t *lvl; - jpc_enc_rlvl_t *endlvls; - jpc_enc_band_t *band; - jpc_enc_band_t *endbands; - jpc_enc_cblk_t *cblk; - jpc_enc_cblk_t *endcblks; - int i; - int j; - int mx; - int bmx; - int v; - jpc_enc_tile_t *tile; - uint_fast32_t prcno; - jpc_enc_prc_t *prc; +static void +encodeBlocksOfPrecinct(jpc_enc_prc_t * const prcP, + jpc_enc_band_t * const bandP, + jpc_enc_tcmpt_t * const tcmptP, + jpc_enc_t * const encoderP, + const char ** const errorP) { - tile = enc->curtile; + if (prcP->cblks) { + int bmx; + uint_fast32_t cblkno; - endcomps = &tile->tcmpts[tile->numtcmpts]; - for (tcmpt = tile->tcmpts; tcmpt != endcomps; ++tcmpt) { - endlvls = &tcmpt->rlvls[tcmpt->numrlvls]; - for (lvl = tcmpt->rlvls; lvl != endlvls; ++lvl) { - if (!lvl->bands) { - continue; - } - endbands = &lvl->bands[lvl->numbands]; - for (band = lvl->bands; band != endbands; ++band) { - if (!band->data) { - continue; + for (cblkno = 0, bmx = 0; cblkno < prcP->numcblks; ++ cblkno) { + jpc_enc_cblk_t * const cblkP = &prcP->cblks[cblkno]; + + int mx; + uint_fast32_t row; + + for (row = 0, mx = 0; + row < jas_matrix_numrows(cblkP->data); + ++row) { + + uint_fast32_t col; + + for (col = 0; col < jas_matrix_numcols(cblkP->data); ++col) { + int const v = abs(jas_matrix_get(cblkP->data, row, col)); + if (v > mx) + mx = v; } - for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) { - if (!prc->cblks) { - continue; - } - bmx = 0; - endcblks = &prc->cblks[prc->numcblks]; - for (cblk = prc->cblks; cblk != endcblks; ++cblk) { - mx = 0; - for (i = 0; i < jas_matrix_numrows(cblk->data); ++i) { - for (j = 0; j < jas_matrix_numcols(cblk->data); ++j) { - v = abs(jas_matrix_get(cblk->data, i, j)); - if (v > mx) { - mx = v; - } - } - } - if (mx > bmx) { - bmx = mx; - } - cblk->numbps = JAS_MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0); - } + } + if (mx > bmx) + bmx = mx; - for (cblk = prc->cblks; cblk != endcblks; ++cblk) { - cblk->numimsbs = band->numbps - cblk->numbps; - assert(cblk->numimsbs >= 0); - } + cblkP->numbps = MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0); + } + + for (cblkno = 0; cblkno < prcP->numcblks; ++ cblkno) { + jpc_enc_cblk_t * const cblkP = &prcP->cblks[cblkno]; - for (cblk = prc->cblks; cblk != endcblks; ++cblk) { - if (jpc_enc_enccblk(enc, cblk->stream, tcmpt, band, cblk)) { - return -1; + assert(cblkP->numbps <= bandP->numbps); + } + + for (cblkno = 0, *errorP = NULL; + cblkno < prcP->numcblks && !*errorP; + ++ cblkno) { + + jpc_enc_cblk_t * const cblkP = &prcP->cblks[cblkno]; + + int rc; + + rc = jpc_enc_enccblk(encoderP, + cblkP->stream, tcmptP, bandP, cblkP); + if (rc != 0) + pm_asprintf(errorP, "Encoding failed on code block %u " + "of %u", (unsigned)cblkno, + (unsigned)prcP->numcblks); + } + } else + *errorP = NULL; +} + + + +int +jpc_enc_enccblks(jpc_enc_t * const encoderP) { +/*---------------------------------------------------------------------------- + Encode all of the code blocks associated with the current tile. +-----------------------------------------------------------------------------*/ + jpc_enc_tile_t * const tileP = encoderP->curtile; + + uint_fast32_t cmptno; + + for (cmptno = 0; cmptno < tileP->numtcmpts; ++cmptno) { + jpc_enc_tcmpt_t * const tcmptP = &tileP->tcmpts[cmptno]; + + unsigned int lvlno; + for (lvlno = 0; lvlno < tcmptP->numrlvls; ++ lvlno) { + jpc_enc_rlvl_t * const lvlP = &tcmptP->rlvls[lvlno]; + + if (lvlP->bands) { + uint_fast32_t bandno; + + for (bandno = 0; bandno < lvlP->numbands; ++bandno) { + jpc_enc_band_t * const bandP = &lvlP->bands[bandno]; + + if (bandP->data) { + uint_fast32_t prcno; + + for (prcno = 0; prcno < lvlP->numprcs; ++prcno) { + + const char * error; + + encodeBlocksOfPrecinct(&bandP->prcs[prcno], + bandP, + tcmptP, + encoderP, + &error); + + if (error) { + pm_strfree(error); + return -1; + } } } } @@ -226,6 +265,8 @@ int jpc_enc_enccblks(jpc_enc_t *enc) return 0; } + + static int getthebyte(jas_stream_t *in, long off) { int c; diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c index fede2bef..e6e3942a 100644 --- a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c +++ b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c @@ -114,9 +114,9 @@ * $Id$ */ -/******************************************************************************\ +/*****************************************************************************\ * Includes -\******************************************************************************/ +\*****************************************************************************/ #include <assert.h> #include <stdio.h> @@ -133,21 +133,21 @@ #include "jpc_flt.h" #include "jpc_util.h" -/******************************************************************************\ +/*****************************************************************************\ * Miscellaneous Functions -\******************************************************************************/ +\*****************************************************************************/ -int jpc_atoaf(const char *s, int *numvalues, double **values) -{ - static char delim[] = ", \t\n"; +static unsigned int +countOfTokens(const char * const s, + const char * const delim) { + + unsigned int n; char buf[4096]; - int n; - double *vs; - char *cp; + const char * cp; strncpy(buf, s, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; - n = 0; + n = 0; /* initial value */ if ((cp = strtok(buf, delim))) { ++n; while ((cp = strtok(0, delim))) { @@ -156,32 +156,57 @@ int jpc_atoaf(const char *s, int *numvalues, double **values) } } } + return n; +} + + + +int +jpc_atoaf(const char * const s, + int * const numvaluesP, + double ** const valuesP) { +/*---------------------------------------------------------------------------- + Parse a string like "3.2,9,-5". Return as *numvaluesP the number of + values in the string and as *valuesP a malloced array of the values. - if (n) { - if (!(vs = jas_malloc(n * sizeof(double)))) { + But if the string is empty (*numvaluesP is zero), return *valuesP NULL. + + Delimiters can be comma as in the example or space, tab, or newline. +-----------------------------------------------------------------------------*/ + char const delim[] = ", \t\n"; + + unsigned int const valueCt = countOfTokens(s, delim); + + if (valueCt > 0) { + unsigned int i; + double * vs; + const char * cp; + char buf[4096]; + + if (!(vs = jas_malloc(valueCt * sizeof(double)))) { return -1; } strncpy(buf, s, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; - n = 0; + i = 0; if ((cp = strtok(buf, delim))) { - vs[n] = atof(cp); - ++n; + vs[i] = atof(cp); + ++i; while ((cp = strtok(0, delim))) { if (cp[0] != '\0') { - vs[n] = atof(cp); - ++n; + vs[i] = atof(cp); + ++i; } } } + assert(i == valueCt); + *numvaluesP = valueCt; + *valuesP = vs; } else { - vs = 0; + *valuesP = NULL; + *numvaluesP = 0; } - - *numvalues = n; - *values = vs; - return 0; } diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c index ee5189ce..8f7409c0 100644 --- a/converter/other/jpeg2000/pamtojpeg2k.c +++ b/converter/other/jpeg2000/pamtojpeg2k.c @@ -53,6 +53,8 @@ struct CmdlineInfo { enum compmode compmode; unsigned int compressionSpec; float compression; + unsigned int size; + unsigned int sizeSpec; char * ilyrrates; enum progression progression; unsigned int numrlvls; @@ -76,7 +78,7 @@ parseCommandLine(int argc, char ** argv, struct CmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that many of the strings that this function returns in the - *cmdline_p structure are actually in the supplied argv array. And + *cmdlineP structure are actually in the supplied argv array. And sometimes, one of these strings is actually just a suffix of an entry in argv! -----------------------------------------------------------------------------*/ @@ -126,6 +128,8 @@ parseCommandLine(int argc, char ** argv, &modeSpec, 0); OPTENT3(0, "compression", OPT_FLOAT, &cmdlineP->compression, &cmdlineP->compressionSpec, 0); + OPTENT3(0, "size", OPT_UINT, &cmdlineP->size, + &cmdlineP->sizeSpec, 0); OPTENT3(0, "ilyrrates", OPT_STRING, &cmdlineP->ilyrrates, &ilyrratesSpec, 0); OPTENT3(0, "progression", OPT_STRING, &progressionOpt, @@ -219,6 +223,9 @@ parseCommandLine(int argc, char ** argv, if (!debuglevelSpec) cmdlineP->debuglevel = 0; + if (cmdlineP->compressionSpec && cmdlineP->sizeSpec) + pm_error("You cannot specify by -compression and -size"); + if (argc - 1 == 0) cmdlineP->inputFilename = strdup("-"); /* he wants stdin */ else if (argc - 1 == 1) @@ -405,6 +412,8 @@ writeJpc(jas_image_t * const jasperP, if (cmdline.compressionSpec) sprintf(rateOpt, "rate=%1.9f", 1.0/cmdline.compression); + else if (cmdline.sizeSpec) + sprintf(rateOpt, "rate=%uB", cmdline.size); else { /* No 'rate' option. This means there is no constraint on the image size, so the encoder will compress losslessly. Note that the diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c index f5a0d39d..09e35410 100644 --- a/converter/ppm/ppmtompeg/frame.c +++ b/converter/ppm/ppmtompeg/frame.c @@ -12,7 +12,7 @@ * Frame_AllocYCC * * Frame_AllocDecoded * * Frame_AllocHalf * - * Frame_Resize * + * Frame_Resize * * * *===========================================================================*/ @@ -52,7 +52,7 @@ *===========*/ /* The maximum number of B-Frames allowed between reference frames. */ -#define B_FRAME_RUN 16 +#define B_FRAME_RUN 16 /*==================* * GLOBAL VARIABLES * @@ -64,13 +64,13 @@ static unsigned int numOfFrames; /*==================================================== * Resize_Array_Width -* +* * This function will resize any array width up * or down in size. The algorithm is based on the * least common multiple approach more commonly * used in audio frequency adjustments. *=====================================================*/ -static void +static void Resize_Array_Width(uint8 ** const inarray, int const in_x, int const in_y, @@ -86,7 +86,7 @@ Resize_Array_Width(uint8 ** const inarray, uint8 pointA,pointB; double slope,diff; #endif - + for (i = 0; i < in_y; ++i) { /* For each row */ unsigned int j; inptr = &inarray[i][0]; @@ -106,7 +106,7 @@ Resize_Array_Width(uint8 ** const inarray, in_total = in_total - out_x; --inptr; } - } else { + } else { #ifdef DOING_INTERPOLATION pointA = *inptr; #endif @@ -124,10 +124,10 @@ Resize_Array_Width(uint8 ** const inarray, } else { *outptr = (pointB - (uint8)(slope*(((float)(out_x)) - diff))); - } + } #endif /* Non-Interpolative solution */ - *outptr = *inptr; + *outptr = *inptr; ++outptr; out_total = out_total + in_x; @@ -153,7 +153,7 @@ Resize_Array_Width(uint8 ** const inarray, * Same as Resize_array_Width except pointer * manipulation must change. *===============================*/ -static void +static void Resize_Array_Height(uint8 ** const inarray, int const in_x, int const in_y, @@ -176,7 +176,7 @@ Resize_Array_Height(uint8 ** const inarray, out_total = 0; k = 0; for(j=0; j < out_y; ++j){ /* for each output value */ - if (in_total == out_total) { + if (in_total == out_total) { outarray[j][i] = inarray[k][i]; out_total=out_total+in_y; while(in_total < out_total){ @@ -187,7 +187,7 @@ Resize_Array_Height(uint8 ** const inarray, in_total = in_total - out_y; --k; } - } else { + } else { #ifdef DOING_INTERPOLATION pointA = inarray[k][i]; if (k != (in_y - 1)) { @@ -210,7 +210,7 @@ Resize_Array_Height(uint8 ** const inarray, in_total = in_total - out_y; --k; } - } + } } } } @@ -220,7 +220,7 @@ Resize_Array_Height(uint8 ** const inarray, /*======================================================== * Resize_Width *======================================================*/ -static void +static void Resize_Width(MpegFrame * const omfrw, MpegFrame * const mfrw, int const in_x, @@ -274,12 +274,12 @@ Resize_Width(MpegFrame * const omfrw, free(mfrw->orig_y[i]); } free(mfrw->orig_y); - + for (i = 0; i < in_y / 2; ++i) { free(mfrw->orig_cr[i]); } free(mfrw->orig_cr); - + for (i = 0; i < in_y / 2; ++i) { free(mfrw->orig_cb[i]); } @@ -300,9 +300,9 @@ Resize_Height(MpegFrame * const omfrh, int const in_x, int const in_y, int const out_y) { - - unsigned int y; - + + unsigned int y; + Fsize_y = out_y; /* Allocate new frame memory */ @@ -347,12 +347,12 @@ Resize_Height(MpegFrame * const omfrh, free(mfrh->orig_y[i]); } free(mfrh->orig_y); - + for (i = 0; i < in_y / 2; ++i) { free(mfrh->orig_cr[i]); } free(mfrh->orig_cr); - + for (i = 0; i < in_y / 2; ++i) { free(mfrh->orig_cb[i]); } @@ -386,8 +386,8 @@ Frame_Init(unsigned int const numOfFramesRequested) { for (idx = 0; idx < numOfFrames; ++idx) { MALLOCVAR(frameMemory[idx]); frameMemory[idx]->inUse = FALSE; - frameMemory[idx]->orig_y = NULL; - frameMemory[idx]->y_blocks = NULL; + frameMemory[idx]->orig_y = NULL; + frameMemory[idx]->y_blocks = NULL; frameMemory[idx]->decoded_y = NULL; frameMemory[idx]->halfX = NULL; frameMemory[idx]->next = NULL; @@ -458,11 +458,11 @@ FreeFrame(MpegFrame * const frameP) { for ( i = 0; i < Fsize_y; ++i ) free(frameP->halfX[i]); free(frameP->halfX); - + for (i = 0; i < Fsize_y-1; ++i) free(frameP->halfY[i]); free(frameP->halfY); - + for (i = 0; i < Fsize_y-1; ++i) free(frameP->halfBoth[i]); free(frameP->halfBoth); @@ -542,7 +542,7 @@ GetUnusedFrame() { "See the man page for help.\n"); exit(1); } - return frameMemory[idx]; + return frameMemory[idx]; } @@ -638,7 +638,7 @@ Frame_AllocBlocks(MpegFrame * const frameP) { MALLOCARRAY(frameP->y_blocks[i], dctx); ERRCHK(frameP->y_blocks[i], "malloc"); } - + MALLOCARRAY(frameP->cr_blocks, dcty / 2); ERRCHK(frameP->cr_blocks, "malloc"); MALLOCARRAY(frameP->cb_blocks, dcty / 2); @@ -672,7 +672,7 @@ Frame_AllocYCC(MpegFrame * const frameP) { /* already allocated */ } else { unsigned int y; - + DBG_PRINT(("ycc_calc:\n")); /* * first, allocate tons of memory @@ -755,7 +755,7 @@ Frame_AllocHalf(MpegFrame * const frameP) { * * allocate memory for decoded frame for the given frame, if required * if makeReference == TRUE, then makes it reference frame - * + * * RETURNS: nothing * * SIDE EFFECTS: none @@ -781,14 +781,14 @@ Frame_AllocDecoded(MpegFrame * const frameP, MALLOCARRAY(frameP->decoded_y[y], Fsize_x); ERRCHK(frameP->decoded_y[y], "malloc"); } - + MALLOCARRAY(frameP->decoded_cr, Fsize_y / 2); ERRCHK(frameP->decoded_cr, "malloc"); for (y = 0; y < (Fsize_y / 2); ++y) { MALLOCARRAY(frameP->decoded_cr[y], Fsize_x / 2); ERRCHK(frameP->decoded_cr[y], "malloc"); } - + MALLOCARRAY(frameP->decoded_cb, Fsize_y / 2); ERRCHK(frameP->decoded_cb, "malloc"); for (y = 0; y < (Fsize_y / 2); ++y) { @@ -810,7 +810,7 @@ Frame_AllocDecoded(MpegFrame * const frameP, * * Frame_Resize by James Boucher * Boston University Multimedia Communications Lab - * + * * This function takes the mf input frame, read in READFrame(), * and resizes all the input component arrays to the output * dimensions specified in the parameter file as OUT_SIZE. @@ -828,11 +828,11 @@ Frame_Resize(MpegFrame * const omf, MpegFrame * frameAP; /* intermediate frame */ MALLOCVAR_NOFAIL(frameAP); - + if (insize_x != outsize_x && insize_y != outsize_y) { Resize_Width(frameAP, mf, insize_x, insize_y, outsize_x); Resize_Height(omf, frameAP, outsize_x, insize_y, outsize_y); - } else + } else if (insize_x ==outsize_x && insize_y != outsize_y) { Resize_Height(omf, mf, insize_x, insize_y, outsize_y); } else @@ -843,3 +843,5 @@ Frame_Resize(MpegFrame * const omf, free(frameAP); } + + diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c index 2a359b2f..dcc02052 100644 --- a/converter/ppm/ppmtompeg/readframe.c +++ b/converter/ppm/ppmtompeg/readframe.c @@ -1,13 +1,13 @@ /*===========================================================================* - * readframe.c - * - * procedures to read in frames - * - * EXPORTED PROCEDURES: - * ReadFrame - * SetFileType - * SetFileFormat - * + * readframe.c + * + * procedures to read in frames + * + * EXPORTED PROCEDURES: + * ReadFrame + * SetFileType + * SetFileFormat + * *===========================================================================*/ /* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ @@ -62,7 +62,7 @@ struct YuvLine { #ifdef __OS2__ #define popen _popen #endif - + /*==================* * Global VARIABLES * @@ -100,7 +100,7 @@ static void DoKillDim (MpegFrame *mf, int w, int h); -void +void SetResize(bool const set) { resizeFrame = set; } @@ -134,7 +134,7 @@ ReadPNM(MpegFrame * const mpegFrameP, static void openFile(struct inputSource * const inputSourceP, unsigned int const frameNumber, - const char * const conversion, + const char * const conversion, FILE ** const ifPP) { if (inputSourceP->stdinUsed) { @@ -145,24 +145,24 @@ openFile(struct inputSource * const inputSourceP, "INPUT_CONVERTER * in the parameter file or supply the " "frames in files by specifying a directory with " "INPUT_DIRECTORY in the parameter file."); - + *ifPP = stdin; } else { const char * fileName; const char * fullFileName; - + GetNthInputFileName(inputSourceP, frameNumber, &fileName); - + pm_asprintf(&fullFileName, "%s/%s", currentPath, fileName); - + CurrFile = fullFileName; - + if (fileType == ANY_FILE_TYPE) { char command[1024]; const char * convertPtr; char * commandPtr; const char * charPtr; - + /* replace every occurrence of '*' with fullFileName */ convertPtr = conversion; commandPtr = command; @@ -172,7 +172,7 @@ openFile(struct inputSource * const inputSourceP, ++commandPtr; ++convertPtr; } - + if (*convertPtr == '*') { /* copy fullFileName */ charPtr = fullFileName; @@ -185,7 +185,7 @@ openFile(struct inputSource * const inputSourceP, } } *commandPtr = '\0'; - + *ifPP = popen(command, "r"); if (*ifPP == NULL) { pm_message( @@ -201,7 +201,7 @@ openFile(struct inputSource * const inputSourceP, *ifPP = fopen(fullFileName, "rb"); if (*ifPP == NULL) pm_error("Couldn't open input file '%s'", fullFileName); - + if (baseFormat == JMOVIE_FILE_TYPE) unlink(fullFileName); } @@ -238,7 +238,7 @@ fileIsAtEnd(FILE * const ifP) { c = getc(ifP); if (c == EOF) { - if (feof(ifP)) + if (feof(ifP)) eof = TRUE; else pm_error("File error on getc() to position to image"); @@ -248,7 +248,7 @@ fileIsAtEnd(FILE * const ifP) { eof = FALSE; rc = ungetc(c, ifP); - if (rc == EOF) + if (rc == EOF) pm_error("File error doing ungetc() to position to image."); } return eof; @@ -262,14 +262,11 @@ ReadFrameFile(MpegFrame * const frameP, const char * const conversion, bool * const eofP) { /*---------------------------------------------------------------------------- - Read a frame from the file 'ifP'. + Read a frame from the file 'ifP', doing adjustments as indicated. Return *eofP == TRUE iff we encounter EOF before we can get the frame. -----------------------------------------------------------------------------*/ - MpegFrame tempFrame; - MpegFrame * framePtr; - /* To make this code fit Netpbm properly, we should remove handling of all types except PNM and use pm_nextimage() to handle sensing of end of stream. @@ -278,53 +275,59 @@ ReadFrameFile(MpegFrame * const frameP, if (fileIsAtEnd(ifP)) *eofP = TRUE; else { + MpegFrame preResizeFrame; + /* The frame object that holds the frame before resizing */ + MpegFrame * rawFrameP; + /* The frame object with which we read in the raw frame */ + *eofP = FALSE; if (resizeFrame) { - tempFrame.inUse = FALSE; - tempFrame.orig_y = NULL; - tempFrame.y_blocks = NULL; - tempFrame.decoded_y = NULL; - tempFrame.halfX = NULL; - framePtr = &tempFrame; + preResizeFrame.inUse = FALSE; + preResizeFrame.orig_y = NULL; + preResizeFrame.y_blocks = NULL; + preResizeFrame.decoded_y = NULL; + preResizeFrame.halfX = NULL; + rawFrameP = &preResizeFrame; } else - framePtr = frameP; + rawFrameP = frameP; switch(baseFormat) { case YUV_FILE_TYPE: /* Encoder YUV */ if ((strncmp (yuvConversion, "EYUV", 4) == 0) || - (strncmp (yuvConversion, "UCB", 3) == 0)) + (strncmp (yuvConversion, "UCB", 3) == 0)) - ReadEYUV(framePtr, ifP, realWidth, realHeight); + ReadEYUV(rawFrameP, ifP, realWidth, realHeight); else /* Abekas-type (interlaced) YUV */ - ReadAYUV(framePtr, ifP, realWidth, realHeight); + ReadAYUV(rawFrameP, ifP, realWidth, realHeight); break; case Y_FILE_TYPE: - ReadY(framePtr, ifP, realWidth, realHeight); + ReadY(rawFrameP, ifP, realWidth, realHeight); break; case PNM_FILE_TYPE: - ReadPNM(framePtr, ifP); + ReadPNM(rawFrameP, ifP); break; case SUB4_FILE_TYPE: - ReadSub4(framePtr, ifP, yuvWidth, yuvHeight); + ReadSub4(rawFrameP, ifP, yuvWidth, yuvHeight); break; case JPEG_FILE_TYPE: case JMOVIE_FILE_TYPE: - ReadJPEG(framePtr, ifP); + ReadJPEG(rawFrameP, ifP); break; default: break; } - if (resizeFrame) - Frame_Resize(frameP, &tempFrame, Fsize_x, Fsize_y, + if (resizeFrame) { + assert(rawFrameP == &preResizeFrame); + Frame_Resize(frameP, &preResizeFrame, Fsize_x, Fsize_y, outputWidth, outputHeight); - + } if (GammaCorrection) DoGamma(frameP, Fsize_x, Fsize_y); @@ -338,7 +341,7 @@ ReadFrameFile(MpegFrame * const frameP, void -ReadFrame(MpegFrame * const frameP, +ReadFrame(MpegFrame * const frameP, struct inputSource * const inputSourceP, unsigned int const frameNumber, const char * const conversion, @@ -693,7 +696,7 @@ SeparateLine(fpointer, lineptr, width) fprintf(stderr, " or any even-length string consisting of the letters U, V, and Y.\n"); exit(1); } - + } } @@ -738,7 +741,7 @@ ReadY(mf, fpointer, width, height) for (y = Fsize_y; y < height; y++) { safe_fread(junk, 1, width, fpointer); } - + for (y = 0 ; y < (Fsize_y >> 1); y++) { memset(mf->orig_cb[y], 128, (Fsize_x>>1)); memset(mf->orig_cr[y], 128, (Fsize_x>>1)); @@ -830,7 +833,7 @@ int w,h; int i,j; if (!init_done) { - for(i=0; i<256; i++) + for(i=0; i<256; i++) GammaVal[i]=(unsigned char) (pow(((double) i)/255.0,GammaValue)*255.0+0.5); init_done=TRUE; } @@ -871,7 +874,7 @@ int w,h; ^ kill_dim_break ^kill_dim_end kill_dim_slope gives the slope (y = kill_dim_slope * x +0) - from 0 to kill_dim_break + from 0 to kill_dim_break * *===========================================================================*/ @@ -930,7 +933,7 @@ int w,h; * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ -/* +/* * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/readframe.c,v 1.27 1995/08/14 22:31:40 smoot Exp $ * $Log: readframe.c,v $ * Revision 1.27 1995/08/14 22:31:40 smoot diff --git a/doc/HISTORY b/doc/HISTORY index 843d8624..2cd5c1c6 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,23 +4,25 @@ Netpbm. CHANGE HISTORY -------------- -22.12.24 BJH Release 11.00.03 +22.12.31 BJH Release 11.01.00 + + pamcat: Add -listfile . + + pamtojpeg2k: add -size option. Doesn't precisely work (and + -compression has never worked precisely either); should be + fixed some day. ppmtompeg: Fix crash with resize option because of invalid memory free. -22.11.14 BJH Release 11.00.02 + fitstopnm: fix invalid memory reference (nonterminated ASCIIZ + string). packaging: Fix library minor number to 100 + Netpbm minor number so it is higher than previous ones in library major 100. I.e. libnetpbm.so.100.101 instead of libnetpbm.so.100.1. Introduced in Netpbm 11.00.00. -22.10.14 BJH Release 11.00.01 - - fitstopnm: fix invalid memory reference (nonterminated ASCIIZ - string). - 22.09.28 BJH Release 11.00.00 (No significance to new major number; just ran out of 2-digit @@ -147,7 +149,7 @@ CHANGE HISTORY 21.12.27 BJH Release 10.97.00 - Add pbnnoise. + Add pbmnoise. pnmpad: Use -halign with -mwidth and default to centering the image instead of left-justifying when no other padding is being diff --git a/editor/pamcat.c b/editor/pamcat.c index c7f0482d..7823bdfc 100644 --- a/editor/pamcat.c +++ b/editor/pamcat.c @@ -9,6 +9,8 @@ =============================================================================*/ +#include <stdio.h> +#include <limits.h> #include <assert.h> #include "pm_c_util.h" @@ -36,12 +38,43 @@ enum Orientation {TOPBOTTOM, LEFTRIGHT}; enum Justification {JUST_CENTER, JUST_MIN, JUST_MAX}; /* Justification of images in concatenation */ +/* FOPEN_MAX is usually defined in stdio.h, PATH_MAX in limits.h + Given below are typical values. Adjust as necessary. + */ + +#ifndef FOPEN_MAX + #define FOPEN_MAX 16 +#endif + +#ifndef PATH_MAX + #define PATH_MAX 255 +#endif + + +static const char ** +copyOfStringList(const char ** const list, + unsigned int const size) { + + const char ** retval; + unsigned int i; + + MALLOCARRAY_NOFAIL(retval, size); + + for (i = 0; i < size; ++i) + retval[i] = pm_strdup(list[i]); + + return retval; +} + + + struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char ** inputFileName; - unsigned int fileCt; + unsigned int inputFileCt; + const char * listfile; /* NULL if not specified */ enum PadColorMethod padColorMethod; enum Orientation orientation; enum Justification justification; @@ -49,7 +82,6 @@ struct CmdlineInfo { }; - static void parseCommandLine(int argc, const char ** const argv, struct CmdlineInfo * const cmdlineP) { @@ -67,6 +99,7 @@ parseCommandLine(int argc, const char ** const argv, unsigned int leftright, topbottom; unsigned int black, white; unsigned int jtop, jbottom, jleft, jright, jcenter; + unsigned int listfileSpec; MALLOCARRAY_NOFAIL(option_def, 100); @@ -82,6 +115,8 @@ parseCommandLine(int argc, const char ** const argv, OPTENT3(0, "jleft", OPT_FLAG, NULL, &jleft, 0); OPTENT3(0, "jright", OPT_FLAG, NULL, &jright, 0); OPTENT3(0, "jcenter", OPT_FLAG, NULL, &jcenter, 0); + OPTENT3(0, "listfile", OPT_STRING, &cmdlineP->listfile, + &listfileSpec, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); opt.opt_table = option_def; @@ -148,27 +183,144 @@ parseCommandLine(int argc, const char ** const argv, } } - if (argc-1 < 1) { - MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, 1); - cmdlineP->inputFileName[0] = "-"; - cmdlineP->fileCt = 1; + if (listfileSpec) { + if (argc-1 > 0) + pm_error ("You can not specify files on the command line and " + "also -listfile."); } else { - unsigned int i; - unsigned int stdinCt; + cmdlineP->listfile = NULL; + + if (argc-1 < 1) { + MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, 1); + cmdlineP->inputFileName[0] = "-"; + cmdlineP->inputFileCt = 1; + } else { + unsigned int i; + unsigned int stdinCt; /* Number of input files user specified as Standard Input */ - MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, argc-1); + MALLOCARRAY_NOFAIL(cmdlineP->inputFileName, argc-1); + + for (i = 0, stdinCt = 0; i < argc-1; ++i) { + cmdlineP->inputFileName[i] = argv[1+i]; + if (streq(argv[1+i], "-")) + ++stdinCt; + } + cmdlineP->inputFileCt = argc-1; + if (stdinCt > 1) + pm_error("At most one input image can come from " + "Standard Input. You specified %u", stdinCt); + } + } +} + + + +static void +freeCmdLine(struct CmdlineInfo const cmdline) { + + if (!cmdline.listfile) + free(cmdline.inputFileName); +} + + - for (i = 0, stdinCt = 0; i < argc-1; ++i) { - cmdlineP->inputFileName[i] = argv[1+i]; - if (streq(argv[1+i], "-")) - ++stdinCt; +static void +createInFileListFmFile(const char * const listFileNm, + bool const verbose, + const char *** const inputFileNmP, + unsigned int * const inputFileCtP) { + + FILE * const lfP = pm_openr(listFileNm); + + const char ** inputFileNm; + unsigned int inputFileCt; + unsigned int emptyLineCt; + unsigned int stdinCt; + int eof; + + MALLOCARRAY_NOFAIL(inputFileNm, FOPEN_MAX); + + for (inputFileCt = emptyLineCt = stdinCt = eof = 0; !eof; ) { + + size_t lineLen; + char * buf; + size_t bufferSz; + + buf = NULL; /* initial value */ + bufferSz = 0; /* initial value */ + + pm_getline(lfP, &buf, &bufferSz, &eof, &lineLen); + + if (!eof) { + if (lineLen == 0) + ++emptyLineCt; + else if (lineLen > PATH_MAX) + pm_error("Path/file name in list file is too long " + "(%u bytes). Maximum is %u bytes", + (unsigned)lineLen, PATH_MAX); + else /* 0 < lineLen < PATH_MAX */ { + if (inputFileCt >= FOPEN_MAX) + pm_error("Too many files in list file. Maximum is %u", + FOPEN_MAX); + else { + inputFileNm[inputFileCt] = buf; + ++inputFileCt; + if (streq(buf, "-")) + ++stdinCt; + } + } } - cmdlineP->fileCt = argc-1; - if (stdinCt > 1) - pm_error("At most one input image can come from Standard Input. " - "You specified %u", stdinCt); } + + pm_close(lfP); + + if (stdinCt > 1) + pm_error("At most one input image can come from Standard Input. " + "You specified %u", stdinCt); + + if (inputFileCt == 0) + pm_error("No files specified in list file."); + + if (verbose) { + pm_message("%u files specified and %u blank lines in list file", + inputFileCt, emptyLineCt); + } + + *inputFileCtP = inputFileCt; + *inputFileNmP = inputFileNm; +} + + + +static void +createInFileList(struct CmdlineInfo const cmdline, + bool const verbose, + const char *** const inputFileNmP, + unsigned int * const inputFileCtP) { + + if (cmdline.listfile) + createInFileListFmFile(cmdline.listfile, verbose, + inputFileNmP, inputFileCtP); + else { + *inputFileCtP = cmdline.inputFileCt; + *inputFileNmP = copyOfStringList(cmdline.inputFileName, + cmdline.inputFileCt); + } +} + + + +static void +freeInFileList(const char ** const inputFileNm, + unsigned int const inputFileCt) { + + unsigned int i; + + for (i = 0; i < inputFileCt; ++i) + pm_strfree(inputFileNm[i]); + + free(inputFileNm); } @@ -1237,6 +1389,8 @@ main(int argc, const char ** argv) { struct CmdlineInfo cmdline; + const char ** inputFileNm; + unsigned int inputFileCt; struct pam * inpam; /* malloc'ed array */ struct pam outpam; unsigned int i; @@ -1245,20 +1399,23 @@ main(int argc, parseCommandLine(argc, argv, &cmdline); - MALLOCARRAY_NOFAIL(inpam, cmdline.fileCt); + createInFileList(cmdline, !!cmdline.verbose, &inputFileNm, &inputFileCt); + + MALLOCARRAY_NOFAIL(inpam, inputFileCt); - for (i = 0; i < cmdline.fileCt; ++i) { - FILE * const ifP = pm_openr(cmdline.inputFileName[i]); + for (i = 0; i < inputFileCt; ++i) { + FILE * ifP; + ifP = pm_openr(inputFileNm[i]); inpam[i].comment_p = NULL; /* Don't want to see the comments */ pnm_readpaminit(ifP, &inpam[i], PAM_STRUCT_SIZE(opacity_plane)); } - computeOutputParms(cmdline.fileCt, cmdline.orientation, inpam, + computeOutputParms(inputFileCt, cmdline.orientation, inpam, cmdline.verbose, &outpam); outpam.file = stdout; - for (i = 0; i < cmdline.fileCt; ++i) + for (i = 0; i < inputFileCt; ++i) pnm_setminallocationdepth(&inpam[i], outpam.depth); pnm_writepaminit(&outpam); @@ -1266,12 +1423,12 @@ main(int argc, if (outpam.format == RPBM_FORMAT) { switch (cmdline.orientation) { case LEFTRIGHT: - concatenateLeftRightPbm(&outpam, inpam, cmdline.fileCt, + concatenateLeftRightPbm(&outpam, inpam, inputFileCt, cmdline.justification, cmdline.padColorMethod); break; case TOPBOTTOM: - concatenateTopBottomPbm(&outpam, inpam, cmdline.fileCt, + concatenateTopBottomPbm(&outpam, inpam, inputFileCt, cmdline.justification, cmdline.padColorMethod); break; @@ -1279,21 +1436,22 @@ main(int argc, } else { switch (cmdline.orientation) { case LEFTRIGHT: - concatenateLeftRightGen(&outpam, inpam, cmdline.fileCt, + concatenateLeftRightGen(&outpam, inpam, inputFileCt, cmdline.justification, cmdline.padColorMethod); break; case TOPBOTTOM: - concatenateTopBottomGen(&outpam, inpam, cmdline.fileCt, + concatenateTopBottomGen(&outpam, inpam, inputFileCt, cmdline.justification, cmdline.padColorMethod); break; } } - for (i = 0; i < cmdline.fileCt; ++i) + for (i = 0; i < inputFileCt; ++i) pm_close(inpam[i].file); - free(cmdline.inputFileName); free(inpam); + freeInFileList(inputFileNm, inputFileCt); + freeCmdLine(cmdline); pm_close(stdout); return 0; diff --git a/editor/pamhomography.c b/editor/pamhomography.c index 59b59ed7..02c0fd72 100644 --- a/editor/pamhomography.c +++ b/editor/pamhomography.c @@ -49,25 +49,55 @@ #define MIN4(A, B, C, D) MIN(MIN(A, B), MIN(C, D)) #define MAX4(A, B, C, D) MAX(MAX(A, B), MAX(C, D)) -/* A point on the image plane. It may or may not lie within the - bounds of the image itself. */ -typedef struct Point { +typedef struct { +/*---------------------------------------------------------------------------- + A point on the image plane. It may or may not lie within the + bounds of the image itself. +-----------------------------------------------------------------------------*/ int x; int y; } Point; -/* A quadrilateral on the image plane */ -typedef struct Quad { + +static Point +pointXy(int const x, + int const y) { + + Point retval; + + retval.x = x; + retval.y = y; + + return retval; +} + + + +typedef struct { +/*---------------------------------------------------------------------------- + A quadrilateral on the image plane. +-----------------------------------------------------------------------------*/ Point ul; Point ur; Point lr; Point ll; } Quad; -/* A user-specified mapping from one quadrilateral to another */ -typedef struct QuadMap { - Quad from; - Quad to; +typedef struct { +/*---------------------------------------------------------------------------- + A specification of a quadrilateral on the image plane, either explicitly + or just as "the whole image". +-----------------------------------------------------------------------------*/ + bool wholeImage; + Quad explicit; /* Meaningful only if 'wholeImage' is false */ +} QuadSpec; + +typedef struct { +/*---------------------------------------------------------------------------- + Specification of a mapping from one quadrilateral to another +-----------------------------------------------------------------------------*/ + QuadSpec from; + QuadSpec to; } QuadMap; struct CmdlineInfo { @@ -75,21 +105,27 @@ struct CmdlineInfo { in a form easy for the program to use. */ const char * inputFilespec; /* "-" if stdin */ - QuadMap qmap; /* Source and target quadrilaterals */ - Quad bbox; /* Bounding box for the target image */ - const char * fillColor; /* Fill color for unused coordinates */ + QuadMap qmap; + /* Source and target quadrilaterals as specified by -to and -from; + Note that the file identified by 'mapfile' also supplies such + information. + */ + QuadSpec view; + /* Bounding box for the target image */ + const char * mapfile; /* Null if not specified */ + const char * fill; /* Null if not specified */ + unsigned int verbose; }; -static unsigned int -parseCoords(const char * const str, - int * const coords) { +static void +parseCoords(const char * const str, + int * const coords, + unsigned int * const nP) { /*---------------------------------------------------------------------------- - Parse a list of up to 16 integers. The function returns the number - of integers encountered. + Parse a list of up to 16 integers. Return as *nP how may there are. -----------------------------------------------------------------------------*/ - const char * p; char * pnext; unsigned int i; @@ -106,138 +142,86 @@ parseCoords(const char * const str, errno = 0; /* strtol() sets errno on error. */ val = strtol(p, &pnext, 10); if (errno == ERANGE) - return i; /* Integer lies out of long int range */ + break; /* Integer lies out of long int range */ if (errno != 0 || pnext == p) - return i; /* Too few integers */ + break; /* Too few integers */ coords[i] = (int)val; if ((long int)coords[i] != val) - return i; /* Integer lies out of int range */ + break; /* Integer lies out of int range */ } - return i; + *nP = i; } -static void -parseViewString(const char * const str, - Quad * const quad) { +static Quad +quadFmViewString(const char * const str) { /*---------------------------------------------------------------------------- Parse a list of four integers in the order {ulx, uly, lrx, lry} into a - quadrilateral. The function aborts on error. ------------------------------------------------------------------------------*/ + quadrilateral. + Abort the program if 'str' is not valid. +-----------------------------------------------------------------------------*/ + Quad retval; int coords[16]; + unsigned int nCoord; + + parseCoords(str, coords, &nCoord); + + if (nCoord != 4) + pm_error("failed to parse '%s' as a list of four integers", str); - if (parseCoords(str, coords) != 4) - pm_error("failed to parse \"%s\" as a list of four integers", str); - quad->ul.x = quad->ll.x = coords[0]; - quad->ul.y = quad->ur.y = coords[1]; - quad->lr.x = quad->ur.x = coords[2]; - quad->lr.y = quad->ll.y = coords[3]; + retval.ul.x = retval.ll.x = coords[0]; + retval.ul.y = retval.ur.y = coords[1]; + retval.lr.x = retval.ur.x = coords[2]; + retval.lr.y = retval.ll.y = coords[3]; + + return retval; } -static void -parseQuadString(const char * const str, - Quad * const quad) { -/*---------------------------------------------------------------------------- - Parse a list of eight integers in the order {ulx, uly, urx, ury, - lrx, lry, llx, lly} into a quadrilateral. The function aborts on - error. ------------------------------------------------------------------------------*/ +static Quad +quadFmIntList(const int * const coords) { + Quad retval; - int coords[16]; + retval.ul.x = coords[0]; + retval.ul.y = coords[1]; + retval.ur.x = coords[2]; + retval.ur.y = coords[3]; + retval.lr.x = coords[4]; + retval.lr.y = coords[5]; + retval.ll.x = coords[6]; + retval.ll.y = coords[7]; - if (parseCoords(str, coords) != 8) - pm_error("failed to parse \"%s\" as a list of eight integers", str); - quad->ul.x = coords[0]; - quad->ul.y = coords[1]; - quad->ur.x = coords[2]; - quad->ur.y = coords[3]; - quad->lr.x = coords[4]; - quad->lr.y = coords[5]; - quad->ll.x = coords[6]; - quad->ll.y = coords[7]; + return retval; } -static void -readMapFile(const char * const fname, - QuadMap * const qmap) { +static Quad +quadFmString(const char * const str) { /*---------------------------------------------------------------------------- - Read from a file either 16 numbers in the order {ulx1, uly1, urx1, ury1, - lrx1, lry1, llx1, lly1, ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, lly2} - or 8 numbers in the order {ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, - lly2}. This function aborts on error. ------------------------------------------------------------------------------*/ - - FILE * fp; - char * str; /* Entire file contents */ - int coords[16]; /* File as a list of up to 16 coordinates */ - char * c; - long int nread; + Parse a list of eight integers in the order {ulx, uly, urx, ury, + lrx, lry, llx, lly} into a quadrilateral. - /* Read the entire file. */ - fp = pm_openr(fname); - str = pm_read_unknown_size(fp, &nread); - REALLOCARRAY_NOFAIL(str, nread + 1); - str[nread] = '\0'; - pm_close(fp); + Abort the program if 'str' is not a valid list of eight integers. +-----------------------------------------------------------------------------*/ + int coords[16]; + unsigned int nCoord; - /* Replace newlines and tabs with spaces to prettify error reporting. */ - for (c = str; *c != '\0'; ++c) - if (isspace(*c)) - *c = ' '; + parseCoords(str, coords, &nCoord); - /* Read either {from, to} or just a {to} quadrilateral. */ - switch (parseCoords(str, coords)) { - case 16: - /* 16 integers: assign both the "from" and the "to" quadrilateral. */ - qmap->from.ul.x = coords[0]; - qmap->from.ul.y = coords[1]; - qmap->from.ur.x = coords[2]; - qmap->from.ur.y = coords[3]; - qmap->from.lr.x = coords[4]; - qmap->from.lr.y = coords[5]; - qmap->from.ll.x = coords[6]; - qmap->from.ll.y = coords[7]; - qmap->to.ul.x = coords[8]; - qmap->to.ul.y = coords[9]; - qmap->to.ur.x = coords[10]; - qmap->to.ur.y = coords[11]; - qmap->to.lr.x = coords[12]; - qmap->to.lr.y = coords[13]; - qmap->to.ll.x = coords[14]; - qmap->to.ll.y = coords[15]; - break; - case 8: - /* 8 integers: assign only the "to" quadrilateral. */ - memset((void *)&qmap->from, 0, sizeof(Quad)); - qmap->to.ul.x = coords[0]; - qmap->to.ul.y = coords[1]; - qmap->to.ur.x = coords[2]; - qmap->to.ur.y = coords[3]; - qmap->to.lr.x = coords[4]; - qmap->to.lr.y = coords[5]; - qmap->to.ll.x = coords[6]; - qmap->to.ll.y = coords[7]; - break; - default: - /* Any other number of integers: issue an error message. */ - pm_error("failed to parse \"%s\" as a list of either 8 or 16 integers", - str); - break; - } + if (nCoord != 8) + pm_error("failed to parse '%s' as a list of eight integers", str); - free(str); + return quadFmIntList(coords); } static void -parseCommandLine(int argc, +parseCommandLine(int argc, const char ** const argv, struct CmdlineInfo * const cmdlineP ) { /*---------------------------------------------------------------------------- @@ -255,23 +239,26 @@ parseCommandLine(int argc, unsigned int option_def_index; - unsigned int mapFileSpec = 0, fromSpec = 0, toSpec = 0; - unsigned int viewSpec = 0, fillColorSpec = 0; - char *mapFile, *from, *to, *view; + unsigned int mapfileSpec, fromSpec, toSpec, viewSpec, fillSpec; + const char * from; + const char * to; + const char * view; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ - OPTENT3(0, "mapfile", OPT_STRING, &mapFile, - &mapFileSpec, 0); + OPTENT3(0, "mapfile", OPT_STRING, &cmdlineP->mapfile, + &mapfileSpec, 0); OPTENT3(0, "from", OPT_STRING, &from, &fromSpec, 0); OPTENT3(0, "to", OPT_STRING, &to, &toSpec, 0); OPTENT3(0, "view", OPT_STRING, &view, &viewSpec, 0); - OPTENT3(0, "fill", OPT_STRING, &cmdlineP->fillColor, - &fillColorSpec, 0); + OPTENT3(0, "fill", OPT_STRING, &cmdlineP->fill, + &fillSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -280,23 +267,29 @@ parseCommandLine(int argc, pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and local variables. */ - if (!fillColorSpec) - cmdlineP->fillColor = NULL; - - memset((void *)&cmdlineP->qmap, 0, sizeof(QuadMap)); - if (mapFileSpec) - readMapFile(mapFile, &cmdlineP->qmap); - if (fromSpec) - parseQuadString(from, &cmdlineP->qmap.from); - if (toSpec) - parseQuadString(to, &cmdlineP->qmap.to); - if (!mapFileSpec && !fromSpec && !toSpec && !viewSpec) - pm_error("You must specify at least one of " - "-mapfile, -qin, -qout, and -view"); - if (viewSpec) - parseViewString(view, &cmdlineP->bbox); - else - memset((void *)&cmdlineP->bbox, 0, sizeof(Quad)); + if (!fillSpec) + cmdlineP->fill = NULL; + + if (!mapfileSpec) + cmdlineP->mapfile = NULL; + + if (fromSpec) { + cmdlineP->qmap.from.wholeImage = false; + cmdlineP->qmap.from.explicit = quadFmString(from); + } else + cmdlineP->qmap.from.wholeImage = true; + + if (toSpec) { + cmdlineP->qmap.to.wholeImage = false; + cmdlineP->qmap.to.explicit = quadFmString(to); + } else + cmdlineP->qmap.to.wholeImage = true; + + if (viewSpec) { + cmdlineP->view.wholeImage = false; + cmdlineP->view.explicit = quadFmViewString(view); + } else + cmdlineP->view.wholeImage = true; if (argc < 2) cmdlineP->inputFilespec = "-"; @@ -304,77 +297,168 @@ parseCommandLine(int argc, cmdlineP->inputFilespec = argv[1]; else pm_error("Too many non-option arguments: %u. " - "Only argument is input file name", argc - 1); + "Only possible argument is input file name", argc - 1); free((void *) option_def); } -static tuple -parseFillColor(const struct pam * const pamP, - const struct CmdlineInfo * const cmdlineP) { +static void +readMapFile(const char * const fname, + QuadMap * const qmapP) { /*---------------------------------------------------------------------------- - Parse the fill color into the correct format for the given PAM metadata. + Read from a file either 16 numbers in the order {ulx1, uly1, urx1, ury1, + lrx1, lry1, llx1, lly1, ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, lly2} + or 8 numbers in the order {ulx2, uly2, urx2, ury2, lrx2, lry2, llx2, + lly2}. + + Abort the program if the file does not contain data in this format. -----------------------------------------------------------------------------*/ + FILE * fp; + char * str; /* Entire file contents */ + int coords[16]; /* File as a list of up to 16 coordinates */ + unsigned int nCoord; + long int nread; - tuple rgb; - tuple fillColor; + /* Read the entire file. */ + fp = pm_openr(fname); + str = pm_read_unknown_size(fp, &nread); + REALLOCARRAY_NOFAIL(str, nread + 1); + str[nread] = '\0'; + pm_close(fp); - if (!cmdlineP->fillColor) { - pnm_createBlackTuple(pamP, &fillColor); - return fillColor; + { + unsigned int i; + /* Replace newlines and tabs with spaces to prettify error + reporting. + */ + for (i = 0; str[i]; ++i) + if (isspace(str[i])) + str[i] = ' '; } + parseCoords(str, coords, &nCoord); - rgb = pnm_parsecolor(cmdlineP->fillColor, pamP->maxval); - fillColor = pnm_allocpamtuple(pamP); - switch (pamP->depth) { - case 1: - /* Grayscale */ - fillColor[0] = (rgb[PAM_RED_PLANE]*299 + - rgb[PAM_GRN_PLANE]*587 + - rgb[PAM_BLU_PLANE]*114)/1000; - break; - case 2: - /* Grayscale + alpha */ - fillColor[0] = (rgb[PAM_RED_PLANE]*299 + - rgb[PAM_GRN_PLANE]*587 + - rgb[PAM_BLU_PLANE]*114)/1000; - fillColor[PAM_GRAY_TRN_PLANE] = pamP->maxval; - break; - case 3: - /* RGB */ - pnm_assigntuple(pamP, fillColor, rgb); + /* Read either {from, to} or just a {to} quadrilateral. */ + switch (nCoord) { + case 16: + /* 16 integers: assign both the "from" and the "to" quadrilateral. */ + qmapP->from.wholeImage = false; + qmapP->from.explicit = quadFmIntList(&coords[0]); + qmapP->to.wholeImage = false; + qmapP->to.explicit = quadFmIntList(&coords[8]); break; - case 4: - /* RGB + alpha */ - pnm_assigntuple(pamP, fillColor, rgb); - fillColor[PAM_TRN_PLANE] = pamP->maxval; + case 8: + /* 8 integers: assign only the "to" quadrilateral. */ + qmapP->from.wholeImage = true; + qmapP->to.explicit = quadFmIntList(coords); break; default: - pm_error("unexpected image depth %d", pamP->depth); + /* Not valid input */ + pm_error("failed to parse contents of map file '%s' ('%s') " + "as a list of either 8 or 16 integers", + fname, str); break; } - return fillColor; + free(str); } -static tuple ** -initOutputImage(const struct pam * const pamP, - const struct CmdlineInfo * const cmdlineP) { +static void +reportQuads(Quad const qfrom, + Quad const qto) { + + pm_message("Copying from ((%d,%d),(%d,%d),(%d,%d),(%d,%d)) " + "to ((%d,%d),(%d,%d),(%d,%d),(%d,%d))", + qfrom.ul.x, qfrom.ul.y, + qfrom.ur.x, qfrom.ur.y, + qfrom.lr.x, qfrom.lr.y, + qfrom.ll.x, qfrom.ll.y, + qto.ul.x, qto.ul.y, + qto.ur.x, qto.ur.y, + qto.lr.x, qto.lr.y, + qto.ll.x, qto.ll.y + ); +} + + + +static void +reportBbox(Quad const bbox) { + + pm_message("The bounding box is ((%d,%d),(%d,%d),(%d,%d),(%d,%d))", + bbox.ul.x, bbox.ul.y, + bbox.ur.x, bbox.ur.y, + bbox.lr.x, bbox.lr.y, + bbox.ll.x, bbox.ll.y + ); +} + + + +static tuple +parseFillColor(const struct pam * const pamP, + const char * const fillColorSpec) { /*---------------------------------------------------------------------------- - Allocate and initialize the output image. + Parse the fill color into the correct format for the given PAM metadata. -----------------------------------------------------------------------------*/ + tuple retval; + + if (!fillColorSpec) + pnm_createBlackTuple(pamP, &retval); + else { + tuple const rgb = pnm_parsecolor(fillColorSpec, pamP->maxval); + + retval = pnm_allocpamtuple(pamP); + + switch (pamP->depth) { + case 1: + /* Grayscale */ + retval[0] = (rgb[PAM_RED_PLANE]*299 + + rgb[PAM_GRN_PLANE]*587 + + rgb[PAM_BLU_PLANE]*114)/1000; + break; + case 2: + /* Grayscale + alpha */ + retval[0] = (rgb[PAM_RED_PLANE]*299 + + rgb[PAM_GRN_PLANE]*587 + + rgb[PAM_BLU_PLANE]*114)/1000; + retval[PAM_GRAY_TRN_PLANE] = pamP->maxval; + break; + case 3: + /* RGB */ + pnm_assigntuple(pamP, retval, rgb); + break; + case 4: + /* RGB + alpha */ + pnm_assigntuple(pamP, retval, rgb); + retval[PAM_TRN_PLANE] = pamP->maxval; + break; + default: + pm_error("unexpected image depth %d", pamP->depth); + break; + } + } + return retval; +} + + +static tuple ** +initOutputImage(const struct pam * const pamP, + const char * const fillColorSpec) { +/*---------------------------------------------------------------------------- + Allocate and initialize the output image data structure. +-----------------------------------------------------------------------------*/ tuple fillColor; /* Fill color to use for unused coordinates */ tuple ** outImg; /* Output image */ unsigned int row; outImg = pnm_allocpamarray(pamP); - fillColor = parseFillColor(pamP, cmdlineP); + fillColor = parseFillColor(pamP, fillColorSpec); for (row = 0; row < pamP->height; ++row) { unsigned int col; @@ -390,138 +474,142 @@ initOutputImage(const struct pam * const pamP, static void -computeSteps(const Quad * const qfrom, - const Quad * const qto, - double * const ustep, - double * const vstep) { +computeSteps(Quad const qfrom, + Quad const qto, + double * const ustepP, + double * const vstepP) { /*---------------------------------------------------------------------------- Compute increments for u and v as these range from 0.0 to 1.0. -----------------------------------------------------------------------------*/ - double fx0, fx1, fxd; double tx0, tx1, txd; double fy0, fy1, fyd; double ty0, ty1, tyd; /* Compute ustep as the inverse of the maximum possible x delta across - either the "from" or "to" quadrilateral. */ - fx0 = MIN4((double)qfrom->ur.x, - (double)qfrom->ul.x, - (double)qfrom->lr.x, - (double)qfrom->ll.x); - fx1 = MAX4((double)qfrom->ur.x, - (double)qfrom->ul.x, - (double)qfrom->lr.x, - (double)qfrom->ll.x); + either the "from" or "to" quadrilateral. + */ + fx0 = MIN4((double)qfrom.ur.x, + (double)qfrom.ul.x, + (double)qfrom.lr.x, + (double)qfrom.ll.x); + fx1 = MAX4((double)qfrom.ur.x, + (double)qfrom.ul.x, + (double)qfrom.lr.x, + (double)qfrom.ll.x); fxd = fx1 - fx0; - tx0 = MIN4((double)qto->ur.x, - (double)qto->ul.x, - (double)qto->lr.x, - (double)qto->ll.x); - tx1 = MAX4((double)qto->ur.x, - (double)qto->ul.x, - (double)qto->lr.x, - (double)qto->ll.x); + + tx0 = MIN4((double)qto.ur.x, + (double)qto.ul.x, + (double)qto.lr.x, + (double)qto.ll.x); + tx1 = MAX4((double)qto.ur.x, + (double)qto.ul.x, + (double)qto.lr.x, + (double)qto.ll.x); txd = tx1 - tx0; + if (fxd == 0.0 && txd == 0.0) - *ustep = 1.0; /* Arbitrary nonzero step */ - *ustep = 0.5/MAX(fxd, txd); - /* Divide into 0.5 instead of 1.0 for additional smoothing. */ + *ustepP = 1.0; /* Arbitrary nonzero step */ + else + *ustepP = 0.5/MAX(fxd, txd); + /* Divide into 0.5 instead of 1.0 for additional smoothing. */ /* Compute vstep as the inverse of the maximum possible y delta across - either the "from" or "to" quadrilateral - . */ - fy0 = MIN4((double)qfrom->ur.y, - (double)qfrom->ul.y, - (double)qfrom->lr.y, - (double)qfrom->ll.y); - fy1 = MAX4((double)qfrom->ur.y, - (double)qfrom->ul.y, - (double)qfrom->lr.y, - (double)qfrom->ll.y); + either the "from" or "to" quadrilateral. + */ + fy0 = MIN4((double)qfrom.ur.y, + (double)qfrom.ul.y, + (double)qfrom.lr.y, + (double)qfrom.ll.y); + fy1 = MAX4((double)qfrom.ur.y, + (double)qfrom.ul.y, + (double)qfrom.lr.y, + (double)qfrom.ll.y); fyd = fy1 - fy0; - ty0 = MIN4((double)qto->ur.y, - (double)qto->ul.y, - (double)qto->lr.y, - (double)qto->ll.y); - ty1 = MAX4((double)qto->ur.y, - (double)qto->ul.y, - (double)qto->lr.y, - (double)qto->ll.y); + ty0 = MIN4((double)qto.ur.y, + (double)qto.ul.y, + (double)qto.lr.y, + (double)qto.ll.y); + ty1 = MAX4((double)qto.ur.y, + (double)qto.ul.y, + (double)qto.lr.y, + (double)qto.ll.y); tyd = ty1 - ty0; + if (fyd == 0.0 && tyd == 0.0) - *vstep = 1.0; /* Arbitrary nonzero step */ - *vstep = 0.5/MAX(fyd, tyd); - /* Divide into 0.5 instead of 1.0 for additional smoothing. */ + *vstepP = 1.0; /* Arbitrary nonzero step */ + else + *vstepP = 0.5/MAX(fyd, tyd); + /* Divide into 0.5 instead of 1.0 for additional smoothing. */ } -static Quad * -prepareQuadrilateral(const struct pam * const pamP, - const Quad * const qdata) { +static Quad +quadrilateralFmSpec(const struct pam * const pamP, + QuadSpec const qdata) { /*---------------------------------------------------------------------------- - If a quadrilateral has all zero points, replace it with a quadrilateral - of the full size of the image. The caller should free the result. + The quadrilateral specified by 'qdata'. -----------------------------------------------------------------------------*/ + Quad retval; - Quad * qcopy; - - MALLOCVAR_NOFAIL(qcopy); - - if (qdata->ul.x == 0 && qdata->ul.y == 0 && - qdata->ur.x == 0 && qdata->ur.y == 0 && - qdata->ll.x == 0 && qdata->ll.y == 0 && - qdata->lr.x == 0 && qdata->lr.y == 0) { + if (qdata.wholeImage) { /* Set the quadrilateral to the image's bounding box. */ - memset((void *)qcopy, 0, sizeof(Quad)); - qcopy->ur.x = pamP->width - 1; - qcopy->lr.x = pamP->width - 1; - qcopy->lr.y = pamP->height - 1; - qcopy->ll.y = pamP->height - 1; + retval.ul = pointXy(0, 0 ); + retval.ur = pointXy(pamP->width - 1, 0 ); + retval.ll = pointXy(0, pamP->height - 1); + retval.lr = pointXy(pamP->width - 1, pamP->height - 1); } else { /* Use the quadrilateral as specified. */ - memcpy(qcopy, qdata, sizeof(Quad)); + retval = qdata.explicit; } - return qcopy; + return retval; } -static void -coordsAtPercent(const Quad * const quad, - double const u, - double const v, - int * const x, - int * const y) { + +static Point +coordsAtPercent(Quad const quad, + double const u, + double const v) { /*---------------------------------------------------------------------------- Return the (x, y) coordinates that lie at (u%, v%) from the upper left to the lower right of a given quadrilateral. -----------------------------------------------------------------------------*/ - - *x = (int) nearbyint((1.0 - u)*(1.0 - v)*quad->ul.x + - u*(1.0 - v)*quad->ur.x + - u*v*quad->lr.x + - (1.0 - u)*v*quad->ll.x); - *y = (int) nearbyint((1.0 - u)*(1.0 - v)*quad->ul.y + - u*(1.0 - v)*quad->ur.y + - u*v*quad->lr.y + - (1.0 - u)*v*quad->ll.y); + return pointXy( + (int) nearbyint((1.0 - u) * (1.0 - v) * quad.ul.x + /* x */ + u * (1.0 - v) * quad.ur.x + + u * v * quad.lr.x + + (1.0 - u) * v * quad.ll.x), + (int) nearbyint((1.0 - u) * (1.0 - v) * quad.ul.y + /* y */ + u * (1.0 - v) * quad.ur.y + + u * v * quad.lr.y + + (1.0 - u) * v * quad.ll.y) + ); } -static void -computeBoundingBox(const Quad * const q, - Quad * const bbox) { +static Quad +boundingBoxOfQuadrilateral(Quad const q) { /*---------------------------------------------------------------------------- - Compute the bounding box of a given quadrilateral. + The bounding box of quadrilateral 'q'. -----------------------------------------------------------------------------*/ + Quad retval; - bbox->ul.x = bbox->ll.x = MIN4(q->ul.x, q->ur.x, q->lr.x, q->ll.x); - bbox->ul.y = bbox->ur.y = MIN4(q->ul.y, q->ur.y, q->lr.y, q->ll.y); - bbox->ur.x = bbox->lr.x = MAX4(q->ul.x, q->ur.x, q->lr.x, q->ll.x); - bbox->ll.y = bbox->lr.y = MAX4(q->ul.y, q->ur.y, q->lr.y, q->ll.y); + int const leftLimit = MIN4(q.ul.x, q.ur.x, q.lr.x, q.ll.x); + int const rghtLimit = MAX4(q.ul.x, q.ur.x, q.lr.x, q.ll.x); + int const topLimit = MIN4(q.ul.y, q.ur.y, q.lr.y, q.ll.y); + int const botLimit = MAX4(q.ul.y, q.ur.y, q.lr.y, q.ll.y); + + retval.ul = pointXy(leftLimit, topLimit); + retval.ur = pointXy(rghtLimit, topLimit); + retval.ll = pointXy(leftLimit, botLimit); + retval.lr = pointXy(rghtLimit, botLimit); + + return retval; } @@ -529,8 +617,8 @@ computeBoundingBox(const Quad * const q, static void mapQuadrilaterals(const struct pam * const inPamP, const struct pam * const outPamP, - const Quad * const qfrom, - const Quad * const qto, + Quad const qfrom, + Quad const qto, tuple ** const inImg, tuple ** const outImg, int const xofs, @@ -540,109 +628,120 @@ mapQuadrilaterals(const struct pam * const inPamP, target image. This is the function that implemens pamhomography's primary functionality. -----------------------------------------------------------------------------*/ - sample ** channel; /* Aggregated values for a single channel */ unsigned long ** tally; /* Number of values at each coordinate in the above */ double ustep, vstep; /* Steps to use when iterating from 0.0 to 1.0 */ - double u, v; - unsigned int plane, row, col; + unsigned int plane; MALLOCARRAY2_NOFAIL(channel, outPamP->height, outPamP->width); - MALLOCARRAY2_NOFAIL(tally, outPamP->height, outPamP->width); + MALLOCARRAY2_NOFAIL(tally, outPamP->height, outPamP->width); computeSteps(qfrom, qto, &ustep, &vstep); for (plane = 0; plane < outPamP->depth; ++plane) { /* Reset the channel colors and tally for each plane, */ - for (row = 0; row < outPamP->height; ++row) + unsigned int row; + double v; + for (row = 0; row < outPamP->height; ++row) { + unsigned int col; for (col = 0; col < outPamP->width; ++col) { channel[row][col] = 0; - tally[row][col] = 0; + tally [row][col] = 0; } - + } /* Iterate from 0% to 100% in the y dimension. */ for (v = 0.0; v <= 1.0; v += vstep) { /* Iterate from 0% to 100% in the x dimension. */ + double u; for (u = 0.0; u <= 1.0; u += ustep) { - int x0, y0; /* "From" coordinate */ - int x1, y1; /* "To" coordinate */ - - /* Map (u%, v%) of one quadrilateral to (u%, v%) of the - other quadrilateral. */ - coordsAtPercent(qfrom, u, v, &x0, &y0); - coordsAtPercent(qto, u, v, &x1, &y1); - - /* Copy the source image's (x0, y0) to the destination - image's (x1, y1) in the current plane. */ - x1 += xofs; - y1 += yofs; - if (x0 >= 0 && y0 >= 0 && - x0 < inPamP->width && y0 < inPamP->height && - x1 >= 0 && y1 >= 0 && - x1 < outPamP->width && y1 < outPamP->height) { - channel[y1][x1] += inImg[y0][x0][plane]; - tally[y1][x1]++; + Point from; /* "From" coordinate */ + Point to; /* "To" coordinate */ + + /* Map (u%, v%) of one quadrilateral to (u%, v%) of the other + quadrilateral. + */ + from = coordsAtPercent(qfrom, u, v); + to = coordsAtPercent(qto, u, v); + + /* Copy the source image's 'from' pixel as the destination + image's 'to' pixel in the current plane. + */ + to.x += xofs; + to.y += yofs; + if (from.x >= 0 && from.y >= 0 && + from.x < inPamP->width && from.y < inPamP->height && + to.x >= 0 && to.y >= 0 && + to.x < outPamP->width && to.y < outPamP->height) { + + channel[to.y][to.x] += inImg[from.y][from.x][plane]; + ++tally[to.y][to.x]; } } } /* Assign the current plane in the output image the average color at each point. */ - for (row = 0; row < outPamP->height; ++row) - for (col = 0; col < outPamP->width; ++col) + for (row = 0; row < outPamP->height; ++row) { + unsigned int col; + for (col = 0; col < outPamP->width; ++col) { if (tally[row][col] != 0) outImg[row][col][plane] = (channel[row][col] + tally[row][col]/2) / tally[row][col]; + } + } } pm_freearray2((void ** const)tally); pm_freearray2((void ** const)channel); - free((void *)qto); - free((void *)qfrom); } static void -processFile(FILE * const ifP, - const struct CmdlineInfo * const cmdlineP) { +processFile(FILE * const ifP, + QuadMap const qmap, + QuadSpec const view, + const char * const fillColorSpec, + bool const verbose) { /*---------------------------------------------------------------------------- Read the input image, create the output image, and map a quadrilateral in the former to a quadrilateral in the latter. -----------------------------------------------------------------------------*/ - struct pam inPam; /* PAM metadata for the input file */ struct pam outPam; /* PAM metadata for the output file */ tuple ** inImg; /* Input image */ tuple ** outImg; /* Output image */ - Quad *qfrom, *qto; /* Source and target quadrilaterals */ + Quad qfrom, qto; /* Source and target quadrilaterals */ Quad bbox; /* Bounding box around the transformed input image */ inImg = pnm_readpam(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type)); /* Extract quadrilaterals and populate them with the image bounds if necessary. */ - qfrom = prepareQuadrilateral(&inPam, &cmdlineP->qmap.from); - qto = prepareQuadrilateral(&inPam, &cmdlineP->qmap.to); + qfrom = quadrilateralFmSpec(&inPam, qmap.from); + qto = quadrilateralFmSpec(&inPam, qmap.to); + + if (verbose) + reportQuads(qfrom, qto); /* Allocate storage for the target image. */ - if (cmdlineP->bbox.ul.x == 0 && cmdlineP->bbox.ul.y == 0 && - cmdlineP->bbox.lr.x == 0 && cmdlineP->bbox.lr.y == 0) - /* User did not specify a target bounding box. Compute optimal - dimensions. */ - computeBoundingBox(qto, &bbox); + if (view.wholeImage) + bbox = boundingBoxOfQuadrilateral(qto); else - /* User specified a target bounding box. Use it. */ - bbox = cmdlineP->bbox; - outPam = inPam; - outPam.file = stdout; - outPam.width = bbox.lr.x - bbox.ul.x + 1; + bbox = view.explicit; + + if (verbose) + reportBbox(bbox); + + outPam = inPam; /* initial value */ + outPam.file = stdout; + outPam.width = bbox.lr.x - bbox.ul.x + 1; outPam.height = bbox.lr.y - bbox.ul.y + 1; - outImg = initOutputImage(&outPam, cmdlineP); + outImg = initOutputImage(&outPam, fillColorSpec); mapQuadrilaterals(&inPam, &outPam, qfrom, qto, @@ -662,16 +761,39 @@ main(int argc, const char *argv[]) { struct CmdlineInfo cmdline; /* Parsed command line */ FILE * ifP; + QuadMap qmap; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); + if (cmdline.mapfile) { + /* Use the from and/or to values from the map file where the user + didn't explicitly state them + */ + QuadMap mapFileValue; + + readMapFile(cmdline.mapfile, &mapFileValue); + + if (cmdline.qmap.from.wholeImage) + qmap.from = mapFileValue.from; + else + qmap.from = cmdline.qmap.from; + + if (cmdline.qmap.to.wholeImage) + qmap.to = mapFileValue.to; + else + qmap.to = cmdline.qmap.to; + } else + qmap = cmdline.qmap; + ifP = pm_openr(cmdline.inputFilespec); - processFile(ifP, &cmdline); + processFile(ifP, qmap, cmdline.view, cmdline.fill, !!cmdline.verbose); pm_close(ifP); return 0; } + + diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c index 7da3122b..f10cc15c 100644 --- a/other/pnmcolormap.c +++ b/other/pnmcolormap.c @@ -1,7 +1,6 @@ -/****************************************************************************** - pnmcolormap.c -******************************************************************************* - +/*============================================================================= + pnmcolormap +=============================================================================== Create a colormap file (a PPM image containing one pixel of each of a set of colors). Base the set of colors on an input image. @@ -20,9 +19,7 @@ copyright notice and this permission notice appear in supporting documentation. This software is provided "as is" without express or implied warranty. - -******************************************************************************/ - +=============================================================================*/ #include <assert.h> #include <math.h> @@ -308,7 +305,7 @@ sortBoxes(struct BoxVector * const boxVectorP, */ static void -findBoxBoundaries(tupletable2 const colorfreqtable, +findBoxBoundaries(tupletable2 const colorFreqTable, unsigned int const depth, unsigned int const boxStart, unsigned int const boxSize, @@ -322,14 +319,14 @@ findBoxBoundaries(tupletable2 const colorfreqtable, unsigned int i; for (plane = 0; plane < depth; ++plane) { - minval[plane] = colorfreqtable.table[boxStart]->tuple[plane]; + minval[plane] = colorFreqTable.table[boxStart]->tuple[plane]; maxval[plane] = minval[plane]; } for (i = 1; i < boxSize; ++i) { unsigned int plane; for (plane = 0; plane < depth; ++plane) { - sample const v = colorfreqtable.table[boxStart + i]->tuple[plane]; + sample const v = colorFreqTable.table[boxStart + i]->tuple[plane]; if (v < minval[plane]) minval[plane] = v; if (v > maxval[plane]) maxval[plane] = v; } @@ -402,7 +399,7 @@ findPlaneWithLargestSpreadByLuminosity(sample const minval[], static void computeBoxSpread(const struct Box * const boxP, - tupletable2 const colorfreqtable, + tupletable2 const colorFreqTable, unsigned int const depth, enum MethodForLargest const methodForLargest, unsigned int * const planeWithLargestP, @@ -420,7 +417,7 @@ computeBoxSpread(const struct Box * const boxP, MALLOCARRAY_NOFAIL(minval, depth); MALLOCARRAY_NOFAIL(maxval, depth); - findBoxBoundaries(colorfreqtable, depth, boxP->index, boxP->colorCt, + findBoxBoundaries(colorFreqTable, depth, boxP->index, boxP->colorCt, minval, maxval); switch (methodForLargest) { @@ -439,13 +436,13 @@ computeBoxSpread(const struct Box * const boxP, static unsigned int -freqTotal(tupletable2 const freqtable) { +freqTotal(tupletable2 const freqTable) { unsigned int i; unsigned int sum; - for (i = 0, sum = 0; i < freqtable.size; ++i) - sum += freqtable.table[i]->value; + for (i = 0, sum = 0; i < freqTable.size; ++i) + sum += freqTable.table[i]->value; return sum; } @@ -453,13 +450,13 @@ freqTotal(tupletable2 const freqtable) { static struct BoxVector -newBoxVector(tupletable2 const colorfreqtable, +newBoxVector(tupletable2 const colorFreqTable, unsigned int const capacity, unsigned int const depth, enum MethodForLargest const methodForLargest) { - unsigned int const colorCt = colorfreqtable.size; - unsigned int const sum = freqTotal(colorfreqtable); + unsigned int const colorCt = colorFreqTable.size; + unsigned int const sum = freqTotal(colorFreqTable); struct BoxVector boxVector; @@ -473,7 +470,7 @@ newBoxVector(tupletable2 const colorfreqtable, boxVector.box[0].colorCt = colorCt; boxVector.box[0].sum = sum; - computeBoxSpread(&boxVector.box[0], colorfreqtable, depth, + computeBoxSpread(&boxVector.box[0], colorFreqTable, depth, methodForLargest, &boxVector.box[0].maxdim, &boxVector.box[0].spread); @@ -497,7 +494,7 @@ destroyBoxVector(struct BoxVector const boxVector) { static void centerBox(int const boxStart, int const boxSize, - tupletable2 const colorfreqtable, + tupletable2 const colorFreqTable, unsigned int const depth, tuple const newTuple) { @@ -507,10 +504,10 @@ centerBox(int const boxStart, int minval, maxval; unsigned int i; - minval = maxval = colorfreqtable.table[boxStart]->tuple[plane]; + minval = maxval = colorFreqTable.table[boxStart]->tuple[plane]; for (i = 1; i < boxSize; ++i) { - int const v = colorfreqtable.table[boxStart + i]->tuple[plane]; + int const v = colorFreqTable.table[boxStart + i]->tuple[plane]; minval = MIN( minval, v); maxval = MAX( maxval, v); } @@ -547,7 +544,7 @@ newColorMap(unsigned int const colorCt, static void averageColors(int const boxStart, int const boxSize, - tupletable2 const colorfreqtable, + tupletable2 const colorFreqTable, unsigned int const depth, tuple const newTuple) { @@ -560,7 +557,7 @@ averageColors(int const boxStart, sum = 0; for (i = 0; i < boxSize; ++i) - sum += colorfreqtable.table[boxStart+i]->tuple[plane]; + sum += colorFreqTable.table[boxStart+i]->tuple[plane]; newTuple[plane] = ROUNDDIV(sum, boxSize); } @@ -571,7 +568,7 @@ averageColors(int const boxStart, static void averagePixels(int const boxStart, int const boxSize, - tupletable2 const colorfreqtable, + tupletable2 const colorFreqTable, unsigned int const depth, tuple const newTuple) { @@ -583,7 +580,7 @@ averagePixels(int const boxStart, /* Count the tuples in question */ n = 0; /* initial value */ for (i = 0; i < boxSize; ++i) - n += colorfreqtable.table[boxStart + i]->value; + n += colorFreqTable.table[boxStart + i]->value; for (plane = 0; plane < depth; ++plane) { @@ -593,8 +590,8 @@ averagePixels(int const boxStart, sum = 0; for (i = 0; i < boxSize; ++i) - sum += colorfreqtable.table[boxStart+i]->tuple[plane] - * colorfreqtable.table[boxStart+i]->value; + sum += colorFreqTable.table[boxStart+i]->tuple[plane] + * colorFreqTable.table[boxStart+i]->value; newTuple[plane] = ROUNDDIV(sum, n); } @@ -605,7 +602,7 @@ averagePixels(int const boxStart, static tupletable2 colormapFromBv(unsigned int const colorCt, struct BoxVector const boxVector, - tupletable2 const colorfreqtable, + tupletable2 const colorFreqTable, unsigned int const depth, enum MethodForRep const methodForRep) { /* @@ -626,19 +623,19 @@ colormapFromBv(unsigned int const colorCt, case REP_CENTER_BOX: centerBox(boxVector.box[boxIdx].index, boxVector.box[boxIdx].colorCt, - colorfreqtable, depth, + colorFreqTable, depth, colormap.table[boxIdx]->tuple); break; case REP_AVERAGE_COLORS: averageColors(boxVector.box[boxIdx].index, boxVector.box[boxIdx].colorCt, - colorfreqtable, depth, + colorFreqTable, depth, colormap.table[boxIdx]->tuple); break; case REP_AVERAGE_PIXELS: averagePixels(boxVector.box[boxIdx].index, boxVector.box[boxIdx].colorCt, - colorfreqtable, depth, + colorFreqTable, depth, colormap.table[boxIdx]->tuple); break; default: @@ -654,14 +651,14 @@ colormapFromBv(unsigned int const colorCt, static void splitBox(struct BoxVector * const boxVectorP, unsigned int const boxIdx, - tupletable2 const colorfreqtable, + tupletable2 const colorFreqTable, unsigned int const depth, enum MethodForLargest const methodForLargest, enum MethodForSplit const methodForSplit) { /*---------------------------------------------------------------------------- Split Box 'boxIdx' in the box vector 'boxVector' (so that bv contains one more box than it did as input). Split it so that each new box represents - about half of the pixels in the distribution given by 'colorfreqtable' for + about half of the pixels in the distribution given by 'colorFreqTable' for the colors in the original box, but with distinct colors in each of the two new boxes. @@ -672,7 +669,7 @@ splitBox(struct BoxVector * const boxVectorP, unsigned int const sum = boxVectorP->box[boxIdx].sum; unsigned int medianIndex; - int lowersum; + unsigned int lowerSum; /* Number of pixels whose value is "less than" the median */ @@ -685,8 +682,8 @@ splitBox(struct BoxVector * const boxVectorP, parameter to compareplane(), which is called by qsort(). */ compareplanePlane = boxVectorP->box[boxIdx].maxdim; - qsort((char*) &colorfreqtable.table[boxStart], boxSize, - sizeof(colorfreqtable.table[boxStart]), + qsort((char*) &colorFreqTable.table[boxStart], boxSize, + sizeof(colorFreqTable.table[boxStart]), compareplane); { @@ -695,9 +692,9 @@ splitBox(struct BoxVector * const boxVectorP, */ unsigned int i; - lowersum = colorfreqtable.table[boxStart]->value; /* initial value */ - for (i = 1; i < boxSize - 1 && lowersum < sum/2; ++i) { - lowersum += colorfreqtable.table[boxStart + i]->value; + lowerSum = colorFreqTable.table[boxStart]->value; /* initial value */ + for (i = 1; i < boxSize - 1 && lowerSum < sum/2; ++i) { + lowerSum += colorFreqTable.table[boxStart + i]->value; } medianIndex = i; } @@ -706,8 +703,8 @@ splitBox(struct BoxVector * const boxVectorP, struct Box * const oldBoxP = &boxVectorP->box[boxIdx]; oldBoxP->colorCt = medianIndex; - oldBoxP->sum = lowersum; - computeBoxSpread(oldBoxP, colorfreqtable, depth, methodForLargest, + oldBoxP->sum = lowerSum; + computeBoxSpread(oldBoxP, colorFreqTable, depth, methodForLargest, &oldBoxP->maxdim, &oldBoxP->spread); } { @@ -715,8 +712,8 @@ splitBox(struct BoxVector * const boxVectorP, newBoxP->index = boxStart + medianIndex; newBoxP->colorCt = boxSize - medianIndex; - newBoxP->sum = sum - lowersum; - computeBoxSpread(newBoxP, colorfreqtable, depth, methodForLargest, + newBoxP->sum = sum - lowerSum; + computeBoxSpread(newBoxP, colorFreqTable, depth, methodForLargest, &newBoxP->maxdim, &newBoxP->spread); ++boxVectorP->boxCt; } @@ -727,21 +724,21 @@ splitBox(struct BoxVector * const boxVectorP, static void -mediancut(tupletable2 const colorfreqtable, +mediancut(tupletable2 const colorFreqTable, unsigned int const depth, - int const newcolorCt, + unsigned int const newColorCt, enum MethodForLargest const methodForLargest, enum MethodForRep const methodForRep, enum MethodForSplit const methodForSplit, tupletable2 * const colormapP) { /*---------------------------------------------------------------------------- - Compute a set of only 'newcolorCt' colors that best represent an + Compute a set of only 'newColorCt' colors that best represent an image whose pixels are summarized by the histogram - 'colorfreqtable'. Each tuple in that table has depth 'depth'. - colorfreqtable.table[i] tells the number of pixels in the subject image + 'colorFreqTable'. Each tuple in that table has depth 'depth'. + colorFreqTable.table[i] tells the number of pixels in the subject image have a particular color. - As a side effect, sort 'colorfreqtable'. + As a side effect, sort 'colorFreqTable'. -----------------------------------------------------------------------------*/ struct BoxVector boxVector; bool multicolorBoxesExist; @@ -749,13 +746,13 @@ mediancut(tupletable2 const colorfreqtable, there is more splitting we can do. */ - boxVector = newBoxVector(colorfreqtable, newcolorCt, depth, + boxVector = newBoxVector(colorFreqTable, newColorCt, depth, methodForLargest); - multicolorBoxesExist = (colorfreqtable.size > 1); + multicolorBoxesExist = (colorFreqTable.size > 1); /* Split boxes until we have enough. */ - while (boxVector.boxCt < newcolorCt && multicolorBoxesExist) { + while (boxVector.boxCt < newColorCt && multicolorBoxesExist) { unsigned int boxIdx; for (boxIdx = 0; @@ -766,10 +763,10 @@ mediancut(tupletable2 const colorfreqtable, if (boxIdx >= boxVector.boxCt) multicolorBoxesExist = FALSE; else - splitBox(&boxVector, boxIdx, colorfreqtable, depth, + splitBox(&boxVector, boxIdx, colorFreqTable, depth, methodForLargest, methodForSplit); } - *colormapP = colormapFromBv(newcolorCt, boxVector, colorfreqtable, + *colormapP = colormapFromBv(newColorCt, boxVector, colorFreqTable, depth, methodForRep); destroyBoxVector(boxVector); @@ -834,7 +831,7 @@ static void computeHistogram(FILE * const ifP, int * const formatP, struct pam * const freqPamP, - tupletable2 * const colorfreqtableP) { + tupletable2 * const colorFreqTableP) { /*---------------------------------------------------------------------------- Make a histogram of the colors in the image stream in the file '*ifP'. @@ -876,13 +873,13 @@ computeHistogram(FILE * const ifP, pnm_nextimage(ifP, &eof); } - colorfreqtableP->table = + colorFreqTableP->table = pnm_tuplehashtotable(&firstPam, tuplehash, colorCount); - colorfreqtableP->size = colorCount; + colorFreqTableP->size = colorCount; pnm_destroytuplehash(tuplehash); - pm_message("%u colors found", colorfreqtableP->size); + pm_message("%u colors found", colorFreqTableP->size); freqPamP->size = sizeof(*freqPamP); freqPamP->len = PAM_STRUCT_SIZE(tuple_type); @@ -899,7 +896,7 @@ computeHistogram(FILE * const ifP, static void computeColorMapFromInput(FILE * const ifP, bool const allColors, - int const reqColors, + unsigned int const reqColors, enum MethodForLargest const methodForLargest, enum MethodForRep const methodForRep, enum MethodForSplit const methodForSplit, @@ -925,23 +922,23 @@ computeColorMapFromInput(FILE * const ifP, *formatP and *freqPamP. (This information is not really relevant to our colormap mission; just a fringe benefit). -----------------------------------------------------------------------------*/ - tupletable2 colorfreqtable; + tupletable2 colorFreqTable; - computeHistogram(ifP, formatP, freqPamP, &colorfreqtable); + computeHistogram(ifP, formatP, freqPamP, &colorFreqTable); if (allColors) { - *colormapP = colorfreqtable; + *colormapP = colorFreqTable; } else { - if (colorfreqtable.size <= reqColors) { - pm_message("Image already has few enough colors (<=%d). " + if (colorFreqTable.size <= reqColors) { + pm_message("Image already has few enough colors (<=%u). " "Keeping same colors.", reqColors); - *colormapP = colorfreqtable; + *colormapP = colorFreqTable; } else { - pm_message("choosing %d colors...", reqColors); - mediancut(colorfreqtable, freqPamP->depth, + pm_message("choosing %u colors...", reqColors); + mediancut(colorFreqTable, freqPamP->depth, reqColors, methodForLargest, methodForRep, methodForSplit, colormapP); - pnm_freetupletable2(freqPamP, colorfreqtable); + pnm_freetupletable2(freqPamP, colorFreqTable); } } } diff --git a/test/Test-Order b/test/Test-Order index 214cf4ba..6aab22bd 100644 --- a/test/Test-Order +++ b/test/Test-Order @@ -74,6 +74,7 @@ pbmclean.test pamcut.test pamcat1.test pamcat2.test +pamcat3.test pnmcat.test pamdice.test pamundice.test diff --git a/test/cut-cat-roundtrip.test b/test/cut-cat-roundtrip.test index c2799a7b..274cb865 100755 --- a/test/cut-cat-roundtrip.test +++ b/test/cut-cat-roundtrip.test @@ -1,6 +1,6 @@ #! /bin/sh # This script tests: pamcut -# Also requires: pamfile pnmcat pnmpad pnmcrop +# Also requires: pamfile pamcat pnmpad pnmcrop tmpdir=${tmpdir:-/tmp} @@ -23,7 +23,7 @@ for i in 0 1 128 224 225 do pamcut -left=$((i+1)) testimg.ppm > ${right_ppm} pamcut -right=$i testimg.ppm > ${left_ppm} - pnmcat -lr ${left_ppm} ${right_ppm} | \ + pamcat -lr ${left_ppm} ${right_ppm} | \ pamcut -left=0 -width=${width} | cksum rm ${left_ppm} ${right_ppm} done @@ -38,7 +38,7 @@ for border in 0 1 128 224 225 do pamcut -left=$((${border}+1)) -width=${width} -pad testimg.ppm > ${right_ppm} pamcut -right=${border} -width=${width} -pad testimg.ppm > ${left_ppm} - pnmcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \ + pamcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \ pamcut -left=$((${width}-${border}-1)) -width=$((${width}*2)) | \ tee ${padded_ppm} | cksum | tr '\n' ' ' pnmcrop -black -right ${padded_ppm} | cksum @@ -53,7 +53,7 @@ for border in 0 1 70 147 do pamcut -top=$((${border}+1)) testimg.ppm > ${bottom_ppm} pamcut -bottom=${border} testimg.ppm > ${top_ppm} - pnmcat -tb ${top_ppm} ${bottom_ppm} | \ + pamcat -tb ${top_ppm} ${bottom_ppm} | \ pamcut -top=0 -height=${height} | cksum rm ${top_ppm} ${bottom_ppm} done @@ -68,7 +68,7 @@ for border in 0 1 70 147 pamcut -top=$((${border}+1)) -height=${height} -pad testimg.ppm \ > ${bottom_ppm} pamcut -bottom=${border} -height=${height} -pad testimg.ppm > ${top_ppm} - pnmcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \ + pamcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \ pamcut -top=$((${height}-${border}-1)) -height=$((${height}*2)) | \ tee ${padded_ppm} | cksum | tr '\n' ' 'cksum pnmcrop -black -bottom ${padded_ppm} | cksum @@ -94,7 +94,7 @@ for i in 0 1 10 30 50 do pamcut -left=$((i+1)) maze.pbm > ${right_ppm} pamcut -right=$i maze.pbm > ${left_ppm} - pnmcat -lr ${left_ppm} ${right_ppm} | \ + pamcat -lr ${left_ppm} ${right_ppm} | \ pamcut -left=0 -width=${width} | cksum rm ${left_ppm} ${right_ppm} done @@ -110,7 +110,7 @@ for border in 0 1 10 30 50 do pamcut -left=$((${border}+1)) -width=${width} -pad maze.pbm > ${right_ppm} pamcut -right=${border} -width=${width} -pad maze.pbm > ${left_ppm} - pnmcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \ + pamcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \ pamcut -left=$((${width}-${border}-1)) -width=$((${width}*2)) | \ tee ${padded_ppm} | cksum | tr '\n' ' ' pnmcrop -black -right ${padded_ppm} | cksum @@ -125,7 +125,7 @@ for border in 0 1 12 21 31 44 do pamcut -top=$((${border}+1)) maze.pbm > ${bottom_ppm} pamcut -bottom=${border} maze.pbm > ${top_ppm} - pnmcat -tb ${top_ppm} ${bottom_ppm} | \ + pamcat -tb ${top_ppm} ${bottom_ppm} | \ pamcut -top=0 -height=${height} | cksum rm ${top_ppm} ${bottom_ppm} done @@ -139,7 +139,7 @@ for border in 0 1 10 50 do pamcut -top=$((${border}+1)) -height=${height} -pad maze.pbm > ${bottom_ppm} pamcut -bottom=${border} -height=${height} -pad maze.pbm > ${top_ppm} - pnmcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \ + pamcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \ pamcut -top=$((${height}-${border}-1)) -height=$((${height}*2)) | \ tee ${padded_ppm} | cksum | tr '\n' ' 'cksum pnmcrop -black -bottom ${padded_ppm} | cksum diff --git a/test/ilbm-roundtrip.ok b/test/ilbm-roundtrip.ok index 07c44515..2d6fcff0 100644 --- a/test/ilbm-roundtrip.ok +++ b/test/ilbm-roundtrip.ok @@ -1,4 +1,4 @@ -Test 1. Should produce 669206373 10102 five times +Test 1. Should produce 669206373 10102 four times 669206373 10102 669206373 10102 669206373 10102 diff --git a/test/ilbm-roundtrip.test b/test/ilbm-roundtrip.test index c0b684ed..e7a1a4e5 100755 --- a/test/ilbm-roundtrip.test +++ b/test/ilbm-roundtrip.test @@ -2,7 +2,7 @@ # This script tests: ppmtoilbm ilbmtoppm # Also requires: pamseq pamdepth pamtopnm pnmremap pnmcolormap -echo "Test 1. Should produce 669206373 10102 five times" +echo "Test 1. Should produce 669206373 10102 four times" #Output is PPM raw, 57 by 59 maxval 255 ppmtoilbm maze.pbm | ilbmtoppm | cksum diff --git a/test/lps-roundtrip.test b/test/lps-roundtrip.test index 78350d71..24076ef1 100755 --- a/test/lps-roundtrip.test +++ b/test/lps-roundtrip.test @@ -30,4 +30,4 @@ pbmtolps -dpi 72 < testgrid.pbm | sed 's/noname/testgrid.pbm/' | \ cmp -s ${test_ps} - echo $? -rm ${test_pgm} +rm ${test_pgm} ${test_ps} diff --git a/test/pamarith.ok b/test/pamarith.ok index 973b8d1b..c2d34dea 100644 --- a/test/pamarith.ok +++ b/test/pamarith.ok @@ -193,7 +193,7 @@ Test 3 3072492814 913 3072492814 913 Test 4 (input = output) -Prints 281226646 481 five times, then 2921940274 59 five times +Prints 281226646 481 six times, then 2921940274 59 six times input image 281226646 481 -minimum diff --git a/test/pamarith.test b/test/pamarith.test index 34922a90..061f9488 100755 --- a/test/pamarith.test +++ b/test/pamarith.test @@ -88,7 +88,7 @@ for fn in "-subtract" "-divide" "-compare" "-shiftleft" "-shiftright" rm ${input3_ppm} ${input4_ppm} ${input4_pgm} echo "Test 4 (input = output)" -echo "Prints 281226646 481 five times, then 2921940274 59 five times" +echo "Prints 281226646 481 six times, then 2921940274 59 six times" for image in maze.pbm ${input1_ppm} do diff --git a/test/pamcat2.test b/test/pamcat2.test index 6c76568d..ecc7ee5e 100755 --- a/test/pamcat2.test +++ b/test/pamcat2.test @@ -28,6 +28,8 @@ pamcat -tb -jc -black ${dotw_pbm} ${check5x5_pbm} ${dotw_pbm} -plain pamcat -tb -jr -black ${dotw_pbm} ${check5x5_pbm} ${dotw_pbm} -plain pamcat -tb -black ${dotw_pbm} ${check5x5_pbm} ${dotw_pbm} -plain +rm ${dotw_pbm} + echo "Test 3." pbmmake -b 1 1 > ${dotb_pbm} @@ -39,6 +41,10 @@ pamcat -tb -jl -white ${dotb_pbm} ${check5x5_pbm} ${dotb_pbm} |\ pamcat -tb -jr -white ${dotb_pbm} ${check5x5_pbm} ${dotb_pbm} |\ pamflip -cw | cksum +rm ${dotb_pbm} +rm ${check5x5_pbm} + + echo "Test 4." ppmmake rgb:20/40/d0 1 1 | tee ${dot_ppm} | pamcat -lr | cksum pamcat -tb ${dot_ppm} | cksum @@ -53,4 +59,6 @@ for just in -jleft -jcenter -jright do pamcat -tb ${just} ${dot_ppm} maze.pbm ${dot_ppm} | cksum done - \ No newline at end of file + +rm ${dot_ppm} + diff --git a/test/pamcat3.ok b/test/pamcat3.ok new file mode 100644 index 00000000..5dbb2cbc --- /dev/null +++ b/test/pamcat3.ok @@ -0,0 +1,19 @@ +Test 1. Should print 585134073 133221 four times +585134073 133221 +585134073 133221 +585134073 133221 +585134073 133221 +Test 2. Should print 1331083756 152559 three times +1331083756 152559 +1331083756 152559 +1331083756 152559 +Test Invalid +Expected failure 1 1 +Expected failure 2 1 +Expected failure 3 1 +Expected failure 4 1 +Expected failure 5 1 +Expected failure 6 1 +Expected failure 7 1 +Expected failure 8 1 +Expected failure 9 1 diff --git a/test/pamcat3.test b/test/pamcat3.test new file mode 100755 index 00000000..3aed0131 --- /dev/null +++ b/test/pamcat3.test @@ -0,0 +1,97 @@ +#! /bin/sh +# This script tests: pamcat +# Also requires: + +tmpdir=${tmpdir:-/tmp} +list=${tmpdir}/list +list2=${tmpdir}/list2 +list3=${tmpdir}/list3 +liste1=${tmpdir}/liste1 +liste2=${tmpdir}/liste2 +liste3=${tmpdir}/liste3 +files="maze.pbm testgrid.pbm testimg.ppm" + +ls ${files} | tee ${list} | awk '{print ""; print $0; print ""}' > ${list3} +sed 's/maze.pbm/-/' ${list} > ${list2} + +echo "Test 1. Should print 585134073 133221 four times" +pamcat -lr -jt ${files} | cksum +pamcat -lr -jt -listfile=${list} | cksum +cat maze.pbm | pamcat -lr -jt -listfile=${list2} | cksum +pamcat -lr -jt -listfile=${list3} | cksum + +echo "Test 2. Should print 1331083756 152559 three times" +pamcat -tb -jl ${files} | cksum +pamcat -tb -jl -listfile=${list} | cksum +cat ${list} | pamcat -tb -jl -listfile=- | cksum + +echo "Test Invalid" + +test_out=${tmpdir}/test_out + +echo 1>&2 +echo "Invalid command-line argument combinations & listfile content." 1>&2 +echo "Error messages should appear below the line." 1>&2 +echo "-----------------------------------------------------------" 1>&2 + +# listfile not specified +pamcat -lr -listfile > ${test_out} || \ + printf "Expected failure 1" + test -s ${test_out}; echo " "$? + rm -f ${test_out} + +# listfile does not exist +pamcat -lr -listfile=`mktemp -u` > ${test_out} || \ + printf "Expected failure 2" + test -s ${test_out}; echo " "$? + rm -f ${test_out} + +# listfile empty +pamcat -lr -listfile=/dev/null > ${test_out} || \ + printf "Expected failure 3" + test -s ${test_out}; echo " "$? + rm -f ${test_out} + +# listfile from stdin, empty +cat /dev/null | pamcat -lr -listfile=- > ${test_out} || \ + printf "Expected failure 4" + test -s ${test_out}; echo " "$? + rm -f ${test_out} + +# Files provided from command line in addition to listfile +pamcat -lr -listfile=${list} testgrid.pbm testgrid.pbm > ${test_out} || \ + printf "Expected failure 5" + test -s ${test_out}; echo " "$? + rm -f ${test_out} + +# "-" (stdin) provided from command line in addition to listfile +pamcat -lr -listfile=${list} - > ${test_out} || \ + printf "Expected failure 6" + test -s ${test_out}; echo " "$? + rm -f ${test_out} + +# listfile has nothing but blank lines +sed 's/^.*$//' ${list3} > ${liste1} +pamcat -lr -listfile=${liste1} > ${test_out} || \ + printf "Expected failure 7" + test -s ${test_out}; echo " "$? + rm -f ${test_out} ${liste1} + +# Non-existing file in listfile +( cat ${list} ; mktemp -u ) > ${liste2} +pamcat -lr -listfile=${liste2} > ${test_out} || \ + printf "Expected failure 8" + test -s ${test_out}; echo " "$? + rm -f ${test_out} ${liste2} + +# Multiple instances of "-" in listfile +( echo "-"; cat ${list}; echo "-"; echo "-" ) > ${liste3} +pamcat -lr -listfile=${liste3} > ${test_out} || \ + printf "Expected failure 9" + test -s ${test_out}; echo " "$? + rm -f ${test_out} ${liste3} + + +rm ${list} ${list2} ${list3} + + diff --git a/test/stdin-pam3.test b/test/stdin-pam3.test index 02257942..40155671 100755 --- a/test/stdin-pam3.test +++ b/test/stdin-pam3.test @@ -72,4 +72,4 @@ for fmt in \ echo ${testprog2}": "${status4} ${status5} ${status6} $? done -rm ${test_pgm} +rm ${test_pgm} ${out1} ${out2} ${out3} ${out4} diff --git a/version.mk b/version.mk index 9b0fe7ec..d8a3f352 100644 --- a/version.mk +++ b/version.mk @@ -1,3 +1,3 @@ NETPBM_MAJOR_RELEASE = 11 -NETPBM_MINOR_RELEASE = 0 -NETPBM_POINT_RELEASE = 3 +NETPBM_MINOR_RELEASE = 1 +NETPBM_POINT_RELEASE = 0 |