diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2010-03-27 19:22:22 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2010-03-27 19:22:22 +0000 |
commit | 4c233fcfb54b386fcd96f71deb1e88e10d635825 (patch) | |
tree | 8cd7f623d29368a59905ccc108e5151bc28d2e62 | |
parent | 3a54a339a59e83834ea18d88cab7104fd9d8b9ca (diff) | |
download | netpbm-mirror-4c233fcfb54b386fcd96f71deb1e88e10d635825.tar.gz netpbm-mirror-4c233fcfb54b386fcd96f71deb1e88e10d635825.tar.xz netpbm-mirror-4c233fcfb54b386fcd96f71deb1e88e10d635825.zip |
Release 10.50.00 - copied from trunk
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@1162 9d0c8265-081b-0410-96cb-a4ca84ce46f8
39 files changed, 2792 insertions, 1133 deletions
diff --git a/analyzer/pnmhistmap.c b/analyzer/pnmhistmap.c index 7e504734..c45ab0c6 100644 --- a/analyzer/pnmhistmap.c +++ b/analyzer/pnmhistmap.c @@ -17,6 +17,7 @@ * - Deal properly with maxvals other than 256 */ +#include <assert.h> #include <string.h> #include "pm_c_util.h" @@ -26,8 +27,6 @@ static double const epsilon = .00001; -#define SCALE_H(value) (hscale_unity ? (value) : (int)((value) * hscale)) - enum wantedColor {WANT_RED=0, WANT_GRN=1, WANT_BLU=2}; struct cmdlineInfo { @@ -53,7 +52,7 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that @@ -93,7 +92,7 @@ parseCommandLine(int argc, char ** argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!lvalSpec) @@ -127,7 +126,7 @@ parseCommandLine(int argc, char ** argv, static unsigned int maxSlotCount(const unsigned int * const hist, - unsigned int const hist_width, + unsigned int const histWidth, bool const no_white, bool const no_black) { /*---------------------------------------------------------------------------- @@ -138,7 +137,7 @@ maxSlotCount(const unsigned int * const hist, unsigned int i; unsigned int const start = (no_black ? 1 : 0); - unsigned int const finish = (no_white ? hist_width - 1 : hist_width); + unsigned int const finish = (no_white ? histWidth - 1 : histWidth); for (hmax = 0, i = start; i < finish; ++i) if (hmax < hist[i]) hmax = hist[i]; @@ -150,36 +149,52 @@ maxSlotCount(const unsigned int * const hist, static void clipHistogram(unsigned int * const hist, - unsigned int const hist_width, + unsigned int const histWidth, unsigned int const hmax) { unsigned int i; - for (i = 0; i < hist_width; ++i) + for (i = 0; i < histWidth; ++i) hist[i] = MIN(hmax, hist[i]); } static void -pgm_hist(FILE * const ifP, - int const cols, - int const rows, - xelval const maxval, - int const format, - bool const dots, - bool const no_white, - bool const no_black, - bool const verbose, - xelval const startval, - xelval const endval, - unsigned int const hist_width, - unsigned int const hist_height, - bool const clipSpec, - unsigned int const clipCount, - double const hscale) { - - bool const hscale_unity = hscale - 1 < epsilon; +countComp(xelval const value, + xelval const startval, + xelval const endval, + unsigned int const histWidth, + unsigned int * const hist) { + + double const hscale = (float)(histWidth-1) / (endval - startval - 1); + + if (value >= startval && value < endval) { + unsigned int const bin = ROUNDU((value-startval) * hscale); + + assert(bin < histWidth); + ++hist[bin]; + } +} + + + +static void +pgmHist(FILE * const ifP, + int const cols, + int const rows, + xelval const maxval, + int const format, + bool const dots, + bool const no_white, + bool const no_black, + bool const verbose, + xelval const startval, + xelval const endval, + unsigned int const histWidth, + unsigned int const histHeight, + bool const clipSpec, + unsigned int const clipCount) { gray * grayrow; bit ** bits; @@ -188,15 +203,15 @@ pgm_hist(FILE * const ifP, double vscale; unsigned int hmax; - MALLOCARRAY(ghist, hist_width); + MALLOCARRAY(ghist, histWidth); if (ghist == NULL) pm_error("Not enough memory for histogram array (%d bytes)", - hist_width * sizeof(int)); - bits = pbm_allocarray(hist_width, hist_height); + histWidth * sizeof(int)); + bits = pbm_allocarray(histWidth, histHeight); if (bits == NULL) pm_error("no space for output array (%d bits)", - hist_width * hist_height); - memset(ghist, 0, hist_width * sizeof(ghist[0])); + histWidth * histHeight); + memset(ghist, 0, histWidth * sizeof(ghist[0])); /* read the pixel values into the histogram arrays */ grayrow = pgm_allocrow(cols); @@ -206,12 +221,8 @@ pgm_hist(FILE * const ifP, for (i = rows; i > 0; --i) { pgm_readpgmrow (ifP, grayrow, cols, maxval, format); - for (j = cols-1; j >= 0; --j) { - int const value = grayrow[j]; - - if (value >= startval && value <= endval) - ++ghist[SCALE_H(value-startval)]; - } + for (j = cols-1; j >= 0; --j) + countComp(grayrow[j], startval, endval, histWidth, ghist); } pgm_freerow(grayrow); @@ -221,33 +232,35 @@ pgm_hist(FILE * const ifP, if (clipSpec) hmax = clipCount; else - hmax = maxSlotCount(ghist, hist_width, no_white, no_black); + hmax = maxSlotCount(ghist, histWidth, no_white, no_black); + + assert(hmax > 0); if (verbose) pm_message("Done: height = %u", hmax); - clipHistogram(ghist, hist_width, hmax); + clipHistogram(ghist, histWidth, hmax); - vscale = (double) hist_height / hmax; + vscale = (double) histHeight / hmax; - for (i = 0; i < hist_width; ++i) { - int mark = hist_height - (int)(vscale * ghist[i]); + for (i = 0; i < histWidth; ++i) { + int mark = histHeight - (int)(vscale * ghist[i]); for (j = 0; j < mark; ++j) bits[j][i] = PBM_BLACK; - if (j < hist_height) + if (j < histHeight) bits[j++][i] = PBM_WHITE; - for ( ; j < hist_height; ++j) + for ( ; j < histHeight; ++j) bits[j][i] = dots ? PBM_BLACK : PBM_WHITE; } - pbm_writepbm(stdout, bits, hist_width, hist_height, 0); + pbm_writepbm(stdout, bits, histWidth, histHeight, 0); } static unsigned int maxSlotCountAll(unsigned int * const hist[3], - unsigned int const hist_width, + unsigned int const histWidth, bool const no_white, bool const no_black) { /*---------------------------------------------------------------------------- @@ -264,7 +277,7 @@ maxSlotCountAll(unsigned int * const hist[3], if (hist[color]) hmax = MAX(hmax, maxSlotCount(hist[color], - hist_width, no_white, no_black)); + histWidth, no_white, no_black)); return hmax; } @@ -273,107 +286,132 @@ maxSlotCountAll(unsigned int * const hist[3], static void createHist(bool const colorWanted[3], - unsigned int const hist_width, + unsigned int const histWidth, unsigned int * (* const histP)[3]) { /*---------------------------------------------------------------------------- Allocate the histogram arrays and set each slot count to zero. -----------------------------------------------------------------------------*/ unsigned int color; - for (color = 0; color < 3; ++color) + for (color = 0; color < 3; ++color) { if (colorWanted[color]) { unsigned int * hist; unsigned int i; - MALLOCARRAY(hist, hist_width); + MALLOCARRAY(hist, histWidth); if (hist == NULL) pm_error ("Not enough memory for histogram arrays (%u bytes)", - hist_width * sizeof(int) * 3); + histWidth * sizeof(hist[0]) * 3); - for (i = 0; i < hist_width; ++i) + for (i = 0; i < histWidth; ++i) hist[i] = 0; (*histP)[color] = hist; } else (*histP)[color] = NULL; + } } static void clipHistogramAll(unsigned int * const hist[3], - unsigned int const hist_width, + unsigned int const histWidth, unsigned int const hmax) { unsigned int color; for (color = 0; color < 3; ++color) if (hist[color]) - clipHistogram(hist[color], hist_width, hmax); + clipHistogram(hist[color], histWidth, hmax); } static void -ppm_hist(FILE * const ifP, - int const cols, - int const rows, - xelval const maxval, - int const format, - bool const dots, - bool const no_white, - bool const no_black, - bool const colorWanted[3], - bool const verbose, - xelval const startval, - xelval const endval, - unsigned int const hist_width, - unsigned int const hist_height, - bool const clipSpec, - unsigned int const clipCount, - double const hscale) { - - bool const hscale_unity = hscale - 1 < epsilon; - +fillPpmBins(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + bool const colorWanted[3], + bool const verbose, + xelval const startval, + xelval const endval, + unsigned int const histWidth, + unsigned int ** const hist) { +/*---------------------------------------------------------------------------- + For each wanted color component, given by colorWanted[], hist[color] is the + histogram. Each histogram as 'histWidth' bins; we ignore color component + values less than 'startval' and greater than or equal to 'endval' and + spread the rest evenly across the 'histWidth' bins. + + We get the color component values from the PNM image on *ifP, + which is positioned to the raster, whose format is described + by 'cols', 'rows', 'maxval', and 'format'. +-----------------------------------------------------------------------------*/ pixel * pixrow; - pixel ** pixels; - int i, j; - unsigned int * hist[3]; /* Subscript is enum wantedColor */ - double vscale; - unsigned int hmax; + unsigned int row; - createHist(colorWanted, hist_width, &hist); - - if ((pixels = ppm_allocarray (hist_width, hist_height)) == NULL) - pm_error("no space for output array (%d pixels)", - hist_width * hist_height); - for (i = 0; i < hist_height; ++i) - memset(pixels[i], 0, hist_width * sizeof(pixels[i][0])); - - /* read the pixel values into the histogram arrays */ pixrow = ppm_allocrow(cols); if (verbose) pm_message("making histogram..."); - for (i = rows; i > 0; --i) { + for (row = 0; row < rows; ++row) { + unsigned int col; ppm_readppmrow(ifP, pixrow, cols, maxval, format); - for (j = cols-1; j >= 0; --j) { - int value; - - if (colorWanted[WANT_RED] && - (value = PPM_GETR(pixrow[j])) >= startval && - value <= endval) - hist[WANT_RED][SCALE_H(value-startval)]++; - if (colorWanted[WANT_GRN] && - (value = PPM_GETG(pixrow[j])) >= startval && - value <= endval) - hist[WANT_GRN][SCALE_H(value-startval)]++; - if (colorWanted[WANT_BLU] && - (value = PPM_GETB(pixrow[j])) >= startval && - value <= endval) - hist[WANT_BLU][SCALE_H(value-startval)]++; + for (col = 0; col < cols; ++col) { + if (colorWanted[WANT_RED]) + countComp(PPM_GETR(pixrow[col]), + startval, endval, histWidth, hist[WANT_RED]); + + if (colorWanted[WANT_GRN]) + countComp(PPM_GETG(pixrow[col]), + startval, endval, histWidth, hist[WANT_GRN]); + + if (colorWanted[WANT_BLU]) + countComp(PPM_GETB(pixrow[col]), + startval, endval, histWidth, hist[WANT_BLU]); } } ppm_freerow(pixrow); +} + + + +static void +ppmHist(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + xelval const maxval, + int const format, + bool const dots, + bool const no_white, + bool const no_black, + bool const colorWanted[3], + bool const verbose, + xelval const startval, + xelval const endval, + unsigned int const histWidth, + unsigned int const histHeight, + bool const clipSpec, + unsigned int const clipCount) { + + pixel ** pixels; + unsigned int i; + unsigned int * hist[3]; /* Subscript is enum wantedColor */ + double vscale; + unsigned int hmax; + + createHist(colorWanted, histWidth, &hist); + + if ((pixels = ppm_allocarray (histWidth, histHeight)) == NULL) + pm_error("no space for output array (%u pixels)", + histWidth * histHeight); + for (i = 0; i < histHeight; ++i) + memset(pixels[i], 0, histWidth * sizeof(pixels[i][0])); + + fillPpmBins(ifP, cols, rows, maxval, format, colorWanted, verbose, + startval, endval, histWidth, hist); /* find the highest-valued slot and set the vertical scale value */ if (verbose) @@ -381,22 +419,24 @@ ppm_hist(FILE * const ifP, if (clipSpec) hmax = clipCount; else - hmax = maxSlotCountAll(hist, hist_width, no_white, no_black); + hmax = maxSlotCountAll(hist, histWidth, no_white, no_black); - clipHistogramAll(hist, hist_width, hmax); + assert(hmax > 0); - vscale = (double) hist_height / hmax; + clipHistogramAll(hist, histWidth, hmax); + + vscale = (double) histHeight / hmax; if (verbose) - pm_message("Done: height = %d, vertical scale factor = %g", + pm_message("Done: height = %u, vertical scale factor = %g", hmax, vscale); - for (i = 0; i < hist_width; ++i) { + for (i = 0; i < histWidth; ++i) { if (hist[WANT_RED]) { unsigned int j; bool plotted; plotted = FALSE; - for (j = hist_height - (int)(vscale * hist[WANT_RED][i]); - j < hist_height && !plotted; + for (j = histHeight - (int)(vscale * hist[WANT_RED][i]); + j < histHeight && !plotted; ++j) { PPM_PUTR(pixels[j][i], maxval); plotted = dots; @@ -406,8 +446,8 @@ ppm_hist(FILE * const ifP, unsigned int j; bool plotted; plotted = FALSE; - for (j = hist_height - (int)(vscale * hist[WANT_GRN][i]); - j < hist_height && !plotted; + for (j = histHeight - (int)(vscale * hist[WANT_GRN][i]); + j < histHeight && !plotted; ++j) { PPM_PUTG(pixels[j][i], maxval); plotted = dots; @@ -417,33 +457,46 @@ ppm_hist(FILE * const ifP, unsigned int j; bool plotted; plotted = FALSE; - for (j = hist_height - (int)(vscale * hist[WANT_BLU][i]); - j < hist_height && !plotted; + for (j = histHeight - (int)(vscale * hist[WANT_BLU][i]); + j < histHeight && !plotted; ++j) { PPM_PUTB(pixels[j][i], maxval); plotted = dots; } } } - ppm_writeppm(stdout, pixels, hist_width, hist_height, maxval, 0); + ppm_writeppm(stdout, pixels, histWidth, histHeight, maxval, 0); +} + + + +static void +reportScale(unsigned int const histWidth, + unsigned int const range, + bool const verbose) { + + double const hscale = (float)(histWidth-1) / (range-1); + + if (hscale - 1.0 < epsilon && verbose) + pm_message("Horizontal scale factor: %g", hscale); } int -main(int argc, char ** argv) { +main(int argc, const char ** argv) { struct cmdlineInfo cmdline; FILE * ifP; int cols, rows; xelval maxval; int format; - unsigned int hist_width; + unsigned int histWidth; unsigned int range; - double hscale; - int hmax; + unsigned int hmax; + xelval startval, endval; - pnm_init (&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); @@ -451,34 +504,33 @@ main(int argc, char ** argv) { pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); - range = MIN(maxval, cmdline.rval) - cmdline.lval + 1; + startval = cmdline.lval; + endval = MIN(maxval, cmdline.rval) + 1; + + range = endval - startval; if (cmdline.widthSpec) - hist_width = cmdline.width; + histWidth = cmdline.width; else - hist_width = range; - - hscale = (float)(hist_width-1) / (range-1); - if (hscale - 1.0 < epsilon && cmdline.verbose) - pm_message("Horizontal scale factor: %g (maxval = %u)", - hscale, maxval); + histWidth = range; + reportScale(histWidth, range, cmdline.verbose); if (cmdline.nmaxSpec) - hmax = cols * rows / hist_width * cmdline.nmax; + hmax = cols * rows / histWidth * cmdline.nmax; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: - ppm_hist(ifP, cols, rows, maxval, format, - cmdline.dots, cmdline.white, cmdline.black, - cmdline.colorWanted, - cmdline.verbose, cmdline.lval, cmdline.rval, - hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale); + ppmHist(ifP, cols, rows, maxval, format, + cmdline.dots, cmdline.white, cmdline.black, + cmdline.colorWanted, + cmdline.verbose, startval, endval, + histWidth, cmdline.height, cmdline.nmaxSpec, hmax); break; case PGM_TYPE: - pgm_hist(ifP, cols, rows, maxval, format, - cmdline.dots, cmdline.white, cmdline.black, - cmdline.verbose, cmdline.lval, cmdline.rval, - hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale); + pgmHist(ifP, cols, rows, maxval, format, + cmdline.dots, cmdline.white, cmdline.black, + cmdline.verbose, startval, endval, + histWidth, cmdline.height, cmdline.nmaxSpec, hmax); break; case PBM_TYPE: pm_error("Cannot do a histogram of a a PBM file"); diff --git a/converter/other/Makefile b/converter/other/Makefile index 83676aaa..1417cd3a 100644 --- a/converter/other/Makefile +++ b/converter/other/Makefile @@ -77,9 +77,9 @@ ifeq ($(TIFFLIB_NEEDS_Z),Y) endif endif -PORTBINARIES = bmptopnm fitstopnm \ +PORTBINARIES = avstopam bmptopnm fitstopnm \ gemtopnm giftopnm hdifftopam infotopam \ - pamtodjvurle pamtofits pamtogif \ + pamtoavs pamtodjvurle pamtofits pamtogif \ pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \ pamtopam pamtopfm pamtopnm pamtouil \ pamtoxvmini \ diff --git a/converter/other/avstopam.c b/converter/other/avstopam.c new file mode 100644 index 00000000..8d32008c --- /dev/null +++ b/converter/other/avstopam.c @@ -0,0 +1,103 @@ +/* ---------------------------------------------------------------------- + * + * Convert an AVS X image to a PAM image + * + * By Scott Pakin <scott+pbm@pakin.org> + * + * ---------------------------------------------------------------------- + * + * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * ---------------------------------------------------------------------- + */ + +#include <stdio.h> + +#include "pm.h" +#include "pam.h" + + + +static void +producePam(FILE * const avsFileP, + struct pam * const pamP) { + + tuple * tuplerow; + unsigned int row; + + tuplerow = pnm_allocpamrow(pamP); + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + tuple const thisTuple = tuplerow[col]; + char c; + pm_readchar(avsFileP, &c); thisTuple[3] = c; + pm_readchar(avsFileP, &c); thisTuple[0] = c; + pm_readchar(avsFileP, &c); thisTuple[1] = c; + pm_readchar(avsFileP, &c); thisTuple[2] = c; + } + pnm_writepamrow(pamP, tuplerow); + } + pnm_freepamrow(tuplerow); +} + + + +int +main(int argc, const char *argv[]) { + + const char * comment = "Produced by avstopam"; /* constant */ + + struct pam outPam; + const char * inputFilename; + FILE * inFileP; + long width; + long height; + + pm_proginit(&argc, argv); + + inputFilename = (argc > 1) ? argv[1] : "-"; + + inFileP = pm_openr(inputFilename); + + pm_readbiglong(inFileP, &width); + pm_readbiglong(inFileP, &height); + + outPam.size = sizeof(struct pam); + outPam.len = PAM_STRUCT_SIZE(comment_p); + outPam.file = stdout; + outPam.format = PAM_FORMAT; + outPam.plainformat = 0; + outPam.width = width; + outPam.height = height; + outPam.depth = 4; + outPam.maxval = 255; + outPam.bytes_per_sample = 1; + sprintf(outPam.tuple_type, "RGB_ALPHA"); + outPam.allocation_depth = 4; + outPam.comment_p = &comment; + + /* Produce a PAM output header. Note that AVS files *always* + contain four channels with one byte per channel. + */ + pnm_writepaminit(&outPam); + + producePam(inFileP, &outPam); + + pm_closer(inFileP); + + return 0; +} diff --git a/converter/other/pamtoavs.c b/converter/other/pamtoavs.c new file mode 100644 index 00000000..4764c9e8 --- /dev/null +++ b/converter/other/pamtoavs.c @@ -0,0 +1,150 @@ +/* ---------------------------------------------------------------------- + * + * Convert a PAM image to an AVS X image + * + * By Scott Pakin <scott+pbm@pakin.org> + * + * ---------------------------------------------------------------------- + * + * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * ---------------------------------------------------------------------- + */ + +#include <stdio.h> +#include "pm.h" +#include "pam.h" + + + +static char +sample2char(sample const s, + sample const maxval) { +/* Scale down a sample to a single byte. */ + + return maxval==255 ? s : s * 255 / maxval; +} + + +#define THIS_SAMPLE_CHAR(PLANE) \ + sample2char(tuplerow[col][PLANE], pamP->maxval) + +static void +produceAvs(struct pam * const pamP, + FILE * const avsFileP) { + + tuple * tuplerow; + + /* Write the AVS header (image width and height as 4-byte + big-endian integers). + */ + pm_writebiglong(avsFileP, pamP->width); + pm_writebiglong(avsFileP, pamP->height); + + /* Write the AVS data (alpha, red, green, blue -- one byte apiece. */ + tuplerow = pnm_allocpamrow(pamP); + switch (pamP->depth) { + case 1: { + /* Black-and-white or grayscale, no alpha */ + unsigned int row; + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + pnm_readpamrow(pamP, tuplerow); + for (col = 0; col < pamP->width; ++col) { + pm_writechar(avsFileP, (char)255); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + } + } + } break; + + case 2: { + /* Black-and-white or grayscale plus alpha */ + unsigned int row; + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + pnm_readpamrow(pamP, tuplerow); + for (col = 0; col < pamP->width; ++col) { + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + } + } + } break; + + case 3: { + /* RGB, no alpha */ + unsigned int row; + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + pnm_readpamrow(pamP, tuplerow); + for (col = 0; col < pamP->width; ++col) { + pm_writechar(avsFileP, (char)255); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(2)); + } + } + } break; + + case 4: { + /* RGB plus alpha */ + unsigned int row; + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + pnm_readpamrow( pamP, tuplerow ); + for (col = 0; col < pamP->width; ++col) { + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(3)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1)); + pm_writechar(avsFileP, THIS_SAMPLE_CHAR(2)); + } + } + } break; + + default: + pm_error("Unrecognized PAM depth %u. We understand only " + "1, 2, 3, and 4", pamP->depth); + break; + } + pnm_freepamrow(tuplerow); +} + + + +int +main(int argc, const char *argv[]) { + struct pam inPam; + const char * inputFilename; + FILE * inFileP; + + pm_proginit(&argc, argv); + + inputFilename = (argc > 1) ? argv[1] : "-"; + + inFileP = pm_openr(inputFilename); + + pnm_readpaminit(inFileP, &inPam, PAM_STRUCT_SIZE(tuple_type)); + + produceAvs(&inPam, stdout); + + pm_closer(inFileP); + + return 0; +} + diff --git a/converter/other/pamtosvg/thin-image.c b/converter/other/pamtosvg/thin-image.c index 40ced794..86d1037c 100644 --- a/converter/other/pamtosvg/thin-image.c +++ b/converter/other/pamtosvg/thin-image.c @@ -171,7 +171,7 @@ thin_image(bitmap_type *image, bool bgSpec, pixel bg, if (PPM_ISGRAY(background)) bg_color = PPM_GETR(background); else - bg_color = PPM_LUMIN(background); + bg_color = ppm_luminosity(background); for (n = num_pixels - 1; n >= 0L; --n) { @@ -306,7 +306,7 @@ void thin1(bitmap_type *image, unsigned char colour) if (PPM_ISGRAY(background)) bg_color = PPM_GETR(background); else - bg_color = PPM_LUMIN(background); + bg_color = ppm_luminosity(background); LOG (" Thinning image.....\n "); xsize = image->width; diff --git a/converter/other/pnmtosgi.c b/converter/other/pnmtosgi.c index 472b5197..169125b3 100644 --- a/converter/other/pnmtosgi.c +++ b/converter/other/pnmtosgi.c @@ -13,11 +13,16 @@ ** implied warranty. ** ** 29Jan94: first version + +** Feb 2010 afu +** Added dimension check to prevent short int from overflowing */ #include "pnm.h" #include "sgi.h" #include "mallocvar.h" + + /*#define DEBUG*/ typedef short ScanElem; @@ -42,6 +47,7 @@ static int rle_compress ARGS((ScanElem *inbuf, int cols)); #define MAXVAL_BYTE 255 #define MAXVAL_WORD 65535 +#define INT16MAX 32767 static char storage = STORAGE_RLE; static ScanLine * channel[3]; @@ -87,9 +93,7 @@ write_header(int const cols, int -main(argc, argv) - int argc; - char *argv[]; +main(int argc,char * argv[]) { FILE *ifp; int argn; @@ -131,6 +135,9 @@ main(argc, argv) pm_usage(usage); pnm_readpnminit(ifp, &cols, &rows, &maxval, &format); + if( rows>INT16MAX || cols>INT16MAX ) + pm_error ("Input image is too large."); + pnmrow = pnm_allocrow(cols); switch( PNM_FORMAT_TYPE(format) ) { @@ -172,9 +179,7 @@ main(argc, argv) static void -write_table(table, tabsize) - long *table; - int tabsize; +write_table(long * table, int const tabsize) { int i; long offset; @@ -194,9 +199,8 @@ write_table(table, tabsize) static void -write_channels(cols, rows, channels, put) - int cols, rows, channels; - void (*put) ARGS((short)); +write_channels(int const cols,int const rows, int const channels, + void (*put) (short)) { int i, row, col; @@ -214,7 +218,7 @@ write_channels(cols, rows, channels, put) } static void -put_big_short(short s) +put_big_short(short const s) { if ( pm_writebigshort( stdout, s ) == -1 ) pm_error( "write error" ); @@ -222,8 +226,7 @@ put_big_short(short s) static void -put_big_long(l) - long l; +put_big_long(long const l) { if ( pm_writebiglong( stdout, l ) == -1 ) pm_error( "write error" ); @@ -231,15 +234,16 @@ put_big_long(l) static void -put_short_as_byte(short s) +put_short_as_byte(short const s) { put_byte((unsigned char)s); } static long * -build_channels(FILE *ifp, int cols, int rows, xelval maxval, - int format, int bpc, int channels) +build_channels(FILE * const ifp, int const cols, int const rows, + xelval const maxval, int const format, + int const bpc, int const channels) { int i, row, col, sgirow; long *table = NULL; @@ -286,11 +290,10 @@ build_channels(FILE *ifp, int cols, int rows, xelval maxval, static ScanElem * -compress(temp, row, rows, cols, chan_no, table, bpc) - ScanElem *temp; - int row, rows, cols, chan_no; - long *table; - int bpc; +compress(ScanElem * temp, + int const row, int const rows, + int const cols, int const chan_no, + long * table, int const bpc) { int len, i, tabrow; ScanElem *p; @@ -323,9 +326,7 @@ slightly modified RLE algorithm from ppmtoilbm.c written by Robert A. Knop (rknop@mop.caltech.edu) */ static int -rle_compress(inbuf, size) - ScanElem *inbuf; - int size; +rle_compress(ScanElem * const inbuf, int const size) { int in, out, hold, count; ScanElem *outbuf = rletemp; diff --git a/converter/other/ppmtopgm.c b/converter/other/ppmtopgm.c index 86e7ae6a..e20c5660 100644 --- a/converter/other/ppmtopgm.c +++ b/converter/other/ppmtopgm.c @@ -36,9 +36,9 @@ convertRaster(FILE * const ifP, outputRow[col] = (gray) ppm_fastlumin(inputRow[col]); } else { /* Can't use fast approximation, so fall back on floats. */ - int col; + unsigned int col; for (col = 0; col < cols; ++col) - outputRow[col] = (gray) (PPM_LUMIN(inputRow[col]) + 0.5); + outputRow[col] = ppm_luminosity(inputRow[col]); } pgm_writepgmrow(ofP, outputRow, cols, maxval, 0); } diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c index cefbdc95..9eab0416 100644 --- a/converter/pbm/pbmtogem.c +++ b/converter/pbm/pbmtogem.c @@ -27,17 +27,21 @@ * removed rounding of the imagewidth to the next word boundary * removed arbitrary limit to imagewidth * changed pattern length to 1 to simplify locating of compressable parts -* in real world images +* in real world images * add solid run and pattern run compression * * Deficiencies: * Compression of repeated scanlines not added * -* Johann Haider (jh@fortec.tuwien.ac.at) +* Johann Haider (jh@fortec.tuwien.ac.at) * * 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de) * Changed to remove architecture dependencies * Added compression of repeated scanlines +* +* Feb 2010 afu +* Added dimension check to prevent short int from overflowing +* Changed code style (ANSI-style function definitions, etc.) */ #include <stdio.h> @@ -47,59 +51,11 @@ #define SOLID_0 0 #define SOLID_1 0xff #define MINRUN 4 +#define INT16MAX 32767 + #define putsolid(v,c) putc((v&0x80)|c, stdout) #define putpattern(v,c) putc(0, stdout);putc(c, stdout);putc(v, stdout) -static void putinit ARGS ((int rows, int cols)); -static void putbit ARGS(( bit b )); -static void putitem ARGS(( void )); -static void putrow ARGS(( void )); -static void flushrow ARGS ((void)); -static void putstring ARGS((register unsigned char *p, register int n)); - -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, format, row, col; - - pbm_init( &argc, argv ); - - if ( argc > 2 ) - pm_usage( "[pbmfile]" ); - - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; - - pbm_readpbminit( ifp, &cols, &rows, &format ); - - bitrow = pbm_allocrow( cols ); - - putinit (rows, cols); - for ( row = 0; row < rows; ++row ) - { -#ifdef DEBUG - fprintf (stderr, "row %d\n", row); -#endif - pbm_readpbmrow( ifp, bitrow, cols, format ); - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - putbit( *bP ); - putrow( ); - } - flushrow (); - - pm_close( ifp ); - - - exit( 0 ); - } - static short item; static int outcol, outmax; static short bitsperitem, bitshift; @@ -107,9 +63,9 @@ static short linerepeat; static unsigned char *outrow, *lastrow; static void -putinit (rows, cols) - int rows, cols; +putinit (int const rows, int const cols) { + if (pm_writebigshort (stdout, (short) 1) == -1 /* Image file version */ || pm_writebigshort (stdout, (short) 8) == -1 /* Header length */ || pm_writebigshort (stdout, (short) 1) == -1 /* Number of planes */ @@ -130,17 +86,6 @@ putinit (rows, cols) } static void -putbit( bit b ) - { - if ( bitsperitem == 8 ) - putitem( ); - ++bitsperitem; - if ( b == PBM_BLACK ) - item += 1 << bitshift; - --bitshift; - } - -static void putitem( ) { outrow[outcol++] = item; @@ -149,19 +94,93 @@ putitem( ) bitshift = 7; } + +static void +putbit( bit const b ) + { + if ( bitsperitem == 8 ) + putitem( ); + ++bitsperitem; + if ( b == PBM_BLACK ) + item += 1 << bitshift; + --bitshift; + } + + static void -putstring (p, n) -register unsigned char *p; -register int n; +putstring ( unsigned char *p, int n) { #ifdef DEBUG fprintf (stderr, "Bitstring, length: %d, pos %d\n", n, outcol); #endif (void) putc((char) 0x80, stdout); /* a Bit string */ - (void) putc(n, stdout); /* count */ + (void) putc(n, stdout); /* count */ fwrite( p, n, 1, stdout ); } + +static void +flushrow( ) + { + unsigned char *outp, *p, *q; + int count; + int col = outmax; + + if (linerepeat > 1) + { + /* Put out line repeat count */ + fwrite ("\0\0\377", 3, 1, stdout); + putchar (linerepeat); + } + for (outp = p = lastrow; col > 0;) + { + for (q = p, count=0; (count < col) && (*q == *p); q++,count++); + if (count > MINRUN) + { + if (p > outp) + { + putstring (outp, p-outp); + outp = p; + } + col -= count; + switch (*p) + { + case SOLID_0: +#ifdef DEBUG +/* if (outcol > 0) */ + fprintf (stderr, "Solid run 0, length: %d\n", count); +#endif + putsolid (SOLID_0, count); + break; + + case SOLID_1: +#ifdef DEBUG + fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol); +#endif + putsolid (SOLID_1, count); + break; + default: +#ifdef DEBUG + fprintf (stderr, "Pattern run, length: %d\n", count); +#endif + putpattern (*p, count); + break; + } + outp = p = q; + } + else + { + p++; + col--; + } + } + if (p > outp) + putstring (outp, p-outp); + if (ferror (stdout)) + pm_error ("write error"); +} + + static void putrow( ) { @@ -173,7 +192,7 @@ putrow( ) { unsigned char *temp; if (linerepeat != -1) /* Unless first line */ - flushrow (); + flushrow (); /* Swap the pointers */ temp = outrow; outrow = lastrow; lastrow = temp; linerepeat = 1; @@ -183,64 +202,47 @@ putrow( ) linerepeat++; } -static void -flushrow( ) - { - register unsigned char *outp, *p, *q; - register int count; - int col = outmax; - if (linerepeat > 1) - { - /* Put out line repeat count */ - fwrite ("\0\0\377", 3, 1, stdout); - putchar (linerepeat); - } - for (outp = p = lastrow; col > 0;) +int +main( int argc, char* argv[]) { - for (q = p, count=0; (count < col) && (*q == *p); q++,count++); - if (count > MINRUN) - { - if (p > outp) - { - putstring (outp, p-outp); - outp = p; - } - col -= count; - switch (*p) - { - case SOLID_0: -#ifdef DEBUG -/* if (outcol > 0) */ - fprintf (stderr, "Solid run 0, length: %d\n", count); -#endif - putsolid (SOLID_0, count); - break; + FILE* ifp; + bit* bitrow; + int rows, cols, format, row, col; - case SOLID_1: -#ifdef DEBUG - fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol); -#endif - putsolid (SOLID_1, count); - break; - default: + pbm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[pbmfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + pbm_readpbminit( ifp, &cols, &rows, &format ); + + if( rows>INT16MAX || cols>INT16MAX ) + pm_error ("Input image is too large."); + + + bitrow = pbm_allocrow( cols ); + + putinit (rows, cols); + for ( row = 0; row < rows; ++row ) + { #ifdef DEBUG - fprintf (stderr, "Pattern run, length: %d\n", count); + fprintf (stderr, "row %d\n", row); #endif - putpattern (*p, count); - break; - } - outp = p = q; - } - else - { - p++; - col--; - } - } - if (p > outp) - putstring (outp, p-outp); - if (ferror (stdout)) - pm_error ("write error"); -} + pbm_readpbmrow( ifp, bitrow, cols, format ); + for ( col = 0; col < cols; ++col ) + putbit( bitrow[col] ); + putrow( ); + } + flushrow (); + + pm_close( ifp ); + + exit( 0 ); + } diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c index 508e8e92..3fdd805d 100644 --- a/converter/pbm/pbmtoybm.c +++ b/converter/pbm/pbmtoybm.c @@ -9,100 +9,129 @@ ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. +** +** Feb 2010 afu +** Added dimension check to prevent short int from overflowing +** Changed code style (ANSI-style function definitions, etc.) */ #include <stdio.h> + +#include "pm.h" #include "pbm.h" #define YBM_MAGIC ( ( '!' << 8 ) | '!' ) +#define INT16MAX 32767 -static void putinit ARGS(( int cols, int rows )); -static void putbit ARGS(( bit b )); -static void putrest ARGS(( void )); -static void putitem ARGS(( void )); +static long item; +static int bitsperitem, bitshift; -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - int rows, cols, format, padright, row, col; - - - pbm_init( &argc, argv ); - - if ( argc > 2 ) - pm_usage( "[pbmfile]" ); - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; - - pbm_readpbminit( ifp, &cols, &rows, &format ); - bitrow = pbm_allocrow( cols ); - - /* Compute padding to round cols up to the nearest multiple of 16. */ - padright = ( ( cols + 15 ) / 16 ) * 16 - cols; - putinit( cols, rows ); - for ( row = 0; row < rows; ++row ) - { - pbm_readpbmrow( ifp, bitrow, cols, format ); - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - putbit( *bP ); - for ( col = 0; col < padright; ++col ) - putbit( 0 ); - } +static void +putitem(void) { - if ( ifp != stdin ) - fclose( ifp ); + pm_writebigshort(stdout, item); - putrest( ); + item = 0; + bitsperitem = 0; + bitshift = 0; +} - exit( 0 ); - } -static long item; -static int bitsperitem, bitshift; static void -putinit( cols, rows ) - int cols, rows; - { - pm_writebigshort( stdout, YBM_MAGIC ); - pm_writebigshort( stdout, cols ); - pm_writebigshort( stdout, rows ); - item = 0; +putinit(int const cols, + int const rows) { + + pm_writebigshort(stdout, YBM_MAGIC); + pm_writebigshort(stdout, cols); + pm_writebigshort(stdout, rows); + + item = 0; bitsperitem = 0; - bitshift = 0; - } + bitshift = 0; +} + + static void -putbit( bit b ) - { - if ( bitsperitem == 16 ) - putitem( ); +putbit(bit const b) { + + if (bitsperitem == 16) + putitem(); + ++bitsperitem; - if ( b == PBM_BLACK ) - item += 1 << bitshift; + + if (b == PBM_BLACK) + item += 1 << bitshift; + ++bitshift; - } +} + + static void -putrest( ) - { - if ( bitsperitem > 0 ) - putitem( ); +putrest(void) { + + if (bitsperitem > 0) + putitem(); +} + + + +int +main(int argc, const char *argv[]) { + + FILE * ifP; + bit * bitrow; + int rows; + int cols; + int format; + unsigned int padright; + unsigned int row; + const char * inputFile; + + pm_proginit(&argc, argv); + + if (argc-1 < 1) + inputFile = "-"; + else { + inputFile = argv[1]; + + if (argc-1 > 2) + pm_error("Too many arguments. The only argument is the optional " + "input file name"); } -static void -putitem( ) - { - pm_writebigshort( stdout, item ); - item = 0; - bitsperitem = 0; - bitshift = 0; + ifP = pm_openr(inputFile); + + pbm_readpbminit(ifP, &cols, &rows, &format); + + if (rows > INT16MAX || cols > INT16MAX) + pm_error("Input image is too large."); + + bitrow = pbm_allocrow(cols); + + /* Compute padding to round cols up to the nearest multiple of 16. */ + padright = ((cols + 15) / 16) * 16 - cols; + + putinit(cols, rows); + for (row = 0; row < rows; ++row) { + unsigned int col; + + pbm_readpbmrow(ifP, bitrow, cols, format); + + for (col = 0; col < cols; ++col) + putbit(bitrow[col]); + + for (col = 0; col < padright; ++col) + putbit(0); } + + if (ifP != stdin) + fclose(ifP); + + putrest(); + + return 0; +} diff --git a/converter/pbm/ybmtopbm.c b/converter/pbm/ybmtopbm.c index 739e168a..3d012483 100644 --- a/converter/pbm/ybmtopbm.c +++ b/converter/pbm/ybmtopbm.c @@ -10,104 +10,122 @@ ** implied warranty. */ -#include <stdio.h> +#include "pm.h" #include "pbm.h" -static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP )); -static bit getbit ARGS(( FILE* file )); +static short const ybmMagic = ( ( '!' << 8 ) | '!' ); -#define YBM_MAGIC ( ( '!' << 8 ) | '!' ) -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - bit* bitrow; - register bit* bP; - short rows, cols, padright, row, col; - short depth; - - pbm_init( &argc, argv ); - if ( argc > 2 ) - pm_usage( "[ybmfile]" ); - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; +static int item; +static int bitsInBuffer, bitshift; - getinit( ifp, &cols, &rows, &depth, &padright ); - if ( depth != 1 ) - pm_error( - "YBM file has depth of %d, must be 1", - (int) depth ); - pbm_writepbminit( stdout, cols, rows, 0 ); - bitrow = pbm_allocrow( cols ); - for ( row = 0; row < rows; ++row ) - { - /* Get data. */ - for ( col = 0, bP = bitrow; col < cols; ++col, ++bP ) - *bP = getbit( ifp ); - /* Discard line padding */ - for ( col = 0; col < padright; ++col ) - (void) getbit( ifp ); - pbm_writepbmrow( stdout, bitrow, cols, 0 ); - } +static void +getinit(FILE * const ifP, + short * const colsP, + short * const rowsP, + short * const depthP, + short * const padrightP) { - pm_close( ifp ); - pm_close( stdout ); + short magic; + int rc; - exit( 0 ); - } + rc = pm_readbigshort(ifP, &magic); + if (rc == -1) + pm_error("EOF / read error"); -static int item; -static int bitsperitem, bitshift; + if (magic != ybmMagic) + pm_error("bad magic number in YBM file"); -static void -getinit( file, colsP, rowsP, depthP, padrightP ) - FILE* file; - short* colsP; - short* rowsP; - short* depthP; - short* padrightP; - { - short magic; + rc = pm_readbigshort(ifP, colsP); + if (rc == -1 ) + pm_error("EOF / read error"); - if ( pm_readbigshort( file, &magic ) == -1 ) - pm_error( "EOF / read error" ); - if ( magic != YBM_MAGIC ) - pm_error( "bad magic number in YBM file" ); - if ( pm_readbigshort( file, colsP ) == -1 ) - pm_error( "EOF / read error" ); - if ( pm_readbigshort( file, rowsP ) == -1 ) - pm_error( "EOF / read error" ); + rc = pm_readbigshort(ifP, rowsP); + if (rc == -1) + pm_error("EOF / read error"); *depthP = 1; - *padrightP = ( ( *colsP + 15 ) / 16 ) * 16 - *colsP; - bitsperitem = 0; - } + *padrightP = ((*colsP + 15) / 16) * 16 - *colsP; +} + + static bit -getbit( file ) - FILE* file; - { +getbit(FILE * const ifP) { + bit b; - if ( bitsperitem == 0 ) - { - item = getc(file) | getc(file)<<8; - if ( item == EOF ) - pm_error( "EOF / read error" ); - bitsperitem = 16; - bitshift = 0; - } - b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE; - --bitsperitem; + if (bitsInBuffer == 0) { + item = (getc(ifP) << 8) | (getc(ifP) << 0); + + if (item == EOF) + pm_error("EOF / read error"); + + bitsInBuffer = 16; + bitshift = 0; + } + + b = ((item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE; + --bitsInBuffer; ++bitshift; return b; +} + + + +int +main(int argc, const char * argv[]) { + + FILE * ifP; + bit * bitrow; + short rows, cols, padright; + unsigned int row; + short depth; + const char * inputFile; + + pm_proginit(&argc, argv); + + if (argc-1 < 1) + inputFile = "-"; + else { + inputFile = argv[1]; + + if (argc-1 > 2) + pm_error("Too many arguments. The only argument is the optional " + "input file name"); + } + + ifP = pm_openr(inputFile); + + bitsInBuffer = 0; + + getinit(ifP, &cols, &rows, &depth, &padright); + if (depth != 1) + pm_error("YBM file has depth of %u, must be 1", (unsigned)depth); + + pbm_writepbminit(stdout, cols, rows, 0); + + bitrow = pbm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + /* Get raster. */ + unsigned int col; + + for (col = 0; col < cols; ++col) + bitrow[col] = getbit(ifP); + + /* Discard line padding */ + for (col = 0; col < padright; ++col) + getbit(ifP); + pbm_writepbmrow(stdout, bitrow, cols, 0); } + + pm_close(ifP); + pm_close(stdout); + + return 0; +} diff --git a/converter/pgm/pgmtolispm.c b/converter/pgm/pgmtolispm.c index abb85494..7d931fb3 100644 --- a/converter/pgm/pgmtolispm.c +++ b/converter/pgm/pgmtolispm.c @@ -14,129 +14,167 @@ ** usually a color image; but a color map is not written in the file, so we ** treat this as a graymap instead. To convert a color image to Lispm ** format, you must convert it to a pgm, and hand-edit a color map... Ick. +** +** Feb 2010 afu +** Added dimension check to prevent short int from overflowing +** Changed code style (ANSI-style function definitions, etc.) */ -#include <stdio.h> +#include "pm.h" #include "pgm.h" #define LISPM_MAGIC "This is a BitMap file" +#define INT16MAX 32767 -static void putinit ARGS(( int cols, int rows, int depth )); -static int depth_to_word_size ARGS(( int depth )); -static void putval ARGS(( gray b )); -static void putrest ARGS(( void )); -static void putitem ARGS(( void )); -int -main( argc, argv ) - int argc; - char* argv[]; - { - FILE* ifp; - gray *grayrow; - register gray* gP; - int rows, cols, depth, format, padright, row, col; - gray maxval; +static unsigned int item; +static unsigned int bitsperitem, maxbitsperitem, bitshift; +static unsigned int +depth_to_word_size(unsigned int const depth) { - pgm_init( &argc, argv ); + /* Lispm architecture specific - if a bitmap is written */ + /* out with a depth of 5, it really has a depth of 8, and */ + /* is stored that way in the file. */ - if ( argc > 2 ) - pm_usage( "[pgmfile]" ); - if ( argc == 2 ) - ifp = pm_openr( argv[1] ); - else - ifp = stdin; + unsigned int const wordSize = + depth == 1 ? 1 : + depth == 2 ? 2 : + depth <= 4 ? 4 : + depth <= 8 ? 8 : + depth <= 16 ? 16 : + depth <= 32 ? 32 : + 0; - pgm_readpgminit( ifp, &cols, &rows, &maxval, &format ); - grayrow = pgm_allocrow( cols ); - depth = pm_maxvaltobits( maxval ); + if (wordSize == 0) + pm_error("depth was %u, which is not in the range 1-32", depth); - /* Compute padding to round cols up to the nearest multiple of 32. */ - padright = ( ( cols + 31 ) / 32 ) * 32 - cols; + return wordSize; +} - putinit( cols, rows, depth ); - for ( row = 0; row < rows; ++row ) - { - pgm_readpgmrow( ifp, grayrow, cols, maxval, format ); - for ( col = 0, gP = grayrow; col < cols; ++col, ++gP ) - putval( *gP ); - for ( col = 0; col < padright; ++col ) - putval( 0 ); - } - pm_close( ifp ); - putrest( ); +static void +putinit(unsigned int const cols, + unsigned int const rows, + unsigned int const depth) { - exit( 0 ); - } + unsigned int const cols32 = ((cols + 31 ) / 32) * 32; -static unsigned int item; -static unsigned int bitsperitem, maxbitsperitem, bitshift; + unsigned int i; -static void -putinit( cols, rows, depth ) - int cols, rows, depth; - { - int i; - int cols32 = ( ( cols + 31 ) / 32 ) * 32; /* Lispms are able to write bit files that are not mod32 wide, but we */ - /* don't. This should be ok, since bit arrays which are not mod32 wide */ - printf(LISPM_MAGIC); /* are pretty useless on a lispm (can't hand them to bitblt). */ - pm_writelittleshort( stdout, cols ); - pm_writelittleshort( stdout, rows ); - pm_writelittleshort( stdout, cols32 ); + /* Lispms are able to write bit files that are not mod32 wide, but we */ + /* don't. This should be ok, since bit arrays which are not mod32 wide */ + /* are pretty useless on a lispm (can't hand them to bitblt). */ + + if (rows > INT16MAX || cols > INT16MAX || cols32 > INT16MAX) + pm_error("Input image is too large."); + + printf(LISPM_MAGIC); + + pm_writelittleshort(stdout, cols); + pm_writelittleshort(stdout, rows); + pm_writelittleshort(stdout, cols32); putchar(depth & 0xFF); - for ( i = 0; i < 9; ++i ) - putchar( 0 ); /* pad bytes */ + for (i = 0; i < 9; ++i) + putchar(0); /* pad bytes */ - item = 0; - bitsperitem = 0; - maxbitsperitem = depth_to_word_size( depth ); - bitshift = 0; - } + item = 0; + bitsperitem = 0; + maxbitsperitem = depth_to_word_size(depth); + bitshift = 0; +} -static int -depth_to_word_size (depth) /* Lispm architecture specific - if a bitmap is written */ - int depth; /* out with a depth of 5, it really has a depth of 8, and */ -{ /* is stored that way in the file. */ - if (depth==0 || depth==1) return ( 1); - else if (depth == 2) return ( 2); - else if (depth <= 4) return ( 4); - else if (depth <= 8) return ( 8); - else if (depth <= 16) return (16); - else if (depth <= 32) return (32); - else { - pm_error( "depth was %d, which is not in the range 1-32", depth ); - return(-1); /* Should never reach here */ - } + + +static void +putitem(void) { + + pm_writelittlelong(stdout, ~item); + + item = 0; + bitsperitem = 0; + bitshift = 0; } static void -putval( gray b ) - { - if ( bitsperitem == 32 ) - putitem( ); - item = item | ( b << bitshift ); +putval(gray const b) { + + if (bitsperitem == 32) + putitem(); + + item = item | (b << bitshift); bitsperitem = bitsperitem + maxbitsperitem; - bitshift = bitshift + maxbitsperitem; - } + bitshift = bitshift + maxbitsperitem; +} + + static void -putrest( ) - { - if ( bitsperitem > 0 ) - putitem( ); +putrest(void) { + + if (bitsperitem > 0) + putitem(); +} + + + +int +main(int argc, const char * argv[]) { + + FILE * ifP; + gray * grayrow; + int rows; + int cols; + unsigned int depth; + int format; + unsigned int padright; + unsigned int row; + gray maxval; + const char * inputFile; + + pm_proginit(&argc, argv); + + if (argc-1 < 1) + inputFile = "-"; + else { + inputFile = argv[1]; + + if (argc-1 > 2) + pm_error("Too many arguments. The only argument is the optional " + "input file name"); } -static void -putitem( ) - { - pm_writelittlelong( stdout, ~item ); - item = 0; - bitsperitem = 0; - bitshift = 0; + ifP = pm_openr(inputFile); + + pgm_readpgminit(ifP, &cols, &rows, &maxval, &format); + + grayrow = pgm_allocrow(cols); + depth = pm_maxvaltobits(maxval); + + /* Compute padding to round cols up to the nearest multiple of 32. */ + padright = ((cols + 31) / 32) * 32 - cols; + + putinit(cols, rows, depth); + + for (row = 0; row < rows; ++row) { + unsigned int col; + + pgm_readpgmrow(ifP, grayrow, cols, maxval, format); + + for (col = 0; col < cols; ++col) + putval(grayrow[col]); + + for (col = 0; col < padright; ++col) + putval(0); } + + pm_close(ifP); + + putrest(); + + return 0; +} diff --git a/converter/ppm/Makefile b/converter/ppm/Makefile index adc3a400..1bade11a 100644 --- a/converter/ppm/Makefile +++ b/converter/ppm/Makefile @@ -20,7 +20,7 @@ PORTBINARIES = 411toppm eyuvtoppm gouldtoppm ilbmtoppm imgtoppm \ ppmtowinicon ppmtoxpm ppmtoyuv ppmtoyuvsplit \ qrttoppm rawtoppm rgb3toppm spctoppm \ sputoppm tgatoppm winicontoppm ximtoppm xpmtoppm xvminitoppm \ - yuvtoppm yuvsplittoppm + yuvsplittoppm yuvtoppm MATHBINARIES = sldtoppm diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c index a703cf39..2567666d 100644 --- a/converter/ppm/ppmtompeg/jpeg.c +++ b/converter/ppm/ppmtompeg/jpeg.c @@ -360,7 +360,7 @@ JMovie2JPEG(const char * const infilename, * * allocate and initialize JPEG decompression object * specify data source (eg, a file) - * jpeg_read_header(); // obtain image dimensions and other parameters + * jpeg_read_header(); obtain image dimensions and other parameters * set parameters for decompression * jpeg_start_decompress(); * while (scan lines remain to be read) diff --git a/doc/HISTORY b/doc/HISTORY index be62355b..d5097040 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,40 +4,69 @@ Netpbm. CHANGE HISTORY -------------- -10.03.23 BJH Release 10.49.05 +10.03.27 BJH Release 10.50.00 - pbmtext: fix crash when BDF font file contains spurious - blank line. Ignore such blank lines. - -10.03.17 BJH Release 10.49.04 + Add pamtoavs, avstopam. Thanks Scott Pakin. - ppmtoilbm: fix arithmetic overflow with image dimension - represented as 16 bit integer. + Add pampaintspill. - pbmpage: fix garbage output. + pnmconvol: Add -normalize . - Build: don't fail due to SIGPWR being undefined. + pm_system(): Close extraneous file descriptors that, among + other things, prevent child from seeing EOF. -10.02.23 BJH Release 10.49.03 + libnetpbm: Add PNM_GETR(), PNM_GETG(), PNM_GETB(). Same + as PPM_GETR(), etc. - pnmhistmap: Fix crash with -width. Always broken. + libnetpbm: Add ppm_luminosity(). Same as PPM_LUMIN, but + returns pixval. - pm_system(): Close extraneous file descriptors that, among - other things, prevent child from seeing EOF. + pnmhisteq: Equalize based on luminosity alone, rather than a + strange combination of luminosity and HSV value. -10.01.25 BJH Release 10.49.02 + pamenlarge: Make special fast path for scale factors up to 10 + (2, 3, and 5 already existed). Thanks Prophet of the Way + <afu@wta.att.ne.jp>. - pamtosvg: fix bug: occasional crash with out of range error. + pamflip: Speed up for most images. Thanks Prophet of the Way + <afu@wta.att.ne.jp>. + + ybmtopbm: Assume YBM format has raster in natural order + instead of byte-reversed. This is what pbmtoybm creates, + and is most logical. We don't know if there are any existing + YBM images or generators of them other than pbmtoybm. -10.01.11 BJH Release 10.49.01 + pbmpscale: speedup. + + pbmclean: speedup. + + pbmtext: fix crash when BDF font file contains spurious + blank line. Ignore such blank lines. + + pbmpscale: fix arithmetic overflow on output image dimensions. + + pbmtogem, pbmtoybm, pgmtolispm, ppmtoilbm, pnmtosgi: fix + arithmetic overflow with image dimension represented as + 16 bit integer. + + pbmpage: fix garbage output. + + pnmhistmap: Fix crash with -width. Always broken. libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp(); - broken in 10.47.00. + broken in 10.47. palmtopnm: fix incorrect "PALM_DIRECT_COLOR_FLAG is not valid - for version 3 encoding type" failure. + for version 3 encoding type" failure. Thanks Paul Bolle + <pebolle@tiscali.nl>. + + pamtosvg: fix bug: occasional crash with out of range error. + Introduced in 10.42. palmtopnm: fix incorrect output with version 3 direct color. + Thanks Paul Bolle <pebolle@tiscali.nl>. + + Build: don't fail due to SIGPWR being undefined. 09.12.30 BJH Release 10.49.00 diff --git a/doc/INSTALL b/doc/INSTALL index 92e78540..9412b296 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -259,8 +259,11 @@ and which one is default is controlled by the DEFAULT_TARGET make variable in config.mk, and its value is one of the choices you make via the Configure dialog. -The standard build is the conventional one. The merge build is a way -to reduce disk space and other resource usage in some configurations. +The standard build is the conventional one. The merge build is a way to +reduce disk space and other resource usage in some configurations. These are +rare configurations, mostly antique ones. The advent of shared libraries +largely obsoleted the merge build. In some configurations, Netpbm uses _more_ +resources with the merge build. In the standard build, hundreds of separate programs get built: ppmtogif, pamcomp, etc. diff --git a/editor/Makefile b/editor/Makefile index bc0f5913..29cdcab1 100644 --- a/editor/Makefile +++ b/editor/Makefile @@ -20,7 +20,7 @@ PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \ pamdice pamditherbw pamedge \ pamenlarge \ pamflip pamfunc pammasksharpen \ - pamperspective \ + pampaintspill pamperspective \ pamscale pamsistoaglyph pamstretch pamthreshold pamundice \ pbmclean pbmmask pbmpscale pbmreduce \ pgmdeshadow pgmenhance \ diff --git a/editor/pamcut.c b/editor/pamcut.c index f659ee70..5fdc6788 100644 --- a/editor/pamcut.c +++ b/editor/pamcut.c @@ -28,7 +28,7 @@ struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char *inputFilespec; /* Filespec of input file */ + const char *inputFileName; /* File name of input file */ /* The following describe the rectangle the user wants to cut out. the value UNSPEC for any of them indicates that value was not @@ -51,7 +51,7 @@ struct cmdlineInfo { static void -parseCommandLine(int argc, char ** const argv, +parseCommandLine(int argc, const char ** const argv, struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that @@ -87,7 +87,7 @@ parseCommandLine(int argc, char ** const argv, opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = TRUE; /* We may have parms that are negative numbers */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (cmdlineP->width < 0) @@ -103,10 +103,10 @@ parseCommandLine(int argc, char ** const argv, switch (argc-1) { case 0: - cmdlineP->inputFilespec = "-"; + cmdlineP->inputFileName = "-"; break; case 1: - cmdlineP->inputFilespec = argv[1]; + cmdlineP->inputFileName = argv[1]; break; case 4: case 5: { @@ -137,9 +137,9 @@ parseCommandLine(int argc, char ** const argv, } if (argc-1 == 4) - cmdlineP->inputFilespec = "-"; + cmdlineP->inputFileName = "-"; else - cmdlineP->inputFilespec = argv[5]; + cmdlineP->inputFileName = argv[5]; break; } } @@ -669,7 +669,7 @@ cutOneImage(FILE * const ifP, int -main(int argc, char *argv[]) { +main(int argc, const char *argv[]) { FILE * const ofP = stdout; @@ -677,11 +677,11 @@ main(int argc, char *argv[]) { FILE * ifP; bool eof; - pnm_init(&argc, argv); + pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); - ifP = pm_openr(cmdline.inputFilespec); + ifP = pm_openr(cmdline.inputFileName); eof = FALSE; while (!eof) { diff --git a/editor/pamenlarge.c b/editor/pamenlarge.c index b3039424..dd61f2cf 100644 --- a/editor/pamenlarge.c +++ b/editor/pamenlarge.c @@ -115,6 +115,8 @@ enlargePbmRowHorizontally(struct pam * const inpamP, static unsigned char const trp3[8] = { 0x00, 0x07, 0x38, 0x3F, 0xC0, 0xC7, 0xF8, 0xFF }; + static unsigned char const quad[4] = { 0x00, 0x0F, 0xF0, 0xFF }; + static unsigned char const quin2[8] = { 0x00, 0x01, 0x3E, 0x3F, 0xC0, 0xC1, 0xFE, 0xFF }; @@ -129,33 +131,127 @@ enlargePbmRowHorizontally(struct pam * const inpamP, case 1: break; /* outrow set to inrow */ case 2: /* Make outrow using prefabricated parts (same for 3, 5). */ for (colChar = 0; colChar < inColChars; ++colChar) { - outrow[colChar*2] = dbl[(inrow[colChar] & 0xF0) >> 4]; - outrow[colChar*2+1]= dbl[inrow[colChar] & 0x0F]; + outrow[colChar*2] = dbl[(inrow[colChar] & 0xF0) >> 4]; + outrow[colChar*2+1] = dbl[(inrow[colChar] & 0x0F) >> 0]; /* Possible outrow overrun by one byte. */ } - break; + break; + case 3: for (colChar = 0; colChar < inColChars; ++colChar) { - outrow[colChar*3] = trp1[(inrow[colChar] & 0xF0) >> 5]; - outrow[colChar*3+1]= trp2[(inrow[colChar] >> 2) & 0x0F]; - outrow[colChar*3+2]= trp3[inrow[colChar] & 0x07]; + outrow[colChar*3] = trp1[(inrow[colChar] & 0xF0) >> 5]; + outrow[colChar*3+1] = trp2[(inrow[colChar] >> 2) & 0x0F]; + outrow[colChar*3+2] = trp3[(inrow[colChar] >> 0) & 0x07]; } break; + + case 4: + for (colChar = 0; colChar < inColChars; ++colChar) { + unsigned int i; + for (i = 0; i < 4; ++i) + outrow[colChar*4+i]= + quad[(inrow[colChar] >> (6 - 2 * i)) & 0x03]; + } + break; + case 5: for (colChar = 0; colChar < inColChars; ++colChar) { - outrow[colChar*5] = pair[ (inrow[colChar] >>6) & 0x03 ] >> 5; - outrow[colChar*5+1]= quin2[(inrow[colChar] >>4) & 0x07 ]; - outrow[colChar*5+2]= pair[ (inrow[colChar] >>3) & 0x03 ] >> 4; - outrow[colChar*5+3]= quin4[(inrow[colChar] >>1) & 0x07 ]; - outrow[colChar*5+4]= pair[ inrow[colChar] & 0x03 ] >>3; + outrow[colChar*5] = pair [(inrow[colChar] >> 6) & 0x03] >> 5; + outrow[colChar*5+1] = quin2[(inrow[colChar] >> 4) & 0x07] >> 0; + outrow[colChar*5+2] = quad [(inrow[colChar] >> 3) & 0x03] >> 0; + outrow[colChar*5+3] = quin4[(inrow[colChar] >> 1) & 0x07] >> 0; + outrow[colChar*5+4] = pair [(inrow[colChar] >> 0) & 0x03] >> 3; + } + break; + + case 6: /* Compound of 2 and 3 */ + for (colChar = 0; colChar < inColChars; ++colChar) { + unsigned char const hi = dbl[(inrow[colChar] & 0xF0) >> 4]; + unsigned char const lo = dbl[(inrow[colChar] & 0x0F) >> 0]; + + outrow[colChar*6] = trp1[(hi & 0xF0) >> 5]; + outrow[colChar*6+1] = trp2[(hi >> 2) & 0x0F]; + outrow[colChar*6+2] = trp3[hi & 0x07]; + + outrow[colChar*6+3] = trp1[(lo & 0xF0) >> 5]; + outrow[colChar*6+4] = trp2[(lo >> 2) & 0x0F]; + outrow[colChar*6+5] = trp3[lo & 0x07]; + } + break; + + case 7: + for (colChar = 0; colChar < inColChars; ++colChar) { + uint32_t hi, lo; + + hi = inrow[colChar] >> 4; + hi = ((((hi>>1) * 0x00082080) | (0x01 & hi)) & 0x00204081 ) * 0x7F; + hi >>= 4; + outrow[colChar*7] = (unsigned char) ( hi >> 16); + outrow[colChar*7+1] = (unsigned char) ((hi >> 8) & 0xFF); + outrow[colChar*7+2] = (unsigned char) ((hi >> 0) & 0xFF); + + lo = inrow[colChar] & 0x001F; + lo = ((((lo>>1) * 0x02082080) | (0x01 & lo)) & 0x10204081 ) * 0x7F; + outrow[colChar*7+3] = (unsigned char) ((lo >> 24) & 0xFF); + outrow[colChar*7+4] = (unsigned char) ((lo >> 16) & 0xFF); + outrow[colChar*7+5] = (unsigned char) ((lo >> 8) & 0xFF); + outrow[colChar*7+6] = (unsigned char) ((lo >> 0) & 0xFF); + } + break; + + case 8: + for (colChar = 0; colChar < inColChars; ++colChar) { + unsigned int i; + for (i = 0; i < 8; ++i) { + outrow[colChar*8+i] = + ((inrow[colChar] >> (7-i)) & 0x01) *0xFF; + } + } + break; + + case 9: + for (colChar = 0; colChar < inColChars; ++colChar) { + outrow[colChar*9] = ((inrow[colChar] >> 7) & 0x01) * 0xFF; + outrow[colChar*9+1] = pair[(inrow[colChar] >> 6) & 0x03] >> 1; + outrow[colChar*9+2] = pair[(inrow[colChar] >> 5) & 0x03] >> 2; + outrow[colChar*9+3] = pair[(inrow[colChar] >> 4) & 0x03] >> 3; + outrow[colChar*9+4] = pair[(inrow[colChar] >> 3) & 0x03] >> 4; + outrow[colChar*9+5] = pair[(inrow[colChar] >> 2) & 0x03] >> 5; + outrow[colChar*9+6] = pair[(inrow[colChar] >> 1) & 0x03] >> 6; + outrow[colChar*9+7] = pair[(inrow[colChar] >> 0) & 0x03] >> 7; + outrow[colChar*9+8] = (inrow[colChar] & 0x01) * 0xFF; + } + break; + + case 10: + for (colChar = 0; colChar < inColChars; ++colChar) { + outrow[colChar*10] = ((inrow[colChar] >> 7) & 0x01 ) * 0xFF; + outrow[colChar*10+1] = pair[(inrow[colChar] >> 6) & 0x03] >> 2; + outrow[colChar*10+2] = pair[(inrow[colChar] >> 5) & 0x03] >> 4; + outrow[colChar*10+3] = pair[(inrow[colChar] >> 4) & 0x03] >> 6; + outrow[colChar*10+4] = ((inrow[colChar] >> 4) & 0x01) * 0xFF; + outrow[colChar*10+5] = ((inrow[colChar] >> 3) & 0x01) * 0xFF; + outrow[colChar*10+6] = pair[(inrow[colChar] >> 2) & 0x03] >> 2; + outrow[colChar*10+7] = pair[(inrow[colChar] >> 1) & 0x03] >> 4; + outrow[colChar*10+8] = pair[(inrow[colChar] >> 0) & 0x03] >> 6; + outrow[colChar*10+9] = ((inrow[colChar] >> 0) & 0x01) * 0xFF; } break; - case 4: default: - /* Unlike the above cases, we iterate through outrow. The color - composition of each outrow byte is computed by consulting - a single bit or two consecutive bits in inrow. + + + default: + /* Unlike the above cases, we iterate through outrow. To compute the + color composition of each outrow byte, we consult a single bit or + two consecutive bits in inrow. + Color changes never happen twice in a single outrow byte. + + This is a generalization of above routines for scale factors + 9 and 10. + + Logic works for scale factors 4, 6, 7, 8, and above (but not 5). */ + for (colChar = 0; colChar < outColChars; ++colChar) { unsigned int const mult = scaleFactor; unsigned int const mod = colChar % mult; @@ -199,12 +295,14 @@ enlargePbm(struct pam * const inpamP, if (scaleFactor == 1) outrow = inrow; - else - outrow = pbm_allocrow_packed(outcols + 32); - /* The 32 (=4 bytes) is to allow writes beyond outrow data - end when scaleFactor is 2, 3, 5. (max 4 bytes when - scaleFactor is 5) - */ + else { + /* Allow writes beyond outrow data end when scaleFactor is + one of the special fast cases: 2, 3, 4, 5, 6, 7, 8, 9, 10. + */ + unsigned int const rightPadding = + scaleFactor > 10 ? 0 : (scaleFactor - 1) * 8; + outrow = pbm_allocrow_packed(outcols + rightPadding); + } pbm_writepbminit(ofP, outcols, outrows, 0); @@ -214,7 +312,8 @@ enlargePbm(struct pam * const inpamP, pbm_readpbmrow_packed(inpamP->file, inrow, inpamP->width, inpamP->format); - if (inpamP->width % 8 > 0) { /* clean final partial byte */ + if (outcols % 8 > 0) { + /* clean final partial byte */ inrow[inColChars-1] >>= 8 - inpamP->width % 8; inrow[inColChars-1] <<= 8 - inpamP->width % 8; } diff --git a/editor/pamflip.c b/editor/pamflip.c index 61c6efce..83f776af 100644 --- a/editor/pamflip.c +++ b/editor/pamflip.c @@ -656,28 +656,44 @@ transformPbmGen(struct pam * const inpamP, computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore); - bitrow = pbm_allocrow(inpamP->width); - newbits = pbm_allocarray(pbm_packed_bytes(outpamP->width), - outpamP->height); + bitrow = pbm_allocrow_packed(inpamP->width); + newbits = pbm_allocarray_packed( outpamP->width, outpamP->height ); /* Initialize entire array to zeroes. One bits will be or'ed in later */ for (row = 0; row < outpamP->height; ++row) { unsigned int col; for (col = 0; col < pbm_packed_bytes(outpamP->width); ++col) - newbits[row][col] = 0; + newbits[row][col] = 0; } for (row = 0; row < inpamP->height; ++row) { unsigned int col; - pbm_readpbmrow(inpamP->file, bitrow, inpamP->width, inpamP->format); - for (col = 0; col < inpamP->width; ++col) { - unsigned int newcol, newrow; - transformPoint(col, row, xform, &newcol, &newrow); - newbits[newrow][newcol/8] |= bitrow[col] << (7 - newcol % 8); - /* Use of "|=" patterned after pbm_readpbmrow_packed. */ - } + + pbm_readpbmrow_packed(inpamP->file, bitrow, + inpamP->width, inpamP->format); + for (col = 0; col < inpamP->width; ) { + if (bitrow[col/8] == 0x00) + col += 8; /* Blank. Skip to next byte. */ + else { /* Examine each pixel. */ + unsigned int const colLimit = MIN(col+8, inpamP->width); + unsigned int i; + + for (i = 0; col < colLimit; ++i, ++col) { + bool const bitIsOne = (bitrow[col/8] >> (7-i)) & 0x01; + if (bitIsOne) { + /* Write in only the one bits. */ + unsigned int newcol, newrow; + transformPoint(col, row, xform, &newcol, &newrow); + newbits[newrow][newcol/8] |= 0x01 << (7 - newcol % 8); + /* Use of "|=" patterned after + pbm_readpbmrow_packed(). + */ + } + } + } + } } - + for (row = 0; row < outpamP->height; ++row) pbm_writepbmrow_packed(outpamP->file, newbits[row], outpamP->width, 0); diff --git a/editor/pampaintspill.c b/editor/pampaintspill.c new file mode 100644 index 00000000..2b6535c1 --- /dev/null +++ b/editor/pampaintspill.c @@ -0,0 +1,386 @@ +/* ---------------------------------------------------------------------- + * + * Bleed colors from non-background colors into the background + * + * By Scott Pakin <scott+pbm@pakin.org> + * + * ---------------------------------------------------------------------- + * + * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * ---------------------------------------------------------------------- + */ + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <alloca.h> +#include <time.h> + +#include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pam.h" +#include "pammap.h" + + +static time_t const timeUpdateDelta = 30; + /* Seconds between progress updates */ +static int const minUpdates = 4; + /* Minimum number of progress updates to output */ + + +struct cmdlineInfo { + /* This structure represents all of the information the user + supplied in the command line but in a form that's easy for the + program to use. + */ + const char * inputFilename; /* '-' if stdin */ + const char * bgcolor; + unsigned int wrap; + unsigned int all; + float power; + unsigned int downsample; +}; + +struct coords { + /* This structure represents an (x,y) coordinate within an image. */ + unsigned int x; + unsigned int y; +}; + +typedef double distFunc_t(struct coords const p0, + struct coords const p1, + unsigned int const width, + unsigned int const height); + /* Distance function */ + + + +static void +parseCommandLine(int argc, const char ** const argv, + struct cmdlineInfo * const cmdlineP ) { + + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options */ + optStruct3 opt; + unsigned int option_def_index; + unsigned int bgcolorSpec, powerSpec,downsampleSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + option_def_index = 0; /* Incremented by OPTENTRY */ + + OPTENT3(0, "bgcolor", OPT_STRING, &cmdlineP->bgcolor, + &bgcolorSpec, 0); + OPTENT3(0, "wrap", OPT_FLAG, NULL, + &cmdlineP->wrap, 0); + OPTENT3(0, "all", OPT_FLAG, NULL, + &cmdlineP->all, 0); + OPTENT3(0, "power", OPT_FLOAT, &cmdlineP->power, + &powerSpec, 0); + OPTENT3(0, "downsample", OPT_UINT, &cmdlineP->downsample, + &downsampleSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = 0; + opt.allowNegNum = 1; + + optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 ); + + if (!bgcolorSpec) + cmdlineP->bgcolor = NULL; + + if (!powerSpec) + cmdlineP->power = -2.0; + + if (!downsampleSpec) + cmdlineP->downsample = 0; + + if (argc-1 < 1) + cmdlineP->inputFilename = "-"; + else { + cmdlineP->inputFilename = argv[1]; + if (argc-1 > 1) + pm_error("Too many arguments: %u. The only argument is the " + "optional input file name", argc-1); + } +} + + + +static void +locatePaintSources(struct pam * const pamP, + tuple ** const tuples, + tuple const bgColor, + unsigned int const downsample, + struct coords ** const paintSourcesP, + unsigned int * const numPaintSourcesP) { +/*-------------------------------------------------------------------- + Construct a list of all pixel coordinates in the input image that + represent a non-background color. + ----------------------------------------------------------------------*/ + struct coords * paintSources; + /* List of paint-source indexes into inImage */ + unsigned int numPaintSources; /* Number of entries in the above */ + unsigned int numAlloced; /* Number of allocated coordinates. */ + unsigned int row; + + paintSources = NULL; + numAlloced = 0; + numPaintSources = 0; + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + if (!pnm_tupleequal(pamP, tuples[row][col], bgColor)) { + /* Add (row, col) to the list of paint sources. */ + if (numPaintSources == numAlloced) { + numAlloced += pamP->width; + REALLOCARRAY(paintSources, numAlloced); + if (!paintSources) + pm_error("Out of memory"); + } + paintSources[numPaintSources].x = col; + paintSources[numPaintSources].y = row; + ++numPaintSources; + } + } + } + + pm_message("Image contains %u background + %u non-background pixels", + pamP->width * pamP->height - numPaintSources, + numPaintSources); + + /* Reduce the number of paint sources to reduce execution time. */ + if (downsample > 0 && downsample < numPaintSources) { + unsigned int i; + + srandom(time(NULL)); + + for (i = 0; i < downsample; ++i) { + unsigned int const swapIdx = i + random() % (numPaintSources - i); + struct coords const swapVal = paintSources[i]; + + paintSources[i] = paintSources[swapIdx]; + paintSources[swapIdx] = swapVal; + } + numPaintSources = downsample; + } + *paintSourcesP = paintSources; + *numPaintSourcesP = numPaintSources; +} + + + +static distFunc_t euclideanDistanceSqr; + +static double +euclideanDistanceSqr(struct coords const p0, + struct coords const p1, + unsigned int const width, + unsigned int const height) { +/*---------------------------------------------------------------------------- + Return the square of the Euclidian distance between p0 and p1. +-----------------------------------------------------------------------------*/ + double const deltax = (double) (p1.x - p0.x); + double const deltay = (double) (p1.y - p0.y); + + return SQR(deltax) + SQR(deltay); +} + + + +static distFunc_t euclideanDistanceTorusSqr; + +static double +euclideanDistanceTorusSqr(struct coords const p0, + struct coords const p1, + unsigned int const width, + unsigned int const height) { +/*---------------------------------------------------------------------------- + Return the square of the Euclidian distance between p0 and p1, assuming + it's a toroidal surface on which the top row curves around to meet the + bottom and the left column to the right. +-----------------------------------------------------------------------------*/ + struct coords p0Adj, p1Adj; + + if (p1.x >= p0.x + width / 2) { + p0Adj.x = p0.x + width; + p1Adj.x = p1.x; + } else if (p0.x >= p1.x + width / 2) { + p0Adj.x = p0.x; + p1Adj.x = p1.x + width; + } else { + p0Adj.x = p0.x; + p1Adj.x = p1.x; + } + if (p1.y >= p0.y + height / 2) { + p0Adj.y = p0.y + height; + p1Adj.y = p1.y; + } else if (p0.y >= p1.y + height / 2) { + p0Adj.y = p0.y; + p1Adj.y = p1.y + height; + } else { + p0Adj.y = p0.y; + p1Adj.y = p1.y; + } + + return euclideanDistanceSqr(p0Adj, p1Adj, 0, 0); +} + + + +static void +reportProgress(unsigned int const rowsComplete, + unsigned int const height) { + + static time_t prevOutputTime = 0; + time_t now; /* Current time in seconds */ + + if (prevOutputTime == 0) + prevOutputTime = time(NULL); + + /* Output our progress only every timeUpdateDelta seconds. */ + now = time(NULL); + if (prevOutputTime) { + if (now - prevOutputTime >= timeUpdateDelta + || rowsComplete % (height/minUpdates) == 0) { + pm_message("%.1f%% complete", + rowsComplete * 100.0 / height); + prevOutputTime = now; + } + } else + prevOutputTime = now; +} + + + +static void +produceOutputImage(struct pam * const pamP, + tuple ** const tuples, + tuple const bgColor, + const struct coords * const paintSources, + unsigned int const numPaintSources, + distFunc_t * const distFunc, + double const distPower, + bool const all) { +/*-------------------------------------------------------------------- + Color each background pixel (or, if allPixels is 1, all pixels) + using a fraction of each paint source as determined by its distance + to the background pixel. +----------------------------------------------------------------------*/ + struct coords target; + + for (target.y = 0; target.y < pamP->height; ++target.y) { + double * newColor; + + MALLOCARRAY(newColor, pamP->depth); + + for (target.x = 0; target.x < pamP->width; ++target.x) { + if (all || + pnm_tupleequal(pamP, tuples[target.y][target.x], bgColor)) { + + unsigned int plane; + unsigned int ps; + double totalWeight; + + for (plane = 0; plane < pamP->depth; ++plane) + newColor[plane] = 0.0; + totalWeight = 0.0; + for (ps = 0; ps < numPaintSources; ++ps) { + struct coords const source = paintSources[ps]; + tuple const paintColor = tuples[source.y][source.x]; + double const distSqr = + (*distFunc)(target, source, + pamP->width, pamP->height); + + if (distSqr > 0.0) { + /* We do special cases for some common cases with code + that is much faster than pow(). + */ + double const weight = + distPower == -2.0 ? 1.0 / distSqr : + distPower == -1.0 ? 1.0 / sqrt(distSqr): + pow(distSqr, distPower/2); + + unsigned int plane; + + for (plane = 0; plane < pamP->depth; ++plane) + newColor[plane] += weight * paintColor[plane]; + + totalWeight += weight; + } + } + for (plane = 0; plane < pamP->depth; ++plane) + tuples[target.y][target.x][plane] = + (sample) (newColor[plane] / totalWeight); + } + } + reportProgress(target.y, pamP->height); + + free(newColor); + } +} + + + +int +main(int argc, const char *argv[]) { + FILE * ifP; + struct cmdlineInfo cmdline; /* Command-line parameters */ + tuple bgColor; /* Input image's background color */ + struct coords * paintSources; + /* List of paint-source indexes into inImage */ + unsigned int numPaintSources; /* Number of entries in the above */ + distFunc_t * distFunc; /* The distance function */ + struct pam inpam; + struct pam outPam; + tuple ** tuples; + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilename); + + tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth)); + + pm_close(ifP); + + distFunc = cmdline.wrap ? euclideanDistanceTorusSqr : euclideanDistanceSqr; + + if (cmdline.bgcolor) + bgColor = pnm_parsecolor(cmdline.bgcolor, inpam.maxval) ; + else + bgColor = pnm_backgroundtuple(&inpam, tuples); + + pm_message("Treating %s as the background color", + pnm_colorname(&inpam, bgColor, PAM_COLORNAME_HEXOK)); + + locatePaintSources(&inpam, tuples, bgColor, cmdline.downsample, + &paintSources, &numPaintSources); + + produceOutputImage(&inpam, tuples, + bgColor, paintSources, numPaintSources, distFunc, + cmdline.power, cmdline.all); + + + outPam = inpam; + outPam.file = stdout; + pnm_writepam(&outPam, tuples); + + return 0; +} diff --git a/editor/pbmclean.c b/editor/pbmclean.c index 65b53e1c..beecf3e8 100644 --- a/editor/pbmclean.c +++ b/editor/pbmclean.c @@ -1,76 +1,62 @@ -/* pbmclean.c - pixel cleaning. Remove pixel if less than n connected - * identical neighbours, n=1 default. - * AJCD 20/9/90 - * stern, Fri Oct 19 00:10:38 MET DST 2001 - * add '-white/-black' flags to restrict operation to given blobs - */ +/*============================================================================= + pbmclean +=============================================================================== + Pixel cleaner: Remove pixel if less than N connected identical neighbors +=============================================================================*/ +#include <assert.h> #include <stdio.h> #include "pm_c_util.h" -#include "pbm.h" +#include "mallocvar.h" #include "shhopt.h" +#include "pbm.h" struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char *inputFilespec; /* Filespecs of input files */ + const char * inputFileName; /* File name of input file */ bool flipWhite; bool flipBlack; unsigned int connect; unsigned int verbose; }; -#define PBM_INVERT(p) ((p) == PBM_WHITE ? PBM_BLACK : PBM_WHITE) - -/* input bitmap size and storage */ -static bit *inrow[3] ; -#define THISROW (1) - -enum compass_heading { - WEST=0, - NORTHWEST=1, - NORTH=2, - NORTHEAST=3, - EAST=4, - SOUTHEAST=5, - SOUTH=6, - SOUTHWEST=7 -}; -/* compass directions from west clockwise. Indexed by enum compass_heading */ -int const xd[] = { -1, -1, 0, 1, 1, 1, 0, -1 } ; -int const yd[] = { 0, -1, -1, -1, 0, 1, 1, 1 } ; static void -parseCommandLine(int argc, char ** argv, +parseCommandLine(int argc, const char ** argv, struct cmdlineInfo *cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optStruct3 opt; /* set by OPTENT3 */ - optEntry *option_def = malloc(100*sizeof(optEntry)); + optEntry * option_def; unsigned int option_def_index; unsigned int black, white; unsigned int minneighborsSpec; + MALLOCARRAY(option_def, 100); + option_def_index = 0; /* incremented by OPTENT3 */ - OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); - OPTENT3(0, "black", OPT_FLAG, NULL, &black, 0); - OPTENT3(0, "white", OPT_FLAG, NULL, &white, 0); - OPTENT3(0, "minneighbors", OPT_UINT, &cmdlineP->connect, + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "black", OPT_FLAG, NULL, &black, 0); + OPTENT3(0, "white", OPT_FLAG, NULL, &white, 0); + OPTENT3(0, "minneighbors", OPT_UINT, &cmdlineP->connect, &minneighborsSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = TRUE; /* We sort of allow negative numbers as parms */ - optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + free(option_def); + if (!black && !white) { cmdlineP->flipBlack = TRUE; cmdlineP->flipWhite = TRUE; @@ -79,7 +65,6 @@ parseCommandLine(int argc, char ** argv, cmdlineP->flipWhite = !!white; } - if (!minneighborsSpec) { /* Now we do a sleazy tour through the parameters to see if one is -N where N is a positive integer. That's for @@ -111,9 +96,9 @@ parseCommandLine(int argc, char ** argv, } if (argc-1 < 1) - cmdlineP->inputFilespec = "-"; + cmdlineP->inputFileName = "-"; else if (argc-1 == 1) - cmdlineP->inputFilespec = argv[1]; + cmdlineP->inputFileName = argv[1]; else pm_error("You specified too many arguments (%d). The only " "argument is the optional input file specification.", @@ -121,121 +106,323 @@ parseCommandLine(int argc, char ** argv, } +static unsigned int +bitpop8(unsigned char const x) { +/*---------------------------------------------------------------------------- + Return the number of 1 bits in 'x' +-----------------------------------------------------------------------------*/ +static unsigned int const p[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; + + return p[x]; +} -static void -nextrow(FILE * const ifd, - int const row, - int const cols, - int const rows, - int const format) { +static unsigned int +bitpop24(uint32_t const w){ /*---------------------------------------------------------------------------- - Advance one row in the input. + Return the number of 1 bits in the lower 24 bits of 'w' + A GCC builtin, __builtin_popcountl(), is available, but it + emits a call to an external function instead of inlining (GCC 4.4.3). - 'row' is the row number that will be the current row. + This table lookup method is faster. -----------------------------------------------------------------------------*/ - bit * shuffle; - - /* First, get the "next" row in inrow[2] if this is the very first - call to nextrow(). - */ - if (inrow[2] == NULL && row < rows) { - inrow[2] = pbm_allocrow(cols); - pbm_readpbmrow(ifd, inrow[2], cols, format); - } - /* Now advance the inrow[] window, rotating the buffer that now holds - the "previous" row to use it for the new "next" row. - */ - shuffle = inrow[0]; - - inrow[0] = inrow[1]; - inrow[1] = inrow[2]; - inrow[2] = shuffle ; - if (row+1 < rows) { - /* Read the "next" row in from the file. Allocate buffer if needed */ - if (inrow[2] == NULL) - inrow[2] = pbm_allocrow(cols); - pbm_readpbmrow(ifd, inrow[2], cols, format); - } else { - /* There is no next row */ - if (inrow[2]) { - pbm_freerow(inrow[2]); - inrow[2] = NULL; - } - } + return (bitpop8((w >> 16) & 0xff) + + bitpop8((w >> 8) & 0xff) + + bitpop8((w >> 0) & 0xff)); } +/*---------------------------------------------------------------------------- +Fast algorithm for counting friendly neighbor pixels + +In this program both input and output rows are in raw (packed) PBM format. + +We handle input rows in groups of three, named "prevrow", "thisrow", +"nextrow" and scan from left to right. At every byte boundary, 10 bits +are read from each of the three rows and placed into a temporary storage +we call "sample". + +prevrow: ... ... _______M NNNNNNNN O_______ ... +thisrow: ... ... _______W cCCCCCCC E_______ ... +nextrow: ... ... _______R SSSSSSSS T_______ ... + +sample : xxMNNNNNNNNOWcCCCCCCCERSSSSSSST + +We count bits by taking the logical and of "sample" and a bit-mask called +"selection", and feeding the result to a table-based bit-population counter. + +For example, the bits around the leftmost bit of the byte ("c") are selected +like this: + +sample : xxMNNNNNNNNOWcCCCCCCCERSSSSSSST +selection: & | __111_______1_1_______111______ + +(In the actual process, "sample" is shifted right and anded against a + constant "selection" mask.) + +The above reports one bits. For the zero (white) bits we replace "sample" +with its inverse. + +If the friendly neighbor count is below a threshold (default 1), we record +that as a one bit in "flipmask". Bits are flipped in units of eight +and written to outrow at the byte boundary. +-----------------------------------------------------------------------------*/ + + + static unsigned int -likeNeighbors(bit * const inrow[3], - unsigned int const col, - unsigned int const cols) { +likeNeighbors(uint32_t const blackSample, + unsigned int const offset) { + + bool const thispoint = ( blackSample >> (18-offset) ) & 0x01; + uint32_t const sample = (thispoint == PBM_BLACK ) + ? blackSample + : ~ blackSample ; + uint32_t const selection = 0x701407; + + return (bitpop24((sample >> (7-offset)) & selection)); +} + + + +static uint32_t +setSample(const bit * const prevrow, + const bit * const thisrow, + const bit * const nextrow, + unsigned int const col){ + + int const col8 = col/8; + + uint32_t sample; + + sample = + ((prevrow[col8 - 1] ) << 29) | + ((prevrow[col8] ) << 21) | + ((prevrow[col8 + 1] & 0x80) << 13) | + ((thisrow[col8 - 1] & 0x01) << 19) | + ((thisrow[col8] ) << 11) | + ((thisrow[col8 + 1] & 0x80) << 3) | + ((nextrow[col8 - 1] & 0x01) << 9) | + ((nextrow[col8] ) << 1) | + ((nextrow[col8 + 1] & 0x80) >> 7); - int const point = inrow[THISROW][col]; - enum compass_heading heading; - int joined; - - joined = 0; /* initial value */ - for (heading = WEST; heading <= SOUTHWEST; ++heading) { - int x = col + xd[heading] ; - int y = THISROW + yd[heading] ; - if (x < 0 || x >= cols || !inrow[y]) { - if (point == PBM_WHITE) joined++; - } else if (inrow[y][x] == point) joined++ ; - } - return joined; + return sample; } -int -main(int argc, char *argv[]) { +static unsigned char +setTestmask(unsigned char const whiteTestmask, + bool const testWhite, + bool const testBlack) { +/* ----------------------------------------------------------------------- + Make a byte pattern of what bits should be tested within a given "thisrow" + (current inrow) byte. 0 means test, 1 means skip. +-------------------------------------------------------------------------- */ + if (testWhite == testBlack) { + assert(testWhite); assert(testBlack); + return 0x00; + } else if (testWhite == TRUE) { + assert(!testBlack); + return whiteTestmask; + } else + return ~whiteTestmask; +} - struct cmdlineInfo cmdline; - FILE *ifp; - bit *outrow; + + +static void +cleanrow(const bit * const prevrow, + const bit * const thisrow, + const bit * const nextrow, + bit * const outrow, + unsigned int const cols, + unsigned int const threshold, + bool const flipWhite, + bool const flipBlack, + unsigned int * const nFlippedP) { +/* ---------------------------------------------------------------------- + Work through row, scanning for bits that require flipping, and write + the results to outrow. + + Returns the number of bits flipped within this one row as *nFlippedP. +-------------------------------------------------------------------------*/ + uint32_t sample; + unsigned char testmask; + unsigned char flipmask; + unsigned int col; + unsigned int nFlipped; + + flipmask = 0x00; /* initial value */ + nFlipped = 0; /* initial value */ + + for (col=0 ; col < cols ; ++col) { + unsigned int const col8 = col / 8; + unsigned int const offset = col % 8; + + if (offset == 0) { + if (flipmask != 0x00) { + /* Some bits have to be flipped */ + outrow[col8 -1] = thisrow [col8 -1] ^ flipmask; + nFlipped += bitpop8(flipmask); + flipmask = 0x00; + } else if (col8 > 0) + outrow[col8 -1] = thisrow [col8 -1]; + + sample = setSample(prevrow, thisrow, nextrow, col); + testmask = setTestmask(thisrow[col8], flipWhite, flipBlack); + } + + if (((testmask << offset) & 0x80 ) ==0) { + if (likeNeighbors(sample, offset ) < threshold) + flipmask |= (0x80 >> offset); + } + } + + { + /* Write out last byte */ + unsigned int const col8Last = pbm_packed_bytes(cols) -1; + + if (flipmask != 0x00) { + outrow[col8Last] = thisrow[col8Last] ^ flipmask; + nFlipped += bitpop8(flipmask); + } else + outrow[col8Last] = thisrow[col8Last]; + } + *nFlippedP = nFlipped; +} + + + +static void +pbmclean(FILE * const ifP, + FILE * const ofP, + struct cmdlineInfo const cmdline, + double * const nFlippedP) { + + bit ** buffer; + bit * prevrow; + bit * thisrow; + bit * nextrow; + bit * outrow; + bit * edgerow; int cols, rows, format; unsigned int row; - unsigned int nFlipped; /* Number of pixels we have flipped so far */ - pbm_init( &argc, argv ); + pbm_readpbminit(ifP, &cols, &rows, &format); - parseCommandLine(argc, argv, &cmdline); + /* Initialize input buffers. + We add a margin of 8 bits each on the left and right of the rows. - ifp = pm_openr(cmdline.inputFilespec); + On the top and bottom of the image we place an imaginary blank row + ("edgerow") to facilitate the process. + */ + { + unsigned int i; + + buffer = pbm_allocarray_packed(cols+16, 3); + edgerow = pbm_allocrow_packed(cols+16); + + for (i = 0; i < pbm_packed_bytes(cols+16); ++i) + edgerow[i] = 0x00; + + for (i = 0; i < 3; ++i) { + /* Add blank (all white) bytes beside the edges */ + buffer[i][0] = buffer[i][ pbm_packed_bytes( cols +16 ) - 1] = 0x00; + } + thisrow = &edgerow[1]; + nextrow = &buffer[0][1]; - inrow[0] = inrow[1] = inrow[2] = NULL; - pbm_readpbminit(ifp, &cols, &rows, &format); + /* Read the top line into nextrow and clean the right end. */ + + pbm_readpbmrow_packed(ifP, nextrow, cols, format); + + if (cols % 8 > 0){ + nextrow[pbm_packed_bytes(cols) -1 ] >>= (8 - cols % 8); + nextrow[pbm_packed_bytes(cols) -1 ] <<= (8 - cols % 8); + } + } outrow = pbm_allocrow(cols); - pbm_writepbminit(stdout, cols, rows, 0) ; + pbm_writepbminit(ofP, cols, rows, 0) ; + + *nFlippedP = 0; /* none flipped yet */ - nFlipped = 0; /* No pixels flipped yet */ for (row = 0; row < rows; ++row) { - unsigned int col; - nextrow(ifp, row, cols, rows, format); - for (col = 0; col < cols; ++col) { - bit const thispoint = inrow[THISROW][col]; - if ((cmdline.flipWhite && thispoint == PBM_WHITE) || - (cmdline.flipBlack && thispoint == PBM_BLACK)) { - if (likeNeighbors(inrow, col, cols) < cmdline.connect) { - outrow[col] = PBM_INVERT(thispoint); - ++nFlipped; - } else - outrow[col] = thispoint; - } else - outrow[col] = thispoint; - } - pbm_writepbmrow(stdout, outrow, cols, 0) ; + unsigned int nFlipped; + + prevrow = thisrow; /* Slide up the input row window */ + thisrow = nextrow; + if (row < rows -1){ + nextrow = &buffer[(row+1)%3][1]; + /* We take the address directly instead of shuffling the rows + with the help of a temporary. This provision is for proper + handling of the initial edgerow. + */ + pbm_readpbmrow_packed(ifP, nextrow, cols, format); + if (cols % 8 > 0){ + nextrow[pbm_packed_bytes(cols) -1 ] >>= (8 - cols % 8); + nextrow[pbm_packed_bytes(cols) -1 ] <<= (8 - cols % 8); + } + } else /* Bottom of image. */ + nextrow = & edgerow[1]; + + cleanrow(prevrow, thisrow, nextrow, outrow, cols, cmdline.connect, + cmdline.flipWhite, cmdline.flipBlack, &nFlipped); + + *nFlippedP += nFlipped; + + pbm_writepbmrow_packed(ofP, outrow, cols, 0) ; } + + pbm_freearray(buffer, 3); + pbm_freerow(edgerow); pbm_freerow(outrow); - pm_close(ifp); +} + + + +int +main(int argc, const char *argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + double nFlipped; + /* Number of pixels we have flipped so far. Use type double to + prevent overflow. + */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + pbmclean(ifP, stdout, cmdline, &nFlipped); if (cmdline.verbose) - pm_message("%d pixels flipped", nFlipped); + pm_message("%f pixels flipped", nFlipped); + + pm_close(ifP); return 0; } diff --git a/editor/pbmpscale.c b/editor/pbmpscale.c index 2e24f3cd..acdb862c 100644 --- a/editor/pbmpscale.c +++ b/editor/pbmpscale.c @@ -3,22 +3,63 @@ */ #include <stdio.h> -#include "pbm.h" +#include "pm_c_util.h" #include "mallocvar.h" +#include "shhopt.h" +#include "pbm.h" +#include "bitarith.h" -/* prototypes */ -void nextrow_pscale ARGS((FILE *ifd, int row)); -int corner ARGS((int pat)); +#define LEFTBITS pm_byteLeftBits +#define RIGHTBITS pm_byteRightBits -/* input bitmap size and storage */ -int rows, columns, format ; -bit *inrow[3] ; +/* Table for translating bit pattern into "corners" flag element */ -#define thisrow (1) +unsigned char const +transTable[512] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, + 0x00, 0x00, 0x04, 0x04, 0xaa, 0xa2, 0x82, 0x82, 0x8a, 0x82, 0x82, 0x82, + 0xa0, 0xa0, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x10, 0x10, + 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x28, 0x28, + 0x0a, 0x03, 0x01, 0x03, 0x0a, 0x03, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0xa8, 0xa0, 0xc0, 0xc0, + 0x88, 0x80, 0x80, 0x80, 0xa0, 0xa0, 0xc0, 0xc0, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28, + 0x00, 0x00, 0x28, 0x28, 0x0c, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a, 0x01, 0x01, 0x0a, 0x0a, + 0x28, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0xa0, 0xa0, + 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a, + 0x01, 0x01, 0x0a, 0x0a, 0x28, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x10, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0, + 0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0x2a, 0x22, 0x03, 0x02, + 0x0a, 0x02, 0x03, 0x02, 0x30, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28, + 0x00, 0x00, 0x28, 0x28, 0x0a, 0x02, 0x03, 0x02, 0x0a, 0x02, 0x03, 0x02, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, + 0x28, 0x20, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x30, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, + 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x28, 0x28, 0x0c, 0xff, 0xff, 0xff, + 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a, + 0x01, 0x01, 0x0a, 0x0a, 0x28, 0x20, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0, + 0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x01, 0x01, 0x0a, 0x0a, 0x01, 0x01, 0x0a, 0x0a, 0x28, 0x20, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, + 0x82, 0x82, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -/* compass directions from west clockwise */ -int xd_pscale[] = { -1, -1, 0, 1, 1, 1, 0, -1 } ; -int yd_pscale[] = { 0, -1, -1, -1, 0, 1, 1, 1 } ; /* starting positions for corners */ #define NE(f) ((f) & 3) @@ -26,180 +67,393 @@ int yd_pscale[] = { 0, -1, -1, -1, 0, 1, 1, 1 } ; #define SW(f) (((f) >> 4) & 3) #define NW(f) (((f) >> 6) & 3) -typedef unsigned short sixteenbits ; -/* list of corner patterns; bit 7 is current color, bits 0-6 are squares - * around (excluding square behind), going clockwise. - * The high byte of the patterns is a mask, which determines which bits are - * not ignored. - */ -sixteenbits patterns[] = { 0x0000, 0xd555, /* no corner */ - 0x0001, 0xffc1, 0xd514, /* normal corner */ - 0x0002, 0xd554, 0xd515, /* reduced corners */ - 0xbea2, 0xdfc0, 0xfd81, - 0xfd80, 0xdf80, - 0x0003, 0xbfa1, 0xfec2 /* reduced if > 1 */ - }; - -/* search for corner patterns, return type of corner found: - * 0 = no corner, - * 1 = normal corner, - * 2 = reduced corner, - * 3 = reduced if cutoff > 1 - */ +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + unsigned int scale; + const char * inputFileName; /* File name of input file */ +}; + + + +static void +parseCommandLine(int argc, const char ** const argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 < 1) + pm_error("You must specify the scale factor as an argument"); + else { + int const scale = atoi(argv[1]); + if (scale < 1) + pm_error("Scale argument must be at least one. You specified %d", + scale); + else + cmdlineP->scale = scale; -int corner(pat) - int pat; -{ - register int i, r=0; - for (i = 0; i < sizeof(patterns)/sizeof(sixteenbits); i++) - if (patterns[i] < 0x100) - r = patterns[i]; - else if ((pat & (patterns[i] >> 8)) == - (patterns[i] & (patterns[i] >> 8))) - return r; - return 0; + if (argc-1 < 2) + cmdlineP->inputFileName = "-"; + else { + cmdlineP->inputFileName = argv[2]; + + if (argc-1 > 2) + pm_error("Too many arguments. The only arguments are the " + "scale factor and optional input file name. " + "You specified %u", argc-1); + } + } } -/* get a new row - */ -void nextrow_pscale(ifd, row) - FILE *ifd; - int row; -{ - bit *shuffle = inrow[0] ; - inrow[0] = inrow[1]; - inrow[1] = inrow[2]; - inrow[2] = shuffle ; - if (row < rows) { - if (shuffle == NULL) - inrow[2] = shuffle = pbm_allocrow(columns); - pbm_readpbmrow(ifd, inrow[2], columns, format) ; - } else inrow[2] = NULL; /* discard storage */ +static void +validateComputableDimensions(unsigned int const width, + unsigned int const height, + unsigned int const scaleFactor) { +/*---------------------------------------------------------------------------- + Make sure that multiplication for output image width and height do not + overflow. + See validateComputetableSize() in libpam.c + and pbm_readpbminitrest() in libpbm2.c +-----------------------------------------------------------------------------*/ + unsigned int const maxWidthHeight = INT_MAX - 2; + unsigned int const maxScaleFactor = maxWidthHeight / MAX(height, width); + + if (scaleFactor > maxScaleFactor) + pm_error("Scale factor '%u' too large. " + "The maximum for this %u x %u input image is %u.", + scaleFactor, width, height, maxScaleFactor); } -int -main(int argc, char ** argv) { +static void +writeBitSpan(unsigned char * const packedBitrow, + int const cols, + int const offset, + int const color) { +/*---------------------------------------------------------------------------- + Write white (color="0") or black (="1") bits into packedBitrow[], + starting at 'offset', length 'cols'. +-----------------------------------------------------------------------------*/ + unsigned char * const dest = &packedBitrow[offset/8]; + unsigned int const rs = offset % 8; + unsigned int const trs = (cols + rs) % 8; + unsigned int const colBytes = pbm_packed_bytes(cols + rs); + unsigned int const last = colBytes - 1; - FILE * ifP; - bit * outrow; - unsigned int row; - int scale, cutoff, ucutoff ; - unsigned char *flags; + unsigned char const origHead = dest[0]; + unsigned char const origEnd = dest[last]; - pbm_init( &argc, argv ); + unsigned int i; - if (argc < 2) - pm_usage("scale [pbmfile]"); + for (i = 0; i < colBytes; ++i) + dest[i] = color * 0xff; - scale = atoi(argv[1]); - if (scale < 1) - pm_error("Scale argument must be at least one. You specified '%s'", - argv[1]); + if (rs > 0) + dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); - if (argc == 3) - ifP = pm_openr(argv[2]); - else - ifP = stdin ; + if (trs > 0) + dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); +} - inrow[0] = inrow[1] = inrow[2] = NULL; - pbm_readpbminit(ifP, &columns, &rows, &format) ; - outrow = pbm_allocrow(columns*scale) ; - MALLOCARRAY(flags, columns); - if (flags == NULL) - pm_error("out of memory") ; - pbm_writepbminit(stdout, columns*scale, rows*scale, 0) ; +static void +setFlags(const bit * const prevrow, + const bit * const thisrow, + const bit * const nextrow, + unsigned char * const flags, + unsigned int const cols ) { +/*---------------------------------------------------------------------------- + Scan one row, examining the row above and row below, and determine + whether there are "corners" for each pixel. Feed a 9 bit sample into + pre-calculated array transTable[512] to calculate all four corner statuses + at once. - cutoff = scale / 2; - ucutoff = scale - 1 - cutoff; - nextrow_pscale(ifP, 0); - for (row = 0; row < rows; ++row) { - unsigned int col; - unsigned int i; - nextrow_pscale(ifP, row+1); - for (col = 0; col < columns; ++col) { - unsigned int i; - flags[col] = 0 ; - for (i = 0; i != 8; i += 2) { - int vec = inrow[thisrow][col] != PBM_WHITE; - unsigned int k; - for (k = 0; k < 7; ++k) { - int x = col + xd_pscale[(k+i)&7] ; - int y = thisrow + yd_pscale[(k+i)&7] ; - vec <<= 1; - if (x >=0 && x < columns && inrow[y]) - vec |= (inrow[y][x] != PBM_WHITE) ; - } - flags[col] |= corner(vec)<<i ; - } + Bits in the 9 bit sample represent the current pixel and neighbors: + NW : N : NE : W: Current : E : SW : S : SE + + Bits in flag are divided into 4 fields of width 2 each: + NW : SW : SE : NE + + Code 0xff is an exception. It is a variation of 0x00. + 0x00 : no corners, no color change from above row (Current == N) + 0xff : no corners, but color changed (Current != N) + + Most transTable[] entries are "no corners". + 0x00 appears 180 times, 0xff 109 times. +-----------------------------------------------------------------------------*/ + +#if 0 + /* The following code is from the previous version, which examined + the corners one by one: + */ + + /* list of corner patterns; bit 7 is current color, bits 0-6 are squares + around (excluding square behind), going clockwise. + The high byte of the patterns is a mask, which determines which bits are + not ignored. + */ + uint16_t const patterns[] + = { 0x0000, 0xd555, /* no corner */ + 0x0001, 0xffc1, 0xd514, /* normal corner */ + 0x0002, 0xd554, 0xd515, 0xbea2, 0xdfc0, 0xfd81, 0xfd80, 0xdf80, + /* reduced corners */ + 0x0003, 0xbfa1, 0xfec2 }; /* reduced if cutoff > 1 */ + + /* + For example, the NE corner is examined with the following 8 bit sample: + Current : W : NW : N : NE : E : SE : S + (SW is the "square behind") + */ +#endif + + uint32_t prevrow24, thisrow24, nextrow24; + unsigned int col; + + /* higher bits are set to 0 */ + prevrow24 = prevrow[0]; /* initial value */ + thisrow24 = thisrow[0]; /* initial value */ + nextrow24 = nextrow[0]; /* initial value */ + + for (col = 0; col < cols; ++col) { + unsigned int const col8 = col / 8; + unsigned int const offset = col % 8; + + unsigned int sample; + + if (offset == 0) { + prevrow24 = prevrow24 << 8 | prevrow[col8 + 1]; + thisrow24 = thisrow24 << 8 | thisrow[col8 + 1]; + nextrow24 = nextrow24 << 8 | nextrow[col8 + 1]; } - for (i = 0; i < scale; i++) { - bit *ptr = outrow ; - int zone = (i > ucutoff) - (i < cutoff) ; - int cut = (zone < 0) ? (cutoff - i) : - (zone > 0) ? (i - ucutoff) : 0 ; - - for (col = 0; col < columns; ++col) { - int pix = inrow[thisrow][col] ; - int flag = flags[col] ; - int cutl, cutr; - unsigned int k; + sample = ( ( prevrow24 >> ( 8 -offset) ) & 0x01c0 ) + | ( ( thisrow24 >> (11 -offset) ) & 0x0038 ) + | ( ( nextrow24 >> (14 -offset) ) & 0x0007 ); + + flags[col] = transTable[sample]; + } +} + + + +static void +expandRow(const bit * const thisrow, + const bit * const prevrow, + bit * const outrow, + unsigned char * const flags, + unsigned int const cols, + int const scale, + int const cutoff, + int const ucutoff) { +/*---------------------------------------------------------------------------- + Process one row, using flags array as reference. If pixel has no corners + output a NxN square of the given color, otherwise output with the + specified corner area(s) clipped off. +-----------------------------------------------------------------------------*/ + unsigned int const outcols = cols * scale; + + unsigned int i; + unsigned int col; + + for (i = 0; i < scale; ++i) { + int const zone = (i > ucutoff) - (i < cutoff); + int const cut1 = + (zone < 0) ? (cutoff - i) : (zone > 0) ? (i - ucutoff) : 0; + + unsigned int outcol; + int cut[4]; + + outcol = 0; /* initial value */ + + cut[0] = 0; + cut[1] = cut1; + cut[2] = cut1 ? cut1 - 1 : 0; + cut[3] = (cut1 && cutoff > 1) ? cut1 - 1 : cut1; + + for (col = 0; col < cols; ++col) { + unsigned int const col8 = col / 8; + unsigned int const offset = col % 8; + int const pix = (thisrow[col8] >> (7-offset) ) & 0x01; + int const flag = flags[col]; + + int cutl, cutr; + + if (flag == 0x00) { + /* There are no corners, no color change */ + outcol += scale; + } else { switch (zone) { case -1: - switch (NW(flag)) { - case 0: cutl = 0; break; - case 1: cutl = cut; break; - case 2: cutl = cut ? cut-1 : 0; break; - case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break; - default: cutl = 0; /* Should never reach here */ - } - switch (NE(flag)) { - case 0: cutr = 0; break; - case 1: cutr = cut; break; - case 2: cutr = cut ? cut-1 : 0; break; - case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break; - default: cutr = 0; /* Should never reach here */ + if (i==0 && flag == 0xff) { + /* No corners, color changed */ + cutl = cutr = 0; + flags[col] = 0x00; + /* Use above skip procedure next cycle */ + } else { + cutl = cut[NW(flag)]; + cutr = cut[NE(flag)]; } break; case 0: cutl = cutr = 0; break ; case 1: - switch (SW(flag)) { - case 0: cutl = 0; break; - case 1: cutl = cut; break; - case 2: cutl = cut ? cut-1 : 0; break; - case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break; - default: cutl = 0; /* should never reach here */ - } - switch (SE(flag)) { - case 0: cutr = 0; break; - case 1: cutr = cut; break; - case 2: cutr = cut ? cut-1 : 0; break; - case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break; - default: cutr = 0; /* should never reach here */ - } + cutl = cut[SW(flag)]; + cutr = cut[SE(flag)]; break; - default: cutl = 0; cutr = 0; /* Should never reach here */ } - for (k = 0; k < cutl; ++k) /* left part */ - *ptr++ = !pix ; - for (k = 0; k < scale-cutl-cutr; ++k) /* center part */ - *ptr++ = pix ; - for (k = 0; k < cutr; ++k) /* right part */ - *ptr++ = !pix ; + + if (cutl > 0) { + writeBitSpan(outrow, cutl, outcol, !pix); + outcol += cutl; + } + { + unsigned int const center = scale - cutl - cutr; + + if (center > 0) { + writeBitSpan(outrow, center, outcol, pix); + outcol += center; + } + } + if (cutr > 0) { + writeBitSpan(outrow, cutr, outcol, !pix); + outcol += cutr; + } } - pbm_writepbmrow(stdout, outrow, scale*columns, 0) ; } + pbm_writepbmrow_packed(stdout, outrow, outcols, 0) ; + } +} + + + +int +main(int argc, const char ** argv) { + + struct cmdlineInfo cmdline; + FILE * ifP; + bit ** buffer; + bit * prevrow; + bit * thisrow; + bit * nextrow; + bit * edgerow; + bit * outrow; + unsigned int row; + unsigned int i; + int cols, rows; + int format; + unsigned int outcols; + unsigned int outrows; + int cutoff; + int ucutoff ; + unsigned char * flags; /* malloc'ed */ + + pm_proginit(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + pbm_readpbminit(ifP, &cols, &rows, &format) ; + + validateComputableDimensions(cols, rows, cmdline.scale); + + outcols = cols * cmdline.scale; + outrows = rows * cmdline.scale; + + /* Initialize input buffers. + We add a margin of 8 bits on the right of the three rows. + + On the top and bottom of the image we place an imaginary blank row + ("edgerow") to facilitate the process. + */ + + buffer = pbm_allocarray_packed(cols + 8, 3); + edgerow = pbm_allocrow_packed(cols + 8); + + for (i = 0; i < pbm_packed_bytes(cols + 8); ++i) + edgerow[i] = 0x00; + + /* Add blank bytes at right edges */ + for (i = 0; i < 3; ++i) + buffer[i][pbm_packed_bytes(cols + 8) - 1] = 0x00; + + thisrow = edgerow; + nextrow = buffer[0]; + + /* Read the top line into nextrow and clean the right end. */ + + pbm_readpbmrow_packed(ifP, nextrow, cols, format); + + if (cols % 8 > 0) { + nextrow[pbm_packed_bytes(cols) - 1] >>= (8 - cols % 8); + nextrow[pbm_packed_bytes(cols) - 1] <<= (8 - cols % 8); + } + + outrow = pbm_allocrow_packed(outcols); + for (i = 0; i < pbm_packed_bytes(outcols); ++i) + outrow[i] = 0x00; + + MALLOCARRAY(flags, cols); + if (flags == NULL) + pm_error("Couldn't get memory for %u columns of flags", cols); + + pbm_writepbminit(stdout, outcols, outrows, 0) ; + + cutoff = cmdline.scale / 2; + ucutoff = cmdline.scale - 1 - cutoff; + + for (row = 0; row < rows; ++row) { + prevrow = thisrow; /* Slide up the input row window */ + thisrow = nextrow; + if (row < rows - 1) { + nextrow = buffer[(row + 1) % 3]; + /* We take the address directly instead of shuffling the rows. + This provision is for proper handling of the initial edgerow. + */ + pbm_readpbmrow_packed(ifP, nextrow, cols, format); + if (cols % 8 > 0) { + nextrow[pbm_packed_bytes(cols) - 1] >>= (8 - cols % 8); + nextrow[pbm_packed_bytes(cols) - 1] <<= (8 - cols % 8); + } + } else + /* Bottom of image. */ + nextrow = edgerow; + + setFlags(prevrow, thisrow, nextrow, flags, cols); + + expandRow(thisrow, prevrow, outrow, flags, cols, cmdline.scale, + cutoff, ucutoff); } + pbm_freearray(buffer,3); + pbm_freerow(edgerow); + pbm_freerow(outrow); + free (flags); pm_close(ifP); return 0; } diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c index b1f4adc8..c29a6fe4 100644 --- a/editor/pnmconvol.c +++ b/editor/pnmconvol.c @@ -66,6 +66,7 @@ struct cmdlineInfo { const char ** matrixfile; unsigned int matrixSpec; struct matrixOpt matrix; + unsigned int normalize; }; @@ -313,6 +314,8 @@ parseCommandLine(int argc, char ** argv, &matrixfileSpec, 0) OPTENT3(0, "nooffset", OPT_FLAG, NULL, &cmdlineP->nooffset, 0); + OPTENT3(0, "normalize", OPT_FLAG, NULL, + &cmdlineP->normalize, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -528,6 +531,52 @@ convKernelDestroy(struct convKernel * const convKernelP) { static void +normalizeKernelPlane(struct convKernel * const convKernelP, + unsigned int const plane) { + + unsigned int row; + float sum; + + for (row = 0, sum = 0.0; row < convKernelP->rows; ++row) { + unsigned int col; + + for (col = 0; col < convKernelP->cols; ++col) { + + sum += convKernelP->weight[plane][row][col]; + } + } + + { + float const scaler = 1.0/sum; + + unsigned int row; + + for (row = 0; row < convKernelP->rows; ++row) { + unsigned int col; + + for (col = 0; col < convKernelP->cols; ++col) + convKernelP->weight[plane][row][col] *= scaler; + } + } +} + + + +static void +normalizeKernel(struct convKernel * const convKernelP) { +/*---------------------------------------------------------------------------- + Modify *convKernelP by scaling every weight in a plane by the same factor + such that the weights in the plane all add up to 1. +-----------------------------------------------------------------------------*/ + unsigned int plane; + + for (plane = 0; plane < convKernelP->planes; ++plane) + normalizeKernelPlane(convKernelP, plane); +} + + + +static void getKernelPnm(const char * const fileName, unsigned int const depth, bool const nooffset, @@ -552,9 +601,20 @@ getKernelPnm(const char * const fileName, static void convKernelCreateMatrixOpt(struct matrixOpt const matrixOpt, + bool const normalize, unsigned int const depth, struct convKernel ** const convKernelPP) { +/*---------------------------------------------------------------------------- + Create a convolution kernel as described by a -matrix command line + option. + + The option value is 'matrixOpt'. + If 'normalize' is true, we normalize whatever numbers the option specifies + so that they add up to one; otherwise, we take the numbers as we find them, + so they may form a biased matrix -- i.e. one which brightens or dims the + image overall. +-----------------------------------------------------------------------------*/ struct convKernel * convKernelP; unsigned int plane; @@ -579,6 +639,9 @@ convKernelCreateMatrixOpt(struct matrixOpt const matrixOpt, matrixOpt.weight[row][col]; } } + if (normalize) + normalizeKernel(convKernelP); + *convKernelPP = convKernelP; } @@ -742,9 +805,21 @@ copyWeight(float ** const srcWeight, static void convKernelCreateSimpleFile(const char ** const fileNameList, + bool const normalize, unsigned int const depth, struct convKernel ** const convKernelPP) { +/*---------------------------------------------------------------------------- + Create a convolution kernel as described by a convolution matrix file. + This is the simple file with floating point numbers in it, not the + legacy pseudo-PNM thing. + + The name of the file is 'fileNameList'. + If 'normalize' is true, we normalize whatever numbers we find in the file + so that they add up to one; otherwise, we take the numbers as we find them, + so they may form a biased matrix -- i.e. one which brightens or dims the + image overall. +-----------------------------------------------------------------------------*/ struct convKernel * convKernelP; unsigned int fileCt; unsigned int planeCt; @@ -792,6 +867,10 @@ convKernelCreateSimpleFile(const char ** const fileNameList, &convKernelP->weight[plane]); } } + + if (normalize) + normalizeKernel(convKernelP); + convKernelP->cols = width; convKernelP->rows = height; *convKernelPP = convKernelP; @@ -803,16 +882,25 @@ static void getKernel(struct cmdlineInfo const cmdline, unsigned int const depth, struct convKernel ** const convKernelPP) { +/*---------------------------------------------------------------------------- + Figure out what the convolution kernel is. It can come from various + sources in various forms, as described on the command line, represented + by 'cmdline'. + We generate a kernel object in standard form (free of any indication of + where it came from) and return a handle to it as *convKernelPP. +-----------------------------------------------------------------------------*/ struct convKernel * convKernelP; if (cmdline.pnmMatrixFileName) getKernelPnm(cmdline.pnmMatrixFileName, depth, cmdline.nooffset, &convKernelP); else if (cmdline.matrixfile) - convKernelCreateSimpleFile(cmdline.matrixfile, depth, &convKernelP); + convKernelCreateSimpleFile(cmdline.matrixfile, cmdline.normalize, + depth, &convKernelP); else if (cmdline.matrixSpec) - convKernelCreateMatrixOpt(cmdline.matrix, depth, &convKernelP); + convKernelCreateMatrixOpt(cmdline.matrix, cmdline.normalize, + depth, &convKernelP); warnBadKernel(convKernelP); @@ -1665,7 +1753,7 @@ convolveHorizontalRowPlane(struct pam * const pamP, outputrow[col][plane] = window[crowso2][col][plane]; else if (col == ccolso2) { unsigned int const leftcol = 0; - // Window is up againt left edge of image + /* Window is up againt left edge of image */ float matrixSum; @@ -1829,12 +1917,12 @@ convolveVerticalRowPlane(struct pam * const pamP, outputrow[col][plane] = circMap[crowso2][col][plane]; else if (col == ccolso2) { unsigned int const leftcol = 0; - // Convolution window is againt left edge of image + /* Convolution window is againt left edge of image */ float matrixSum; unsigned int ccol; - // Slide window down in the first kernel's worth of columns + /* Slide window down in the first kernel's worth of columns */ for (ccol = 0; ccol < convKernelP->cols; ++ccol) { convColumnSum[leftcol + ccol] += circMap[addrow][leftcol + ccol][plane]; @@ -1854,7 +1942,7 @@ convolveVerticalRowPlane(struct pam * const pamP, float matrixSum; unsigned int ccol; - // Slide window down in the column that just entered the window + /* Slide window down in the column that just entered the window */ convColumnSum[addcol] += circMap[addrow][addcol][plane]; convColumnSum[addcol] -= circMap[subrow][addcol][plane]; diff --git a/editor/pnmhisteq.c b/editor/pnmhisteq.c index 1987efc3..97dbe472 100644 --- a/editor/pnmhisteq.c +++ b/editor/pnmhisteq.c @@ -144,7 +144,7 @@ computeLuminosityHistogram(xel * const * const xels, for (col = 0; col < cols; ++col) { xel const thisXel = xels[row][col]; if (!monoOnly || PPM_ISGRAY(thisXel)) { - xelval const l = PPM_LUMIN(thisXel); + xelval const l = ppm_luminosity(thisXel); lmin = MIN(lmin, l); lmax = MAX(lmax, l); @@ -302,6 +302,48 @@ reportMap(const unsigned int * const lumahist, +static xel +scaleXel(xel const thisXel, + double const scaler) { +/*---------------------------------------------------------------------------- + Scale the components of 'xel' by multiplying by 'scaler'. + + Assume this doesn't cause it to exceed maxval. +-----------------------------------------------------------------------------*/ + xel retval; + + PNM_ASSIGN(retval, + ((xelval)(PNM_GETR(thisXel) * scaler + 0.5)), + ((xelval)(PNM_GETG(thisXel) * scaler + 0.5)), + ((xelval)(PNM_GETB(thisXel) * scaler + 0.5))); + + return retval; +} + + + +static xel +remapRgbValue(xel const thisXel, + xelval const maxval, + const gray * const lumamap) { +/*---------------------------------------------------------------------------- + Return the color 'thisXel' with its HSV value changed per 'lumamap' but + the same hue and saturation. + + 'maxval' is the maxval for 'xel' and our return value. +-----------------------------------------------------------------------------*/ + struct hsv const hsv = + ppm_hsv_from_color(thisXel, maxval); + xelval const oldValue = + MIN(maxval, ROUNDU(hsv.v * maxval)); + xelval const newValue = + lumamap[oldValue]; + + return scaleXel(thisXel, (double)newValue/oldValue); +} + + + static void remap(xel ** const xels, unsigned int const cols, @@ -323,16 +365,8 @@ remap(xel ** const xels, if (monoOnly && PPM_ISGRAY(thisXel)) { /* Leave this pixel alone */ } else { - struct hsv hsv; - xelval iv; - - hsv = ppm_hsv_from_color(thisXel, maxval); - iv = MIN(maxval, ROUNDU(hsv.v * maxval)); - - hsv.v = MIN(1.0, - ((double) lumamap[iv]) / ((double) maxval)); - - xels[row][col] = ppm_color_from_hsv(hsv, maxval); + xels[row][col] = remapRgbValue(xels[row][col], + maxval, lumamap); } } } diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c index 27d51115..36604dc9 100644 --- a/editor/pnmnorm.c +++ b/editor/pnmnorm.c @@ -211,7 +211,7 @@ buildHistogram(FILE * const ifp, if (PNM_FORMAT_TYPE(format) == PPM_TYPE) { switch(brightMethod) { case BRIGHT_LUMINOSITY: - brightness = PPM_LUMIN(p); + brightness = ppm_luminosity(p); break; case BRIGHT_COLORVALUE: brightness = ppm_colorvalue(p); @@ -589,7 +589,7 @@ brightScaler(xel const p, switch (brightMethod) { case BRIGHT_LUMINOSITY: - oldBrightness = PPM_LUMIN(p); + oldBrightness = ppm_luminosity(p); break; case BRIGHT_COLORVALUE: oldBrightness = ppm_colorvalue(p); diff --git a/editor/pnmremap.c b/editor/pnmremap.c index 5b58addb..29e05905 100644 --- a/editor/pnmremap.c +++ b/editor/pnmremap.c @@ -30,6 +30,7 @@ #include "nstring.h" #include "shhopt.h" #include "pam.h" +#include "ppm.h" #include "pammap.h" #define MAXCOLORS 32767u @@ -1122,7 +1123,7 @@ getSpecifiedMissingColor(struct pam * const pamP, specColor[PAM_GRN_PLANE] = PPM_GETG(color); specColor[PAM_BLU_PLANE] = PPM_GETB(color); } else if (pamP->depth == 1) { - specColor[0] = PPM_LUMIN(color); + specColor[0] = ppm_luminosity(color); } else { pm_error("You may not use -missing with a colormap that is not " "of depth 1 or 3. Yours has depth %u", diff --git a/editor/ppmdist.c b/editor/ppmdist.c index 90c2e3d3..bca96adc 100644 --- a/editor/ppmdist.c +++ b/editor/ppmdist.c @@ -125,7 +125,7 @@ main(int argc, char *argv[]) { * by frequency - but again, for a small number of colors * it's a small matter. */ - colorToGrayMap[color].gray = PPM_LUMIN(hist[color].color); + colorToGrayMap[color].gray = ppm_luminosity(hist[color].color); } /* diff --git a/generator/pbmpage.c b/generator/pbmpage.c index fcf7af42..a2f47bcc 100644 --- a/generator/pbmpage.c +++ b/generator/pbmpage.c @@ -17,6 +17,7 @@ #include <math.h> #include <stdio.h> +#include "pm_c_util.h" #include "pbm.h" /* US is 8.5 in by 11 in */ @@ -31,10 +32,9 @@ struct bitmap { - int Width; /* width and height in 600ths of an inch */ - int Height; - int Pwidth; /* width in bytes */ - char *bitmap; + unsigned int Width; /* width and height in 600ths of an inch */ + unsigned int Height; + bit ** bitmap; }; static struct bitmap bitmap; @@ -42,11 +42,10 @@ static struct bitmap bitmap; static void -setpixel(int const x, - int const y, - int const c) { +setpixel(unsigned int const x, + unsigned int const y, + unsigned int const c) { - int const charidx = y * bitmap.Pwidth + x/8; char const bitmask = 128 >> (x % 8); if (x < 0 || x >= bitmap.Width) @@ -55,151 +54,148 @@ setpixel(int const x, return; if (c) - bitmap.bitmap[charidx] |= bitmask; + bitmap.bitmap[y][x/8] |= bitmask; else - bitmap.bitmap[charidx] &= ~bitmask; + bitmap.bitmap[y][x/8] &= ~bitmask; } static void -setplus(int x,int y,int s) +setplus(unsigned int const x, + unsigned int const y, + unsigned int const s) { /*---------------------------------------------------------------------------- Draw a black plus sign centered at (x,y) with arms 's' pixels long. Leave the exact center of the plus white. -----------------------------------------------------------------------------*/ -{ - int i; - - for(i=0; i<s; i++) - { - setpixel(x+i,y,1); - setpixel(x-i,y,1); - setpixel(x,y+i,1); - setpixel(x,y-i,1); - } + unsigned int i; + + for (i = 0; i < s; ++i) { + setpixel(x+i, y, 1); + setpixel(x-i, y, 1); + setpixel(x, y+i, 1); + setpixel(x, y-i, 1); + } } static void -setblock(int x,int y,int s) -{ - int i,j; +setblock(unsigned int const x, + unsigned int const y, + unsigned int const s) { + + unsigned int i; + + for (i = 0; i < s; ++i) { + unsigned int j; - for(i=0; i<s; i++) - for(j=0; j<s; j++) - setpixel(x+i,y+j,1); + for (j = 0; j < s; ++j) + setpixel(x+i, y+j, 1); + } } static void -setchar(int x,int y,char c) -{ - int xo,yo; - static char charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e }, - { 0x00, 0x42, 0x7f, 0x40, 0x00 }, - { 0x42, 0x61, 0x51, 0x49, 0x46 }, - { 0x22, 0x41, 0x49, 0x49, 0x36 }, - { 0x18, 0x14, 0x12, 0x7f, 0x10 }, - { 0x27, 0x45, 0x45, 0x45, 0x39 }, - { 0x3e, 0x49, 0x49, 0x49, 0x32 }, - { 0x01, 0x01, 0x61, 0x19, 0x07 }, - { 0x36, 0x49, 0x49, 0x49, 0x36 }, - { 0x26, 0x49, 0x49, 0x49, 0x3e } }; - - if(c<='9' && c>='0') - for(xo=0; xo<5; xo++) - for(yo=0; yo<8; yo++) - if((charmap[c-'0'][xo]>>yo)&1) - setblock(x+xo*3,y+yo*3,3); +setchar(unsigned int const x, + unsigned int const y, + char const c) { + + static char const charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e }, + { 0x00, 0x42, 0x7f, 0x40, 0x00 }, + { 0x42, 0x61, 0x51, 0x49, 0x46 }, + { 0x22, 0x41, 0x49, 0x49, 0x36 }, + { 0x18, 0x14, 0x12, 0x7f, 0x10 }, + { 0x27, 0x45, 0x45, 0x45, 0x39 }, + { 0x3e, 0x49, 0x49, 0x49, 0x32 }, + { 0x01, 0x01, 0x61, 0x19, 0x07 }, + { 0x36, 0x49, 0x49, 0x49, 0x36 }, + { 0x26, 0x49, 0x49, 0x49, 0x3e } }; + + if (c <= '9' && c >= '0') { + unsigned int xo; + + for (xo = 0; xo < 5; ++xo) { + unsigned int yo; + + for (yo = 0; yo < 8; ++yo) { + if ((charmap[c-'0'][xo] >> yo) & 0x01) + setblock(x + xo*3, y + yo*3, 3); + } + } + } } static void -setstring(int x,int y,char* s) -{ - char* p; - int xo; +setstring(unsigned int const x, + unsigned int const y, + const char * const s) { - for(xo=0, p=s; *p; xo+=21, p++) - setchar(x+xo,y,*p); + const char * p; + unsigned int xo; + + for (xo = 0, p = s; *p; xo += 21, ++p) + setchar(x + xo, y, *p); } static void -setCG(int x,int y) -{ - int xo,yo,zo; - - for(xo=0; xo<=50; xo++) - { - yo=sqrt(50.0*50.0-xo*xo); - setpixel(x+xo,y+yo,1); - setpixel(x+yo,y+xo,1); - setpixel(x-1-xo,y-1-yo,1); - setpixel(x-1-yo,y-1-xo,1); - setpixel(x+xo,y-1-yo,1); - setpixel(x-1-xo,y+yo,1); - for(zo=0; zo<yo; zo++) - { - setpixel(x+xo,y-1-zo,1); - setpixel(x-1-xo,y+zo,1); +setCG(unsigned int const x, + unsigned int const y) { + + unsigned int xo; + + for (xo = 0; xo <= 50; ++xo) { + unsigned int const yo = sqrt(SQR(50.0) - SQR(xo)); + + unsigned int zo; + + setpixel(x + xo, y + yo, 1); + setpixel(x+yo, y + xo, 1); + setpixel(x-1-xo, y-1-yo, 1); + setpixel(x-1-yo, y-1-xo, 1); + setpixel(x+xo, y-1-yo, 1); + setpixel(x-1-xo, y+yo, 1); + + for(zo = 0; zo < yo; ++zo) { + setpixel(x + xo, y-1-zo, 1); + setpixel(x-1-xo, y+zo, 1); + } } - } } static void -outputPbm(FILE * const file, +outputPbm(FILE * const ofP, struct bitmap const bitmap) { /*---------------------------------------------------------------------------- Create a pbm file containing the image from the global variable bitmap[]. -----------------------------------------------------------------------------*/ int const forceplain = 0; - bit *pbmrow; - int row; - int bitmap_cursor; + + unsigned int row; - pbm_writepbminit(file, bitmap.Width, bitmap.Height, forceplain); - - /* We round the allocated row space up to a multiple of 8 so the ugly - fast code below can work. - */ - pbmrow = pbm_allocrow(((bitmap.Width+7)/8)*8); + pbm_writepbminit(ofP, bitmap.Width, bitmap.Height, forceplain); - bitmap_cursor = 0; - for (row = 0; row < bitmap.Height; row++) { - int col; - for (col = 0; col < bitmap.Width;) { - /* A little ugliness makes a big speed difference here. */ - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<7); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<6); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<5); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<4); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<3); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<2); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<1); - pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<0); - - bitmap_cursor++; - } - pbm_writepbmrow(file, pbmrow, bitmap.Width, forceplain); + for (row = 0; row < bitmap.Height; ++row) { + pbm_writepbmrow_packed(ofP, bitmap.bitmap[row], + bitmap.Width, forceplain); } - pbm_freerow(pbmrow); - pm_close(file); } + static void framePerimeter(unsigned int const Width, unsigned int const Height) { - unsigned int x,y; + unsigned int x, y; /* Top edge */ for (x = 0; x < Width; ++x) @@ -221,57 +217,66 @@ framePerimeter(unsigned int const Width, int -main(int argc,char** argv) { +main(int argc, const char** argv) { - int TP=1; - int x,y; + int TP; + unsigned int x, y; char buf[128]; - int Width; /* width and height in 600ths of an inch */ - int Height; + /* width and height in 600ths of an inch */ + unsigned int Width; + unsigned int Height; - pbm_init(&argc, argv); + pm_proginit(&argc, argv); if (argc > 1 && strcmp(argv[1], "-a4") == 0) { - Width = A4WIDTH; + Width = A4WIDTH; Height = A4HEIGHT; - argc--; - argv++; + --argc; + ++argv; } else { - Width = USWIDTH; + Width = USWIDTH; Height = USHEIGHT; } - bitmap.Width = Width; + if (argc > 1) + TP = atoi(argv[1]); + else + TP = 1; + + bitmap.Width = Width; bitmap.Height = Height; - bitmap.Pwidth = (Width + 7) / 8; - bitmap.bitmap = malloc(bitmap.Pwidth * bitmap.Height); + bitmap.bitmap = pbm_allocarray_packed(Width, bitmap.Height); - for (x = 0; x < bitmap.Pwidth * bitmap.Height; ++x) - bitmap.bitmap[x] = 0x00; - - if (argc>1) - TP = atoi(argv[1]); + for (y = 0; y < bitmap.Height; ++y) { + unsigned int x; + for (x = 0; x < pbm_packed_bytes(bitmap.Width); ++x) + bitmap.bitmap[y][x] = 0x00; + } switch (TP) { case 1: framePerimeter(Width, Height); - for (x = 0; x < Width; x += 100) + for (x = 0; x < Width; x += 100) { + unsigned int y; for(y = 0; y < Height; y += 100) setplus(x, y, 4); + } for(x = 0; x < Width; x += 100) { sprintf(buf,"%d", x); setstring(x + 3, (Height/200) * 100 + 3, buf); } - for (y=0; y < Height; y += 100) { + for (y = 0; y < Height; y += 100) { sprintf(buf, "%d", y); setstring((Width/200) * 100 + 3, y + 3, buf); } for (x = 0; x < Width; x += 10) for (y = 0; y < Height; y += 100) setplus(x, y, ((x%100) == 50) ? 2 : 1); - for (x=0; x < Width; x += 100) + for (x = 0; x < Width; x += 100) { + unsigned int y; for (y = 0; y < Height; y += 10) setplus(x, y, ((y%100) == 50) ? 2 : 1); + } setCG(Width/2, Height/2); break; case 2: @@ -290,5 +295,9 @@ main(int argc,char** argv) { outputPbm(stdout, bitmap); + pbm_freearray(bitmap.bitmap, Height); + + pm_close(stdout); + return 0; } diff --git a/lib/libpam.c b/lib/libpam.c index b890434e..9384e178 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -1066,6 +1066,67 @@ pnm_getopacity(const struct pam * const pamP, +tuple +pnm_backgroundtuple(struct pam * const pamP, + tuple ** const tuples) { +/*-------------------------------------------------------------------- + This function was copied from libpnm3.c's pnm_backgroundxel() and + modified to use tuples instead of xels. +----------------------------------------------------------------------*/ + tuple tuplePtr, bgtuple, ul, ur, ll, lr; + + /* Guess a good background value. */ + ul = tuples[0][0]; + ur = tuples[0][pamP->width-1]; + ll = tuples[pamP->height-1][0]; + lr = tuples[pamP->height-1][pamP->width-1]; + bgtuple = NULL; + + /* We first recognize three corners equal. If not, we look for any + two. If not, we just average all four. + */ + if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, ll)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, ur) && + pnm_tupleequal(pamP, ur, lr)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, ll) && + pnm_tupleequal(pamP, ll, lr)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ur, ll) && + pnm_tupleequal(pamP, ll, lr)) + tuplePtr = ur; + else if (pnm_tupleequal(pamP, ul, ur)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, ll)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ul, lr)) + tuplePtr = ul; + else if (pnm_tupleequal(pamP, ur, ll)) + tuplePtr = ur; + else if (pnm_tupleequal(pamP, ur, lr)) + tuplePtr = ur; + else if (pnm_tupleequal(pamP, ll, lr)) + tuplePtr = ll; + else { + /* Reimplement libpnm3.c's mean4() but for tuples. */ + unsigned int plane; + bgtuple = pnm_allocpamtuple(pamP); + for (plane = 0; plane < pamP->depth; ++plane) + bgtuple[plane] = (ul[plane] + ur[plane] + ll[plane] + lr[plane]) / 4; + } + if (!bgtuple) { + unsigned int plane; + bgtuple = pnm_allocpamtuple(pamP); + for (plane = 0; plane < pamP->depth; ++plane) + bgtuple[plane] = tuplePtr[plane]; + } + + return bgtuple; +} + + + /*============================================================================= pm_system() Standard Input feeder and Standard Output accepter functions. =============================================================================*/ diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c index ff75d0a1..793de6c5 100644 --- a/lib/libpbmfont.c +++ b/lib/libpbmfont.c @@ -1275,9 +1275,9 @@ skipCharacter(FILE * const fp) { static void -validateEncoding(const char ** const arg, - unsigned int * const codepointP, - bool * const badCodepointP) { +interpEncoding(const char ** const arg, + unsigned int * const codepointP, + bool * const badCodepointP) { /*---------------------------------------------------------------------------- With arg[] being the ENCODING statement from the font, return as *codepointP the codepoint that it indicates (code point is the character @@ -1315,12 +1315,29 @@ validateEncoding(const char ** const arg, - static void -processCharsLine(FILE * const fp, - const char ** const arg, - struct font * const fontP) { +readEncoding(FILE * const ifP, + unsigned int * const codepointP, + bool * const badCodepointP) { + + const char * arg[32]; + + expect(ifP, "ENCODING", arg); + + interpEncoding(arg, codepointP, badCodepointP); +} + + +static void +processChars(FILE * const fp, + const char ** const arg, + struct font * const fontP) { +/*---------------------------------------------------------------------------- + Process the CHARS block in a BDF font file, assuming the file is positioned + just after the CHARS line and 'arg' is the contents of that CHARS line. + Read the rest of the block and apply its contents to *fontP. +-----------------------------------------------------------------------------*/ unsigned int const nCharacters = atoi(arg[1]); unsigned int nCharsDone; @@ -1354,12 +1371,8 @@ processCharsLine(FILE * const fp, pm_error("no memory for font glyph for '%s' character", charName); - { - const char * arg[32]; - expect(fp, "ENCODING", arg); + readEncoding(fp, &codepoint, &badCodepoint); - validateEncoding(arg, &codepoint, &badCodepoint); - } if (badCodepoint) skipCharacter(fp); else { @@ -1389,7 +1402,7 @@ processCharsLine(FILE * const fp, const char * arg[32]; expect(fp, "ENDCHAR", arg); } - assert(codepoint < 256); /* Ensured by validateEncoding() */ + assert(codepoint < 256); /* Ensured by readEncoding() */ fontP->glyph[codepoint] = glyphP; } @@ -1401,19 +1414,26 @@ processCharsLine(FILE * const fp, static void -processFontLine(FILE * const fp, - const char * const line, - const char ** const arg, - struct font * const fontP, - bool * const endOfFontP) { +processBdfFontLine(FILE * const fp, + const char * const line, + const char ** const arg, + struct font * const fontP, + bool * const endOfFontP) { +/*---------------------------------------------------------------------------- + Process a nonblank line just read from a BDF font file. + This processing may involve reading more lines. +-----------------------------------------------------------------------------*/ *endOfFontP = FALSE; /* initial assumption */ + assert(arg[0] != NULL); /* Entry condition */ + if (streq(arg[0], "COMMENT")) { /* ignore */ } else if (streq(arg[0], "SIZE")) { /* ignore */ } else if (streq(arg[0], "STARTPROPERTIES")) { + /* Read off the properties and ignore them all */ unsigned int const propCount = atoi(arg[1]); unsigned int i; for (i = 0; i < propCount; ++i) { @@ -1431,8 +1451,11 @@ processFontLine(FILE * const fp, fontP->y = atoi(arg[4]); } else if (streq(arg[0], "ENDFONT")) { *endOfFontP = true; - } else if (!strcmp(arg[0], "CHARS")) - processCharsLine(fp, arg, fontP); + } else if (streq(arg[0], "CHARS")) { + processChars(fp, arg, fontP); + } else { + /* ignore */ + } } @@ -1440,18 +1463,17 @@ processFontLine(FILE * const fp, struct font * pbm_loadbdffont(const char * const name) { - FILE * fp; - char line[1024]; - const char * arg[32]; + FILE * ifP; struct font * fontP; + const char * wordList[32]; bool endOfFont; - fp = fopen(name, "rb"); - if (!fp) + ifP = fopen(name, "rb"); + if (!ifP) pm_error("Unable to open BDF font file name '%s'. errno=%d (%s)", name, errno, strerror(errno)); - expect(fp, "STARTFONT", arg); + expect(ifP, "STARTFONT", wordList); MALLOCVAR(fontP); if (fontP == NULL) @@ -1471,12 +1493,14 @@ pbm_loadbdffont(const char * const name) { endOfFont = FALSE; while (!endOfFont) { + char line[1024]; + const char * wordList[32]; int rc; - rc = readline(fp, line, arg); + rc = readline(ifP, line, wordList); if (rc < 0) pm_error("End of file before ENDFONT statement in BDF font file"); - processFontLine(fp, line, arg, fontP, &endOfFont); + processBdfFontLine(ifP, line, wordList, fontP, &endOfFont); } return fontP; } diff --git a/lib/libpgm1.c b/lib/libpgm1.c index 4d93e4be..64a43534 100644 --- a/lib/libpgm1.c +++ b/lib/libpgm1.c @@ -189,7 +189,7 @@ readRpgmRow(FILE * const fileP, asprintfN(&error, "Unable to allocate memory for row buffer " "for %u columns", cols); else { - ssize_t rc; + size_t rc; rc = fread(rowBuffer, 1, bytesPerRow, fileP); if (rc == 0) asprintfN(&error, "Error reading row. fread() errno=%d (%s)", diff --git a/lib/libpnm3.c b/lib/libpnm3.c index c41a2108..0426ebcb 100644 --- a/lib/libpnm3.c +++ b/lib/libpnm3.c @@ -45,7 +45,7 @@ mean4(int const format, break; default: - pm_error( "Invalid format passed to pnm_backgroundxel()"); + pm_error("Invalid format passed to pnm_backgroundxel()"); } return retval; } diff --git a/lib/libppmd.c b/lib/libppmd.c index 3f824302..deb47c0f 100644 --- a/lib/libppmd.c +++ b/lib/libppmd.c @@ -966,14 +966,25 @@ typedef struct int edge; } coord; -typedef struct fillobj { +typedef struct fillState { int n; + /* Number of elements in 'coords' */ int size; int curedge; int segstart; int ydir; int startydir; coord * coords; +} fillState; + +typedef struct fillobj { + + /* The only reason we have a struct fillState separate from + struct fillobj is that the drawproc interface is defined to + have drawing not modify the fillobj, i.e. it passed + const fillobj * to the drawing program. + */ + struct fillState * stateP; } fillobj; #define SOME 1000 @@ -984,16 +995,24 @@ struct fillobj * ppmd_fill_create(void) { fillobj * fillObjP; + struct fillState * stateP; MALLOCVAR(fillObjP); if (fillObjP == NULL) pm_error("out of memory allocating a fillhandle"); - fillObjP->n = 0; - fillObjP->size = SOME; - MALLOCARRAY(fillObjP->coords, fillObjP->size); - if (fillObjP->coords == NULL) + + MALLOCVAR(stateP); + if (stateP == NULL) + pm_error("out of memory allocating a fillhandle"); + + stateP->n = 0; + stateP->size = SOME; + MALLOCARRAY(stateP->coords, stateP->size); + if (stateP->coords == NULL) pm_error("out of memory allocating a fillhandle"); - fillObjP->curedge = 0; + stateP->curedge = 0; + + fillObjP->stateP = stateP; /* Turn off line clipping. */ /* UGGH! We must eliminate this global variable */ @@ -1021,14 +1040,95 @@ ppmd_fill_init(void) { void -ppmd_fill_destroy(struct fillobj * fillObjP) { +ppmd_fill_destroy(struct fillobj * const fillObjP) { - free(fillObjP->coords); + free(fillObjP->stateP->coords); + free(fillObjP->stateP); free(fillObjP); } +static void +addCoord(struct fillState * const stateP, + ppmd_point const point) { + + stateP->coords[stateP->n].point = point; + stateP->coords[stateP->n].edge = stateP->curedge; + + ++stateP->n; +} + + + +static void +startNewSegment(struct fillState * const stateP) { +/*---------------------------------------------------------------------------- + Close off the segment we're currently building and start a new one. +-----------------------------------------------------------------------------*/ + if (stateP->startydir != 0 && stateP->ydir != 0) { + /* There's stuff in the current segment. */ + if (stateP->startydir == stateP->ydir) { + /* Oops, first edge and last edge of current segment are the same. + Change all points in the first edge to be in the last. + */ + int const firstEdge = stateP->coords[stateP->segstart].edge; + int const lastEdge = stateP->coords[stateP->n - 1].edge; + coord * const segStartCoordP = &stateP->coords[stateP->segstart]; + coord * const segEndCoordP = &stateP->coords[stateP->n]; + + coord * fcP; + + for (fcP = segStartCoordP; + fcP < segEndCoordP && fcP->edge == firstEdge; + ++fcP) + fcP->edge = lastEdge; + } + } + /* And start new segment. */ + ++stateP->curedge; + stateP->segstart = stateP->n; + stateP->ydir = 0; + stateP->startydir = 0; +} + + + +static void +continueSegment(struct fillState * const stateP, + int const dy) { +/*---------------------------------------------------------------------------- + 'dy' is how much the current point is above the previous one. +-----------------------------------------------------------------------------*/ + if (dy != 0) { + if (stateP->ydir != 0 && stateP->ydir != dy) { + /* Direction changed. Insert a fake coord, old + position but new edge number. + */ + ++stateP->curedge; + addCoord(stateP, stateP->coords[stateP->n - 1].point); + } + stateP->ydir = dy; + if (stateP->startydir == 0) + stateP->startydir = dy; + } +} + + + + +/* ppmd_fill_drawprocp() is a drawproc that turns an outline drawing function + into a filled shape function. This is a somewhat off-label application of + a drawproc: A drawproc is intended just to draw a point. So e.g. you + might draw a circle with a fat brush by calling ppmd_circle with a drawproc + that draws a point as a 10-pixel disk. + + But ppmd_fill_drawproc() just draws a point the trivial way: as one pixel. + However, it tracks every point that is drawn in a form that a subsequent + ppmd_fill() call can use to to fill in the shape drawn, assuming it turns + out to be a closed shape. +*/ + void ppmd_fill_drawprocp(pixel ** const pixels, unsigned int const cols, @@ -1037,85 +1137,39 @@ ppmd_fill_drawprocp(pixel ** const pixels, ppmd_point const p, const void * const clientdata) { - fillobj * fh; - coord * cp; - - fh = (fillobj*) clientdata; + const fillobj * const fillObjP = clientdata; + struct fillState * const stateP = fillObjP->stateP; - /* If these are the same coords we saved last time, don't bother. */ - if (fh->n > 0) { - ppmd_point const lastPoint = fh->coords[fh->n - 1].point; - if (pointsEqual(p, lastPoint)) - return; - } - - /* Ok, these are new; make room for two more coords. */ - if (fh->n + 1 >= fh->size) { - fh->size += SOME; - REALLOCARRAY(fh->coords, fh->size); - if (fh->coords == NULL) + /* Make room for two more coords, the max we might add. */ + if (stateP->n + 2 > stateP->size) { + stateP->size += SOME; + REALLOCARRAY(stateP->coords, stateP->size); + if (stateP->coords == NULL) pm_error("out of memory enlarging a fillhandle"); } - /* Check for extremum and set the edge number. */ - if (fh->n == 0) { + if (stateP->n == 0) { /* Start first segment. */ - fh->segstart = fh->n; - fh->ydir = 0; - fh->startydir = 0; + stateP->segstart = stateP->n; + stateP->ydir = 0; + stateP->startydir = 0; + addCoord(stateP, p); } else { - coord * const ocp = &(fh->coords[fh->n - 1]); - int dx, dy; - - dx = p.x - ocp->point.x; - dy = p.y - ocp->point.y; - if (dx < -1 || dx > 1 || dy < -1 || dy > 1) { - /* Segment break. Close off old one. */ - if (fh->startydir != 0 && fh->ydir != 0) - if (fh->startydir == fh->ydir) { - /* Oops, first edge and last edge are the same. - Renumber the first edge in the old segment. - */ - const coord * const fcpLast= &(fh->coords[fh->n -1]); - coord * fcp; - - int oldedge; - - fcp = &(fh->coords[fh->segstart]); - oldedge = fcp->edge; - for (; fcp <= fcpLast && fcp->edge == oldedge ; ++fcp) - fcp->edge = ocp->edge; - } - /* And start new segment. */ - ++fh->curedge; - fh->segstart = fh->n; - fh->ydir = 0; - fh->startydir = 0; + ppmd_point const prevPoint = stateP->coords[stateP->n - 1].point; + int const dx = p.x - prevPoint.x; + int const dy = p.y - prevPoint.y; + + if (dx == 0 && dy == 0) { + /* These are the same coords we had last time; don't bother */ } else { - /* Segment continues. */ - if (dy != 0) { - if (fh->ydir != 0 && fh->ydir != dy) { - /* Direction changed. Insert a fake coord, old - position but new edge number. - */ - ++fh->curedge; - cp = &fh->coords[fh->n]; - cp->point = ocp->point; - cp->edge = fh->curedge; - ++fh->n; - } - fh->ydir = dy; - if (fh->startydir == 0) - fh->startydir = dy; - } + if (abs(dx) > 1 || abs(dy) > 1) + startNewSegment(stateP); + else + continueSegment(stateP, dy); + + addCoord(stateP, p); } } - - /* Save this coord. */ - cp = &fh->coords[fh->n]; - cp->point = p; - cp->edge = fh->curedge; - ++fh->n; } @@ -1173,10 +1227,12 @@ ppmd_fill(pixel ** const pixels, int const cols, int const rows, pixval const maxval, - struct fillobj * const fh, + struct fillobj * const fillObjP, ppmd_drawproc drawProc, const void * const clientdata) { + struct fillState * const fh = fillObjP->stateP; + int pedge; int i, edge, lx, rx, py; coord * cp; diff --git a/lib/pam.h b/lib/pam.h index c28c5c2c..3d1f8532 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -483,6 +483,10 @@ pnm_YCbCr_to_rgbtuple(const struct pam * const pamP, ((tuple)[PAM_RED_PLANE] == (tuple)[PAM_GRN_PLANE] && \ (tuple)[PAM_RED_PLANE] == (tuple)[PAM_BLU_PLANE]) +tuple +pnm_backgroundtuple(struct pam * const pamP, + tuple ** const tuples); + /*---------------------------------------------------------------------------- These are meant for passing to pm_system() as Standard Input feeder and Standard Output accepter. diff --git a/lib/pnm.h b/lib/pnm.h index e4bd34a8..de5f1d91 100644 --- a/lib/pnm.h +++ b/lib/pnm.h @@ -20,6 +20,9 @@ typedef pixval xelval; #define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL #define PNM_MAXMAXVAL PPM_MAXMAXVAL #define PNM_GET1(x) PPM_GETB(x) +#define PNM_GETR(x) PPM_GETR(x) +#define PNM_GETG(x) PPM_GETG(x) +#define PNM_GETB(x) PPM_GETB(x) #define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,0,0,v) #define PNM_ASSIGN(x,r,g,b) PPM_ASSIGN(x,r,g,b) #define PNM_EQUAL(x,y) PPM_EQUAL(x,y) diff --git a/lib/ppm.h b/lib/ppm.h index 0695295f..57b6c04a 100644 --- a/lib/ppm.h +++ b/lib/ppm.h @@ -250,6 +250,12 @@ ppm_hsv_from_color(pixel const color, pixval const maxval); static __inline pixval +ppm_luminosity(pixel const p) { + + return (pixval)(PPM_LUMIN(p) + 0.5); +} + +static __inline pixval ppm_colorvalue(pixel const p) { /*---------------------------------------------------------------------------- The color value (V is HSV) as a pixval diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h index 2175fc5d..e5e138e1 100644 --- a/lib/util/shhopt.h +++ b/lib/util/shhopt.h @@ -1,4 +1,5 @@ -/*============================================================================= +#if 0 +============================================================================= HERE IS AN EXAMPLE OF THE USE OF SHHOPT: @@ -6,8 +7,9 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT: int main ( int argc, char **argv ) { - // initial values here are just to demonstrate what gets set and - // what doesn't by the code below. + /* initial values here are just to demonstrate what gets set and + what doesn't by the code below. + */ int help_flag = 7; unsigned int help_spec =7; unsigned int height_spec =7; @@ -67,8 +69,8 @@ Now run this program with something like myprog -v /etc/passwd -name=Bryan --hei=4 -========================================================================*/ - +======================================================================== +#endif /* 0 */ #ifndef SHHOPT_H #define SHHOPT_H diff --git a/netpbm.c b/netpbm.c index c47cb37e..7f3e7c96 100644 --- a/netpbm.c +++ b/netpbm.c @@ -29,13 +29,17 @@ int main(int argc, char *argv[]) { - char* cp; + const char * cp; if (strcmp(pm_arg0toprogname(argv[0]), "netpbm") == 0) { ++argv; --argc; if (argc < 1 || !*argv) { - fprintf(stderr, "Usage: netpbm netpbm_program_name [args ...]\n"); + fprintf(stderr, + "When you invoke this program by the name 'netpbm', " + "You must supply at least one argument: the name of " + "the Netpbm program to run, e.g. " + "'netpbm pamfile /tmp/myfile.ppm'\n"); exit(1); } } diff --git a/version.mk b/version.mk index 4cb726b4..ebeabbf3 100644 --- a/version.mk +++ b/version.mk @@ -1,4 +1,4 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 49 -NETPBM_POINT_RELEASE = 5 +NETPBM_MINOR_RELEASE = 50 +NETPBM_POINT_RELEASE = 0 |