diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-12-07 02:53:31 -0800 |
commit | cbb6286cb92020dd7ae88798ed831ed76fd2130e (patch) | |
tree | 782a01c00d5e064aa67ea3f9241a8ef1de1060c6 /dip.c | |
download | links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.tar.gz links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.tar.bz2 links-cbb6286cb92020dd7ae88798ed831ed76fd2130e.zip |
Imported Upstream version 2.6upstream/2.6upstream
Diffstat (limited to 'dip.c')
-rw-r--r-- | dip.c | 2206 |
1 files changed, 2206 insertions, 0 deletions
@@ -0,0 +1,2206 @@ +/* dip.c + * Digital Image Processing routines + * (c) 2000-2002 Karel 'Clock' Kulhavy + * This file is a part of the Links program, released under GPL. + * This does various utilities for digital image processing including font + * rendering. + */ + +#include "cfg.h" + +#ifdef G + +#include "links.h" + +#ifdef HAVE_MATH_H +#include <math.h> +#endif /* HAVE_MATH_H */ + +#endif +int dither_letters=1; + +double user_gamma=1.0; /* 1.0 for 64 lx. This is the number user directly changes in the menu */ + +double display_red_gamma=2.2; /* Red gamma exponent of the display */ +double display_green_gamma=2.2; /* Green gamma exponent of the display */ +double display_blue_gamma=2.2; /* Blue gamma exponent of the display */ +#ifdef G + +/* #define this if you want to report missing letters to stderr. + * Leave it commented up for normal operation and releases! */ +/* #define REPORT_UNKNOWN 1 */ + +double sRGB_gamma=0.45455; /* For HTML, which runs + * according to sRGB standard. Number + * in HTML tag is linear to photons raised + * to this power. + */ + +unsigned long aspect=65536; /* aspect=65536 for 320x240 + * aspect=157286 for 640x200 (tall pixels) + * Defines aspect ratio of screen pixels. + * aspect=(196608*xsize+ysize<<1)/(ysize<<1); + * Default is 65536 because we assume square pixel + * when not specified otherwise. Aspect is ratio of + * the pixel height (in milimeters) to pixel width, + * multiplied by 65536. */ +unsigned long aspect_native=65536; /* Like aspect, but not influenced by + * user, just determined by graphics driver. + */ + +/* Limitation: No input image's dimension may exceed 2^(32-1-8) pixels. + */ + +/* Each input byte represents 1 byte (gray). The question whether 0 is + * black or 255 is black doesn't matter. + */ + +/* These constants represent contrast-enhancement and sharpen filter (which is one filter + * together) that is applied onto the letters to enhance their appearance at low height. + * They were determined by experiment for several values, interpolated, checked and tuned. + * If you change them, don't wonder if the letters start to look strange. + * The numers in the comments denote which height the line applies for. + */ +static float fancy_constants[64]={ + 0,3, /* 1 */ + .1,3, /* 2 */ + .2,3, /* 3 */ + .3,2.9, /* 4 */ + .4,2.7, /* 5 */ + .4,2.5, /* 6 */ + .4,2, /* 7 */ + .5,2, /* 8 */ + .4,2, /* 9 */ + .38,1.9, /* 10 */ + .36,1.8, /* 11 */ + .33,1.7, /* 12 */ + .30,1.6, /* 13 */ + .25,1.5, /* 14 */ + .20,1.5, /* 15 */ + .15,1.5, /* 16 */ + .14,1.5, /* 17 */ + .13,1.5, /* 18 */ + .12,1.5, /* 19 */ + .12,1.5, /* 20 */ + .12,1.5, /* 21 */ + .12,1.5, /* 22 */ + .11,1.5, /* 23 */ + .10,1.4, /* 24 */ + .09,1.3, /* 25 */ + .08,1.3, /* 26 */ + .04,1.2, /* 27 */ + .04,1.2, /* 28 */ + .02,1.1, /* 29 */ + .02,1.1, /* 30 */ + .01,1, /* 31 */ + .01,1 /* 32 */ +}; + + +/* This shall be hopefully reasonably fast and portable + * We assume ix is <65536. If not, the letters will be smaller in + * horizontal dimension (because of overflow) but this will not cause + * a segfault. 65536 pixels wide bitmaps are not normal and will anyway + * exhaust the memory. + */ +static inline int compute_width (int ix, int iy, int required_height) +{ + int width; + unsigned long reg; + + reg=(unsigned long)aspect*(unsigned long)required_height; + + if (reg>=0x1000000UL){ + /* It's big */ + reg=(reg+32768)>>16; + width=(reg*ix+(iy>>1))/iy; + }else{ + /* It's small */ + reg=(reg+128)>>8; + iy<<=8; + width=(reg*ix+(iy>>1))/iy; + } + if (width<1) width=1; + return width; +} + +static struct lru font_cache; + /* This is a cache for screen-ready colored bitmaps + * of lettrs and/or alpha channels for these (are the + * same size, only one byte per pixel and are used + * for letters with an image under them ) + */ + +/* Each input byte represents 1 byte (gray). The question whether 0 is + * black or 255 is black doesn't matter. + */ + +inline static void add_col_gray(unsigned *col_buf, unsigned char *ptr, int + line_skip, int n, unsigned weight) +{ + for (;n;n--){ + *col_buf+=weight*(*ptr); + ptr+=line_skip; + col_buf++; + } +} + +/* line_skip is in pixels. The column contains the whole pixels (R G B) + * We assume unsigned short holds at least 16 bits. */ +inline static void add_col_color(unsigned *col_buf, unsigned short *ptr + , int line_skip, int n, unsigned weight) +{ + for (;n;n--){ + *col_buf+=weight*(*ptr); + col_buf[1]+=weight*ptr[1]; + col_buf[2]+=weight*ptr[2]; + ptr+=line_skip; + col_buf+=3; + } +} + + /* We assume unsigned short holds at least 16 bits. */ +inline static void add_row_gray(unsigned *row_buf, unsigned char *ptr, int n, + unsigned weight) +{ + for (;n;n--){ + *row_buf+=weight**ptr; + ptr++; + row_buf++; + } +} + +/* n is in pixels. pixel is 3 unsigned shorts in series */ + /* We assume unsigned short holds at least 16 bits. */ +inline static void add_row_color(unsigned *row_buf, unsigned short *ptr, int n, unsigned weight) +{ + for (;n;n--){ + *row_buf+=weight**ptr; + row_buf[1]+=weight*ptr[1]; + row_buf[2]+=weight*ptr[2]; + ptr+=3; + row_buf+=3; + } +} + +/* We assume unsigned holds at least 32 bits */ +inline static void emit_and_bias_col_gray(unsigned *col_buf, unsigned char *out, int + line_skip, int n, unsigned weight) +{ + unsigned half=weight>>1; + + for (;n;n--){ + *out=*col_buf/weight; + out+=line_skip; + *col_buf++=half; + } +} + +/* We assume unsigned holds at least 32 bits */ +static inline void bias_buf_gray(unsigned *col_buf, int n, unsigned half) +{ + for (;n;n--) *col_buf++=half; +} + +/* We assume unsigned holds at least 32 bits */ +inline static void bias_buf_color(unsigned *col_buf, int n, unsigned half) +{ + for (;n;n--){ + *col_buf=half; + col_buf[1]=half; + col_buf[2]=half; + col_buf+=3; + } + /* System activated */ +} + +/* line skip is in pixels. Pixel is 3*unsigned short */ +/* We assume unsigned holds at least 32 bits */ +/* We assume unsigned short holds at least 16 bits. */ +inline static void emit_and_bias_col_color(unsigned *col_buf + , unsigned short *out, int line_skip, int n, unsigned weight) +{ + unsigned half=weight>>1; + + for (;n;n--){ + *out=(*col_buf)/weight; + *col_buf=half; + out[1]=col_buf[1]/weight; + col_buf[1]=half; + /* The following line is an enemy of the State and will be + * prosecuted according to the Constitution of The United States + * Cap. 20/3 ix. Sel. Bill 12/1920 + * Moses 12/20 Erizea farizea 2:2:1:14 + */ + out[2]=col_buf[2]/weight; + col_buf[2]=half; + out+=line_skip; + col_buf+=3; + } +} + +/* We assume unsigned holds at least 32 bits */ +inline static void emit_and_bias_row_gray(unsigned *row_buf, unsigned char *out, int n + ,unsigned weight) +{ + unsigned half=weight>>1; + + for (;n;n--){ + *out++=*row_buf/weight; + *row_buf++=half; + } +} + +/* n is in pixels. pixel is 3 unsigned shorts in series. */ +/* We assume unsigned holds at least 32 bits */ +/* We assume unsigned short holds at least 16 bits. */ +inline static void emit_and_bias_row_color(unsigned *row_buf, unsigned short + *out, int n, unsigned weight) +{ + unsigned half=weight>>1; + + for (;n;n--){ + *out=*row_buf/weight; + *row_buf=half; + out[1]=row_buf[1]/weight; + row_buf[1]=half; + out[2]=row_buf[2]/weight; + row_buf[2]=half; + out+=3; + row_buf+=3; + } +} + +/* For enlargement only -- does linear filtering. + * Allocates output and frees input. + * We assume unsigned holds at least 32 bits */ +inline static void enlarge_gray_horizontal(unsigned char *in, int ix, int y + ,unsigned char ** out, int ox) +{ + unsigned *col_buf; + int total; + int out_pos,in_pos,in_begin,in_end; + unsigned half=(ox-1)>>1; + unsigned char *outptr; + unsigned char *inptr; + + if (ox && (unsigned)ox * (unsigned)y / (unsigned)ox != (unsigned)y) overalloc(); + if ((unsigned)ox * (unsigned)y > MAXINT) overalloc(); + outptr=mem_alloc(ox*y); + inptr=in; + *out=outptr; + if (ix==1){ + /* Dull copying */ + for (;y;y--){ + memset(outptr,*inptr,ox); + outptr+=ox; + inptr++; + } + mem_free(in); + }else{ + total=(ix-1)*(ox-1); + if ((unsigned)y > MAXINT / sizeof(*col_buf)) overalloc(); + col_buf=mem_alloc(y*sizeof(*col_buf)); + bias_buf_gray(col_buf, y, half); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+ox-1; + add_col_gray(col_buf,inptr, ix, y, in_end-out_pos); + add_col_gray(col_buf,inptr+1, ix, y, out_pos-in_begin); + emit_and_bias_col_gray(col_buf,outptr,ox,y,ox-1); + outptr++; + out_pos+=ix-1; + if (out_pos>in_end){ + in_pos=in_end; + inptr++; + } + if (out_pos>total){ + mem_free(in); + mem_free(col_buf); + return; + } + goto again; + } + /* Rohan, oh Rohan... */ + /* ztracena zeme :) */ +} + +/* For enlargement only -- does linear filtering + * Frees input and allocates output. + * We assume unsigned holds at least 32 bits + */ +static inline void enlarge_color_horizontal(unsigned short *ina, int ix, int y, + unsigned short ** outa, int ox) +{ + unsigned *col_buf; + int total,a,out_pos,in_pos,in_begin,in_end; + unsigned half=(ox-1)>>1; + unsigned skip=3*ix; + unsigned oskip=3*ox; + unsigned short *out, *in; + + if (ix==ox){ + *outa=ina; + return; + } + if (ox && (unsigned)ox * (unsigned)y / (unsigned)ox != (unsigned)y) overalloc(); + if ((unsigned)ox * (unsigned)y > MAXINT / 3 / sizeof(*out)) overalloc(); + out=mem_alloc(sizeof(*out)*3*ox*y); + *outa=out; + in=ina; + if (ix==1){ + for (;y;y--,in+=3) for (a=ox;a;a--,out+=3){ + *out=*in; + out[1]=in[1]; + out[2]=in[2]; + } + mem_free(ina); + return; + } + total=(ix-1)*(ox-1); + if ((unsigned)y > MAXINT / 3 / sizeof(*col_buf)) overalloc(); + col_buf=mem_alloc(y*3*sizeof(*col_buf)); + bias_buf_color(col_buf,y,half); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+ox-1; + add_col_color(col_buf,in,skip,y + ,in_end-out_pos); + add_col_color(col_buf,in+3,skip,y + ,out_pos-in_begin); + emit_and_bias_col_color(col_buf,out,oskip,y,ox-1); + out+=3; + out_pos+=ix-1; + if (out_pos>in_end){ + in_pos=in_end; + in+=3; + } + if (out_pos>total){ + mem_free(col_buf); + mem_free(ina); + return; + } + goto again; +} + +/* Works for both enlarging and diminishing. Linear resample, no low pass. + * Automatically mem_frees the "in" and allocates "out". */ +/* We assume unsigned holds at least 32 bits */ +inline static void scale_gray_horizontal(unsigned char *in, int ix, int y + ,unsigned char ** out, int ox) +{ + unsigned *col_buf; + int total=ix*ox; + int out_pos,in_pos,in_begin,in_end,out_end; + unsigned char *outptr; + unsigned char *inptr; + + if (ix<ox){ + enlarge_gray_horizontal(in,ix,y,out,ox); + return; + }else if (ix==ox){ + *out=in; + return; + } + if (ox && (unsigned)ox * (unsigned)y / (unsigned)ox != (unsigned)y) overalloc(); + if ((unsigned)ox * (unsigned)y > MAXINT) overalloc(); + outptr=mem_alloc(ox*y); + inptr=in; + *out=outptr; + if ((unsigned)y > MAXINT / sizeof(*col_buf)) overalloc(); + col_buf=mem_alloc(y*sizeof(*col_buf)); + bias_buf_gray(col_buf, y, ix>>1); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+ox; + out_end=out_pos+ix; + if (in_begin<out_pos)in_begin=out_pos; + if (in_end>out_end)in_end=out_end; + add_col_gray(col_buf,inptr,ix,y,in_end-in_begin); + in_end=in_pos+ox; + if (out_end>=in_end){ + in_pos=in_end; + inptr++; + } + if (out_end<=in_end){ + emit_and_bias_col_gray(col_buf,outptr,ox,y,ix); + out_pos=out_pos+ix; + outptr++; + } + if (out_pos==total) { + mem_free(in); + mem_free(col_buf); + return; + } + goto again; +} + +/* Works for both enlarging and diminishing. Linear resample, no low pass. + * Does only one color component. + * Frees ina and allocates outa. + * If ox*3<=ix, and display_optimize, performs optimization for LCD. + */ +inline static void scale_color_horizontal(unsigned short *ina, int ix, int y, + unsigned short **outa, int ox) +{ + unsigned *col_buf; + int total=ix*ox; + int out_pos,in_pos,in_begin,in_end,out_end; + unsigned skip=3*ix; + unsigned oskip=3*ox; + unsigned short *in, *out; + + if (ix==ox){ + *outa=ina; + return; + } + if (ix<ox){ + enlarge_color_horizontal(ina,ix,y,outa,ox); + return; + }else if (ix==ox){ + *outa=ina; + return; + } + if (ox && (unsigned)ox * (unsigned)y / (unsigned)ox != (unsigned)y) overalloc(); + if ((unsigned)ox * (unsigned)y > MAXINT / 3 / sizeof(*out)) overalloc(); + out=mem_alloc(sizeof(*out)*3*ox*y); + *outa=out; + in=ina; + if ((unsigned)y > MAXINT / 3 / sizeof(*col_buf)) overalloc(); + col_buf=mem_alloc(y*3*sizeof(*col_buf)); + bias_buf_color(col_buf,y,ix>>1); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+ox; + out_end=out_pos+ix; + if (in_begin<out_pos)in_begin=out_pos; + if (in_end>out_end)in_end=out_end; + add_col_color(col_buf,in,skip,y,in_end-in_begin); + in_end=in_pos+ox; + if (out_end>=in_end){ + in_pos=in_end; + in+=3; + } + if (out_end<=in_end){ + emit_and_bias_col_color(col_buf,out,oskip,y,ix); + out_pos=out_pos+ix; + out+=3; + } + if (out_pos==total) { + mem_free(ina); + mem_free(col_buf); + return; + } + goto again; +} + +/* For magnification only. Does linear filtering. */ +/* We assume unsigned holds at least 32 bits */ +inline static void enlarge_gray_vertical(unsigned char *in, int x, int iy, + unsigned char ** out ,int oy) +{ + unsigned *row_buf; + int total; + int out_pos,in_pos,in_begin,in_end; + int half=(oy-1)>>1; + unsigned char *outptr; + unsigned char *inptr; + + if (iy==1){ + if (x && (unsigned)x * (unsigned)oy / (unsigned)x != (unsigned)oy) overalloc(); + if ((unsigned)x * (unsigned)oy > MAXINT) overalloc(); + outptr=mem_alloc(oy*x); + *out=outptr; + for(;oy;oy--,outptr+=x) + memcpy(outptr,in,x); + mem_free(in); + } + else if (iy==oy){ + *out=in; + }else{ + if (x && (unsigned)x * (unsigned)oy / (unsigned)x != (unsigned)oy) overalloc(); + if ((unsigned)x * (unsigned)oy > MAXINT) overalloc(); + outptr=mem_alloc(oy*x); + inptr=in; + *out=outptr; + total=(iy-1)*(oy-1); + if ((unsigned)x > MAXINT / sizeof(*row_buf)) overalloc(); + row_buf=mem_alloc(x*sizeof(*row_buf)); + bias_buf_gray(row_buf, x, half); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+oy-1; + add_row_gray(row_buf, inptr, x, in_end-out_pos); + add_row_gray(row_buf, inptr+x, x, out_pos-in_begin); + emit_and_bias_row_gray(row_buf, outptr, x, oy-1); + outptr+=x; + out_pos+=iy-1; + if (out_pos>in_end){ + in_pos=in_end; + inptr+=x; + } + if (out_pos>total){ + mem_free(in); + mem_free(row_buf); + return; + } + goto again; + } +} + +/* For magnification only. Does linear filtering */ +/* We assume unsigned holds at least 32 bits */ +inline static void enlarge_color_vertical(unsigned short *ina, int x, int iy, + unsigned short **outa ,int oy) +{ + unsigned *row_buf; + int total,out_pos,in_pos,in_begin,in_end; + int half=(oy-1)>>1; + unsigned short *out, *in; + + if (iy==oy){ + *outa=ina; + return; + } + /* Rivendell */ + if (x && (unsigned)x * (unsigned)oy / (unsigned)x != (unsigned)oy) overalloc(); + if ((unsigned)x * (unsigned)oy > MAXINT / 3 / sizeof(*out)) overalloc(); + out=mem_alloc(sizeof(*out)*3*oy*x); + *outa=out; + in=ina; + if (iy==1){ + for (;oy;oy--){ + memcpy(out,in,3*x*sizeof(*out)); + out+=3*x; + } + mem_free(ina); + return; + } + total=(iy-1)*(oy-1); + if ((unsigned)x > MAXINT / 3 / sizeof(*row_buf)) overalloc(); + row_buf=mem_alloc(x*3*sizeof(*row_buf)); + bias_buf_color(row_buf,x,half); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+oy-1; + add_row_color(row_buf,in,x + ,in_end-out_pos); + add_row_color(row_buf,in+3*x,x + ,out_pos-in_begin); + emit_and_bias_row_color(row_buf,out,x,oy-1); + out+=3*x; + out_pos+=iy-1; + if (out_pos>in_end){ + in_pos=in_end; + in+=3*x; + } + if (out_pos>total){ + mem_free(ina); + mem_free(row_buf); + return; + } + goto again; + +} + +/* Both enlarges and diminishes. Linear filtering. + * Automatically allocates output and frees input. + * We assume unsigned holds at least 32 bits */ +inline static void scale_gray_vertical(unsigned char *in, int x, int iy, + unsigned char ** out ,int oy) +{ + unsigned *row_buf; + int total=iy*oy; + int out_pos,in_pos,in_begin,in_end,out_end; + unsigned char *outptr; + unsigned char *inptr; + + /* Snow White, Snow White... */ + if (iy<oy){ + enlarge_gray_vertical(in,x,iy,out,oy); + return; + } + if (iy==oy){ + *out=in; + return; + } + if (x && (unsigned)x * (unsigned)oy / (unsigned)x != (unsigned)oy) overalloc(); + if ((unsigned)x * (unsigned)oy > MAXINT) overalloc(); + outptr=mem_alloc(x*oy); + inptr=in; + *out=outptr; + if ((unsigned)x > MAXINT / sizeof(*row_buf)) overalloc(); + row_buf=mem_calloc(x*sizeof(*row_buf)); + bias_buf_gray(row_buf, x, iy>>1); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+oy; + out_end=out_pos+iy; + if (in_begin<out_pos)in_begin=out_pos; + if (in_end>out_end)in_end=out_end; + add_row_gray(row_buf,inptr,x,in_end-in_begin); + in_end=in_pos+oy; + if (out_end>=in_end){ + in_pos=in_end; + inptr+=x; + } + if (out_end<=in_end){ + emit_and_bias_row_gray(row_buf,outptr,x,iy); + out_pos=out_pos+iy; + outptr+=x; + } + if (out_pos==total){ + mem_free(in); + mem_free(row_buf); + return; + } + goto again; +} + +/* Both enlarges and diminishes. Linear filtering. Sizes are + in pixels. Sizes are not in bytes. 1 pixel=3 unsigned shorts. + We assume unsigned short can hold at least 16 bits. + We assume unsigned holds at least 32 bits. + */ +inline static void scale_color_vertical(unsigned short *ina, int x, int iy + ,unsigned short **outa, int oy) +{ + unsigned *row_buf; + int total=iy*oy; + int out_pos,in_pos,in_begin,in_end,out_end; + unsigned short *in, *out; + + if (iy==oy){ + *outa=ina; + return; + } + if (iy<oy){ + enlarge_color_vertical(ina,x,iy,outa,oy); + return; + } + if (x && (unsigned)x * (unsigned)oy / (unsigned)x != (unsigned)oy) overalloc(); + if ((unsigned)x * (unsigned)oy > MAXINT / 3 / sizeof(*out)) overalloc(); + out=mem_alloc(sizeof(*out)*3*oy*x); + *outa=out; + in=ina; + if ((unsigned)x > MAXINT / 3 / sizeof(*row_buf)) overalloc(); + row_buf=mem_alloc(x*3*sizeof(*row_buf)); + bias_buf_color(row_buf,x,iy>>1); + out_pos=0; + in_pos=0; + again: + in_begin=in_pos; + in_end=in_pos+oy; + out_end=out_pos+iy; + if (in_begin<out_pos)in_begin=out_pos; + if (in_end>out_end)in_end=out_end; + add_row_color(row_buf,in,x,in_end-in_begin); + in_end=in_pos+oy; + if (out_end>=in_end){ + in_pos=in_end; + in+=3*x; + } + if (out_end<=in_end){ + emit_and_bias_row_color(row_buf,out,x,iy); + out_pos=out_pos+iy; + out+=3*x; + } + if (out_pos==total){ + mem_free(ina); + mem_free(row_buf); + return; + } + goto again; +} + + +/* Scales grayscale 8-bit map. Both enlarges and diminishes. Uses either low + * pass or bilinear filtering. Automatically mem_frees the "in". + * Automatically allocates "out". + */ +inline static void scale_gray(unsigned char *in, int ix, int iy, unsigned char **out + ,int ox, int oy) +{ + unsigned char *intermediate_buffer; + + if (!ix||!iy){ + if (in) mem_free(in); + if (ox && (unsigned)ox * (unsigned)oy / (unsigned)ox != (unsigned)oy) overalloc(); + if ((unsigned)ox * (unsigned)oy > MAXINT) overalloc(); + *out=mem_calloc(ox*oy); + return; + } + if (ix*oy<ox*iy){ + scale_gray_vertical(in,ix,iy,&intermediate_buffer,oy); + scale_gray_horizontal(intermediate_buffer,ix,oy,out,ox); + }else{ + scale_gray_horizontal(in,ix,iy,&intermediate_buffer,ox); + scale_gray_vertical(intermediate_buffer,ox,iy,out,oy); + } +} + +/* To be called only when global variable display_optimize is 1 or 2. + * Performs a decimation according to this variable. Data shrink to 1/3 + * and x is the smaller width. + * There must be 9*x*y unsigned shorts of data. + * x must be >=1. + * Performs realloc onto the buffer after decimation to save memory. + */ +static void decimate_3(unsigned short **data0, int x, int y) +{ + unsigned short *data=*data0; + unsigned short *ahead=data; + int i, futuresize; + if (x && (unsigned)x * (unsigned)y / (unsigned)x != (unsigned)y) overalloc(); + if ((unsigned)x * (unsigned)y > MAXINT / 3 / sizeof(**data0)) overalloc(); + futuresize=x*y*3*sizeof(**data0); + +#ifdef DEBUG + if (!(x>0&&y>0)) internal("zero width or height in decimate_3"); +#endif /* #Ifdef DEBUG */ + if (display_optimize==1){ + if (x==1){ + for (;y;y--,ahead+=9,data+=3){ + data[0]=(ahead[0]+ahead[0]+ahead[3])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[5]+ahead[8]+ahead[8])/3; + } + }else{ + for (;y;y--){ + data[0]=(ahead[0]+ahead[0]+ahead[3])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[5]+ahead[8]+ahead[11])/3; + for (ahead+=9,data+=3,i=x-2;i;i--,ahead+=9,data+=3){ + data[0]=(ahead[-3]+ahead[0]+ahead[3])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[5]+ahead[8]+ahead[11])/3; + } + data[0]=(ahead[-3]+ahead[0]+ahead[3])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[5]+ahead[8]+ahead[8])/3; + ahead+=9,data+=3; + } + } + }else{ + /* display_optimize==2 */ + if (x==1){ + for (;y;y--,ahead+=9,data+=3){ + data[0]=(ahead[3]+ahead[6]+ahead[6])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[2]+ahead[2]+ahead[5])/3; + } + }else{ + for (;y;y--){ + data[0]=(ahead[3]+ahead[6]+ahead[9])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[2]+ahead[2]+ahead[5])/3; + for (ahead+=9,data+=3,i=x-2;i;i--,ahead+=9,data+=3){ + data[0]=(ahead[3]+ahead[6]+ahead[9])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[-1]+ahead[2]+ahead[5])/3; + } + data[0]=(ahead[3]+ahead[6]+ahead[6])/3; + data[1]=(ahead[1]+ahead[4]+ahead[7])/3; + data[2]=(ahead[-1]+ahead[2]+ahead[5])/3; + ahead+=9,data+=3; + } + } + } + *data0=mem_realloc(*data0,futuresize); +} + +/* Scales color 48-bits-per-pixel bitmap. Both enlarges and diminishes. Uses + * either low pass or bilinear filtering. The memory organization for both + * input and output are red, green, blue. All three of them are unsigned shorts 0-65535. + * Allocates output and frees input + * We assume unsigned short holds at least 16 bits. + */ +void scale_color(unsigned short *in, int ix, int iy, unsigned short **out, + int ox, int oy) +{ + unsigned short *intermediate_buffer; + int do_optimize; + int ox0=ox; + + if (!ix||!iy){ + if (in) mem_free(in); + if (ox && (unsigned)ox * (unsigned)oy / (unsigned)ox != (unsigned)oy) overalloc(); + if ((unsigned)ox * (unsigned)oy > MAXINT / 3 / sizeof(**out)) overalloc(); + *out=mem_calloc(ox*oy*sizeof(**out)*3); + return; + } + if (display_optimize&&ox*3<=ix){ + do_optimize=1; + ox0=ox; + ox*=3; + }else do_optimize=0; + if (ix*oy<ox*iy){ + scale_color_vertical(in,ix,iy,&intermediate_buffer,oy); + scale_color_horizontal(intermediate_buffer,ix,oy,out,ox); + }else{ + scale_color_horizontal(in,ix,iy,&intermediate_buffer,ox); + scale_color_vertical(intermediate_buffer,ox,iy,out,oy); + } + if (do_optimize) decimate_3(out, ox0, oy); +} + +/* Fills a block with given color. length is number of pixels. pixel is a + * tribyte. 24 bits per pixel. + */ +void mix_one_color_24(unsigned char *dest, int length, + unsigned char r, unsigned char g, unsigned char b) +{ + for (;length;length--){ + dest[0]=r; + dest[1]=g; + dest[2]=b; + dest+=3; + } +} + +/* Fills a block with given color. length is number of pixels. pixel is a + * tribyte. 48 bits per pixel. + * We assume unsigned short holds at least 16 bits. + */ +void mix_one_color_48(unsigned short *dest, int length, + unsigned short r, unsigned short g, unsigned short b) +{ + for (;length;length--){ + dest[0]=r; + dest[1]=g; + dest[2]=b; + dest+=3; + } +} + +/* Mixes ink and paper of a letter, using alpha as alpha mask. + * Only mixing in photon space makes physical sense so that the input values + * must always be equivalent to photons and not to electrons! + * length is number of pixels. pixel is a tribyte + * alpha is 8-bit, rgb are all 16-bit + * We assume unsigned short holds at least 16 bits. + */ +inline static void mix_two_colors(unsigned short *dest, unsigned char *alpha, int length + ,unsigned short r0, unsigned short g0, unsigned short b0, + unsigned short r255, unsigned short g255, unsigned short b255) +{ + unsigned mask,cmask; + + for (;length;length--){ + mask=*alpha++; + if (((unsigned char)(mask+1))>=2){ + cmask=255-mask; + dest[0]=(mask*r255+cmask*r0+127)/255; + dest[1]=(mask*g255+cmask*g0+127)/255; + dest[2]=(mask*b255+cmask*b0+127)/255; + }else{ + if (mask){ + dest[0]=r255; + dest[1]=g255; + dest[2]=b255; + }else{ + dest[0]=r0; + dest[1]=g0; + dest[2]=b0; + } + } + dest+=3; + } +} + +/* We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_and_undercolor_32_to_48_table(unsigned short *dest, + unsigned char *src, int lenght, unsigned short *table + ,unsigned short rb, unsigned short gb, unsigned short bb) +{ + unsigned alpha, ri, gi, bi, calpha; + + for (;lenght;lenght--) + { + ri=table[src[0]]; + gi=table[src[1]+256]; + bi=table[src[2]+512]; + alpha=src[3]; + src+=4; + if (((unsigned char)(alpha+1))>=2){ + calpha=255U-alpha; + dest[0]=(ri*alpha+calpha*rb+127)/255; + dest[1]=(gi*alpha+calpha*gb+127)/255; + dest[2]=(bi*alpha+calpha*bb+127)/255; + }else{ + if (alpha){ + dest[0]=ri; + dest[1]=gi; + dest[2]=bi; + }else{ + dest[0]=rb; + dest[1]=gb; + dest[2]=bb; + } + } + dest+=3; + } +} + +/* src is a block of four-bytes RGBA. All bytes are gamma corrected. length is + * number of pixels. output is input powered to the given gamma, passed into + * dest. src and dest may be identical and it will work. rb, gb, bb are 0-65535 + * in linear monitor output photon space + */ +/* We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_and_undercolor_32_to_48(unsigned short *dest, + unsigned char *src, int lenght, float red_gamma + ,float green_gamma, float blue_gamma, unsigned short rb, unsigned + short gb, unsigned short bb) +{ + float r,g,b; + unsigned alpha, calpha; + unsigned ri,gi,bi; + const float inv_255=1/255.0; + + for (;lenght;lenght--) + { + r=src[0]; + g=src[1]; + b=src[2]; + alpha=src[3]; + src+=4; + r*=inv_255; + g*=inv_255; + b*=inv_255; + r=pow(r,red_gamma); + g=pow(g,green_gamma); + b=pow(b,blue_gamma); + ri=(r*65535)+0.5; + gi=(g*65535)+0.5; + bi=(b*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + /* To prevent segfaults in case of crappy floating arithmetics + */ + if (ri>=65536) ri=65535; + if (gi>=65536) gi=65535; + if (bi>=65536) bi=65535; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + if (((alpha+1U)&0xffU)>=2U){ + calpha=255U-alpha; + *dest=(ri*alpha+calpha*rb+127U)/255U; + dest[1]=(gi*alpha+calpha*gb+127U)/255U; + dest[2]=(bi*alpha+calpha*bb+127U)/255U; + }else{ + if (alpha){ + *dest=ri; + dest[1]=gi; + dest[2]=bi; + }else{ + *dest=rb; + dest[1]=gb; + dest[2]=bb; + } + } + dest+=3; + } +} + +/* src is a block of four-bytes RGBA. All bytes are gamma corrected. length is + * number of pixels. output is input powered to the given gamma, passed into + * dest. src and dest may be identical and it will work. rb, gb, bb are 0-65535 + * in linear monitor output photon space. alpha 255 means full image no background. + */ +/* We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_and_undercolor_64_to_48(unsigned short *dest, + unsigned short *src, int lenght, float red_gamma + ,float green_gamma, float blue_gamma, unsigned short rb, unsigned + short gb, unsigned short bb) +{ + float r,g,b; + unsigned alpha, calpha; + unsigned short ri,gi,bi; + const float inv_65535=1/((float)65535); + + for (;lenght;lenght--) + { + r=src[0]; + g=src[1]; + b=src[2]; + alpha=src[3]; + src+=4; + r*=inv_65535; + g*=inv_65535; + b*=inv_65535; + r=pow(r,red_gamma); + g=pow(g,green_gamma); + b=pow(b,blue_gamma); + ri=r*65535+0.5; + gi=g*65535+0.5; + bi=b*65535+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + /* To prevent segfaults in case of crappy floating arithmetics + */ + if (ri>=65536) ri=65535; + if (gi>=65536) gi=65535; + if (bi>=65536) bi=65535; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + if (((alpha+1U)&255U)>=2U){ + calpha=65535U-alpha; + *dest=(ri*alpha+calpha*rb+32767U)/65535U; + dest[1]=(gi*alpha+calpha*gb+32767U)/65535U; + dest[2]=(bi*alpha+calpha*bb+32767U)/65535U; + }else{ + if (alpha){ + *dest=ri; + dest[1]=gi; + dest[2]=bi; + }else{ + *dest=rb; + dest[1]=gb; + dest[2]=bb; + } + } + dest+=3; + } +} + +/* src is a block of four-bytes RGBA. All bytes are gamma corrected. length is + * number of pixels. output is input powered to the given gamma, passed into + * dest. src and dest may be identical and it will work. rb, gb, bb are 0-65535 + * in linear monitor output photon space. alpha 255 means full image no background. + * We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_and_undercolor_64_to_48_table(unsigned short *dest + ,unsigned short *src, int lenght, unsigned short *gamma_table + ,unsigned short rb, unsigned short gb, unsigned short bb) +{ + unsigned alpha, calpha; + unsigned short ri,gi,bi; + + for (;lenght;lenght--) + { + ri=gamma_table[*src]; + gi=gamma_table[src[1]+65536]; + bi=gamma_table[src[2]+131072]; + alpha=src[3]; + src+=4; + if (((alpha+1)&0xffff)>=2){ + calpha=65535-alpha; + *dest=(ri*alpha+calpha*rb+32767)/65535; + dest[1]=(gi*alpha+calpha*gb+32767)/65535; + dest[2]=(bi*alpha+calpha*bb+32767)/65535; + }else{ + if (alpha){ + *dest=ri; + dest[1]=gi; + dest[2]=bi; + }else{ + *dest=rb; + dest[1]=gb; + dest[2]=bb; + } + } + dest+=3; + } +} + +/* src is a block of three-bytes. All bytes are gamma corrected. length is + * number of triplets. output is input powered to the given gamma, passed into + * dest. src and dest may be identical and it will work. + * We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_48_to_48(unsigned short *dest, + unsigned short *src, int lenght, float red_gamma + ,float green_gamma, float blue_gamma) +{ + float a, inv_65535=1/((float)65535); + + for (;lenght;lenght--,src+=3,dest+=3) + { + a=*src; + a*=inv_65535; + a=pow(a,red_gamma); + *dest=(a*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*dest>=0x10000) *dest=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + a=src[1]; + a*=inv_65535; + a=pow(a,green_gamma); + dest[1]=(a*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (dest[1]>=0x10000) dest[1]=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + a=src[2]; + a*=inv_65535; + a=pow(a,blue_gamma); + dest[2]=(a*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (dest[2]>=0x10000) dest[2]=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } +} + +/* src is a block of three-bytes. All bytes are gamma corrected. length is + * number of triples. output is input powered to the given gamma, passed into + * dest. src and dest may be identical and it will work. + * We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_48_to_48_table(unsigned short *dest, + unsigned short *src, int lenght, unsigned short *table) +{ + for (;lenght;lenght--,src+=3,dest+=3) + { + *dest=table[*src]; + dest[1]=table[src[1]+65536]; + dest[2]=table[src[2]+131072]; + } +} + +/* src is a block of three-bytes. All bytes are gamma corrected. length is + * number of triples. output is input powered to the given gamma, passed into + * dest. src and dest may be identical and it will work. + * We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_24_to_48(unsigned short *dest, unsigned char *src, int + lenght, float red_gamma, float green_gamma, float + blue_gamma) +{ + float a; + float inv_255=1/((float)255); + + for (;lenght;lenght--,src+=3,dest+=3) + { + a=*src; + a*=inv_255; + a=pow(a,red_gamma); + *dest=(a*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*dest>=0x10000) *dest=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + a=src[1]; + a*=inv_255; + a=pow(a,green_gamma); + dest[1]=(a*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (dest[1]>=0x10000) dest[1]=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + a=src[2]; + a*=inv_255; + a=pow(a,blue_gamma); + dest[2]=(a*65535)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (dest[2]>=0x10000) dest[2]=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } +} + +/* Allocates new gamma_table and fills it with mapping 8 bits -> + * power to user_gamma/cimg->*_gamma -> 16 bits + * We assume unsigned short holds at least 16 bits. */ +void make_gamma_table(struct cached_image *cimg) +{ + double rg=user_gamma/cimg->red_gamma; + double gg=user_gamma/cimg->green_gamma; + double bg=user_gamma/cimg->blue_gamma; + double inv; + int a; + unsigned short *ptr_16; + + if (cimg->buffer_bytes_per_pixel<=4){ + /* 8-bit */ + inv=1/((double)255); + ptr_16=mem_alloc(768*sizeof(*(cimg->gamma_table))); + cimg->gamma_table=ptr_16; + for (a=0;a<256;a++,ptr_16++){ + *ptr_16=65535*pow(((double)a)*inv,rg)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + /* To test against crappy arithmetics */ + if (*ptr_16>=0x10000) *ptr_16=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } + for (a=0;a<256;a++,ptr_16++){ + *ptr_16=65535*pow(((double)a)*inv,gg)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*ptr_16>=0x10000) *ptr_16=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } + for (a=0;a<256;a++,ptr_16++){ + *ptr_16=65535*pow(((double)a)*inv,bg)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*ptr_16>=0x10000) *ptr_16=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } + }else{ + /* 16-bit */ + inv=1/((double)65535); + ptr_16=mem_alloc(196608*sizeof(*(cimg->gamma_table))); + cimg->gamma_table=ptr_16; + for (a=0;a<0x10000;a++,ptr_16++){ + *ptr_16=65535*pow(((double)a)*inv,rg)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*ptr_16>=0x10000) *ptr_16=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } + for (a=0;a<0x10000;a++,ptr_16++){ + *ptr_16=65535*pow(((double)a)*inv,gg)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*ptr_16>=0x10000) *ptr_16=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } + for (a=0;a<0x10000;a++,ptr_16++){ + *ptr_16=65535*pow(((double)a)*inv,bg)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (*ptr_16>=0x10000) *ptr_16=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + } + } +} + +/* We assume unsigned short holds at least 16 bits. */ +void apply_gamma_exponent_24_to_48_table(unsigned short *dest, unsigned char *src, int + lenght, unsigned short *table) +{ + for (;lenght;lenght--,src+=3,dest+=3) + { + dest[0]=table[src[0]]; + dest[1]=table[src[1]+256]; + dest[2]=table[src[2]+512]; + } +} + +#if 0 +/* Input is 0-255 (8-bit). Output is 0-255 (8-bit)*/ +unsigned char apply_gamma_single_8_to_8(unsigned char input, float gamma) +{ + return 255*pow(((float) input)/255,gamma)+0.5; +} +#endif + +/* Input is 0-255 (8-bit). Output is 0-65535 (16-bit)*/ +/* We assume unsigned short holds at least 16 bits. */ +unsigned short apply_gamma_single_8_to_16(unsigned char input, float gamma) +{ + float a=input; + unsigned short retval; + + a/=255; + a=pow(a,gamma); + a*=65535; + retval = a+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (retval>=0x10000) retval=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + return retval; +} + +/* Input is 0-65535 (16-bit). Output is 0-255 (8-bit)*/ +/* We assume unsigned short holds at least 16 bits. */ +unsigned char apply_gamma_single_16_to_8(unsigned short input, float gamma) +{ + return pow(((float)input)/65535,gamma)*255+0.5; +} + +/* Input is 0-65535 (16-bit). Output is 0-255 (8-bit)*/ +unsigned short apply_gamma_single_16_to_16(unsigned short input, float gamma) +{ + unsigned short retval; + + retval = 65535*pow(((float)input)/65535,gamma)+0.5; +#if !SIZEOF_UNSIGNED_SHORT || SIZEOF_UNSIGNED_SHORT > 2 + if (retval>=0x10000) retval=0xffff; +#endif /* #if SIZEOF_UNSIGNED_SHORT > 2 */ + return retval; +} + +/* Points to the next unread byte from png data block */ +extern unsigned char font_data[]; +extern struct letter letter_data[]; +extern struct font font_table[]; +extern int n_fonts; /* Number of fonts. font number 0 is system_font (it's + * images are in system_font/ directory) and is used + * for special purpose. + */ + +/* Returns a pointer to a structure describing the letter found or NULL + * if the letter is not found. Tries all possibilities in the style table + * before returning NULL. + */ +static struct letter *find_stored_letter(int *style_table, int letter_number) +{ + int first, last, half, diff, font_index, font_number; + + for (font_index=n_fonts-1;font_index;font_index--) + { + font_number=*style_table++; + + first=font_table[font_number].begin; + last=font_table[font_number].length+first-1; + + while(first<=last){ + half=(first+last)>>1; + diff=letter_data[half].code-letter_number; + if (diff>=0){ + if (diff==0){ + return letter_data+half; + }else{ + /* Value in table is bigger */ + last=half-1; + } + }else{ + /* Value in the table is smaller */ + first=half+1; + } + } + } + + /* 0 is system font, 0 is blotch char. This must be present + * or we segfault :) */ +#ifdef REPORT_UNKNOWN + fprintf(stderr,"letter 0x%04x not found\n",letter_number); +#endif /* #ifdef REPORT_UNKNOWN */ + return letter_data+font_table[0].begin; +} + +static void read_stored_data(png_structp png_ptr, png_bytep data, png_uint_32 length) +{ + struct read_work *work; + + work=png_get_io_ptr(png_ptr); + if (length>(png_uint_32)work->length) png_error(png_ptr,"Ran out of input data"); + memcpy(data,work->pointer,length); + work->length-=length; + work->pointer+=length; +} + +static void my_png_warning(png_structp a, png_const_charp b) +{ +} + +static void my_png_error(png_structp a, png_const_charp error_string) +{ + error("Error when loading compiled-in font: %s.",error_string); +} + +/* Loads width and height of the PNG (nothing is scaled). Style table is + * already incremented. + */ +inline static void load_metric(int *x, int *y, int char_number, int *style_table) +{ + struct letter *l; + + l=find_stored_letter(style_table,char_number); + if (!l){ + *x=0; + *y=0; + }else{ + *x=l->xsize; + *y=l->ysize; + } + return; +} + +/* The data tha fall out of this function express this: 0 is paper. 255 is ink. 34 + * is 34/255ink+(255-34)paper. No gamma is involved in this formula, as you can see. + * The multiplications and additions take place in photon space. + */ +static void load_char(unsigned char **dest, int *x, int *y, +unsigned char *png_data, int png_length, struct style *style) +{ + png_structp png_ptr; + png_infop info_ptr; + double gamma; + int y1,number_of_passes; + unsigned char **ptrs; + struct read_work work; + + work.pointer = png_data; + work.length = png_length; + + retry1: + png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, my_png_error, my_png_warning); + if (!png_ptr) { + if (out_of_memory(NULL, 0)) goto retry1; + error("png_create_read_struct failed"); + fatal_tty_exit(); + exit(RET_FATAL); + } + retry2: + info_ptr=png_create_info_struct(png_ptr); + if (!info_ptr) { + if (out_of_memory(NULL, 0)) goto retry2; + error("png_create_info_struct failed"); + fatal_tty_exit(); + exit(RET_FATAL); + } + png_set_read_fn(png_ptr,&work,(png_rw_ptr)&read_stored_data); + png_read_info(png_ptr, info_ptr); + *x=png_get_image_width(png_ptr,info_ptr); + *y=png_get_image_height(png_ptr,info_ptr); + if (png_get_gAMA(png_ptr,info_ptr, &gamma)) + png_set_gamma(png_ptr, 1.0, gamma); + else + png_set_gamma(png_ptr, 1.0, sRGB_gamma); + { + int bit_depth; + int color_type; + + color_type=png_get_color_type(png_ptr, info_ptr); + bit_depth=png_get_bit_depth(png_ptr, info_ptr); + if (color_type==PNG_COLOR_TYPE_GRAY){ + if (bit_depth<8){ + png_set_expand(png_ptr); + } + if (bit_depth==16){ + png_set_strip_16(png_ptr); + } + } + if (color_type==PNG_COLOR_TYPE_PALETTE){ + png_set_expand(png_ptr); +#ifdef HAVE_PNG_SET_RGB_TO_GRAY + png_set_rgb_to_gray(png_ptr, 1, -1, -1); +#else + goto end; +#endif + } + if (color_type & PNG_COLOR_MASK_ALPHA){ + png_set_strip_alpha(png_ptr); + } + if (color_type==PNG_COLOR_TYPE_RGB || + color_type==PNG_COLOR_TYPE_RGB_ALPHA){ +#ifdef HAVE_PNG_SET_RGB_TO_GRAY + png_set_rgb_to_gray(png_ptr, 1, -1, -1); +#else + goto end; +#endif + } + + } + /* If the depth is different from 8 bits/gray, make the libpng expand + * it to 8 bit gray. + */ + number_of_passes=png_set_interlace_handling(png_ptr); + png_read_update_info(png_ptr,info_ptr); + if (*x && (unsigned)*x * (unsigned)*y / (unsigned)*x != (unsigned)*y) overalloc(); + if ((unsigned)*x * (unsigned)*y > MAXINT) overalloc(); + *dest=mem_alloc(*x*(*y)); + if ((unsigned)*y > MAXINT / sizeof(*ptrs)) overalloc(); + ptrs=mem_alloc(*y*sizeof(*ptrs)); + for (y1=0;y1<*y;y1++) ptrs[y1]=*dest+*x*y1; + for (;number_of_passes;number_of_passes--){ + png_read_rows(png_ptr, ptrs, NULL, *y); + } + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + mem_free(ptrs); + return; +#ifndef HAVE_PNG_SET_RGB_TO_GRAY + end: + if (*x && (unsigned)*x * (unsigned)*y / (unsigned)*x != (unsigned)*y) overalloc(); + if ((unsigned)*x * (unsigned)*y > MAXINT) overalloc(); + *dest=mem_calloc(*x*(*y)); + return; +#endif +} + +/* Like load_char, but we dictate the y. + */ +static void load_scaled_char(void **dest, int *x, int y, +unsigned char *png_data, int png_length, struct style *style) +{ + unsigned char *interm; + unsigned char *interm2; + unsigned char *i2ptr,*dptr; + int ix, iy, y0, x0, c; + float conv0, conv1,sharpness,contrast; + + load_char(&interm, &ix,&iy,png_data, png_length,style); + if (style->mono_space>=0) + *x=compute_width(style->mono_space, style->mono_height, y); + else + *x=compute_width(ix,iy,y); + if (display_optimize) *x*=3; + scale_gray(interm, ix,iy, (unsigned char **)dest, *x, y); + if (y>32||y<=0) return ; /* No convolution */ + ix=*x+2; /* There is one-pixel border around */ + iy=y+2; + if (ix && (unsigned)ix * (unsigned)iy / (unsigned)ix != (unsigned)iy) overalloc(); + if ((unsigned)ix * (unsigned)iy > MAXINT) overalloc(); + interm2=mem_alloc(ix*iy); + i2ptr=interm2+ix+1; + dptr=*dest; + memset(interm2,0,ix); + memset(interm2+(iy-1)*ix,0,ix); + for (y0=y;y0;y0--){ + i2ptr[-1]=0; + memcpy(i2ptr,dptr,*x); + i2ptr[ix-1]=0; + i2ptr+=ix; + dptr+=*x; + } + i2ptr=interm2+ix+1; + dptr=*dest; + + /* Determine the sharpness and contrast */ + sharpness=fancy_constants[2*y-2]; + contrast=fancy_constants[2*y-1]; + + /* Compute the matrix constants from contrast and sharpness */ + conv0=(1+sharpness)*contrast; + conv1=-sharpness*0.25*contrast; + + for (y0=y;y0;y0--){ + for (x0=*x;x0;x0--){ + /* Convolution */ + c=((*i2ptr)*conv0)+i2ptr[-ix]*conv1+i2ptr[-1]*conv1+i2ptr[1]*conv1+i2ptr[ix]*conv1+0.5; + if (((unsigned)c)>=256) c=c<0?0:255; + *dptr=c; + dptr++; + i2ptr++; + } + i2ptr+=2; + } + mem_free(interm2); +} + +static struct font_cache_entry *locked_bw_entry = NULL; +static struct font_cache_entry *locked_color_entry = NULL; + +/* Adds required entry into font_cache and returns pointer to the entry. + * We assume the entry is FC_COLOR. + */ +static struct font_cache_entry *supply_color_cache_entry (struct graphics_driver +*gd, struct style *style, struct letter *letter) +{ + struct font_cache_entry *found, *new; + unsigned short *primary_data; + int do_free=0; + struct font_cache_entry template; + unsigned short red, green, blue; + unsigned bytes_consumed; + + template.bitmap.y=style->height; + template.type=FC_BW; + /* The BW entries will be added only for background images which + * are not yet implemented, but will be in the future. + */ + + found=lru_lookup(&font_cache, &template, letter->bw_list); + if (!found){ + found=mem_alloc(sizeof(*found)); + found->bitmap.y=style->height; + load_scaled_char(&(found->bitmap.data),&(found->bitmap.x), + found->bitmap.y, font_data+letter->begin, + letter->length, style); + do_free=1; + } else { + locked_bw_entry = found; + } + new=mem_alloc(sizeof(*new)); + locked_color_entry = new; + new->type=FC_COLOR; + new->bitmap=found->bitmap; + new->r0=style->r0; + new->g0=style->g0; + new->b0=style->b0; + new->r1=style->r1; + new->g1=style->g1; + new->b1=style->b1; + new->mono_space=style->mono_space; + new->mono_height=style->mono_height; + + if (new->bitmap.x && (unsigned)new->bitmap.x * (unsigned)new->bitmap.y / (unsigned)new->bitmap.x != (unsigned)new->bitmap.y) overalloc(); + if ((unsigned)new->bitmap.x * (unsigned)new->bitmap.y > MAXINT / 3 / sizeof(*primary_data)) overalloc(); + primary_data=mem_alloc(3 + *new->bitmap.x*new->bitmap.y*sizeof(*primary_data)); + + /* We assume the gamma of HTML styles is in sRGB space */ + round_color_sRGB_to_48(&red, &green, &blue, + ((style->r0)<<16)|((style->g0)<<8)|(style->b0)); + mix_two_colors(primary_data, found->bitmap.data, + found->bitmap.x*found->bitmap.y, + red,green,blue, + apply_gamma_single_8_to_16(style->r1,user_gamma/sRGB_gamma), + apply_gamma_single_8_to_16(style->g1,user_gamma/sRGB_gamma), + apply_gamma_single_8_to_16(style->b1,user_gamma/sRGB_gamma) + ); + if (display_optimize){ + /* A correction for LCD */ + new->bitmap.x/=3; + decimate_3(&primary_data,new->bitmap.x,new->bitmap.y); + } + /* We have a buffer with photons */ + if (gd->get_empty_bitmap(&(new->bitmap))) + goto skip_dither; + if (dither_letters) + dither(primary_data, &(new->bitmap)); + else + (*round_fn)(primary_data,&(new->bitmap)); + skip_dither: + mem_free(primary_data); + gd->register_bitmap(&(new->bitmap)); + if (do_free){ + mem_free(found->bitmap.data); + mem_free(found); + } else { + locked_bw_entry = NULL; + } + bytes_consumed=new->bitmap.x*new->bitmap.y*(gd->depth&7); + /* Number of bytes per pixel in passed bitmaps */ + bytes_consumed+=sizeof(*new); + bytes_consumed+=sizeof(struct lru_entry); + lru_insert(&font_cache, new, &(letter->color_list), + bytes_consumed); + return new; +} + +static int destroy_font_cache_bottom(void) +{ + struct font_cache_entry *bottom; + bottom=lru_get_bottom(&font_cache); + if (!bottom) return 0; + if (bottom == locked_bw_entry || bottom == locked_color_entry) return 0; + if (bottom->type==FC_COLOR){ + drv->unregister_bitmap(&(bottom->bitmap)); + }else{ + mem_free(bottom->bitmap.data); + } + mem_free(bottom); + lru_destroy_bottom(&font_cache); + return 1; +} + +/* Prunes the cache to comply with maximum size */ +static int prune_font_cache(void) +{ + int r = 0; + + while (font_cache.bytes > (unsigned)font_cache_size) { + if (destroy_font_cache_bottom()) { + r = 1; + } else { + break; + } + } + return r; +} + +/* Prints a letter to the specified position and + * returns the width of the printed letter */ +inline static int print_letter(struct graphics_driver *gd, struct + graphics_device *device, int x, int y, struct style *style, + int char_number) + +{ + int xw; + struct font_cache_entry *found; + struct font_cache_entry template; + struct letter *letter; + + /* Find a suitable letter */ + letter=find_stored_letter(style->table,char_number); +#ifdef DEBUG + if (!letter) internal("print_letter could not find a letter - even not the blotch!"); +#endif /* #ifdef DEBUG */ + template.type=FC_COLOR; + template.r0=style->r0; + template.r1=style->r1; + template.g0=style->g0; + template.g1=style->g1; + template.b0=style->b0; + template.b1=style->b1; + template.bitmap.y=style->height; + template.mono_space=style->mono_space; + template.mono_height=style->mono_height; + + found=lru_lookup(&font_cache, &template, letter->color_list); + if (!found) found=supply_color_cache_entry(gd, style, letter); + else locked_color_entry = found; + gd->draw_bitmap(device, &(found->bitmap), x, y); + xw = found->bitmap.x; + if (locked_color_entry != found) internal("bad letter lock"); + locked_color_entry = NULL; + prune_font_cache(); + return xw; +} + +/* Must return values that are: + * >=0 + * <=height + * at least 1 apart + * Otherwise g_print_text will print nonsense (but won't segfault) + */ +static void get_underline_pos(int height, int *top, int *bottom) +{ + int thickness, baseline; + thickness=(height+15)/16; + baseline=height/7; + if (baseline<=0) baseline=1; + if (thickness>baseline) thickness=baseline; + *top=height-baseline; + *bottom=*top+thickness; +} + +/* *width will be advanced by the width of the text */ +void g_print_text(struct graphics_driver *gd, struct graphics_device *device, +int x, int y, struct style *style, unsigned char *text, int *width) +{ + int original_flags, top_underline, bottom_underline, original_width, + my_width; + struct rect saved_clip; + + if (y+style->height<=device->clip.y1||y>=device->clip.y2) goto o; + if (style -> flags){ + /* Underline */ + if (!width){ + width=&my_width; + *width=0; + } + original_flags=style->flags; + original_width=*width; + style -> flags=0; + get_underline_pos(style->height, &top_underline, &bottom_underline); + restrict_clip_area(device, &saved_clip, 0, 0, device->size.x2, y+ + top_underline); + g_print_text(gd, device, x, y, style, text, width); + gd->set_clip_area(device, &saved_clip); + if (bottom_underline-top_underline==1){ + /* Line */ + drv->draw_hline(device, x, y+top_underline + , x+*width-original_width + , style->underline_color); + }else{ + /* Area */ + drv->fill_area(device, x, y+top_underline, + x+*width-original_width + ,y+bottom_underline, + style->underline_color); + } + if (bottom_underline<style->height){ + /* Do the bottom half only if the underline is above + * the bottom of the letters. + */ + *width=original_width; + restrict_clip_area(device, &saved_clip, 0, + y+bottom_underline, device->size.x2, + device->size.y2); + g_print_text(gd, device, x, y, style, text, width); + gd->set_clip_area(device, &saved_clip); + } + style -> flags=original_flags; + return; + } + while (*text) { + int p; + int u; + GET_UTF_8(text, u); + /* 00-09, 0b-1f, 80, 81, 84, 86-9f ignorovat + * 0a = LF + * 82 = ' ' + * 83 = nobrk + * 85 = radkovy zlom + * a0 = NBSP + * ad = soft hyphen + */ + #if 0 + if ( (u>=0x00&&u<=0x09)|| + (u>=0x0b&&u<=0x1f)|| + u==0x80|| + u==0x82|| + u==0x84|| + (u>=0x86&&u<=0x9f) + )continue; + if (u==0x82)u=' '; + #endif + /* stare Mikulasovo patchovani, musim to opravit -- Brain */ + if (!u || u == 0xad) continue; + if (u == 0x01 || u == 0xa0) u = ' '; + p=print_letter(gd,device,x,y,style, u); + x += p; + if (width) { + *width += p; + continue; + } + if (x>=device->clip.x2) return; + } + return; + o: + if (width) *width += g_text_width(style, text); +} + +/* 0=equality 1=inequality */ +static int compare_font_entries(void *entry, void *template) +{ + struct font_cache_entry*e1=entry; + struct font_cache_entry*e2=template; + + if (e1->type==FC_COLOR){ + return( + (e1->r0!=e2->r0)|| + (e1->g0!=e2->g0)|| + (e1->b0!=e2->b0)|| + (e1->r1!=e2->r1)|| + (e1->g1!=e2->g1)|| + (e1->b1!=e2->b1)|| + (e1->bitmap.y!=e2->bitmap.y)|| + (e1->mono_space!=e2->mono_space)|| + (e1->mono_space>=0&&e1->mono_height!=e2->mono_height)); + }else{ + return e1->bitmap.y!=e2->bitmap.y; + } + +} + +/* If the cache already exists, it is destroyed and reallocated. If you call it with the same + * size argument, only a cache flush will yield. + */ +static void init_font_cache(void) +{ + lru_init(&font_cache, &compare_font_entries); +} + +/* Ensures there are no lru_entry objects allocated - destroys them. + * Also destroys the bitmaps asociated with them. Does not destruct the + font_cache per se. + */ +static void destroy_font_cache(void) +{ + while (destroy_font_cache_bottom()) + ; + if (lru_get_bottom(&font_cache)) + internal("destroy_font_cache: cache not freed due to locks"); +} + +/* Returns 0 in case the char is not found. */ +static inline int g_get_width(struct style *style, unsigned charcode) +{ + int x, y, width; + + if (!charcode || charcode == 0xad) return 0; + if (charcode == 0x01 || charcode == 0xa0) charcode = ' '; + if (style->mono_space>=0){ + x=style->mono_space; + y=style->mono_height; + }else load_metric(&x,&y,charcode,style->table); + if (!(x&&y)) width=0; + else width=compute_width(x,y,style->height); + return width; +} + +int g_text_width(struct style *style, unsigned char *text) +{ + int w = 0; + while (*text) { + int u; + GET_UTF_8(text, u); + w += g_get_width(style, u); + } + return w; +} + +int g_char_width(struct style *style, unsigned charcode) +{ + return g_get_width(style, charcode); +} + +int g_wrap_text(struct wrap_struct *w) +{ + unsigned char *init_text = w->text; + while (*w->text) { + int u; + int s; + unsigned char *l_text = w->text; + if (*l_text == ' ') w->last_wrap = l_text, + w->last_wrap_obj = w->obj; + GET_UTF_8(w->text, u); + if (!u) continue; + s = g_get_width(w->style, u); + if ((w->pos += s) <= w->width) { + c: + if (u != 0xad || *w->text == ' ' || w->force_break) continue; + s = g_char_width(w->style, '-'); + if (w->pos + s <= w->width || (!w->last_wrap && !w->last_wrap_obj)) { + w->last_wrap = l_text; + w->last_wrap_obj = w->obj; + continue; + } + } + if (w->force_break && !w->last_wrap && l_text != init_text) { + w->last_wrap = l_text; + w->last_wrap_obj = w->obj; + } + if (!w->last_wrap && !w->last_wrap_obj) goto c; + return 0; + } + return 1; +} + +void update_aspect(void) +{ + aspect=aspect_on?(aspect_native*bfu_aspect+0.5):65536UL; + destroy_font_cache(); +} + +unsigned long fontcache_info(int type) +{ + switch (type) { + case CI_BYTES: + return font_cache.bytes; + case CI_FILES: + return font_cache.items; + default: + internal("fontcache_info: query %d", type); + return 0; + } +} + +static int shrink_font_cache(int u) +{ + int freed_something = 0; + int has_something; + if (u == SH_CHECK_QUOTA) { + freed_something = prune_font_cache(); + } + if (u == SH_FREE_ALL) { + while (destroy_font_cache_bottom()) + freed_something = 1; + } + if (u == SH_FREE_SOMETHING) { + freed_something = destroy_font_cache_bottom(); + } + has_something = !!lru_get_bottom(&font_cache); + return (freed_something ? ST_SOMETHING_FREED : 0) | + (has_something ? 0 : ST_CACHE_EMPTY); +} + +void init_dip(void) +{ + init_font_cache(); + update_aspect(); + register_cache_upcall(shrink_font_cache, "fontcache"); +} + +static void recode_font_name(unsigned char **name) +{ + int dashes=0; + unsigned char *p; + + if (!strcmp(*name,"monospaced")) *name="courier-medium-roman-serif-mono"; + if (!strcmp(*name,"monospace")) *name="courier-medium-roman-serif-mono"; + else if (!strcmp(*name,"")) *name="century_school-medium-roman-serif-vari"; + p=*name; + while(*p){ + if (*p=='-')dashes++; + p++; + } + if (dashes!=4) *name="century_school-medium-roman-serif-vari"; +} + +/* Compares single=a multi=b-c-a as matching. + * 0 matches + * 1 doesn't match + */ +static int compare_family(unsigned char *single, unsigned char *multi) +{ + unsigned char *p,*r; + int single_length=strlen(single); + + r=multi; + while(1){ + p=r; + while (*r&&*r!='-')r++; + if ((r-p==single_length)&&!strncmp(single,p,r-p)) return 0; + if (!*r) return 1; + r++; + } + return 1; +} + +/* Input name must contain exactly 4 dashes, otherwise the + * result is undefined (parsing into weight, slant, adstyl, spacing + * will result deterministically random results). + * Returns 1 if the font is monospaced or 0 if not. + */ +static int fill_style_table(int * table, unsigned char *name) +{ + unsigned char *p; + unsigned char *family, *weight, *slant, *adstyl, *spacing; + int pass,result,f; + int masks[6]={0x1f,0x1f,0xf,0x7,0x3,0x1}; + int xors[6]={0,0x10,0x8,0x4,0x2,0x1}; + /* Top bit of the values belongs to family, bottom to spacing */ + int monospaced; + + /* Parse the name */ + recode_font_name(&name); + family=stracpy(name); + p=family; + while(*p&&*p!='-') p++; + *p=0; + p++; + weight=p; + while(*p&&*p!='-') p++; + *p=0; + p++; + slant=p; + while(*p&&*p!='-') p++; + *p=0; + p++; + adstyl=p; + while(*p&&*p!='-') p++; + *p=0; + p++; + spacing=p; + monospaced=!strcmp(spacing,"mono"); + + for (pass=0;pass<6;pass++){ + for (f=1;f<n_fonts;f++){ + /* Font 0 must not be int style_table */ + result=compare_family(family,font_table[f].family); + result<<=1; + result|=!!strcmp(weight,font_table[f].weight); + result<<=1; + result|=!!strcmp(slant,font_table[f].slant); + result<<=1; + result|=!!strcmp(adstyl,font_table[f].adstyl); + result<<=1; + result|=!!strcmp(spacing,font_table[f].spacing); + result^=xors[pass]; + result&=masks[pass]; + if (!result) /* Fot complies */ + *table++=f; + } + } + mem_free(family); + return monospaced; +} + +struct style *g_invert_style(struct style *old) +{ + int length; + + struct style *st; + st = mem_alloc(sizeof(struct style)); + st->refcount=1; + st->r0=old->r1; + st->g0=old->g1; + st->b0=old->b1; + st->r1=old->r0; + st->g1=old->g0; + st->b1=old->b0; + st->height=old->height; + st->flags=old->flags; + if (st->flags) + { + /* We have to get a foreground color for underlining */ + st->underline_color=dip_get_color_sRGB( + (st->r1<<16)|(st->g1<<8)|(st->b1)); + } + if ((unsigned)n_fonts > MAXINT / sizeof(*st->table)) overalloc(); + length=sizeof(*st->table)*(n_fonts-1); + st->table=mem_alloc(length); + memcpy(st->table,old->table,length); + st->mono_space=old->mono_space; + st->mono_height=old->mono_height; + return st; +} + +/* Never returns NULL. */ +struct style *g_get_style(int fg, int bg, int size, unsigned char *font, int flags) +{ + struct style *st; + + st = mem_alloc(sizeof(struct style)); + /* strcpy(st->font, font); */ + st->refcount = 1; + st->r0 = bg >> 16; + st->g0 = (bg >> 8) & 255; + st->b0 = bg & 255; + st->r1 = fg >> 16; + st->g1 = (fg >> 8) & 255; + st->b1 = fg & 255; + if (size<=0) size=1; + st->height = size; + st->flags=flags&FF_UNDERLINE; + if (st->flags) + { + /* We have to get a foreground color for underlining */ + st->underline_color=dip_get_color_sRGB(fg); + } + if ((unsigned)n_fonts > MAXINT / sizeof(*st->table)) overalloc(); + st->table=mem_alloc(sizeof(*st->table)*(n_fonts-1)); + if(fill_style_table(st->table, font)) + load_metric(&(st->mono_space), &(st->mono_height),' ',st->table); + else + st->mono_space=-1; + return st; +} + +struct style *g_clone_style(struct style *st) +{ + st->refcount++; + return st; +} + +void g_free_style(struct style *st) +{ + if (--st->refcount) return; + mem_free(st->table); + mem_free(st); +} + +long gamma_cache_color; +int gamma_cache_rgb = -2; + +/* IEC 61966-2-1 + * Input gamma: sRGB space (directly from HTML, i. e. unrounded) + * Output: color index for graphics driver that is closest to the + * given sRGB value. + * We assume unsigned short holds at least 16 bits. */ +long real_dip_get_color_sRGB(int rgb) +{ + unsigned short r,g,b; + int new_rgb; + + round_color_sRGB_to_48(&r,&g,&b,rgb); + r=apply_gamma_single_16_to_8(r,1/display_red_gamma); + g=apply_gamma_single_16_to_8(g,1/display_green_gamma); + b=apply_gamma_single_16_to_8(b,1/display_blue_gamma); + new_rgb=b|(g<<8)|(r<<16); + gamma_cache_rgb = rgb; + /* The get_color takes values with gamma of display_*_gamma */ + return gamma_cache_color = drv->get_color(new_rgb); +} + +/* ATTENTION!!! allocates using malloc. Due to braindead Xlib, which + * frees it using free and thus it is not possible to use mem_alloc. */ +void get_links_icon(unsigned char **data, int *width, int *height, int depth) +{ + struct bitmap b; + unsigned short *tmp1; + double g=user_gamma/sRGB_gamma; + + b.x=48; + b.y=48; + *width=b.x; + *height=b.y; + b.skip=b.x*(depth&7); + retry: + if (!(b.data=*data=malloc(b.skip*b.y))) { + out_of_memory("icon malloc", b.skip*b.y); + goto retry; + } + tmp1=mem_alloc(6*b.y*b.x); + apply_gamma_exponent_24_to_48(tmp1,links_icon,b.x*b.y,g,g,g); + dither(tmp1, &b); + mem_free(tmp1); +} + +#endif /* G */ |