/*************************************************************************** image.c - code for reading and parsing a pbm, pgm or ppm format file also, rgb -> cymk conversion and black separation ------------------- begin : Thu Jan 13 2000 copyright : (C) 2000 by pnm2ppa project email : ***************************************************************************/ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #define __IMAGE_C__ #include "ppa_syslog.h" #include "global.h" #include "defaults.h" #include "debug.h" #include "image.h" #include "gamma.h" #include "dither.h" #include "lang.h" #include int initImage (image_t *image, FILE *fptr) { char line[1024]; int colors = 0; int k, k1; image->fptr = fptr; image->version = none; image->blackCurLine = 0; image->colorCurLine = 0; image->bufferCurLine = 0; image->unread = 0; image->colors = 0; gCalibrate = false; gByteCount = 3; if (gGammaMode) { /* produce color calibration page, uses the default papersize */ image->version = P6_PPMRAW; image->width = gWidth; image->height = gHeight; g300 = false; } else { if (fgets (line, 1024, fptr) == NULL) return 0; line[strlen (line) - 1] = 0; /* we have to handle the six pnm formats . */ if (!strcmp (line, "P1")) { DPRINTF("input format is P1: pbm (ascii)\n"); image->version = P1_PBM; if (!gPixmapMode) { gColorMode = false ; gByteCount = 1; } else gFastMode = true; strcpy(gFormat,"BitMap"); } if (!strcmp (line, "P2")) { DPRINTF("input format is P2: pgm (ascii)\n"); image->version = P2_PGM; if (!gPixmapMode) { gColorMode = false ; gByteCount = 1; } else gFastMode = true; strcpy(gFormat,"GreyMap"); } if (!strcmp (line, "P3")) { DPRINTF("input format is P3: ppm (ascii)\n"); image->version = P3_PPM; strcpy(gFormat,"PixMap"); } if (!strcmp (line, "P4")) { DPRINTF("input format is P4: pbm (binary)\n"); image->version = P4_PBMRAW; if (!gPixmapMode) { gColorMode = false ; gByteCount = 1; } else gFastMode = true; strcpy(gFormat,"BitMap"); } if (!strcmp (line, "P5")) { DPRINTF("input format is P5: pgm (binary)\n"); image->version = P5_PGMRAW; if (!gPixmapMode) { gColorMode = false ; gByteCount = 1; } else gFastMode = true; strcpy(gFormat,"GreyMap"); } if (!strcmp (line, "P6")) { DPRINTF("input format is P6: ppm (binary)\n"); image->version = P6_PPMRAW; strcpy(gFormat,"PixMap"); } if (image->version == none) { DPRINTF("input format is not recognized as PNM\n"); snprintf(syslog_message,message_size,"initImage(): %s", gMessages[E_IM_BADFORMAT] ); wrap_syslog (LOG_CRIT,"%s",syslog_message); return 0; } /* initialize switch that is used to terminate further printing * if EOF is detected while reading the input image */ gTerminate = false ; do { if (fgets (line, 1024, fptr) == NULL) return 0; line[strlen (line) - 1] = 0; if (!strcmp (line, "# pnm2ppa calibration image")){ gCalibrate = true; g300 = false; DPRINTF("initImage(): Calibration Page input detected!\n"); } } while (line[0] == '#'); if (2 != sscanf (line, "%d %d", &image->width, &image->height)) return 0; /* use the image dimensions read from the pnm innput file to * set gWidth and gHeight, and test that these are accepatable * for the printer model * this will mean that any user input or default papersizes * will only be uses for calibration or gamma correction output * which is not generated by pnm imput */ /* if we treat the input as 300dpi, double image dimensions */ if(g300) { DPRINTF("300dpi input mode\n"); image->width *= 2; image->height *= 2; } gWidth = image->width ; gHeight = image->height ; /* test that papersize is acceptable */ if ( (gWidth > gmaxWidth ) || (gWidth < gminWidth ) || (gHeight > gmaxHeight ) || (gHeight < gminHeight ) ) { snprintf(syslog_message,message_size,"initImage(): %s %dx%d @600dpi\n", gMessages[E_IM_BADPAPERSIZE], gWidth, gHeight); wrap_syslog (LOG_CRIT,"%s",syslog_message); return 0; } switch(image->version) { case P2_PGM: case P3_PPM: case P5_PGMRAW: case P6_PPMRAW: if (fgets (line, 1024, fptr) == NULL) return 0; if (1 != sscanf (line, "%d", &colors)) return 0; /* colors should be 255, as the code is currently implemented */ if (colors != 255) { snprintf(syslog_message,message_size,"initImage(): %s %d\n", gMessages[E_IM_BADMAXVALUE], colors); wrap_syslog (LOG_CRIT,"%s",syslog_message); return 0; } break; case P1_PBM: case P4_PBMRAW: colors = 1; break; case none: default: snprintf(syslog_message,message_size,"initImage(): %s", gMessages[E_IM_BADFORMAT]); wrap_syslog (LOG_CRIT,"%s",syslog_message); return 0; break; } image->colors = colors ; DPRINTF( "initImage: width=%d, height=%d, colors = %d," " ByteCount = %d\n", image->width, image->height, image->colors, gByteCount); } /* initialize buffer */ image->bufsize = 8192; image->bufpos = 0; image->buflines = 0; image->buftype = empty; for (k = 0; k < gMaxPass; k++) { image->buffer[k] = malloc (image->bufsize); if (!image->buffer[k]) { for (k1 = 0; k1 < k; k1++) free (image->buffer[k1]); return 0; } } return 1; } int remallocBuffer (image_t *image, size_t bytes) { int linesize; unsigned char *newbuf; /* size_t newsize; */ int newsize ; int k; if (image->buftype == bitmap) linesize = (image->width + 7) / 8; else if (image->buftype == color) linesize = (image->width / 2 + 7) / 8 * 3; else linesize = 0; /* if space is adequate, do nothing */ if (image->bufpos + image->buflines * linesize + bytes < image->bufsize) return 0; /* if there's an offset, move the image at buffer[k][bufpos] to the * the beginning of the buffer, and reset the offset to 0. */ if (image->bufpos != 0) { DPRINTF("Moving image[k][bufpos] to image[k][0]\n"); for (k = 0; k < gMaxPass; k++) memmove (image->buffer[k], &image->buffer[k][image->bufpos], image->buflines * linesize); image->bufpos = 0; if (image->bufpos + image->buflines * linesize + bytes < image->bufsize) return 0; } /* calculate the number of bits and add one to double the needed memory size.*/ newsize = pow(2, (int)((log(image->buflines * linesize + bytes)/log(2))+1)); DPRINTF("resizing memory from %d bytes to %d bytes\n", image->bufsize, newsize); for (k=0; k < gMaxPass; k++) { newbuf = malloc(newsize); if ( ! newbuf ) { snprintf(syslog_message,message_size, "remallocBuffer(): %s", gMessages[E_BADMALLOC]); wrap_syslog (LOG_CRIT,"%s",syslog_message); exit (1); } memcpy(newbuf, image->buffer[k], image->buflines * linesize); free(image->buffer[k]); image->buffer[k] = newbuf; } image->bufsize = newsize; return 0; } BOOLEAN convert_black_data (image_t *image, unsigned char *line, unsigned char *black, BOOLEAN line_is_nonblank) { /* Lets document this - please correct anything incorrect here! (duncan, Feb 12 2000, updated July 2000) - The entries in line[] are written by one of the functions FS_Color_Dither(), HT_Color_Dither(), HT_Black_Dither() in dither.c - The entries in line[] represent the ( image->width ) pixels on a line. Each pixel is represented by three consecutive entries (line[i] , line [i + 1] ,line [i + 2] ) , where i = 0, 3, 6 , ... , < (image->width * 3) The three entries correspond to Cyan, Magenta, and Yellow ink. - entries in line[] take values 1, 2,...,(gMaxPass + 1) , 0 or 255, where gMaxPass is the maximum number of passes of the color print head. * gMaxPass is only used in Color mode (gColorMode=true) * In normal color mode (gEcomode=false, gFastmode=false) gMaxPass = 4 * In Economy mode (gEcoMode = true) gMaxPass = 1 * In Fast Color mode (gFastMode = true) gMaxPass = 1 - values line [] = 1, 2, ..., gMaxPass correspond to gMaxPass, gMaxPass-1, ..., 1 drops of ink of a given color (C, M,or Y). - the value line [] = 0 also means gMaxPass drops of ink - the value line [] > gMaxPass (usually = gMaxPass+1 or 255) means 0 drops of color ink. - an entry (0, 0, 0) corresponds to a black drop, which can either be a drop of black ink, or gMaxPass drops each of C,M and Y ink, If this is implemented with a drop of black ink, convert_black_data() changes (0,0,0) to (255,255,255), so it won't get repeated using colored ink by the following call to convert_color_data(). - whitespace is coded by (255,255,255). - triplication of bytes is switched off when gByteCount == 1 Color data is only printed on even lines. Black data is printed on even and odd lines, except in Black-and-White Economy mode (gColorMode=false, gEcoMode=true), when only even lines of black are printed. If the odd line is not printed, its black data is added to the next even line. */ /* FEB 2002 (duncan) try to clean this up for pictures vs. text. identify regions of black surrounded by white space as text ONLY if they are less that "textwidth" dots wide. Otherwise, they are treated as part of an image, and will try to use both ink types, if any color was found on the line. */ int i, j, k; unsigned char mask = 0x80; unsigned char *pos = black; int blackness ; BOOLEAN use_color = false; BOOLEAN has_black = false; /* max_text_width is arbitary: make it configurable? textwidth = maximum width of a black region that is printed only with black ink when color is present on the line, on the assumption it is text, not image. */ const int max_text_width = 8; /* color dots at 300dpi */ unsigned char curr_stuff= 0 ;/* 0 = white, 1 = not white (current pixel)*/ unsigned char remove_black = 1, have_color = 0; unsigned char first_black=0 , second_black=0 ; /*controls black ink density*/ blackness = gBlackness ; j = image->bufferCurLine ; /* black ink quantity control (duncan) */ /* this should be extended to control color ink printing of "black" pixels */ if ( blackness == 1 ) { /* one black ink drop, on even lines only */ if ( (j % 2) ) { first_black = 0 ; /* odd lines */ second_black = 0; } else { first_black = 0 ; /* even lines */ second_black = 1 ; } } else if ( blackness == 2 ) /* two black ink drops */ { if ( (j % 2) ) { first_black = 1 ; /* odd lines */ second_black = 0; } else { #if 0 first_black = 0 ; /* even lines */ second_black = 1 ; #endif /* treat odd and even lines the same, to avoid artifacts?*/ first_black = 1 ; /* even lines */ second_black = 0 ; } } else if ( blackness == 3 ) /* two drops on even lines, one on odd lines */ { first_black = 1; if ( j % 2 ) second_black = 0; /* odd lines */ else second_black = 1 ; /* even lines */ } else if (blackness == 4 ) /* two black ink drops, all lines */ { first_black = 1 ; second_black = 1 ; } else /* no black ink */ { first_black = 0 ; second_black = 0 ; } /* In Pixmap mode black ink cartridge is disabled (this should be renamed to (say) NoBlack mode ) */ if (gPixmapMode || !line_is_nonblank) memset (pos, 0x00, (image->width + 7) / 8); else { if (gColorMode && gByteCount ==3 ) /* beginning of code that assumes gByteCount == 3 */ { /* This is the most delicate situation, we (may) have both color * and black on the same line; * we want to: * - Print half of the black ink to avoid "wet" effect * - get a good "covering" black in pixmaps (neither too dense, * neither too sparse...) */ /* check for presence of color in the line */ for (i = 0; i < image->width * 3; i += 6) { if (line[i] != line[i+1] || line[i+1] != line[i+2]) { use_color = true; break; } } curr_stuff=0; for (i = 0; i < image->width * 3; i += 6) { if (mask == 0x80) *pos = 0; if (curr_stuff==0) { int black_width = 0; /* this is a test to see if the current non-whitespace * pixel is part of a region of black delineated by * whitespace (have_color=0) * or is in a region containing some * colored pixels (in this case have_color=1) */ if (! (line[i]>gMaxPass && /*if line is not white*/ line[i+1]> gMaxPass && line[i+2]>gMaxPass) ) { have_color=0; k=i; while ( (!(have_color)) && (kwidth * 3) && (! (line[k]>gMaxPass && line[k+1]> gMaxPass && line[k+2]>gMaxPass) )) { if ((line[k] && line[k]<=gMaxPass) || (line[k+1] && line[k+1]<=gMaxPass) || (line[k+2] && line[k+2]<=gMaxPass)) have_color=1; k+=6; black_width++; /* treat wide black regions as having color */ if (use_color && black_width > max_text_width) have_color=1; } if (have_color) remove_black=0; else remove_black=1; curr_stuff=1; } } if ( !(line[i] || line[i + 1] || line[i + 2])) { /* pixel is black; there are two possible * behaviors, depending on whether remove_black * is set by Drugo's new code above. * these behaviors may need some further * adjustments. */ if (gCalibrate || remove_black) { /* in this case the black region is * identified as delineated by whitespace * (probably = text?). * So print with black ink, * and no colored ink. To avoid using * too much black ink (wets paper) * first_black and second_black * are used as controls on amount * of black ink. */ if (first_black && gBlack ) *pos |= mask; /* one drop of black ink */ mask >>= 1; if (mask == 0) { pos++; mask = 0x80; } if (second_black && gBlack ) *pos |= mask; /* one drop of black ink */ /* suppress use of colored ink on this pixel */ line[i] = line[i + 1] = line[i + 2] = 255; line[i + 3] = line[i + 4] = line[i + 5] = 255; } else { /* this black region is bounded by a colored * region, and is probably part of an image. * so: print half strength black ink, and use * some colored ink. * * note: in EcoMode, only even lines are processed. * in normal mode, skip even lines. * j is the line number. */ if ( (j % 2 || gEcoMode ) && gBlack ) *pos |= mask;/* a drop of black, every second line */ mask >>= 1; if (mask == 0) { pos++; mask = 0x80; } /* reduce amount of colored ink to avoid wetting: * try resetting line[] to gMaxPass-1 * gives 2 passes of the color head in high quality, * (gMaxPass=4, line[] = 3), * 1 pass in ecoMode (gMaxPass = 1, line[] = 0) */ line[i] = line[i + 1] = line[i + 2] = gMaxPass-1; line[i + 3] = line[i + 4] = line[i + 5] = gMaxPass-1; } } else { /* pixel is not black */ mask >>= 1; if (mask == 0) { pos++; mask = 0x80; } if (line[i] > gMaxPass && line[i + 1] > gMaxPass && line[i + 2] > gMaxPass) { /* pixel is whitespace */ curr_stuff=0; } } mask >>= 1; if (mask == 0) { pos++; mask = 0x80; } } } /* end of code that assumes gByteCount == 3 */ else /* !gColorMode, gByteCount could be 1 or 3 */ { /* this next part is for printing in black and white only. * if !gColorMode, the line contains only black or white, so * we can print safely 1 drop out of 2 without any problem */ *pos = 0; for (i = 0 ; i < image->width *gByteCount ; i += 2*gByteCount ) { if (mask == 0x80) *pos = 0; if (!line[i]) /* black even pixel */ { if (first_black && gBlack) *pos |= mask; /* one drop of black ink */ mask >>= 1; if (mask == 0) { pos++; mask = 0x80; } if (second_black && gBlack) *pos |= mask; /* one drop of black ink */ } else /* blank even pixel */ { mask >>= 1; if (mask == 0) { pos++; mask = 0x80; } } mask >>= 1; /* odd pixels are left black */ if (mask == 0) { pos++; mask = 0x80; } } memset (line, 0xff, image->width ); } } if (!(gPixmapMode || !line_is_nonblank)) { unsigned char *p; for (p = black; p <= pos; p++) { if( *p ) { has_black = true; break; } } } return has_black; } BOOLEAN convert_color_data (image_t *image, unsigned char *line, unsigned char *color[], int c_off, BOOLEAN line_is_nonblank) { int i, k, j; unsigned char mask; unsigned char *pos; BOOLEAN has_color = false; j = 1; for (k = 0; k < gMaxPass; k++) { mask = 0x80; pos = &color[gMaxPass - j][c_off]; j++; memset (pos, 0x00, (image->width / 2 + 7) / 8 * 3); if (line_is_nonblank) for (i = 0; i < image->width * 3; i += 6) { if ((line[i] < gMaxPass - k + 1) && gCyan) pos[0] |= mask; if ((line[i + 1] < gMaxPass - k + 1) && gMagenta) pos[1] |= mask; if ((line[i + 2] < gMaxPass - k + 1) && gYellow) pos[2] |= mask; mask >>= 1; if (mask == 0) { pos += 3; mask = 0x80; } } if (line_is_nonblank && k == 0) { unsigned char *p; for (p = &color[gMaxPass -1][c_off]; p <= pos; p++) { if( *p) { has_color = true; break; } } } } return has_color; } /* read a line of data from the file and convert to the necessary format */ BOOLEAN read_line (image_t * image, unsigned char *black[], int b_off, unsigned char *color[], int c_off) { static unsigned char *oldline = NULL; /* take care to free this! */ unsigned char *prevline = NULL; /* take care to free this! */ unsigned char *line = NULL; /* take care to free this! */ int i, j, k ; int fread_eof = 0; BOOLEAN line_is_nonblank = true; static BOOLEAN prevline_is_nonblank = true; int image_width = image->width; const BOOLEAN repeat_line = (g300 && image->bufferCurLine %2); /* for 300dpi input (g300 is true) , the data is duplicated horizontally and vertically to convert it to 600dpi format. odd lines are copies of the preceeding even line, which was stored as "oldline" : */ if(g300) { image_width /= 2; if (repeat_line) { /* in 300dpi input mode, the data from the previous even line should be used again for this odd line*/ assert(oldline); line = oldline; oldline = NULL; } } if(!line) if (!(line = malloc (image->width * gByteCount))) { /* make this nicer... propagate upwards */ snprintf(syslog_message,message_size, "read_line(): %s", gMessages[E_BADMALLOC]); wrap_syslog (LOG_CRIT,"%s",syslog_message); if (oldline) free (oldline); exit (1); } /* All the six pnm input formats * (pbm, pbmraw, pgm, pgmraw, ppm, ppmraw ) * can be read from the input, and converted to * ppmraw format for further processing. */ if (!gGammaMode && !gTerminate && !repeat_line) { switch (image->version) { /* for completeness, the non-raw pnm formats are supported */ case P1_PBM: { /* read pbm format; convert to ppm format if gByteCount = 3 */ /* This is included for completeness, * but this implementation is PAINFULLY slow ! * is there a better way? * (YES, USE THE PPMRAW FORMAT ONLY !) */ int index = 0; int incr = gByteCount; if(g300) incr *= 2; for ( i = 0 ; i < image_width ; ) { switch( getc( image->fptr ) ) { case EOF: snprintf(syslog_message,message_size, "read_line(pbm,ascii): %s PBM\n", gMessages[E_IM_EOFREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); /* input data is truncated; set gTerminate mode * and replace lost part of image with whitespace * until print run ends. */ gTerminate = true ; break; case '1': line[index] = 0; index += incr; i ++; break; case '0': line[index] = 255; index += incr; i ++; break; default : break; } } if(g300) { for ( j = 0 ; j < index ; j += incr ) line[j+gByteCount] = line[j]; } if (gByteCount == 3) { for (j = 0; j < index ; j += 3 ) line[j + 2]= line[j + 1] = line[j]; } } break; case P2_PGM: { /* read pgm format; convert to ppm format if gByteCount = 3 */ /* This is included for completeness, * but this implementation is PAINFULLY slow ! * is there a better way? * (YES, USE THE PGMRAW FORMAT ONLY !) */ int index = 0; unsigned int value ; int incr = gByteCount; if(g300) incr *= 2; for ( i = 0 ; i < image_width ; i ++ ) { switch(fscanf(image->fptr, "%u ", &value )) { case 1: break ; case EOF: snprintf(syslog_message,message_size,"read_line(pgm,ascii): %s PGM\n", gMessages[E_IM_EOFREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); /* input data is truncated; set gTerminate mode * and replace lost part of image with whitespace * until print run ends. */ gTerminate = true ; break; default: snprintf(syslog_message,message_size,"read_line(pgm,ascii): %s", gMessages[E_IM_BADREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); line = NULL; if (oldline) { free (oldline); oldline = NULL; } exit (1); break; } if(value <= 255) { line[index] = value; index += incr; } else { snprintf(syslog_message,message_size,"read_line(pgm,ascii): %s", gMessages[E_IM_BADIMAGE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); } } if(g300) { for ( j = 0 ; j < index ; j += incr ) line[j+gByteCount] = line[j]; } if (gByteCount == 3) { for (j = 0; j < index ; j += 3 ) line[j + 2]= line[j + 1] = line[j]; } } break; case P3_PPM: { /* read pbm format */ /* This is included for completeness, * but this implementation is PAINFULLY slow ! * is there a better way? * (YES, USE THE PPMRAW FORMAT ONLY !) */ unsigned int value ; int index = 0; assert(gByteCount == 3); for ( i = 0 ; i < image_width * 3 ; i ++ ) { switch (fscanf(image->fptr, "%u ", &value ) ) { case 1: break ; case EOF: snprintf(syslog_message,message_size,"read_line(ppm,ascii): %s PPM\n", gMessages[E_IM_EOFREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); /* input data is truncated; set gTerminate mode * and replace lost part of image with whitespace * until print run ends. */ gTerminate = true ; break; default: snprintf(syslog_message,message_size,"read_line(ppm,ascii): %s", gMessages[E_IM_BADREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); break; } if(value <= 255){ if (g300) { line[index+3] = line[index] = value; index++; if(!(index %3)) index+=3; } else { line[index] = value ; index ++; } } else { snprintf(syslog_message,message_size,"read_line(ppm,ascii) %s", gMessages[E_IM_BADIMAGE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); } } } break; case P4_PBMRAW: /* read pbmraw format; convert to ppm format if gByteCount = 3 */ { int pbm_width, count, index=0; int incr = gByteCount; char* pbmraw_data; unsigned char mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; pbm_width = ( image_width + 7 ) / 8; if(g300) incr *= 2; if (! (pbmraw_data = malloc(pbm_width)) ) { snprintf(syslog_message,message_size,"read_line(pbmraw): %s", gMessages[E_BADMALLOC]); wrap_syslog (LOG_CRIT,"%s",syslog_message); if (oldline) free (oldline); free (line); exit (1); } count = fread(pbmraw_data, 1 , pbm_width, image->fptr); if (pbm_width == count) { /* success */ /* pbm raw format: 0=white, 1=black; * bits are stored eight per byte, high bit * first, low bit last. */ index = 0; for ( i = 0 ; i < image_width ; i ++ ) { k = i % 8 ; j = i / 8 ; if ( pbmraw_data[j] & mask[k] ) line[index] = 0; else line[index] = 255; index += incr; } free(pbmraw_data); } else { /* error reading line */ free(pbmraw_data); fread_eof=feof(image->fptr); clearerr(image->fptr); if (fread_eof) { snprintf(syslog_message,message_size,"read_line(pbmraw): %s PBMRAW\n", gMessages[E_IM_EOFREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); /* input data is truncated; set gTerminate mode * and replace lost part of image with whitespace * until print run ends. */ gTerminate = true ; } else { snprintf(syslog_message,message_size,"read_line(pgmraw): %s", gMessages[E_IM_BADREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); } } if(g300) { for ( j = 0 ; j < index ; j += incr ) line[j + gByteCount] = line[j]; } if (gByteCount == 3) { for (j = 0; j < index ; j += 3 ) line[j + 2]= line[j + 1] = line[j]; } } break; case P5_PGMRAW: /* read pgmraw format; convert to ppm format if gByteCount = 3 */ { int index=0 ; char* pgm_data; int incr = gByteCount; if(g300) incr *= 2; pgm_data = malloc(image_width); if (!pgm_data) { snprintf(syslog_message,message_size,"read_line(pgmraw): %s", gMessages[E_BADMALLOC]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); } if ( 1 == fread (pgm_data, image_width , 1, image->fptr) ) { /* success */ index = 0 ; for ( i = 0 ; i < image_width ; i ++ ) { line[index] = pgm_data[i] ; index += incr; } free(pgm_data); } else { /* error reading line */ free(pgm_data); fread_eof=feof(image->fptr); clearerr(image->fptr); if (fread_eof) { snprintf(syslog_message,message_size,"read_line:(pgmraw) %s PGMRAW\n", gMessages[E_IM_EOFREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); /* input data is truncated; set gTerminate mode * and replace lost part of image with whitespace * until print run ends. */ gTerminate = true ; } else { snprintf(syslog_message,message_size,"read_line(pgmraw): %s", gMessages[E_IM_BADREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); } } if(g300) { for (j = 0 ; j < index ; j += incr ) line[j + gByteCount] = line[j]; } if (gByteCount == 3){ for (j = 0; j < index ; j += 3 ) line[j + 2]= line[j + 1] = line[j]; } } break; case P6_PPMRAW: /* read ppmraw format */ { assert(gByteCount == 3); if (1 != fread (line, image_width * 3, 1, image->fptr) ) { fread_eof=feof(image->fptr); clearerr(image->fptr); if (fread_eof) { snprintf(syslog_message,message_size,"read_line(ppmraw):%s \n", gMessages[E_IM_EOFREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); /* input data is truncated; set gTerminate mode * and replace lost part of image with whitespace * until print run ends. */ gTerminate = true ; } else { snprintf(syslog_message,message_size,"read_line(ppmraw): %s", gMessages[E_IM_BADREADLINE]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); } } /* if input data was at 300dpi, expand it to 600dpi format */ if(g300) { int j,k; for ( i = image_width - 1 ; i > 0 ; i--) { j = 3 * i ; k = 2 * j; line[ k + 3 ] = line[ k ] = line[ j ]; line[ k + 4 ] = line[ k + 1 ] = line[ j + 1 ]; line[ k + 5 ] = line[ k + 2 ] = line[ j + 2 ]; } line[3] = line[0]; line[4] = line[1]; line[5] = line[2]; } } break; case none: default: snprintf(syslog_message,message_size,"read_line(?): %s", gMessages[E_IM_BADFORMAT]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free (line); if (oldline) free (oldline); exit (1); break; } } else if (gGammaMode) ProduceGamma (image, line ) ; /* generate color calibration page */ if ( gTerminate || image->bufferCurLine < image->top || image->bufferCurLine >= image->height - image->bottom ) { /* * gTerminate becomes true if EOF was obtained when a line was read . * The input image is incomplete . * Print only previously-read complete lines. * unread lines are replaced with blanks for the rest of the print run * * also blank out lines above the top margin, * or below the bottom margin. */ line_is_nonblank = false; memset(line, 0xff, image->width * gByteCount); } if(line_is_nonblank) { if (repeat_line) line_is_nonblank = prevline_is_nonblank ; else { /* crop image to left and right margins */ int imin = 0, imax = image->width * gByteCount; if(image->left > 0) { imin = image->left * gByteCount; memset(line,0xff, imin); } if(image->right > 0) { imax = (image->width - image->right) * gByteCount; memset(line + imax, 0xff, image->right * gByteCount); } /* check if cropped line is really nonblank */ line_is_nonblank = false; for ( i = imin ; i < imax ; i++ ) { if (line[i] != 255) { line_is_nonblank = true; break; } } } } /* mark line as read */ image->LineType[image->bufferCurLine] = image->LineType[image->bufferCurLine] | READLINE; if (!gTerminate && g300 && !repeat_line){ /* in 300dpi mode, store the data when bufferCurLine is even, to use it again for the next odd line at 600dpi */ if (!(prevline = malloc (image->width * gByteCount))) { /* make this nicer... propagate upwards */ snprintf(syslog_message,message_size, "read_line(): %s", gMessages[E_BADMALLOC]); wrap_syslog (LOG_CRIT,"%s",syslog_message); free(line); if (oldline) free (oldline); exit (1); } prevline_is_nonblank = line_is_nonblank; if (prevline_is_nonblank) memcpy(prevline, line, image->width * gByteCount); else memset(prevline, 0xff, image->width * gByteCount); } /* just to be safe ... */ if(gTerminate && oldline) { free(oldline); oldline = NULL; } /* process ALL non-blank lines to avoid artifacts (odd lines are only processed for black in *_Dither() functions ) hopefully, this minimal processing is quite fast */ if(line_is_nonblank) { image->LineType[image->bufferCurLine] = image->LineType[image->bufferCurLine] | NONBLANK; if (gColorMode) { if (gFastMode) HT_Color_Dither (image, line); else FS_Color_Dither (image, line); } else HT_Black_Dither (image, line); } /* if the current line is odd, we only need to return black data */ if (image->bufferCurLine % 2 == 1) { if ( (!gEcoMode || gColorMode) && !gPixmapMode ) { if (convert_black_data (image, line, &black[0][b_off],line_is_nonblank)) image->LineType[image->bufferCurLine] = image->LineType[image->bufferCurLine] | BLACKLINE; } else { /* in B&W EcoMode, or in PixmapMode, * don't print the odd line, but store it * (unless this is the last line!) */ if ( image->bufferCurLine + 1 < image->height ) { memset(&black[0] [b_off], 0x00, (image->width +7 ) / 8 ); oldline = malloc (image->width * gByteCount); memcpy(oldline, line, image->width * gByteCount); } } } else { /* return both black and color data on even lines */ /* note that convert_color_data must be called AFTER convert_black_data */ if ( oldline ) { /* add any black from previous unprinted odd line to next even line */ if (gByteCount == 3) { for ( i = 0; i < image->width * 3; i += 3 ) { if ( !oldline[i] && !oldline[i + 1] && !oldline[i + 2] ) line[i + 2] = line[i + 1] = line[i] = 0 ; } } else /* gByteCount = 1 */ { for ( i = 0; i < image->width ; i ++ ) { if ( !oldline[i] ) line[i] = 0 ; } } free(oldline); oldline = NULL; } if (convert_black_data (image, line, &black[0][b_off],line_is_nonblank)) image->LineType[image->bufferCurLine] = image->LineType[image->bufferCurLine] | BLACKLINE; if (gColorMode) if (convert_color_data (image, line, color, c_off,line_is_nonblank)) image->LineType[image->bufferCurLine] = image->LineType[image->bufferCurLine] | COLORLINE; } free (line); if(prevline) { assert(!oldline); oldline = prevline; } image->bufferCurLine++; return line_is_nonblank; } /* Drugo */ /* buffer a black line or two and return color data */ void buffer_black_line (image_t *image, unsigned char *data[], int offset) { int lines, linesize = (image->width + 7) / 8; BOOLEAN nonblank = false; int curline = image->bufferCurLine; /* if the current line is odd, we need to read 2 black lines */ if (image->bufferCurLine % 2 == 1) lines = 2; else lines = 1; remallocBuffer (image, linesize * lines); /* read_line into buffer and 'data' */ read_line (image, image->buffer, image->bufpos + image->buflines * linesize, data, offset); if (image->LineType[curline] & BLACKLINE) nonblank = true; image->buflines++; /* if we have to read 2 lines, then we haven't gotten any color data yet */ if (lines == 2) { curline++; read_line (image, image->buffer, image->bufpos + image->buflines * linesize, data, offset); image->buflines++; if (image->LineType[curline] & BLACKLINE) nonblank = true; } /* if no black data exists in buffer, advance blackCurLine */ if (image->buftype == empty && !nonblank ) { image->buflines = 0; image->blackCurLine += lines; ; } else image->buftype = bitmap; } /* buffer a color line and return black data */ void buffer_color_line (image_t *image, unsigned char *data[], int offset) { int linesize = (image->width / 2 + 7) / 8 * 3; BOOLEAN nonblank = false; int curline = image->bufferCurLine; /* if the current line is odd, we don't need to buffer any color data */ if (image->bufferCurLine % 2 == 1) { read_line (image, data, offset, NULL, 0); return; } /* we need to buffer 1 color line */ if (gColorMode) remallocBuffer (image, linesize); read_line (image, data, offset, image->buffer, image->bufpos + image->buflines * linesize); if (image->LineType[curline] & COLORLINE) nonblank = true; image->buflines++; /* if no color data exists in buffer, advance colorCurLine */ if (image->buftype == empty && !nonblank ) { image->buflines = 0; image->colorCurLine += 2; } else image->buftype = color; } /* Reads a single line of black image data into data which must be at least (image->width+7)/8 bytes of storage */ int im_black_readline (image_t *image, unsigned char *data[], int offset) { if (image->blackCurLine >= image->height) return 0; if (image->unread) { memcpy (data[0] + offset, image->revdata, (image->width + 7) / 8); image->blackCurLine++; image->unread = 0; free (image->revdata); return 1; } /* if there are buffered black lines, use one of them */ /* if there aren't buffered black lines, process the next line and */ /* buffer a color line */ if (image->buftype == bitmap) { memcpy (data[0] + offset, &image->buffer[0][image->bufpos], (image->width + 7) / 8); image->bufpos += (image->width + 7) / 8; if (--image->buflines == 0) { image->bufpos = 0; image->buftype = empty; } } else buffer_color_line (image, data, offset); image->blackCurLine++; return 1; } /* Reads a single line of color image data into data which must be at least (image->width/2+7)/8*3 bytes of storage */ int im_color_readline (image_t *image, unsigned char *data[], int offset) { int k; if (image->colorCurLine >= image->height) { DPRINTF ("im_color_readline returning 0 immediately!\n"); return 0; } /* if there are buffered color lines, use one of them */ /* if there aren't buffered color lines, process the next line and */ /* buffer a black line */ if (image->buftype == color) { for (k = 0; k < gMaxPass; k++) memcpy (data[k] + offset, &image->buffer[k][image->bufpos], 3 * ((image->width / 2 + 7) / 8)); image->bufpos += 3 * ((image->width / 2 + 7) / 8); image->buflines--; if (image->buflines == 0) { image->bufpos = 0; image->buftype = empty; } } else buffer_black_line (image, data, offset); image->colorCurLine += 2; return 1; } /* push a black line back into the buffer; we read too much! */ void im_unreadline (image_t * image, void *data) { /* can only store one line in the unread buffer */ if (image->unread) return; image->unread = 1; image->revdata = malloc ((image->width + 7) / 8); memcpy (image->revdata, data, (image->width + 7) / 8); image->blackCurLine--; }