static void doPartialBlockTop(const struct pam * const inpamP, bit ** const inrow, const bit * const blockPartial[16], unsigned int const topOfFullBlock, uint16_t ** const outplane) { if (topOfFullBlock > 0) { unsigned int colChar, row; unsigned int pad = 16 - topOfFullBlock; for (colChar=0; colChar < pbm_packed_bytes(inpamP->width); ++colChar) inrow[0][colChar] = 0x00; for (row = 0; row < topOfFullBlock; ++row){ pbm_readpbmrow_packed(inpamP->file, inrow[row+pad], inpamP->width, inpamP->format); if (inpamP->width % 8 > 0){ /* Clear partial byte at end of input row */ int const lastByte = pbm_packed_bytes(inpamP->width) -1; inrow[row+pad][lastByte] >>= (8 - inpamP->width % 8); inrow[row+pad][lastByte] <<= (8 - inpamP->width % 8); } }
bit pbm_backgroundbitrow(unsigned const char * const packedBits, unsigned int const cols, unsigned int const offset) { /*---------------------------------------------------------------------------- PBM version of pnm_backgroundxelrow() with additional offset parameter. When offset == 0, produces the same return value as does pnm_backgroundxelrow(promoted_bitrow, cols, ...) -----------------------------------------------------------------------------*/ const unsigned char * const row = &packedBits[offset/8]; unsigned int const rs = offset % 8; unsigned int const last = pbm_packed_bytes(cols + rs) - 1; unsigned int retval; bool const firstbit = (row[0] >> (7-rs)) & 0x01; bool const lastbit = (row[last] >> (7- (cols+rs-1)%8)) & 0x01; if (firstbit == lastbit) retval = firstbit; else { if (bitpop(row, cols, rs) >= cols/2) retval = PBM_BLACK; else retval = PBM_WHITE; } return retval; }
static void padFillBitrow(unsigned char * const destBitrow, unsigned char const padColor, unsigned int const cols, unsigned int const offset) { /*---------------------------------------------------------------------------- Fill destBitrow, starting at offset, with padColor. padColor is a byte -- 0x00 or 0xff -- not a single bit. -----------------------------------------------------------------------------*/ unsigned char * const dest = &destBitrow[offset/8]; unsigned int const rs = offset % 8; unsigned int const trs = (cols + rs) % 8; unsigned int const colByteCnt = pbm_packed_bytes(cols + rs); unsigned int const last = colByteCnt - 1; unsigned char const origHead = dest[0]; unsigned char const origEnd = dest[last]; unsigned int i; assert(colByteCnt > 0); for (i = 0; i < colByteCnt; ++i) dest[i] = padColor; if (rs > 0) dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); if (trs > 0) dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); }
static int bitpop(const unsigned char * const packedRow, unsigned int const cols, unsigned int const offset) { /*---------------------------------------------------------------------------- Return the number of 1 bits in 'packedRow', ignoring 0 to 7 bits at the row start (= on the left edge), indicated by offset. -----------------------------------------------------------------------------*/ unsigned int const fullLength = cols + offset; unsigned int sum; if (fullLength <= 8) { /* All bits are in a single byte */ sum = bitpop8((packedRow[0] << offset ) & (0xff << (8 - cols))); } else { unsigned int const colByteCnt = pbm_packed_bytes(fullLength); unsigned int const fullByteCnt = fullLength/8; unsigned int i; /* First byte, whether it is full or not */ sum = bitpop8(packedRow[0] << offset ); /* Second byte to last full byte */ for (i = 1; i < fullByteCnt; ++i) sum += bitpop8(packedRow[i]); /* Partial byte at the right end */ if (colByteCnt > fullByteCnt) sum += bitpop8(packedRow[i] >> (8 - fullLength%8)); } return sum; }
static void makeBlackPBMRow(unsigned char * const bitrow, unsigned int const cols) { unsigned int const colByteCnt = pbm_packed_bytes(cols); unsigned int i; for (i = 0; i < colByteCnt; ++i) bitrow[i] = PBM_BLACK * 0xff; if (PBM_BLACK != 0 && cols % 8 > 0) bitrow[colByteCnt-1] <<= (8 - cols % 8); }
static void fillRowPBM(unsigned char * const bitrow, unsigned int const cols, unsigned int const blackWhite) { /*---------------------------------------------------------------------------- Fill the packed PBM row buffer bitrow[] with 'cols' columns of black or white: black if 'blackWhite' is 1; white if it is '0'. 'blackWhite' cannot be anything else. -----------------------------------------------------------------------------*/ unsigned int const colChars = pbm_packed_bytes(cols); unsigned int i; assert(blackWhite == 0 || blackWhite == 1); for (i = 0; i < colChars; ++i) bitrow[i] = blackWhite * 0xff; if (cols % 8 > 0) bitrow[colChars-1] <<= 8 - cols % 8; }
static void copyBitrow(const unsigned char * const source, unsigned char * const destBitrow, unsigned int const cols, unsigned int const offset) { /*---------------------------------------------------------------------------- Copy from source to destBitrow, without shifting. Preserve surrounding image data. -----------------------------------------------------------------------------*/ unsigned char * const dest = & destBitrow[ offset/8 ]; /* Copy destination, with leading full bytes ignored. */ unsigned int const rs = offset % 8; /* The "little offset", as measured from start of dest. Source is already shifted by this value. */ unsigned int const trs = (cols + rs) % 8; /* The number of partial bits in the final char. */ unsigned int const colByteCnt = pbm_packed_bytes(cols + rs); /* # bytes to process, including partial ones on both ends. */ unsigned int const last = colByteCnt - 1; unsigned char const origHead = dest[0]; unsigned char const origEnd = dest[last]; unsigned int i; assert(colByteCnt >= 1); for (i = 0; i < colByteCnt; ++i) dest[i] = source[i]; if (rs > 0) dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs); if (trs > 0) dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs); }
int main(int argc, char* argv[]) { FILE* ifP; int rows, cols; int format; unsigned int row, idx, len; unsigned int h, v; unsigned char *bytes, *cprbytes; struct cmdlineInfo cmdline; pbm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFileName); pbm_readpbminit(ifP, &cols, &rows, &format); bytes = malloc(24*pbm_packed_bytes(cols)+2); cprbytes = malloc(2*24*pbm_packed_bytes(cols)); if (bytes == NULL || cprbytes == NULL) pm_error("Cannot allocate memory"); h = v = 3600/cmdline.resolution; /* Set raster graphic mode. */ printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1); /* Set line spacing in units of 1/360 inches. */ printf("%c%c%c", esc, '+', 24*h/10); /* Write out raster stripes 24 rows high. */ for (row = 0; row < rows; row += 24) { unsigned int const linesThisStripe = (rows-row<24) ? rows%24 : 24; printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, v, h, linesThisStripe, cols%256, cols/256); /* Read pbm rows, each padded to full byte */ for (idx = 0; idx < 24 && row+idx < rows; ++idx) pbm_readpbmrow_packed(ifP,bytes+idx*pbm_packed_bytes(cols), cols,format); /* Write raster data. */ if (cmdline.compress != 0) { /* compressed */ len = enc_epson_rle(linesThisStripe * pbm_packed_bytes(cols), bytes, cprbytes); fwrite(cprbytes,len,1,stdout); } else /* uncompressed */ fwrite(bytes, pbm_packed_bytes(cols), linesThisStripe, stdout); if (rows-row >= 24) putchar('\n'); } free(bytes); free(cprbytes); pm_close(ifP); /* Reset printer. */ printf("%c%c", esc, '@'); return 0; }
static void concatenateTopBottomPbm(FILE * const ofP, unsigned int const nfiles, int const newcols, int const newrows, enum justification const justification, struct imgInfo const img[], enum backcolor const backcolor) { unsigned char * const outrow = pbm_allocrow_packed(newcols); /* Like the left-right PBM case, all padding and image data goes directly into outrow. There is no proberow. */ unsigned char background, backgroundPrev; /* 0x00 means white; 0xff means black */ unsigned int padleft; bool backChange; /* Background color is different from that of the previous input image. */ unsigned int i; unsigned int row, startRow; outrow[pbm_packed_bytes(newcols)-1] = 0x00; switch (backcolor){ case BACK_AUTO: /* do nothing */ break; case BACK_BLACK: background = 0xff; break; case BACK_WHITE: background = 0x00; break; } for (i = 0; i < nfiles; ++i) { if (img[i].cols == newcols) { /* No padding */ startRow = 0; backChange = FALSE; padleft = 0; outrow[pbm_packed_bytes(newcols)-1] = 0x00; } else { /* Determine amount of padding and color */ switch (justification) { case JUST_MIN: padleft = 0; break; case JUST_MAX: padleft = newcols - img[i].cols; break; case JUST_CENTER: padleft = (newcols - img[i].cols) / 2; break; } switch (backcolor) { case BACK_AUTO: { bit bgBit; startRow = 1; pbm_readpbmrow_bitoffset(img[i].ifP, outrow, img[i].cols, img[i].format, padleft); bgBit = pbm_backgroundbitrow(outrow, img[i].cols, padleft); background = bgBit == PBM_BLACK ? 0xff : 0x00; backChange = (i == 0 || background != backgroundPrev); } break; case BACK_WHITE: case BACK_BLACK: startRow = 0; backChange = (i==0); break; } if (backChange || (i > 0 && img[i-1].cols > img[i].cols)) { unsigned int const padright = newcols - padleft - img[i].cols; if (padleft > 0) padFillBitrow(outrow, background, padleft, 0); if (padright > 0) padFillBitrow(outrow, background, padright, padleft + img[i].cols); } } if (startRow == 1) /* Top row already read for auto background color determination. Write it out. */ pbm_writepbmrow_packed(ofP, outrow, newcols, 0); for (row = startRow; row < img[i].rows; ++row) { pbm_readpbmrow_bitoffset(img[i].ifP, outrow, img[i].cols, img[i].format, padleft); pbm_writepbmrow_packed(ofP, outrow, newcols, 0); } backgroundPrev = background; } pbm_freerow_packed(outrow); }
static void concatenateLeftRightPbm(FILE * const ofP, unsigned int const nfiles, unsigned int const newcols, unsigned int const newrows, enum justification const justification, struct imgInfo const img[], enum backcolor const backcolor) { unsigned char * const outrow = pbm_allocrow_packed(newcols); /* We use just one outrow. All padding and image data (with the exeption of following img2.proberow) goes directly into this packed PBM row. */ struct imgInfoPbm2 * img2; /* malloc'ed array, one element per image. Shadows img[] */ unsigned int row; getPbmImageInfo(img, nfiles, newrows, justification, backcolor, &img2); outrow[pbm_packed_bytes(newcols)-1] = 0x00; for (row = 0; row < newrows; ++row) { unsigned int i; for (i = 0; i < nfiles; ++i) { if ((row == 0 && img2[i].padtop > 0) || row == img2[i].padtop + img[i].rows) { /* This row begins a run of padding, either above or below file 'i', so set 'outrow' to padding. */ padFillBitrow(outrow, img2[i].background, img[i].cols, img2[i].offset); } if (row == img2[i].padtop && img2[i].proberow != NULL) { /* Top row has been read to proberow[] to determine background. Copy it to outrow[]. */ copyBitrow(img2[i].proberow, outrow, img[i].cols, img2[i].offset); } else if (row >= img2[i].padtop && row < img2[i].padtop + img[i].rows) { pbm_readpbmrow_bitoffset( img[i].ifP, outrow, img[i].cols, img[i].format, img2[i].offset); } else { /* It's a row of padding, so outrow[] is already set appropriately. */ } } pbm_writepbmrow_packed(ofP, outrow, newcols, 0); } destroyPbmImg2(img2, nfiles); pbm_freerow_packed(outrow); }
static void extractRowsPBM(const struct pam * const inpamP, const struct pam * const outpamP, int const leftcol, int const rightcol, int const toprow, int const bottomrow) { unsigned char * bitrow; int readOffset, writeOffset; int row; unsigned int totalWidth; assert(leftcol <= rightcol); assert(toprow <= bottomrow); if (leftcol > 0) { totalWidth = MAX(rightcol+1, inpamP->width) + 7; if (totalWidth > INT_MAX) /* Prevent overflows in pbm_allocrow_packed() */ pm_error("Specified right edge is too far " "from the right end of input image"); readOffset = 0; writeOffset = leftcol; } else { totalWidth = -leftcol + MAX(rightcol+1, inpamP->width); if (totalWidth > INT_MAX) pm_error("Specified left/right edge is too far " "from the left/right end of input image"); readOffset = -leftcol; writeOffset = 0; } bitrow = pbm_allocrow_packed(totalWidth); if (toprow < 0 || leftcol < 0 || rightcol >= inpamP->width){ makeBlackPBMRow(bitrow, totalWidth); if (toprow < 0) { int row; for (row=0; row < 0 - toprow; ++row) pbm_writepbmrow_packed(outpamP->file, bitrow, outpamP->width, 0); } } for (row = 0; row < inpamP->height; ++row){ if (row >= toprow && row <= bottomrow) { pbm_readpbmrow_bitoffset(inpamP->file, bitrow, inpamP->width, inpamP->format, readOffset); pbm_writepbmrow_bitoffset(outpamP->file, bitrow, outpamP->width, 0, writeOffset); if (rightcol >= inpamP->width) /* repair right padding */ bitrow[writeOffset/8 + pbm_packed_bytes(outpamP->width) - 1] = 0xff * PBM_BLACK; } else pnm_readpamrow(inpamP, NULL); /* read and discard */ } if (bottomrow - (inpamP->height-1) > 0) { int row; makeBlackPBMRow(bitrow, outpamP->width); for (row = 0; row < bottomrow - (inpamP->height-1); ++row) pbm_writepbmrow_packed(outpamP->file, bitrow, outpamP->width, 0); } pbm_freerow_packed(bitrow); }
int main(int argc, const char * argv[]) { FILE * ifP; int rows, cols; int format; unsigned int row; unsigned int idx; unsigned int outColByteCt; unsigned int stripeByteCt; unsigned int hres, vres; unsigned char * inBuff; unsigned char * bitrow[256]; unsigned char * compressedData; struct CmdlineInfo cmdline; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFileName); pbm_readpbminit(ifP, &cols, &rows, &format); if (cols / 256 > 127) /* Limit in official Epson manual */ pm_error("Image width is too large"); outColByteCt = pbm_packed_bytes(cols); stripeByteCt = cmdline.stripeHeight * outColByteCt; MALLOCARRAY(inBuff, stripeByteCt); if (inBuff == NULL) pm_error("Out of memory trying to create input buffer of %u bytes", stripeByteCt); if (cmdline.compress != 0) pm_rlenc_allocoutbuf(&compressedData, stripeByteCt, PM_RLE_PACKBITS); else compressedData = NULL; for (idx = 0; idx <= cmdline.stripeHeight; ++idx) bitrow[idx]= &inBuff[idx * outColByteCt]; hres = vres = 3600 / cmdline.resolution; /* Possible values for hres, vres: 20, 10, 5 */ if (!cmdline.raw) writeSetup(hres); /* Write out raster stripes */ for (row = 0; row < rows; row += cmdline.stripeHeight ) { unsigned int const rowsThisStripe = MIN(rows - row, cmdline.stripeHeight); unsigned int const outCols = outColByteCt * 8; if (rowsThisStripe > 0) { unsigned int idx; printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, vres, hres, cmdline.stripeHeight, outCols % 256, outCols / 256); /* Read pbm rows, each padded to full byte */ for (idx = 0; idx < rowsThisStripe; ++idx) { pbm_readpbmrow_packed (ifP, bitrow[idx], cols, format); pbm_cleanrowend_packed(bitrow[idx], cols); } /* If at bottom pad with empty rows up to stripe height */ if (rowsThisStripe < cmdline.stripeHeight ) memset(bitrow[rowsThisStripe], 0, (cmdline.stripeHeight - rowsThisStripe) * outColByteCt); /* Write raster data */ if (cmdline.compress != 0) { /* compressed */ size_t compressedDataCt; pm_rlenc_compressbyte(inBuff, compressedData, PM_RLE_PACKBITS, stripeByteCt, &compressedDataCt); fwrite(compressedData, compressedDataCt, 1, stdout); } else /* uncompressed */ fwrite(inBuff, stripeByteCt, 1, stdout); /* Emit newline to print the stripe */ putchar('\n'); } } free(inBuff); free(compressedData); pm_close(ifP); /* Form feed */ if (cmdline.formfeed) putchar('\f'); if (!cmdline.raw) { /* Reset printer. a*/ printf("%c%c", esc, '@'); } return 0; }