summaryrefslogtreecommitdiff
path: root/dip.c
diff options
context:
space:
mode:
Diffstat (limited to 'dip.c')
-rw-r--r--dip.c2206
1 files changed, 2206 insertions, 0 deletions
diff --git a/dip.c b/dip.c
new file mode 100644
index 0000000..ac9381b
--- /dev/null
+++ b/dip.c
@@ -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 */