gimp/plug-ins/print/print-util.c

2266 lines
53 KiB
C

/*
* "$Id$"
*
* Print plug-in driver utility functions for the GIMP.
*
* Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
* Robert Krawitz (rlk@alum.mit.edu)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* This file must include only standard C header files. The core code must
* compile on generic platforms that don't support glib, gimp, gtk, etc.
*/
/* #define PRINT_DEBUG */
#include "print.h"
#include <math.h>
#include <limits.h>
#ifndef __GNUC__
# define inline
#endif /* !__GNUC__ */
typedef struct
{
unsigned steps;
unsigned short *composite;
unsigned short *red;
unsigned short *green;
unsigned short *blue;
unsigned shiftval;
unsigned bin_size;
unsigned bin_shift;
} lut_t;
/*
* RGB to grayscale luminance constants...
*/
#define LUM_RED 31
#define LUM_GREEN 61
#define LUM_BLUE 8
/* rgb/hsv conversions taken from Gimp common/autostretch_hsv.c */
static vars_t default_vars =
{
"", /* Name of file or command to print to */
"ps2", /* Name of printer "driver" */
"", /* Name of PPD file */
OUTPUT_COLOR, /* Color or grayscale output */
"", /* Output resolution */
"", /* Size of output media */
"", /* Type of output media */
"", /* Source of output media */
"", /* Ink type */
"", /* Dither algorithm */
1.0, /* Output brightness */
100.0, /* Scaling (100% means entire printable area, */
/* -XXX means scale by PPI) */
-1, /* Orientation (-1 = automatic) */
-1, /* X offset (-1 = center) */
-1, /* Y offset (-1 = center) */
1.0, /* Screen gamma */
1.0, /* Contrast */
1.0, /* Cyan */
1.0, /* Magenta */
1.0, /* Yellow */
0, /* Linear */
1.0, /* Output saturation */
1.0, /* Density */
IMAGE_CONTINUOUS, /* Image type */
0, /* Unit 0=Inch */
1.0, /* Application gamma placeholder */
0, /* Page width */
0 /* Page height */
};
static vars_t min_vars =
{
"", /* Name of file or command to print to */
"ps2", /* Name of printer "driver" */
"", /* Name of PPD file */
OUTPUT_COLOR, /* Color or grayscale output */
"", /* Output resolution */
"", /* Size of output media */
"", /* Type of output media */
"", /* Source of output media */
"", /* Ink type */
"", /* Dither algorithm */
0, /* Output brightness */
5.0, /* Scaling (100% means entire printable area, */
/* -XXX means scale by PPI) */
-1, /* Orientation (-1 = automatic) */
-1, /* X offset (-1 = center) */
-1, /* Y offset (-1 = center) */
0.1, /* Screen gamma */
0, /* Contrast */
0, /* Cyan */
0, /* Magenta */
0, /* Yellow */
0, /* Linear */
0, /* Output saturation */
.1, /* Density */
IMAGE_CONTINUOUS, /* Image type */
0, /* Unit 0=Inch */
1.0, /* Application gamma placeholder */
0, /* Page width */
0 /* Page height */
};
static vars_t max_vars =
{
"", /* Name of file or command to print to */
"ps2", /* Name of printer "driver" */
"", /* Name of PPD file */
OUTPUT_COLOR, /* Color or grayscale output */
"", /* Output resolution */
"", /* Size of output media */
"", /* Type of output media */
"", /* Source of output media */
"", /* Ink type */
"", /* Dither algorithm */
2.0, /* Output brightness */
100.0, /* Scaling (100% means entire printable area, */
/* -XXX means scale by PPI) */
-1, /* Orientation (-1 = automatic) */
-1, /* X offset (-1 = center) */
-1, /* Y offset (-1 = center) */
4.0, /* Screen gamma */
4.0, /* Contrast */
4.0, /* Cyan */
4.0, /* Magenta */
4.0, /* Yellow */
0, /* Linear */
9.0, /* Output saturation */
2.0, /* Density */
IMAGE_CONTINUOUS, /* Image type */
0, /* Unit 0=Inch */
1.0, /* Application gamma placeholder */
0, /* Page width */
0 /* Page height */
};
#define FMAX(a, b) ((a) > (b) ? (a) : (b))
#define FMIN(a, b) ((a) < (b) ? (a) : (b))
static inline void
calc_rgb_to_hsl(unsigned short *rgb, double *hue, double *sat,
double *lightness)
{
double red, green, blue;
double h, s, l;
double min, max;
double delta;
int maxval;
red = rgb[0] / 65535.0;
green = rgb[1] / 65535.0;
blue = rgb[2] / 65535.0;
if (red > green)
{
if (red > blue)
{
max = red;
maxval = 0;
}
else
{
max = blue;
maxval = 2;
}
min = FMIN(green, blue);
}
else
{
if (green > blue)
{
max = green;
maxval = 1;
}
else
{
max = blue;
maxval = 2;
}
min = FMIN(red, blue);
}
l = (max + min) / 2.0;
delta = max - min;
if (delta < .000001) /* Suggested by Eugene Anikin <eugene@anikin.com> */
{
s = 0.0;
h = 0.0;
}
else
{
if (l <= .5)
s = delta / (max + min);
else
s = delta / (2 - max - min);
if (maxval == 0)
h = (green - blue) / delta;
else if (maxval == 1)
h = 2 + (blue - red) / delta;
else
h = 4 + (red - green) / delta;
if (h < 0.0)
h += 6.0;
else if (h > 6.0)
h -= 6.0;
}
*hue = h;
*sat = s;
*lightness = l;
}
static inline double
hsl_value(double n1, double n2, double hue)
{
if (hue < 0)
hue += 6.0;
else if (hue > 6)
hue -= 6.0;
if (hue < 1.0)
return (n1 + (n2 - n1) * hue);
else if (hue < 3.0)
return (n2);
else if (hue < 4.0)
return (n1 + (n2 - n1) * (4.0 - hue));
else
return (n1);
}
static inline void
calc_hsl_to_rgb(unsigned short *rgb, double h, double s, double l)
{
if (s < .0000001)
{
if (l > 1)
l = 1;
else if (l < 0)
l = 0;
rgb[0] = l * 65535;
rgb[1] = l * 65535;
rgb[2] = l * 65535;
}
else
{
double m1, m2;
double h1, h2;
h1 = h + 2;
h2 = h - 2;
if (l < .5)
m2 = l * (1 + s);
else
m2 = l + s - (l * s);
m1 = (l * 2) - m2;
rgb[0] = 65535 * hsl_value(m1, m2, h1);
rgb[1] = 65535 * hsl_value(m1, m2, h);
rgb[2] = 65535 * hsl_value(m1, m2, h2);
}
}
static inline void
update_cmyk(unsigned short *rgb)
{
int c = 65535 - rgb[0];
int m = 65535 - rgb[1];
int y = 65535 - rgb[2];
int nc, nm, ny;
int k;
if (c == 0 && m == 0 && y == 0)
return;
k = FMIN(FMIN(c, m), y);
/*
* This is an attempt to achieve better color balance. The goal
* is to weaken the pure cyan, magenta, and yellow and strengthen
* pure red, green, and blue.
*
* We also don't want S=1 V=1 cyan to be 100% cyan; it's simply
* too dark.
*/
nc = (c * 3 + FMIN(c, FMAX(m, y)) * 5 + FMAX(m, y) * 0 + k) / 8;
nm = (m * 3 + FMIN(m, FMAX(c, y)) * 5 + FMAX(c, y) * 0 + k) / 8;
ny = (y * 3 + FMIN(y, FMAX(c, m)) * 5 + FMAX(c, m) * 0 + k) / 8;
/*
* Make sure we didn't go overboard. We don't want to go too
* close to white unnecessarily.
*/
nc = c + (nc - c) / 3;
nm = m + (nm - m) / 3;
ny = y + (ny - y) / 3;
if (nc > 65535)
nc = 65535;
if (nm > 65535)
nm = 65535;
if (ny > 65535)
ny = 65535;
rgb[0] = 65535 - nc;
rgb[1] = 65535 - nm;
rgb[2] = 65535 - ny;
}
/*
* A lot of this stuff needs to be factored out of here
*/
static inline unsigned short
lookup_value(unsigned short value, int lut_size, unsigned short *lut,
unsigned shiftval, unsigned bin_size, unsigned bin_shift)
{
unsigned subrange;
unsigned remainder;
unsigned below;
unsigned above;
if (lut_size == 65536)
return lut[value];
subrange = value >> bin_shift;
remainder = value & (bin_size - 1);
below = lut[subrange];
if (remainder == 0)
return below;
if (subrange == (bin_size - 1))
above = lut[subrange];
else
above = lut[subrange + 1];
if (above == below)
return above;
else
return below + (((above - below) * remainder) >> bin_shift);
}
/*
* 'gray_to_gray()' - Convert grayscale image data to grayscale (brightness
* adjusted).
*/
static void
gray_to_gray(unsigned char *grayin, /* I - RGB pixels */
unsigned short *grayout, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes-per-pixel in grayin */
unsigned char *cmap, /* I - Colormap (unused) */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int use_previous = 0;
int o0 = 0;
lut_t *lut = (lut_t *)(vars->lut);
while (width > 0)
{
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == grayin[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = grayin[0];
grayout[0] = lut->composite[grayin[0]];
}
}
else
{
if (i0 == grayin[0] && i1 == grayin[1])
use_previous = 1;
else
{
use_previous = 0;
i0 = grayin[0];
i1 = grayin[1];
grayout[0] = lut->composite[grayin[0] * grayin[1] / 255 +
255 - grayin[1]];
}
}
if (use_previous)
{
grayout[0] = o0;
}
else
{
if (vars->density != 1.0)
{
double t = (65535.0 + ((grayout[0] - 65535.0) * vars->density));
if (t < 0.0)
t = 0.0;
grayout[0] = t + .5;
}
o0 = grayout[0];
}
grayin += bpp;
grayout ++;
width --;
}
}
static void
gray_to_monochrome(unsigned char *grayin, /* I - RGB pixels */
unsigned short *grayout, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes-per-pixel in grayin */
unsigned char *cmap, /* I - Colormap (unused) */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int use_previous = 0;
int o0 = 0;
lut_t *lut = (lut_t *)(vars->lut);
while (width > 0)
{
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == grayin[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = grayin[0];
grayout[0] = lut->composite[grayin[0]];
}
}
else
{
if (i0 == grayin[0] && i1 == grayin[1])
use_previous = 1;
else
{
use_previous = 0;
i0 = grayin[0];
i1 = grayin[1];
grayout[0] = lut->composite[grayin[0] * grayin[1] / 255 +
255 - grayin[1]];
}
}
if (use_previous)
{
grayout[0] = o0;
}
else
{
if (grayout[0] < 32768)
grayout[0] = 0;
else
grayout[0] = 65535;
o0 = grayout[0];
}
grayin += bpp;
grayout ++;
width --;
}
}
/*
* 'indexed_to_gray()' - Convert indexed image data to grayscale.
*/
static void
indexed_to_gray(unsigned char *indexed, /* I - Indexed pixels */
unsigned short *gray, /* O - Grayscale pixels */
int width, /* I - Width of row */
int bpp, /* I - bpp in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int o0 = 0;
int use_previous = 0;
int i;
lut_t *lut = (lut_t *)(vars->lut);
unsigned char gray_cmap[256]; /* Grayscale colormap */
/* Really should precompute this silly thing... */
for (i = 0; i < 256; i ++, cmap += 3)
gray_cmap[i] = (cmap[0] * LUM_RED +
cmap[1] * LUM_GREEN +
cmap[2] * LUM_BLUE) / 100;
while (width > 0)
{
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == indexed[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = indexed[0];
gray[0] = lut->composite[gray_cmap[i0]];
}
}
else
{
if (i0 == indexed[0] && i1 == indexed[1])
use_previous = 1;
else
{
use_previous = 0;
i0 = indexed[0];
i1 = indexed[1];
gray[0] = lut->composite[gray_cmap[i0 * i1 / 255]
+ 255 - i1];
}
}
if (use_previous)
{
gray[0] = o0;
}
else
{
if (vars->density != 1.0)
{
double t = (65535.0 + ((gray[0] - 65535.0) * vars->density));
if (t < 0.0)
t = 0.0;
gray[0] = t + .5;
}
o0 = gray[0];
}
indexed += bpp;
gray ++;
width --;
}
}
static void
indexed_to_monochrome(unsigned char *indexed, /* I - Indexed pixels */
unsigned short *gray, /* O - Grayscale pixels */
int width, /* I - Width of row */
int bpp, /* I - bpp in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int o0 = 0;
int use_previous = 0;
int i;
lut_t *lut = (lut_t *)(vars->lut);
unsigned char gray_cmap[256]; /* Grayscale colormap */
/* Really should precompute this silly thing... */
for (i = 0; i < 256; i ++, cmap += 3)
gray_cmap[i] = (cmap[0] * LUM_RED +
cmap[1] * LUM_GREEN +
cmap[2] * LUM_BLUE) / 100;
while (width > 0)
{
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == indexed[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = indexed[0];
gray[0] = lut->composite[gray_cmap[i0]];
}
}
else
{
if (i0 == indexed[0] && i1 == indexed[1])
use_previous = 1;
else
{
use_previous = 0;
i0 = indexed[0];
i1 = indexed[1];
gray[0] = lut->composite[gray_cmap[i0 * i1 / 255]
+ 255 - i1];
}
}
if (use_previous)
{
gray[0] = o0;
}
else
{
if (gray[0] < 32768)
gray[0] = 0;
else
gray[0] = 65535;
o0 = gray[0];
}
indexed += bpp;
gray ++;
width --;
}
}
/*
* 'rgb_to_gray()' - Convert RGB image data to grayscale.
*/
static void
rgb_to_gray(unsigned char *rgb, /* I - RGB pixels */
unsigned short *gray, /* O - Grayscale pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes-per-pixel in RGB */
unsigned char *cmap, /* I - Colormap (unused) */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int i2 = -1;
int i3 = -1;
int o0 = 0;
int use_previous = 0;
lut_t *lut = (lut_t *)(vars->lut);
while (width > 0)
{
if (bpp == 3)
{
/*
* No alpha in image...
*/
if (i0 == rgb[0] && i1 == rgb[1] && i2 == rgb[2])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgb[0];
i1 = rgb[1];
i2 = rgb[2];
gray[0] = lut->composite[(rgb[0] * LUM_RED +
rgb[1] * LUM_GREEN +
rgb[2] * LUM_BLUE) / 100];
}
}
else
{
if (i0 == rgb[0] && i1 == rgb[1] && i2 == rgb[2] && i3 == rgb[3])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgb[0];
i1 = rgb[1];
i2 = rgb[2];
i3 = rgb[3];
gray[0] = lut->composite[((rgb[0] * LUM_RED +
rgb[1] * LUM_GREEN +
rgb[2] * LUM_BLUE) *
rgb[3] / 25500 + 255 - rgb[3])];
}
}
if (use_previous)
{
gray[0] = o0;
}
else
{
if (vars->density != 1.0)
{
double t = (65535.0 + ((gray[0] - 65535.0) * vars->density));
if (t < 0.0)
t = 0.0;
gray[0] = t + .5;
}
o0 = gray[0];
}
rgb += bpp;
gray ++;
width --;
}
}
static void
rgb_to_monochrome(unsigned char *rgb, /* I - RGB pixels */
unsigned short *gray, /* O - Grayscale pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes-per-pixel in RGB */
unsigned char *cmap, /* I - Colormap (unused) */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int i2 = -1;
int i3 = -1;
int o0 = 0;
int use_previous = 0;
lut_t *lut = (lut_t *)(vars->lut);
while (width > 0)
{
if (bpp == 3)
{
/*
* No alpha in image...
*/
if (i0 == rgb[0] && i1 == rgb[1] && i2 == rgb[2])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgb[0];
i1 = rgb[1];
i2 = rgb[2];
gray[0] = lut->composite[(rgb[0] * LUM_RED +
rgb[1] * LUM_GREEN +
rgb[2] * LUM_BLUE) / 100];
}
}
else
{
if (i0 == rgb[0] && i1 == rgb[1] && i2 == rgb[2] && i3 == rgb[3])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgb[0];
i1 = rgb[1];
i2 = rgb[2];
i3 = rgb[3];
gray[0] = lut->composite[((rgb[0] * LUM_RED +
rgb[1] * LUM_GREEN +
rgb[2] * LUM_BLUE) *
rgb[3] / 25500 + 255 - rgb[3])];
}
}
if (use_previous)
{
gray[0] = o0;
}
else
{
if (gray[0] < 32768)
gray[0] = 0;
else
gray[0] = 65535;
o0 = gray[0];
}
rgb += bpp;
gray ++;
width --;
}
}
/*
* 'rgb_to_rgb()' - Convert rgb image data to RGB.
*/
static void
rgb_to_rgb(unsigned char *rgbin, /* I - RGB pixels */
unsigned short *rgbout, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes/pix in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
unsigned ld = vars->density * 65536;
double isat = 1.0;
double ssat = vars->saturation;
int i0 = -1;
int i1 = -1;
int i2 = -1;
int i3 = -1;
int o0 = 0;
int o1 = 0;
int o2 = 0;
int use_previous = 0;
lut_t *lut = (lut_t *)(vars->lut);
int compute_saturation = ssat <= .99999 || ssat >= 1.00001;
int split_saturation = ssat > 1.4;
if (split_saturation)
ssat = sqrt(ssat);
if (ssat > 1)
isat = 1.0 / ssat;
#if 0
printf("rgb-to-rgb: ssat=%f, isat=%f, do-sat=%d, split-sat=%d\n", ssat, isat, compute_saturation, split_saturation);
#endif
while (width > 0)
{
double h, s, v;
switch (bpp)
{
case 1:
/*
* No alpha in image, using colormap...
*/
if (i0 == rgbin[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgbin[0];
rgbout[0] = cmap[rgbin[0] * 3 + 0] * 257;
rgbout[1] = cmap[rgbin[0] * 3 + 1] * 257;
rgbout[2] = cmap[rgbin[0] * 3 + 2] * 257;
}
break;
case 2:
if (i0 == rgbin[0] && i1 == rgbin[1])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgbin[0];
i1 = rgbin[1];
rgbout[0] = (cmap[rgbin[0] * 3 + 0] *
rgbin[1] / 255 + 255 - rgbin[1]) * 257;
rgbout[1] = (cmap[rgbin[0] * 3 + 0] *
rgbin[1] / 255 + 255 - rgbin[1]) * 257;
rgbout[2] = (cmap[rgbin[0] * 3 + 0] *
rgbin[1] / 255 + 255 - rgbin[1]) * 257;
}
break;
case 3:
/*
* No alpha in image...
*/
if (i0 == rgbin[0] && i1 == rgbin[1] && i2 == rgbin[2])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgbin[0];
i1 = rgbin[1];
i2 = rgbin[2];
rgbout[0] = rgbin[0] * 257;
rgbout[1] = rgbin[1] * 257;
rgbout[2] = rgbin[2] * 257;
}
break;
case 4:
if (i0 == rgbin[0] && i1 == rgbin[1] && i2 == rgbin[2] &&
i3 == rgbin[3])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgbin[0];
i1 = rgbin[1];
i2 = rgbin[2];
i3 = rgbin[3];
rgbout[0] = (rgbin[0] * rgbin[3] / 255 + 255 - rgbin[3]) * 257;
rgbout[1] = (rgbin[1] * rgbin[3] / 255 + 255 - rgbin[3]) * 257;
rgbout[2] = (rgbin[2] * rgbin[3] / 255 + 255 - rgbin[3]) * 257;
}
break;
}
if (use_previous)
{
rgbout[0] = o0;
rgbout[1] = o1;
rgbout[2] = o2;
}
else
{
if (compute_saturation &&
(rgbout[0] != rgbout[1] || rgbout[0] != rgbout[2]))
{
rgbout[0] = 65535 - rgbout[0];
rgbout[1] = 65535 - rgbout[1];
rgbout[2] = 65535 - rgbout[2];
calc_rgb_to_hsl(rgbout, &h, &s, &v);
if (ssat < 1)
s *= ssat;
else
{
double s1 = s * ssat;
double s2 = 1.0 - ((1.0 - s) * isat);
s = FMIN(s1, s2);
}
if (s > 1)
s = 1.0;
calc_hsl_to_rgb(rgbout, h, s, v);
rgbout[0] = 65535 - rgbout[0];
rgbout[1] = 65535 - rgbout[1];
rgbout[2] = 65535 - rgbout[2];
}
update_cmyk(rgbout); /* Fiddle with the INPUT */
rgbout[0] = lookup_value(rgbout[0], lut->steps,
lut->red, lut->shiftval,
lut->bin_size, lut->bin_shift);
rgbout[1] = lookup_value(rgbout[1], lut->steps,
lut->green, lut->shiftval,
lut->bin_size, lut->bin_shift);
rgbout[2] = lookup_value(rgbout[2], lut->steps,
lut->blue, lut->shiftval,
lut->bin_size, lut->bin_shift);
if (split_saturation &&
(rgbout[0] != rgbout[1] || rgbout[0] != rgbout[2]))
{
rgbout[0] = 65535 - rgbout[0];
rgbout[1] = 65535 - rgbout[1];
rgbout[2] = 65535 - rgbout[2];
calc_rgb_to_hsl(rgbout, &h, &s, &v);
if (ssat < 1)
s *= ssat;
else
{
double s1 = s * ssat;
double s2 = 1.0 - ((1.0 - s) * isat);
s = FMIN(s1, s2);
}
if (s > 1)
s = 1.0;
calc_hsl_to_rgb(rgbout, h, s, v);
rgbout[0] = 65535 - rgbout[0];
rgbout[1] = 65535 - rgbout[1];
rgbout[2] = 65535 - rgbout[2];
}
if (ld < 65536)
{
int i;
for (i = 0; i < 3; i++)
{
unsigned t = rgbout[i];
t = 65535 - (65535 - t) * ld / 65536;
rgbout[i] = (unsigned short) t;
}
}
o0 = rgbout[0];
o1 = rgbout[1];
o2 = rgbout[2];
}
rgbin += bpp;
rgbout += 3;
width --;
}
}
static void
indexed_to_rgb(unsigned char *indexed, /* I - Indexed pixels */
unsigned short *rgb, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes-per-pixel in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
rgb_to_rgb(indexed, rgb, width, bpp, cmap, vars);
}
/*
* 'gray_to_rgb()' - Convert gray image data to RGB.
*/
static void
gray_to_rgb(unsigned char *grayin, /* I - grayscale pixels */
unsigned short *rgbout, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes/pix in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
int use_previous = 0;
int i0 = -1;
int i1 = -1;
int o0 = 0;
int o1 = 0;
int o2 = 0;
lut_t *lut = (lut_t *)(vars->lut);
while (width > 0)
{
unsigned short trgb[3];
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == grayin[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = grayin[0];
trgb[0] = grayin[0] * 257;
trgb[1] = grayin[0] * 257;
trgb[2] = grayin[0] * 257;
}
}
else
{
if (i0 == grayin[0] && i1 == grayin[1])
use_previous = 1;
else
{
int lookup = (grayin[0] * grayin[1] / 255 + 255 - grayin[1]) *
257;
use_previous = 0;
i0 = grayin[0];
i1 = grayin[1];
trgb[0] = lookup;
trgb[1] = lookup;
trgb[2] = lookup;
}
}
if (use_previous)
{
rgbout[0] = o0;
rgbout[1] = o1;
rgbout[2] = o2;
}
else
{
update_cmyk(trgb);
rgbout[0] = lookup_value(trgb[0], lut->steps,
lut->red, lut->shiftval,
lut->bin_size, lut->bin_shift);
rgbout[1] = lookup_value(trgb[1], lut->steps,
lut->green, lut->shiftval,
lut->bin_size, lut->bin_shift);
rgbout[2] = lookup_value(trgb[2], lut->steps,
lut->blue, lut->shiftval,
lut->bin_size, lut->bin_shift);
if (vars->saturation != 1.0)
{
double t;
int i;
for (i = 0; i < 3; i++)
{
t = (65535.0 + ((rgbout[i] - 65535.0) * vars->density));
if (t < 0.0)
t = 0.0;
rgbout[i] = t + .5;
}
}
o0 = rgbout[0];
o1 = rgbout[1];
o2 = rgbout[2];
}
grayin += bpp;
rgbout += 3;
width --;
}
}
static void
fast_indexed_to_rgb(unsigned char *indexed, /* I - Indexed pixels */
unsigned short *rgb, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes-per-pixel in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
int i0 = -1;
int i1 = -1;
int o0 = 0;
int o1 = 0;
int o2 = 0;
lut_t *lut = (lut_t *)(vars->lut);
int use_previous = 0;
double isat = 1.0;
if (vars->saturation > 1)
isat = 1.0 / vars->saturation;
while (width > 0)
{
double h, s, v;
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == indexed[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = indexed[0];
rgb[0] = lut->red[cmap[i0 * 3 + 0]];
rgb[1] = lut->green[cmap[i0 * 3 + 1]];
rgb[2] = lut->blue[cmap[i0 * 3 + 2]];
}
}
else
{
if (i0 == indexed[0] && i1 == indexed[1])
use_previous = 1;
else
{
use_previous = 0;
i0 = indexed[0];
i1 = indexed[1];
rgb[0] = lut->red[cmap[i0 * 3 + 0] * i1 / 255 + 255 - i1];
rgb[1] = lut->green[cmap[i0 * 3 + 1] * i1 / 255 + 255 -i1];
rgb[2] = lut->blue[cmap[i0 * 3 + 2] * i1 / 255 + 255 - i1];
}
}
if (use_previous)
{
rgb[0] = o0;
rgb[1] = o1;
rgb[2] = o2;
}
else
{
if (vars->saturation != 1.0)
{
calc_rgb_to_hsl(rgb, &h, &s, &v);
if (vars->saturation < 1)
s *= vars->saturation;
else
{
double s1 = s * vars->saturation;
double s2 = 1.0 - ((1.0 - s) * isat);
s = FMIN(s1, s2);
}
if (s > 1)
s = 1.0;
calc_hsl_to_rgb(rgb, h, s, v);
}
if (vars->density != 1.0)
{
double t;
int i;
for (i = 0; i < 3; i++)
{
t = (65535.0 + ((rgb[i] - 65535.0) * vars->density));
if (t < 0.0)
t = 0.0;
rgb[i] = t + .5;
}
}
o0 = rgb[0];
o1 = rgb[1];
o2 = rgb[2];
}
indexed += bpp;
rgb += 3;
width --;
}
}
/*
* 'rgb_to_rgb()' - Convert rgb image data to RGB.
*/
static void
fast_rgb_to_rgb(unsigned char *rgbin, /* I - RGB pixels */
unsigned short *rgbout, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes/pix in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
unsigned ld = vars->density * 65536;
int i0 = -1;
int i1 = -1;
int i2 = -1;
int i3 = -1;
int o0 = 0;
int o1 = 0;
int o2 = 0;
lut_t *lut = (lut_t *)(vars->lut);
int use_previous = 0;
double isat = 1.0;
if (vars->saturation > 1)
isat = 1.0 / vars->saturation;
while (width > 0)
{
double h, s, v;
if (bpp == 3)
{
/*
* No alpha in image...
*/
if (i0 == rgbin[0] && i1 == rgbin[1] && i2 == rgbin[2])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgbin[0];
i1 = rgbin[1];
i2 = rgbin[2];
rgbout[0] = lut->red[rgbin[0]];
rgbout[1] = lut->green[rgbin[1]];
rgbout[2] = lut->blue[rgbin[2]];
}
}
else
{
if (i0 == rgbin[0] && i1 == rgbin[1] && i2 == rgbin[2] &&
i3 == rgbin[3])
use_previous = 1;
else
{
use_previous = 0;
i0 = rgbin[0];
i1 = rgbin[1];
i2 = rgbin[2];
i3 = rgbin[3];
rgbout[0] = lut->red[i0 * i3 / 255 + 255 - i3];
rgbout[1] = lut->green[i1 * i3 / 255 + 255 - i3];
rgbout[2] = lut->blue[i2 * i3 / 255 + 255 - i3];
}
}
if (use_previous)
{
rgbout[0] = o0;
rgbout[1] = o1;
rgbout[2] = o2;
}
else
{
if (vars->saturation != 1.0)
{
calc_rgb_to_hsl(rgbout, &h, &s, &v);
if (vars->saturation < 1)
s *= vars->saturation;
else
{
double s1 = s * vars->saturation;
double s2 = 1.0 - ((1.0 - s) * isat);
s = FMIN(s1, s2);
}
if (s > 1)
s = 1.0;
calc_hsl_to_rgb(rgbout, h, s, v);
}
if (ld < 65536)
{
int i;
for (i = 0; i < 3; i++)
{
unsigned t = rgbout[i];
t = 65535 - (65535 - t) * ld / 65536;
rgbout[i] = (unsigned short) t;
}
}
o0 = rgbout[0];
o1 = rgbout[1];
o2 = rgbout[2];
}
rgbin += bpp;
rgbout += 3;
width --;
}
}
/*
* 'gray_to_rgb()' - Convert gray image data to RGB.
*/
static void
fast_gray_to_rgb(unsigned char *grayin, /* I - grayscale pixels */
unsigned short *rgbout, /* O - RGB pixels */
int width, /* I - Width of row */
int bpp, /* I - Bytes/pix in indexed */
unsigned char *cmap, /* I - Colormap */
const vars_t *vars
)
{
int use_previous = 0;
int i0 = -1;
int i1 = -1;
int o0 = 0;
int o1 = 0;
int o2 = 0;
lut_t *lut = (lut_t *)(vars->lut);
while (width > 0)
{
if (bpp == 1)
{
/*
* No alpha in image...
*/
if (i0 == grayin[0])
use_previous = 1;
else
{
use_previous = 0;
i0 = grayin[0];
rgbout[0] = lut->red[grayin[0]];
rgbout[1] = lut->green[grayin[0]];
rgbout[2] = lut->blue[grayin[0]];
}
}
else
{
if (i0 == grayin[0] && i1 == grayin[1])
use_previous = 1;
else
{
int lookup = (grayin[0] * grayin[1] / 255 +
255 - grayin[1]);
use_previous = 0;
i0 = grayin[0];
i1 = grayin[1];
rgbout[0] = lut->red[lookup];
rgbout[1] = lut->green[lookup];
rgbout[2] = lut->blue[lookup];
}
}
if (use_previous)
{
rgbout[0] = o0;
rgbout[1] = o1;
rgbout[2] = o2;
}
else
{
if (vars->density != 1.0)
{
double t;
int i;
for (i = 0; i < 3; i++)
{
t = (65535.0 + ((rgbout[i] - 65535.0) * vars->density));
if (t < 0.0)
t = 0.0;
rgbout[i] = t + .5;
}
}
o0 = rgbout[0];
o1 = rgbout[1];
o2 = rgbout[2];
}
grayin += bpp;
rgbout += 3;
width --;
}
}
#define ICLAMP(value) \
do \
{ \
if (user->value < min->value) \
user->value = min->value; \
else if (user->value > max->value) \
user->value = max->value; \
} while (0)
void
merge_printvars(vars_t *user, const vars_t *print)
{
const vars_t *max = print_maximum_settings();
const vars_t *min = print_minimum_settings();
user->cyan = (user->cyan * print->cyan);
ICLAMP(cyan);
user->magenta = (user->magenta * print->magenta);
ICLAMP(magenta);
user->yellow = (user->yellow * print->yellow);
ICLAMP(yellow);
user->contrast = (user->contrast * print->contrast);
ICLAMP(contrast);
user->brightness = (user->brightness * print->brightness);
ICLAMP(brightness);
user->gamma /= print->gamma;
ICLAMP(gamma);
user->saturation *= print->saturation;
ICLAMP(saturation);
user->density *= print->density;
ICLAMP(density);
}
static lut_t *
allocate_lut(size_t steps)
{
int i;
lut_t *ret = malloc(sizeof(lut_t));
ret->steps = steps;
ret->composite = malloc(sizeof(unsigned short) * steps);
ret->red = malloc(sizeof(unsigned short) * steps);
ret->green = malloc(sizeof(unsigned short) * steps);
ret->blue = malloc(sizeof(unsigned short) * steps);
ret->shiftval = 0;
for (i = 1; i < steps; i += i)
ret->shiftval++;
ret->bin_size = 65536 / steps;
ret->bin_shift = 16 - ret->shiftval;
return ret;
}
void
free_lut(vars_t *v)
{
if (v->lut)
{
lut_t *lut = (lut_t *)(v->lut);
if (lut->composite)
free(lut->composite);
if (lut->red)
free(lut->red);
if (lut->green)
free(lut->green);
if (lut->blue)
free(lut->blue);
lut->steps = 0;
lut->composite = NULL;
lut->red = NULL;
lut->green = NULL;
lut->blue = NULL;
free(v->lut);
v->lut = NULL;
}
}
/* #define PRINT_LUT */
void
compute_lut(size_t steps, vars_t *uv)
{
double pixel, /* Pixel value */
red_pixel, /* Pixel value */
green_pixel, /* Pixel value */
blue_pixel; /* Pixel value */
int i;
#ifdef PRINT_LUT
FILE *ltfile = fopen("/mnt1/lut", "w");
#endif
/*
* Got an output file/command, now compute a brightness lookup table...
*/
double cyan = uv->cyan;
double magenta = uv->magenta;
double yellow = uv->yellow;
double print_gamma = uv->gamma;
double contrast = uv->contrast;
double app_gamma = uv->app_gamma;
double brightness = uv->brightness;
double screen_gamma = app_gamma / 1.7; /* Why 1.7??? */
lut_t *lut;
/*
* Monochrome mode simply thresholds the input
* to decide whether to print at all. The printer gamma
* is intended to represent the analog response of the printer.
* Using it shifts the threshold, which is not the intent
* of how this works.
*/
if (uv->image_type == IMAGE_MONOCHROME)
print_gamma = 1.0;
uv->lut = allocate_lut(steps);
lut = (lut_t *)(uv->lut);
for (i = 0; i < steps; i ++)
{
double temp_pixel;
pixel = (double) i / (double) (steps - 1);
/*
* First, correct contrast
*/
if (pixel >= .5)
temp_pixel = 1.0 - pixel;
else
temp_pixel = pixel;
if (temp_pixel <= .000001 && contrast <= .0001)
temp_pixel = .5;
else if (temp_pixel > 1)
temp_pixel = .5 * pow(2 * temp_pixel, pow(contrast, contrast));
else if (temp_pixel < 1)
temp_pixel = 0.5 -
((0.5 - .5 * pow(2 * temp_pixel, contrast)) * contrast);
if (temp_pixel > .5)
temp_pixel = .5;
else if (temp_pixel < 0)
temp_pixel = 0;
if (pixel < .5)
pixel = temp_pixel;
else
pixel = 1 - temp_pixel;
/*
* Second, do brightness
*/
if (brightness < 1)
pixel = pixel * brightness;
else
pixel = 1 - ((1 - pixel) * (2 - brightness));
/*
* Third, correct for the screen gamma
*/
pixel = 1.0 - pow(pixel, screen_gamma);
/*
* Third, fix up cyan, magenta, yellow values
*/
if (pixel < 0.0)
pixel = 0.0;
else if (pixel > 1.0)
pixel = 1.0;
if (pixel > .9999 && cyan < .00001)
red_pixel = 0;
else
red_pixel = 1 - pow(1 - pixel, cyan);
if (pixel > .9999 && magenta < .00001)
green_pixel = 0;
else
green_pixel = 1 - pow(1 - pixel, magenta);
if (pixel > .9999 && yellow < .00001)
blue_pixel = 0;
else
blue_pixel = 1 - pow(1 - pixel, yellow);
/*
* Finally, fix up print gamma and scale
*/
pixel = 65535 * (1 - pow(pixel, print_gamma)) + .5;
red_pixel = 65535 * (1 - pow(red_pixel, print_gamma)) + .5;
green_pixel = 65535 * (1 - pow(green_pixel, print_gamma)) + .5;
blue_pixel = 65535 * (1 - pow(blue_pixel, print_gamma)) + .5;
if (pixel <= 0.0)
lut->composite[i] = 0;
else if (pixel >= 65535.0)
lut->composite[i] = 65535;
else
lut->composite[i] = (unsigned)(pixel);
if (red_pixel <= 0.0)
lut->red[i] = 0;
else if (red_pixel >= 65535.0)
lut->red[i] = 65535;
else
lut->red[i] = (unsigned)(red_pixel);
if (green_pixel <= 0.0)
lut->green[i] = 0;
else if (green_pixel >= 65535.0)
lut->green[i] = 65535;
else
lut->green[i] = (unsigned)(green_pixel);
if (blue_pixel <= 0.0)
lut->blue[i] = 0;
else if (blue_pixel >= 65535.0)
lut->blue[i] = 65535;
else
lut->blue[i] = (unsigned)(blue_pixel);
#ifdef PRINT_LUT
fprintf(ltfile, "%3i %5d %5d %5d %5d %f %f %f %f %f %f %f %f\n",
i, lut->composite[i], lut->red[i],
lut->green[i], lut->blue[i], pixel, red_pixel,
green_pixel, blue_pixel, print_gamma, screen_gamma,
print_gamma, app_gamma);
#endif
}
#ifdef PRINT_LUT
fclose(ltfile);
#endif
}
/*
* 'default_media_size()' - Return the size of a default page size.
*/
/*
* Sizes are converted to 1/72in, then rounded down so that we don't
* print off the edge of the paper.
*/
const static papersize_t paper_sizes[] =
{
/* Common imperial page sizes */
{ "Letter", 612, 792, PAPERSIZE_ENGLISH }, /* 8.5in x 11in */
{ "Legal", 612, 1008, PAPERSIZE_ENGLISH }, /* 8.5in x 14in */
{ "Tabloid", 792, 1224, PAPERSIZE_ENGLISH }, /* 11in x 17in */
{ "Executive", 522, 756, PAPERSIZE_ENGLISH }, /* 7.25 * 10.5in */
{ "Postcard", 283, 416, PAPERSIZE_ENGLISH }, /* 100mm x 147mm */
{ "3x5", 216, 360, PAPERSIZE_ENGLISH },
{ "4x6", 288, 432, PAPERSIZE_ENGLISH },
{ "Epson 4x6 Photo Paper", 306, 495, PAPERSIZE_ENGLISH },
{ "5x7", 360, 504, PAPERSIZE_ENGLISH },
{ "5x8", 360, 576, PAPERSIZE_ENGLISH },
{ "HalfLetter", 396, 612, PAPERSIZE_ENGLISH },
{ "6x8", 432, 576, PAPERSIZE_ENGLISH },
{ "8x10", 576, 720, PAPERSIZE_ENGLISH },
{ "Manual", 396, 612, PAPERSIZE_ENGLISH }, /* 5.5in x 8.5in */
{ "12x18", 864, 1296, PAPERSIZE_ENGLISH },
{ "13x19", 936, 1368, PAPERSIZE_ENGLISH },
/* Other common photographic paper sizes */
{ "8x12", 576, 864, PAPERSIZE_ENGLISH }, /* Sometimes used for 35 mm */
{ "11x14", 792, 1008, PAPERSIZE_ENGLISH },
{ "16x20", 1152, 1440, PAPERSIZE_ENGLISH },
{ "16x24", 1152, 1728, PAPERSIZE_ENGLISH }, /* 20x24 for 35 mm */
{ "20x24", 1440, 1728, PAPERSIZE_ENGLISH },
{ "20x30", 1440, 2160, PAPERSIZE_ENGLISH }, /* 24x30 for 35 mm */
{ "24x30", 1728, 2160, PAPERSIZE_ENGLISH },
{ "24x36", 1728, 2592, PAPERSIZE_ENGLISH }, /* Sometimes used for 35 mm */
{ "30x40", 2160, 2880, PAPERSIZE_ENGLISH },
/* International Paper Sizes (mostly taken from BS4000:1968) */
/*
* "A" series: Paper and boards, trimmed sizes
*
* "A" sizes are in the ratio 1 : sqrt(2). A0 has a total area
* of 1 square metre. Everything is rounded to the nearest
* millimetre. Thus, A0 is 841mm x 1189mm. Every other A
* size is obtained by doubling or halving another A size.
*/
{ "4A", 4768, 6749, PAPERSIZE_METRIC }, /* 1682mm x 2378mm */
{ "2A", 3370, 4768, PAPERSIZE_METRIC }, /* 1189mm x 1682mm */
{ "A0", 2384, 3370, PAPERSIZE_METRIC }, /* 841mm x 1189mm */
{ "A1", 1684, 2384, PAPERSIZE_METRIC }, /* 594mm x 841mm */
{ "A2", 1191, 1684, PAPERSIZE_METRIC }, /* 420mm x 594mm */
{ "A3", 842, 1191, PAPERSIZE_METRIC }, /* 297mm x 420mm */
{ "A4", 595, 842, PAPERSIZE_METRIC }, /* 210mm x 297mm */
{ "A5", 420, 595, PAPERSIZE_METRIC }, /* 148mm x 210mm */
{ "A6", 297, 420, PAPERSIZE_METRIC }, /* 105mm x 148mm */
{ "A7", 210, 297, PAPERSIZE_METRIC }, /* 74mm x 105mm */
{ "A8", 148, 210, PAPERSIZE_METRIC }, /* 52mm x 74mm */
{ "A9", 105, 148, PAPERSIZE_METRIC }, /* 37mm x 52mm */
{ "A10", 73, 105, PAPERSIZE_METRIC }, /* 26mm x 37mm */
/*
* Stock sizes for normal trims.
* Allowance for trim is 3 millimetres.
*/
{ "RA0", 2437, 3458, PAPERSIZE_METRIC }, /* 860mm x 1220mm */
{ "RA1", 1729, 2437, PAPERSIZE_METRIC }, /* 610mm x 860mm */
{ "RA2", 1218, 1729, PAPERSIZE_METRIC }, /* 430mm x 610mm */
{ "RA3", 864, 1218, PAPERSIZE_METRIC }, /* 305mm x 430mm */
{ "RA4", 609, 864, PAPERSIZE_METRIC }, /* 215mm x 305mm */
/*
* Stock sizes for bled work or extra trims.
*/
{ "SRA0", 2551, 3628, PAPERSIZE_METRIC }, /* 900mm x 1280mm */
{ "SRA1", 1814, 2551, PAPERSIZE_METRIC }, /* 640mm x 900mm */
{ "SRA2", 1275, 1814, PAPERSIZE_METRIC }, /* 450mm x 640mm */
{ "SRA3", 907, 1275, PAPERSIZE_METRIC }, /* 320mm x 450mm */
{ "SRA4", 637, 907, PAPERSIZE_METRIC }, /* 225mm x 320mm */
/*
* "B" series: Posters, wall charts and similar items.
*/
{ "4B ISO", 5669, 8016, PAPERSIZE_METRIC }, /* 2000mm x 2828mm */
{ "2B ISO", 4008, 5669, PAPERSIZE_METRIC }, /* 1414mm x 2000mm */
{ "B0 ISO", 2834, 4008, PAPERSIZE_METRIC }, /* 1000mm x 1414mm */
{ "B1 ISO", 2004, 2834, PAPERSIZE_METRIC }, /* 707mm x 1000mm */
{ "B2 ISO", 1417, 2004, PAPERSIZE_METRIC }, /* 500mm x 707mm */
{ "B3 ISO", 1000, 1417, PAPERSIZE_METRIC }, /* 353mm x 500mm */
{ "B4 ISO", 708, 1000, PAPERSIZE_METRIC }, /* 250mm x 353mm */
{ "B5 ISO", 498, 708, PAPERSIZE_METRIC }, /* 176mm x 250mm */
{ "B6 ISO", 354, 498, PAPERSIZE_METRIC }, /* 125mm x 176mm */
{ "B7 ISO", 249, 354, PAPERSIZE_METRIC }, /* 88mm x 125mm */
{ "B8 ISO", 175, 249, PAPERSIZE_METRIC }, /* 62mm x 88mm */
{ "B9 ISO", 124, 175, PAPERSIZE_METRIC }, /* 44mm x 62mm */
{ "B10 ISO", 87, 124, PAPERSIZE_METRIC }, /* 31mm x 44mm */
{ "B0 JIS", 2919, 4127, PAPERSIZE_METRIC },
{ "B1 JIS", 2063, 2919, PAPERSIZE_METRIC },
{ "B2 JIS", 1459, 2063, PAPERSIZE_METRIC },
{ "B3 JIS", 1029, 1459, PAPERSIZE_METRIC },
{ "B4 JIS", 727, 1029, PAPERSIZE_METRIC },
{ "B5 JIS", 518, 727, PAPERSIZE_METRIC },
{ "B6 JIS", 362, 518, PAPERSIZE_METRIC },
{ "B7 JIS", 257, 362, PAPERSIZE_METRIC },
{ "B8 JIS", 180, 257, PAPERSIZE_METRIC },
{ "B9 JIS", 127, 180, PAPERSIZE_METRIC },
{ "B10 JIS", 90, 127, PAPERSIZE_METRIC },
/*
* "C" series: Envelopes or folders suitable for A size stationery.
*/
{ "C0", 2599, 3676, PAPERSIZE_METRIC }, /* 917mm x 1297mm */
{ "C1", 1836, 2599, PAPERSIZE_METRIC }, /* 648mm x 917mm */
{ "C2", 1298, 1836, PAPERSIZE_METRIC }, /* 458mm x 648mm */
{ "C3", 918, 1298, PAPERSIZE_METRIC }, /* 324mm x 458mm */
{ "C4", 649, 918, PAPERSIZE_METRIC }, /* 229mm x 324mm */
{ "C5", 459, 649, PAPERSIZE_METRIC }, /* 162mm x 229mm */
{ "B6-C4", 354, 918, PAPERSIZE_METRIC }, /* 125mm x 324mm */
{ "C6", 323, 459, PAPERSIZE_METRIC }, /* 114mm x 162mm */
{ "DL", 311, 623, PAPERSIZE_METRIC }, /* 110mm x 220mm */
{ "C7-6", 229, 459, PAPERSIZE_METRIC }, /* 81mm x 162mm */
{ "C7", 229, 323, PAPERSIZE_METRIC }, /* 81mm x 114mm */
{ "C8", 161, 229, PAPERSIZE_METRIC }, /* 57mm x 81mm */
{ "C9", 113, 161, PAPERSIZE_METRIC }, /* 40mm x 57mm */
{ "C10", 79, 113, PAPERSIZE_METRIC }, /* 28mm x 40mm */
/*
* US CAD standard paper sizes
*/
{ "ArchA", 648, 864, PAPERSIZE_ENGLISH },
{ "ArchB", 864, 1296, PAPERSIZE_ENGLISH },
{ "ArchC", 1296, 1728, PAPERSIZE_ENGLISH },
{ "ArchD", 1728, 2592, PAPERSIZE_ENGLISH },
{ "ArchE", 2592, 3456, PAPERSIZE_ENGLISH },
/*
* Foolscap
*/
{ "flsa", 612, 936, PAPERSIZE_ENGLISH }, /* American foolscap */
{ "flse", 648, 936, PAPERSIZE_ENGLISH }, /* European foolscap */
/*
* Sizes for book production
* The BPIF and the Publishers Association jointly recommend ten
* standard metric sizes for case-bound titles as follows:
*/
{ "Crown Quarto", 535, 697, PAPERSIZE_METRIC }, /* 189mm x 246mm */
{ "Large Crown Quarto", 569, 731, PAPERSIZE_METRIC }, /* 201mm x 258mm */
{ "Demy Quarto", 620, 782, PAPERSIZE_METRIC }, /* 219mm x 276mm */
{ "Royal Quarto", 671, 884, PAPERSIZE_METRIC }, /* 237mm x 312mm */
/*{ "ISO A4", 595, 841, PAPERSIZE_METRIC }, 210mm x 297mm */
{ "Crown Octavo", 348, 527, PAPERSIZE_METRIC }, /* 123mm x 186mm */
{ "Large Crown Octavo", 365, 561, PAPERSIZE_METRIC }, /* 129mm x 198mm */
{ "Demy Octavo", 391, 612, PAPERSIZE_METRIC }, /* 138mm x 216mm */
{ "Royal Octavo", 442, 663, PAPERSIZE_METRIC }, /* 156mm x 234mm */
/*{ "ISO A5", 419, 595, PAPERSIZE_METRIC }, 148mm x 210mm */
/* Paperback sizes in common usage */
{ "Small paperback", 314, 504, PAPERSIZE_METRIC }, /* 111mm x 178mm */
{ "Penguin small paperback", 314, 513, PAPERSIZE_METRIC }, /* 111mm x 181mm */
{ "Penguin large paperback", 365, 561, PAPERSIZE_METRIC }, /* 129mm x 198mm */
/* Miscellaneous sizes */
{ "Hagaki Card", 283, 420, PAPERSIZE_METRIC }, /* 100 x 148 mm */
{ "Oufuku Card", 420, 567, PAPERSIZE_METRIC }, /* 148 x 200 mm */
{ "Long 3", 340, 666, PAPERSIZE_METRIC }, /* Japanese long envelope #3 */
{ "Long 4", 255, 581, PAPERSIZE_METRIC }, /* Japanese long envelope #4 */
{ "Kaku", 680, 941, PAPERSIZE_METRIC }, /* Japanese Kaku envelope #4 */
{ "Commercial 10", 297, 684, PAPERSIZE_ENGLISH }, /* US Commercial 10 env */
{ "A2 Invitation", 315, 414, PAPERSIZE_ENGLISH }, /* US A2 invitation */
{ "", 0, 0, PAPERSIZE_METRIC }
};
int
known_papersizes(void)
{
return sizeof(paper_sizes) / sizeof(papersize_t);
}
const papersize_t *
get_papersizes(void)
{
return paper_sizes;
}
const papersize_t *
get_papersize_by_name(const char *name)
{
const papersize_t *val = &(paper_sizes[0]);
while (strlen(val->name) > 0)
{
if (!strcasecmp(val->name, name))
return val;
val++;
}
return NULL;
}
#define IABS(a) ((a) > 0 ? a : -(a))
static int
paper_size_mismatch(int l, int w, const papersize_t *val)
{
int hdiff = IABS(l - (int) val->length);
int vdiff = fabs(w - (int) val->width);
return hdiff + vdiff;
}
const papersize_t *
get_papersize_by_size(int l, int w)
{
int score = INT_MAX;
const papersize_t *ref = NULL;
const papersize_t *val = &(paper_sizes[0]);
while (strlen(val->name) > 0)
{
if (val->width == w && val->length == l)
return val;
else
{
int myscore = paper_size_mismatch(l, w, val);
if (myscore < score && myscore < 20)
{
ref = val;
score = myscore;
}
}
val++;
}
return ref;
}
void
default_media_size(const printer_t *printer,
/* I - Printer model (not used) */
const vars_t *v, /* I */
int *width, /* O - Width in points */
int *length) /* O - Length in points */
{
if (v->page_width > 0 && v->page_height > 0)
{
*width = v->page_width;
*length = v->page_height;
}
else
{
const papersize_t *papersize = get_papersize_by_name(v->media_size);
if (!papersize)
{
*width = 1;
*length = 1;
}
else
{
*width = papersize->width;
*length = papersize->length;
}
}
}
/*
* The list of printers has been moved to printers.c
*/
#include "print-printers.c"
int
known_printers(void)
{
return printer_count;
}
const printer_t *
get_printers(void)
{
return printers;
}
const printer_t *
get_printer_by_index(int idx)
{
return &(printers[idx]);
}
const printer_t *
get_printer_by_long_name(const char *long_name)
{
const printer_t *val = &(printers[0]);
int i;
for (i = 0; i < known_printers(); i++)
{
if (!strcmp(val->long_name, long_name))
return val;
val++;
}
return NULL;
}
const printer_t *
get_printer_by_driver(const char *driver)
{
const printer_t *val = &(printers[0]);
int i;
for (i = 0; i < known_printers(); i++)
{
if (!strcmp(val->driver, driver))
return val;
val++;
}
return NULL;
}
int
get_printer_index_by_driver(const char *driver)
{
int idx = 0;
const printer_t *val = &(printers[0]);
for (idx = 0; idx < known_printers(); idx++)
{
if (!strcmp(val->driver, driver))
return idx;
val++;
}
return -1;
}
const char *
default_dither_algorithm(void)
{
return dither_algo_names[0];
}
convert_t
choose_colorfunc(int output_type,
int image_bpp,
const unsigned char *cmap,
int *out_bpp,
const vars_t *v)
{
if (v->image_type == IMAGE_MONOCHROME)
{
*out_bpp = 1;
if (image_bpp >= 3)
return rgb_to_monochrome;
else if (cmap == NULL)
return gray_to_monochrome;
else
return indexed_to_monochrome;
}
else if (output_type == OUTPUT_COLOR)
{
*out_bpp = 3;
if (image_bpp >= 3)
{
if (v->image_type == IMAGE_CONTINUOUS)
return rgb_to_rgb;
else
return fast_rgb_to_rgb;
}
else if (cmap == NULL)
{
if (v->image_type == IMAGE_CONTINUOUS)
return gray_to_rgb;
else
return fast_gray_to_rgb;
}
else
{
if (v->image_type == IMAGE_CONTINUOUS)
return indexed_to_rgb;
else
return fast_indexed_to_rgb;
}
}
else
{
*out_bpp = 1;
if (image_bpp >= 3)
return rgb_to_gray;
else if (cmap == NULL)
return gray_to_gray;
else
return indexed_to_gray;
}
}
void
compute_page_parameters(int page_right, /* I */
int page_left, /* I */
int page_top, /* I */
int page_bottom, /* I */
double scaling, /* I */
int image_width, /* I */
int image_height, /* I */
Image image, /* IO */
int *orientation, /* IO */
int *page_width, /* O */
int *page_height, /* O */
int *out_width, /* O */
int *out_height, /* O */
int *left, /* O */
int *top) /* O */
{
*page_width = page_right - page_left;
*page_height = page_top - page_bottom;
/* In AUTO orientation, just orient the paper the same way as the image. */
if (*orientation == ORIENT_AUTO)
{
if ((*page_width >= *page_height && image_width >= image_height)
|| (*page_height >= *page_width && image_height >= image_width))
*orientation = ORIENT_PORTRAIT;
else
*orientation = ORIENT_LANDSCAPE;
}
if (*orientation == ORIENT_LANDSCAPE)
Image_rotate_ccw(image);
else if (*orientation == ORIENT_UPSIDEDOWN)
Image_rotate_180(image);
else if (*orientation == ORIENT_SEASCAPE)
Image_rotate_cw(image);
image_width = Image_width(image);
image_height = Image_height(image);
/*
* Calculate width/height...
*/
if (scaling == 0.0)
{
*out_width = *page_width;
*out_height = *page_height;
}
else if (scaling < 0.0)
{
/*
* Scale to pixels per inch...
*/
*out_width = image_width * -72.0 / scaling;
*out_height = image_height * -72.0 / scaling;
}
else
{
/*
* Scale by percent...
*/
/*
* Decide which orientation gives the proper fit
* If we ask for 50%, we do not want to exceed that
* in either dimension!
*/
int twidth0 = *page_width * scaling / 100.0;
int theight0 = twidth0 * image_height / image_width;
int theight1 = *page_height * scaling / 100.0;
int twidth1 = theight1 * image_width / image_height;
*out_width = FMIN(twidth0, twidth1);
*out_height = FMIN(theight0, theight1);
}
if (*out_width == 0)
*out_width = 1;
if (*out_height == 0)
*out_height = 1;
/*
* Adjust offsets depending on the page orientation...
*/
if (*orientation == ORIENT_LANDSCAPE || *orientation == ORIENT_SEASCAPE)
{
int x;
x = *left;
*left = *top;
*top = x;
}
if ((*orientation == ORIENT_UPSIDEDOWN || *orientation == ORIENT_SEASCAPE)
&& *left >= 0)
{
*left = *page_width - *left - *out_width;
if (*left < 0) {
*left = 0;
}
}
if ((*orientation == ORIENT_UPSIDEDOWN || *orientation == ORIENT_LANDSCAPE)
&& *top >= 0)
{
*top = *page_height - *top - *out_height;
if (*top < 0) {
*top = 0;
}
}
if (*left < 0)
*left = (*page_width - *out_width) / 2;
if (*top < 0)
*top = (*page_height - *out_height) / 2;
}
int
verify_printer_params(const printer_t *p, const vars_t *v)
{
char **vptr;
int count;
int i;
int answer = 1;
if (strlen(v->media_size) > 0)
{
vptr = (*p->parameters)(p, NULL, "PageSize", &count);
if (count > 0)
{
for (i = 0; i < count; i++)
if (!strcmp(v->media_size, vptr[i]))
goto good_page_size;
answer = 0;
fprintf(stderr, "%s is not a valid page size\n", v->media_size);
}
good_page_size:
for (i = 0; i < count; i++)
free(vptr[i]);
free(vptr);
}
else
{
int height, width;
(*p->limit)(p, v, &width, &height);
#if 0
fprintf(stderr, "limit %d %d dims %d %d\n", width, height,
v->page_width, v->page_height);
#endif
if (v->page_height <= 0 || v->page_height > height ||
v->page_width <= 0 || v->page_width > width)
{
answer = 0;
fprintf(stderr, "Image size is not valid\n");
}
}
if (strlen(v->media_type) > 0)
{
vptr = (*p->parameters)(p, NULL, "MediaType", &count);
if (count > 0)
{
for (i = 0; i < count; i++)
if (!strcmp(v->media_type, vptr[i]))
goto good_media_type;
answer = 0;
fprintf(stderr, "%s is not a valid media type\n", v->media_type);
}
good_media_type:
for (i = 0; i < count; i++)
free(vptr[i]);
free(vptr);
}
if (strlen(v->media_source) > 0)
{
vptr = (*p->parameters)(p, NULL, "InputSlot", &count);
if (count > 0)
{
for (i = 0; i < count; i++)
if (!strcmp(v->media_source, vptr[i]))
goto good_media_source;
answer = 0;
fprintf(stderr, "%s is not a valid media source\n", v->media_source);
}
good_media_source:
for (i = 0; i < count; i++)
free(vptr[i]);
free(vptr);
}
if (strlen(v->resolution) > 0)
{
vptr = (*p->parameters)(p, NULL, "Resolution", &count);
if (count > 0)
{
for (i = 0; i < count; i++)
if (!strcmp(v->resolution, vptr[i]))
goto good_resolution;
answer = 0;
fprintf(stderr, "%s is not a valid resolution\n", v->resolution);
}
good_resolution:
for (i = 0; i < count; i++)
free(vptr[i]);
free(vptr);
}
if (strlen(v->ink_type) > 0)
{
vptr = (*p->parameters)(p, NULL, "InkType", &count);
if (count > 0)
{
for (i = 0; i < count; i++)
if (!strcmp(v->ink_type, vptr[i]))
goto good_ink_type;
answer = 0;
fprintf(stderr, "%s is not a valid ink type\n", v->ink_type);
}
good_ink_type:
for (i = 0; i < count; i++)
free(vptr[i]);
free(vptr);
}
for (i = 0; i < num_dither_algos; i++)
if (!strcmp(v->dither_algorithm, dither_algo_names[i]))
return answer;
fprintf(stderr, "%s is not a valid dither algorithm\n", v->dither_algorithm);
return 0;
}
const vars_t *
print_default_settings()
{
return &default_vars;
}
const vars_t *
print_maximum_settings()
{
return &max_vars;
}
const vars_t *
print_minimum_settings()
{
return &min_vars;
}
#ifdef QUANTIFY
unsigned quantify_counts[NUM_QUANTIFY_BUCKETS] = {0};
struct timeval quantify_buckets[NUM_QUANTIFY_BUCKETS] = {{0,0}};
int quantify_high_index = 0;
int quantify_first_time = 1;
struct timeval quantify_cur_time;
struct timeval quantify_prev_time;
void print_timers()
{
int i;
printf("Quantify timers:\n");
for (i = 0; i <= quantify_high_index; i++) {
if (quantify_counts[i] == 0) continue;
printf("Bucket %d:\t%ld.%ld s\thit %u times\n", i, quantify_buckets[i].tv_sec, quantify_buckets[i].tv_usec, quantify_counts[i]);
quantify_buckets[i].tv_sec = 0;
quantify_buckets[i].tv_usec = 0;
quantify_counts[i] = 0;
}
}
#endif