#include /* * Author(s): Ameet A. Raval, Frans van Hoesel, Andrew Ford, szukw000 * * NOTE: rle16(): files can be sufficiently shorter * if the image has a small number of colors. * rle8() : extends the file. */ #include #include #include #include #include #include "ps_image.hh" #define MAX_COL 12 #define TM_BASE_YEAR 1900 /* RLE values */ #define USHORT_BLOCK_MASK 0x8000 #define USHORT_BLOCK_LEN 21845 #define USHORT_MAX_INDEX 21845 #define UCHAR_MAX_INDEX 256 #define FULLY_TRANSPARENT 0 #define FULLY_OPAQUE 255 static void ps_print_hex(PSInfo *ps, unsigned short val, int flush) { static unsigned char hexline[128]; static unsigned char digit[] = "0123456789abcdef"; if(!flush) { hexline[ps->PS_hexi++] = (unsigned char) digit[((unsigned) val >>(unsigned) 4) & (unsigned) 0x0f]; hexline[ps->PS_hexi++] = (unsigned char) digit[(unsigned) val & (unsigned) 0x0f]; } if((flush && ps->PS_hexi) || (ps->PS_hexi >77)) { hexline[ps->PS_hexi] = '\0'; ps->PS_hexi = 0; fprintf(ps->writer, "%s\n", hexline); } } struct ps_color { unsigned char red, green, blue; unsigned char dirty; }; static unsigned char *mono_to_rgb(PSInfo *ps) { unsigned char *buf, *d, *s; unsigned int i, max; max = ps->image_w * ps->image_h; buf = (unsigned char*)malloc(max * 3); if(buf == NULL) { fprintf(stderr,"ps_image.cxx:%d:\n\tmemory out\n",__LINE__); return NULL; } d = buf; s = ps->src_buf; for(i = 0; i < max; ++i) { *d++ = *s; *d++ = *s; *d++ = *s; ++s; } return buf; } /* rgba_to_rgb() uses the alpha_composite()-code from : * libpng-VERSION/contrib/gregbook/rpng2-x.c */ static unsigned char *rgba_to_rgb(PSInfo *ps) { unsigned char *buf, *d, *s; unsigned int i, max; unsigned char bg_red, bg_green, bg_blue; unsigned short red, green, blue, alpha, c; bg_red = ps->bg_red; bg_green = ps->bg_green; bg_blue = ps->bg_blue; max = ps->image_w * ps->image_h; buf = (unsigned char*)malloc(max * 3); if(buf == NULL) { fprintf(stderr,"ps_image.cxx:%d:\n\tmemory out\n",__LINE__); return NULL; } d = buf; s = ps->src_buf; for(i = 0; i < max; ++i) { red = (unsigned short)*s++; green = (unsigned short)*s++; blue = (unsigned short)*s++; alpha = (unsigned short)*s++; if(alpha == FULLY_OPAQUE) { *d++ = (unsigned char)red; *d++ = (unsigned char)green; *d++ = (unsigned char)blue; continue; } if(alpha == FULLY_TRANSPARENT) { *d++ = bg_red; *d++ = bg_green; *d++ = bg_blue; continue; } /* transition */ c = red * alpha + (unsigned short)bg_red * (FULLY_OPAQUE - alpha) + 128; *d++ = (unsigned char)((c + (c>>8))>>8); c = green * alpha + (unsigned short)bg_green * (FULLY_OPAQUE - alpha) + 128; *d++ = (unsigned char)((c + (c>>8))>>8); c = blue * alpha + (unsigned short)bg_blue * (FULLY_OPAQUE - alpha) + 128; *d++ = (unsigned char)((c + (c>>8))>>8); }/* for(i ) */ return buf; } /* * Run-Length-Encoding of image. * RLE is done to reduce the file size and therefore the time to send * the file to the printer. You get longer processing time instead. * * RLE is encoded as such: * # 'run' of count+1 equal pixels * # count+1 non-equal pixels * count can range between 0 and 127 for uchar * and between 0 and 21845 for ushort * * returns the length of the RLE line vector * * Implementation Limits of PostScript: 'string' 65535 * * max. triple: 21845 * * Depending of the image size the encoding is split into: * * if len is <= 2^16 - 1: rle16_encode() */ static int rle16_encode(unsigned short *scanline, unsigned short *rleline, unsigned short *block, int wide) { int i, j, rlen; unsigned short blocklen, isrun; unsigned short pix; blocklen = isrun = 0; rlen = 0; for(i = 0; i < wide; i++) { /* there are 5 possible states: * 0: block empty. * 1: block is a run, current pix == previous pix * 2: block is a run, current pix != previous pix * 3: block not a run, current pix == previous pix * 4: block not a run, current pix != previous pix */ pix = scanline[i]; if(!blocklen) { /* case 0: empty */ block[blocklen++] = pix; isrun = 1; } else if(isrun) { if(pix == block[blocklen-1]) { /* case 1: isrun, prev == cur */ block[blocklen++] = pix; } else { /* case 2: isrun, prev != cur */ if(blocklen>1) { /* we have a run block to flush */ rleline[rlen++] = blocklen-1; rleline[rlen++] = block[0]; /* start new run block with pix */ block[0] = pix; blocklen = 1; } else { /* blocklen<=1, turn into non-run */ isrun = 0; block[blocklen++] = pix; } } } else { /* not a run */ if(pix == block[blocklen-1]) { /* case 3: non-run, prev == cur */ if(blocklen>1) { /* have a non-run block to flush */ rleline[rlen++] = (blocklen-1) | USHORT_BLOCK_MASK; for(j=0; jwriter, "/cmap %d string def\n\n\n", nc * 3); /* load up the colormap */ fputs("currentfile cmap readhexstring\n", ps->writer); for(i=0; i < nc; i++) { fprintf(ps->writer, "%02x%02x%02x", ps_colors[i].red, ps_colors[i].green, ps_colors[i].blue); if((i%(MAX_COL+1)) == MAX_COL) fputs("\n", ps->writer); } /* lose return values from readhexstring */ fputs("\npop pop\n", ps->writer); }/* write_colormap() */ static int create_image_data(PSInfo *ps, unsigned char *src_buf, struct ps_color **out_colors, unsigned short **out_dst, int *out_dst_len) { unsigned char *src; struct ps_color *ps_colors; unsigned short *dst; unsigned int src_len, dst_len, i, free_index; unsigned int r, g, b; src = src_buf; src_len = ps->image_w * ps->image_h * 3; i = (src_len/3) * (unsigned int)sizeof(struct ps_color); dst = (unsigned short*)calloc(1, i); ps_colors = (struct ps_color*)calloc(1, i); if(dst == NULL || ps_colors == NULL) { goto fails; } *out_dst = dst; *out_colors = ps_colors; ps_colors[0].red = *src++; ps_colors[0].green = *src++; ps_colors[0].blue = *src++; free_index = 1; src_len -= 3; *dst++ = 0; dst_len = 1; while(src_len > 0) { r = *src++; g = *src++; b = *src++; src_len -= 3; i = (r<<3) + (r>>11) + g; i = (i<<3) + (i>>23) + b; i %= free_index; while(i < free_index) { if(ps_colors[i].dirty == 0) { ps_colors[i].red = (unsigned char)r; ps_colors[i].green = (unsigned char)g; ps_colors[i].blue = (unsigned char)b; ps_colors[i].dirty = 1; break; } if(r == ps_colors[i].red && g == ps_colors[i].green && b == ps_colors[i].blue) break; ++i; } if(i == free_index) { if(++free_index == USHORT_MAX_INDEX) { /* no colormap, no ps_colors */ break; } ps_colors[i].red = (unsigned char)r; ps_colors[i].green = (unsigned char)g; ps_colors[i].blue = (unsigned char)b; ps_colors[i].dirty = 1; } *dst++ = i; ++dst_len; }/* while(src_len > 0) */ *out_dst_len = dst_len; return free_index; fails: if(dst != NULL) free(dst); fprintf(stderr,"ps_image.cxx:%d:\n\tmemory out\n",__LINE__); return 0; }/* create_image_data() */ const char *prolog_open_cs = "%%!PS-Adobe-3.0\n" "%%%%Creator: %s\n" "%%%%Title: %s\n" "%%%%LanguageLevel: 2\n" "%%%%CreationDate: %s\n" "%%%%Pages: 1\n" "%%%%PageOrder: Ascend\n"; const char *rle16_cs = "/RLECMAPIMAGE16 { /buffer 2 string def /rgbval 3 string def\n" " /block 65535 string def\n" " { currentfile buffer readhexstring pop pop\n" " /bcount buffer 0 get 256 mul buffer 1 get add store\n" " bcount 32768 ge\n" " {\n" " 0 1 bcount 32768 sub\n" " { currentfile buffer readhexstring pop pop\n" " /mapidx buffer 0 get 256 mul buffer 1 get add 3 mul store\n" " /rgbval cmap mapidx 3 getinterval store\n" " block exch 3 mul rgbval putinterval\n" " } for\n" " block 0 bcount 32767 sub 3 mul getinterval\n" " }\n" " {\n" " currentfile buffer readhexstring pop pop\n" " /mapidx buffer 0 get 256 mul buffer 1 get add 3 mul store\n" " /rgbval cmap mapidx 3 getinterval store\n" " 0 1 bcount { block exch 3 mul rgbval putinterval } for\n" " block 0 bcount 1 add 3 mul getinterval\n" " } ifelse\n" " }\n" " false 3 colorimage\n" "} bind def\n"; const char *ps_date(void) { static char buf[128]; struct tm *stm; time_t t; t = time(NULL); stm = localtime(&t); #ifdef _WIN32 sprintf_s(buf, 128, "%4d-%02d-%02d", stm->tm_year + TM_BASE_YEAR, stm->tm_mon+1, stm->tm_mday); #else snprintf(buf, 128, "%4d-%02d-%02d", stm->tm_year + TM_BASE_YEAR, stm->tm_mon+1, stm->tm_mday); #endif return buf; } static void write_header(PSInfo *ps, int max_index) { double lhs, top, rhs, bot, box_w, box_h, x, y; const char *moveto; fprintf(ps->writer, prolog_open_cs, PACKAGE_STRING, ps->title_s, ps_date()); lhs = ps->fmargin_lhs; top = ps->fmargin_top; rhs = ps->fmargin_rhs; bot = ps->fmargin_bot; if(ps->portrait) { box_w = ps->fmedia_w - lhs - rhs; box_h = ps->fmedia_h - top - bot; ps->fscale_w = ps->image_w; ps->fscale_h = ps->image_h; if(ps->fscale_w > box_w) { ps->fscale_w = box_w; ps->fscale_h = box_w * (double)ps->image_h/(double)ps->image_w; } else if(ps->fscale_h > box_h) { ps->fscale_w = box_h * (double)ps->image_h/(double)ps->image_w; ps->fscale_h = box_h; } x = lhs; y = box_h + bot; if(ps->center) { if(ps->fscale_h < box_h) y -= (box_h - ps->fscale_h)/2.; if(ps->fscale_w < box_w) x += (box_w - ps->fscale_w)/2.; } fprintf(ps->writer, "%%%%Orientation: Portrait\n" "%%%%BoundingBox: %d %d %d %d\n" "%%%%DocumentMedia: %s %d %d 0 () ()\n", (int)lhs, (int)top, (int)box_w, (int)box_h, ps->media_s, (int)ps->fmedia_w, (int)ps->fmedia_h); moveto = "save\n%.4f %.4f moveto\n"; } else { assert(ps->landscape); box_w = ps->fmedia_w - lhs - rhs; box_h = ps->fmedia_h - top - bot; ps->fscale_w = ps->image_w; ps->fscale_h = ps->image_h; if(ps->fscale_h > box_w) { ps->fscale_w = box_w * (double)ps->image_w/(double)ps->image_h; ps->fscale_h = box_w; } else if(ps->fscale_w > box_h) { ps->fscale_w = box_h; ps->fscale_h = box_h * (double)ps->image_w/(double)ps->image_h; } x = lhs; y = bot; if(ps->center) { if(ps->fscale_h < box_w) x += (box_w - ps->fscale_h)/2.; if(ps->fscale_w < box_h) y += (box_h - ps->fscale_w)/2.; } fprintf(ps->writer, "%%%%Orientation: Landscape\n" "%%%%BoundingBox: %d %d %d %d\n" "%%%%DocumentMedia: %s %d %d 0 () ()\n", (int)lhs, (int)top, (int)box_w, (int)box_h, ps->media_s, (int)ps->fmedia_w, (int)ps->fmedia_h); moveto = "save\n%.4f %.4f translate 90 rotate 0. 0. moveto\n"; } fputs("%%EndComments\n%%BeginProlog\n", ps->writer); if(max_index < USHORT_MAX_INDEX) fputs(rle16_cs, ps->writer); fputs("%%EndProlog\n%%BeginSetup\n%%EndSetup\n%%Page: 1 1\n" "%%BeginPageSetup\n%%EndPageSetup\n", ps->writer); fprintf(ps->writer, moveto, x, y); #ifdef DEBUG fprintf(stderr,"%s:%d:write_header\n\tMARGINS(%.4f,%.4f,%.4f,%.4f)\n\t" "BOX w(%.4f) h(%.4f) SCALE w(%.4f) h(%.4f)\n\tX(%.4f) Y(%.4f)\n", __FILE__,__LINE__, top,rhs,bot,lhs,box_w,box_h,ps->fscale_w, ps->fscale_h,x,y); #endif }/* write_header() */ int PS_image_draw(PSInfo *ps) { struct ps_color *ps_colors; unsigned short *dst_buf, *src; unsigned char *rgb_buf; unsigned short *rle_line; unsigned short *block; int i, j, width, height, rle_len, dst_len; int max_index; width = ps->image_w; height = ps->image_h; dst_buf = NULL; ps_colors = NULL; if(ps->image_channels == 1) rgb_buf = mono_to_rgb(ps); else if(ps->image_channels == 4) rgb_buf = rgba_to_rgb(ps); else rgb_buf = ps->src_buf; if(rgb_buf == NULL) { fprintf(stderr,".cxx:%d:\n\tmemory out\n",__LINE__); return 0; } max_index = create_image_data(ps, rgb_buf, &ps_colors, &dst_buf, &dst_len); if(max_index == 0) { if(ps->image_channels != 3) free(rgb_buf); if(dst_buf) free(dst_buf); return 0; } /* small images without colormap: */ if(max_index < UCHAR_MAX_INDEX) max_index = USHORT_MAX_INDEX; write_header(ps, max_index); fprintf(ps->writer, "20 dict begin\n/pix %d string def\n\n", width * 3); /* position and scaling */ fprintf(ps->writer, "gsave currentpoint %.4f sub translate" " %.4f %.4f scale\n", ps->fscale_h, ps->fscale_w, ps->fscale_h); if(max_index < USHORT_MAX_INDEX) { write_colormap(ps, max_index, ps_colors); } fprintf(ps->writer, "%d %d 8\n", width, height); /* mapping matrix */ fprintf(ps->writer, "[%d 0 0 -%d 0 %d]\n", width, height, height); rle_line = NULL; block = NULL; if(max_index < USHORT_MAX_INDEX) { unsigned short val; rle_line = (unsigned short*) malloc(width * 2 * sizeof(unsigned short)); block = (unsigned short*) malloc(width * sizeof(unsigned short)); if(rle_line == NULL || block == NULL) { goto fails; } fputs("RLECMAPIMAGE16\n", ps->writer); src = dst_buf; for(i=0; i < height; i++) { rle_len = rle16_encode(src, rle_line, block, width); src += width; j = -1; while(++j < rle_len) { val = rle_line[j]; ps_print_hex(ps, val>>8, 0); ps_print_hex(ps, val%256, 0); } ps_print_hex(ps, '\0', 1); /* Flush the hex buffer */ } free(rle_line); free(block); } else /* max_index >= USHORT_MAX_INDEX */ { unsigned char *src; int col, src_len; fputs("{currentfile pix readhexstring pop}\n" "false 3 colorimage\n", ps->writer); src = rgb_buf; src_len = width * height * 3; col = 0; while(src_len > 0) { fprintf(ps->writer, "%02x%02x%02x", src[0], src[1], src[2]); src += 3; src_len -= 3; if(++col > MAX_COL) { fputs("\n", ps->writer); col = 0; } } } if(ps->image_channels != 3) free(rgb_buf); free(dst_buf); free(ps_colors); fputs("end\ngrestore\nrestore\nshowpage\n%%Trailer\n%%EOF\n", ps->writer); return 1; fails: if(rle_line != NULL) free(rle_line); fprintf(stderr,"ps_image.cxx:%d:\n\tmemory out\n",__LINE__); return 0; } /* PS_image_write() */