home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
- Distributed by Free Software Foundation, Inc.
-
- This file is part of Ghostscript.
-
- Ghostscript is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
- to anyone for the consequences of using it or for whether it serves any
- particular purpose or works at all, unless he says so in writing. Refer
- to the Ghostscript General Public License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- Ghostscript, but only under the conditions described in the Ghostscript
- General Public License. A copy of this license is supposed to have been
- given to you along with Ghostscript so you can know your rights and
- responsibilities. It should be in a file named COPYING. Among other
- things, the copyright notice and this notice must be preserved on all
- copies. */
-
- /* gxdither.c */
- #include "gx.h"
- #include "gxfixed.h"
- #include "gxmatrix.h"
- #include "gzstate.h"
- #include "gzdevice.h"
- #include "gzcolor.h"
- #include "gzht.h"
-
- /*
- * Improved dithering for Ghostscript. The underlying device imaging
- * model supports dithering between two colors to generate intermediate
- * shades.
- *
- * The strategy is to first see if the color is either pure white or
- * pure black. In this case the problem is trivial.
- *
- * Next, if we are on a High Quality RGB display with 8 or more
- * bits for each R, G and B, we simply set the color and return.
- *
- * Now, if the device is black and white, or the color happens
- * to be achromatic, we perform simple B/W dithering.
- *
- * Otherwise, things are a bit more complicated. If the device
- * supports N shades of each R, G and B independently, there are a total
- * of N*N*N colors. These colors form a 3-D grid in a cubical color
- * space. The following dithering technique works by locating the
- * color we want in this 3-D color grid and finding the eight colors
- * that surround it. In the case of dithering into 8 colors with 1
- * bit for each red, green and blue, these eight colors will always
- * be the same.
- *
- * Now we consider all possible diagonal paths between the eight colors
- * and chose the path that runs closest to our desired color in 3-D
- * color space. There are 28 such paths. Then we find the position
- * on the path that is closest to out color.
- *
- * The search is made more fast by always reflecting our color into
- * the bottom octant of the cube and comparing it to 7 paths.
- * After the best path and the best position on that path are found,
- * the results are reflected back into the original color space.
- *
- * NOTE: This code has been tested for B/W and Color imaging with
- * 1, 2, 3 and 8 bits per component.
- *
- * --- original code by Paul Haeberli @ Silicon Graphics - 1990
- * --- extensively revised by L. Peter Deutsch, Aladdin Enterprises
- */
-
- void gx_color_load(P2(gx_device_color *, gs_state *));
-
- #define WEIGHT1 (unsigned long)(100) /* 1.0 */
- #define WEIGHT2 (unsigned long)(71) /* 1/sqrt(2.0) */
- #define WEIGHT3 (unsigned long)(62) /* 1/sqrt(3.0)+tad */
-
- #define DIAG_R (0x1)
- #define DIAG_G (0x2)
- #define DIAG_B (0x4)
- #define DIAG_RG (0x3)
- #define DIAG_GB (0x6)
- #define DIAG_BR (0x5)
- #define DIAG_RGB (0x7)
-
- static unsigned short lum[8] = {
- (0*lum_blue_weight+0*lum_green_weight+0*lum_red_weight),
- (0*lum_blue_weight+0*lum_green_weight+1*lum_red_weight),
- (0*lum_blue_weight+1*lum_green_weight+0*lum_red_weight),
- (0*lum_blue_weight+1*lum_green_weight+1*lum_red_weight),
- (1*lum_blue_weight+0*lum_green_weight+0*lum_red_weight),
- (1*lum_blue_weight+0*lum_green_weight+1*lum_red_weight),
- (1*lum_blue_weight+1*lum_green_weight+0*lum_red_weight),
- (1*lum_blue_weight+1*lum_green_weight+1*lum_red_weight),
- };
-
- /* Note that this routine assumes that the incoming color */
- /* has already been mapped through the transfer functions. */
- void
- gx_color_render(gs_color *pcolor, gx_device_color *pdevc, gs_state *pgs)
- { device *pdev = pgs->device;
- unsigned long max_value, mulval;
- #define divval (max_color_param_long+1)
- unsigned long hsize;
- gx_device *dev;
-
- /* Make a special check for black and white. */
- if ( pcolor->is_gray )
- { if ( pcolor->luminance == 0 )
- { pdevc->color2 = pdevc->color1 = pdev->black;
- pdevc->halftone_level = 0; /* pure color */
- return;
- }
- else if ( pcolor->luminance == max_color_param )
- { pdevc->color2 = pdevc->color1 = pdev->white;
- pdevc->halftone_level = 0; /* pure color */
- return;
- }
- }
-
- /* get a few handy values */
- dev = pdev->info;
- max_value = dev->max_rgb_value;
- mulval = max_value+1;
-
- /* If we are on a high quality RGB display, don't bother dithering. */
- if (max_value >= 255)
- { color_param r = (pcolor->red*mulval)/divval;
- color_param g = (pcolor->green*mulval)/divval;
- color_param b = (pcolor->blue*mulval)/divval;
- pdevc->color2 = pdevc->color1 =
- (*dev->procs->map_rgb_color)(dev,r,g,b);
- pdevc->halftone_level = 0; /* pure color */
- return;
- }
- hsize = (unsigned)pgs->halftone->order_size;
-
- /* see if we should do it in black and white */
- if ( !dev->has_color || pcolor->is_gray )
- {
-
- /* if bw device or gray color, use luminance of the color */
- unsigned long nshades = hsize*max_value+1;
- unsigned long lx = (nshades*color_luminance(pcolor))/divval;
- color_param l = lx/hsize;
- pdevc->halftone_level = lx%hsize;
-
- /* check halftone level to see if we have to dither */
- pdevc->color1 = (*dev->procs->map_rgb_color)(dev,l,l,l);
- if ( pdevc->halftone_level == 0 )
- pdevc->color2 = pdevc->color1;
- else
- { ++l;
- pdevc->color2 =
- (*dev->procs->map_rgb_color)(dev,l,l,l);
- gx_color_load(pdevc, pgs);
- }
- return;
- }
-
- /* must do real color dithering */
- { unsigned long want_r = pcolor->red*max_value;
- unsigned long want_g = pcolor->green*max_value;
- unsigned long want_b = pcolor->blue*max_value;
- color_param r = want_r/max_color_param_long;
- color_param g = want_g/max_color_param_long;
- color_param b = want_b/max_color_param_long;
- /* rem_{r,g,b} are short, so we can get away with short arithmetic. */
- color_param rem_r = (color_param)want_r-(r*max_color_param);
- color_param rem_g = (color_param)want_g-(g*max_color_param);
- color_param rem_b = (color_param)want_b-(b*max_color_param);
- int adjust_r, adjust_b, adjust_g;
- unsigned short amax;
- unsigned long dmax;
- int axisc, diagc;
- unsigned short lum_invert;
- unsigned long dot1, dot2, dot3;
- int level;
-
- /* Check for no dithering required */
- if ( !(rem_r | rem_g | rem_b) )
- { pdevc->color2 = pdevc->color1 =
- (*dev->procs->map_rgb_color)(dev, r, g, b);
- pdevc->halftone_level = 0;
- return;
- }
-
- /* flip the remainder color into the 0, 0, 0 octant */
- lum_invert = 0;
- #define half ((color_param)(max_color_param_long>>1))
- if ( rem_r > half )
- rem_r = max_color_param - rem_r,
- adjust_r = -1, r++, lum_invert += lum_red_weight;
- else
- adjust_r = 1;
- if ( rem_g > half)
- rem_g = max_color_param - rem_g,
- adjust_g = -1, g++, lum_invert += lum_green_weight;
- else
- adjust_g = 1;
- if ( rem_b > half )
- rem_b = max_color_param - rem_b,
- adjust_b = -1, b++, lum_invert += lum_blue_weight;
- else
- adjust_b = 1;
- pdevc->color1 = (*dev->procs->map_rgb_color)(dev, r, g, b);
- /*
- * Dot the color with each axis to find the best one of 7;
- * find the color at the end of the axis chosen.
- */
- if ( rem_g > rem_r )
- { if ( rem_b > rem_g )
- amax = rem_b, axisc = DIAG_B;
- else
- amax = rem_g, axisc = DIAG_G;
- if ( rem_b > rem_r )
- dmax = (unsigned long)rem_g+rem_b, diagc = DIAG_GB;
- else
- dmax = (unsigned long)rem_r+rem_g, diagc = DIAG_RG;
- }
- else
- { if ( rem_b > rem_r )
- amax = rem_b, axisc = DIAG_B;
- else
- amax = rem_r, axisc = DIAG_R;
- if ( rem_b > rem_g )
- dmax = (unsigned long)rem_b+rem_r, diagc = DIAG_BR;
- else
- dmax = (unsigned long)rem_r+rem_g, diagc = DIAG_RG;
- }
-
- dot1 = amax*WEIGHT1;
- dot2 = dmax*WEIGHT2;
- dot3 = (ulong)rem_r+rem_g+rem_b; /* rgb axis */
- if ( dot1 > dot2 )
- { if ( dot3*WEIGHT3 > dot1 )
- diagc = DIAG_RGB,
- level = (hsize * dot3) / (3 * max_color_param_long);
- else
- diagc = axisc,
- level = (hsize * amax) / max_color_param_long;
- }
- else
- { if ( dot3*WEIGHT3 > dot2 )
- diagc = DIAG_RGB,
- level = (hsize * dot3) / (3 * max_color_param_long);
- else
- level = (hsize * dmax) / (2 * max_color_param_long);
- };
- #ifdef DEBUG
- if ( gs_debug['c'] )
- { dprintf3("[c]rgb=%x,%x,%x -->\n",
- (unsigned)pcolor->red, (unsigned)pcolor->green,
- (unsigned)pcolor->blue);
- dprintf6(" %x+%x,%x+%x,%x+%x;",
- (unsigned)r, (unsigned)rem_r, (unsigned)g, (unsigned)rem_g,
- (unsigned)b, (unsigned)rem_b);
- dprintf3(" adjust=%d,%d,%d;\n",
- adjust_r, adjust_g, adjust_b);
- }
- #endif
-
- if ( (pdevc->halftone_level = level) == 0 )
- pdevc->color2 = pdevc->color1;
- else
- {
- /* construct the second color, inverting back to original space if needed */
- if (diagc & DIAG_R) r += adjust_r;
- if (diagc & DIAG_G) g += adjust_g;
- if (diagc & DIAG_B) b += adjust_b;
- /* get the second device color, sorting by luminance */
- if ( lum[diagc] < lum_invert )
- { pdevc->color2 = pdevc->color1;
- pdevc->color1 = (*dev->procs->map_rgb_color)(dev, r, g, b);
- pdevc->halftone_level = level = hsize - level;
- }
- else
- pdevc->color2 = (*dev->procs->map_rgb_color)(dev, r, g, b);
- gx_color_load(pdevc, pgs);
- }
-
- #ifdef DEBUG
- if ( gs_debug['c'] )
- { dprintf5("[c]diagc=%d; color1=%lx, color2=%lx, level=%d/%d\n",
- diagc, (ulong)pdevc->color1, (ulong)pdevc->color2,
- level, (unsigned)hsize);
- }
- #endif
-
- }
- }
-