/* Copyright (C) 1995, 2000 artofcode LLC. All rights reserved. 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. */ /*$Id: gdevht.c,v 1.3.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */ /* Halftoning device implementation */ #include "gx.h" #include "gserrors.h" #include "gxdevice.h" #include "gdevht.h" #include "gxdcolor.h" #include "gxdcconv.h" #include "gxdither.h" /* The device procedures */ private dev_proc_open_device(ht_open); private dev_proc_map_rgb_color(ht_map_rgb_color); private dev_proc_map_color_rgb(ht_map_color_rgb); private dev_proc_fill_rectangle(ht_fill_rectangle); private dev_proc_map_cmyk_color(ht_map_cmyk_color); private dev_proc_map_rgb_alpha_color(ht_map_rgb_alpha_color); private dev_proc_fill_path(ht_fill_path); private dev_proc_stroke_path(ht_stroke_path); private dev_proc_fill_mask(ht_fill_mask); private dev_proc_fill_trapezoid(ht_fill_trapezoid); private dev_proc_fill_parallelogram(ht_fill_parallelogram); private dev_proc_fill_triangle(ht_fill_triangle); private dev_proc_draw_thin_line(ht_draw_thin_line); private const gx_device_ht gs_ht_device = {std_device_dci_body(gx_device_ht, 0, "halftoner", 0, 0, 1, 1, 1, 8, 255, 0, 0, 0), {ht_open, gx_forward_get_initial_matrix, gx_forward_sync_output, gx_forward_output_page, gx_default_close_device, ht_map_rgb_color, ht_map_color_rgb, ht_fill_rectangle, gx_default_tile_rectangle, gx_default_copy_mono, gx_default_copy_color, gx_default_draw_line, gx_default_get_bits, gx_forward_get_params, gx_forward_put_params, ht_map_cmyk_color, gx_forward_get_xfont_procs, gx_forward_get_xfont_device, ht_map_rgb_alpha_color, gx_forward_get_page_device, gx_forward_get_alpha_bits, gx_default_copy_alpha, gx_forward_get_band, gx_default_copy_rop, ht_fill_path, ht_stroke_path, ht_fill_mask, ht_fill_trapezoid, ht_fill_parallelogram, ht_fill_triangle, ht_draw_thin_line, gx_default_begin_image, gx_default_image_data, gx_default_end_image, gx_default_strip_tile_rectangle, gx_default_strip_copy_rop, gx_forward_get_clipping_box, gx_default_begin_typed_image, gx_no_get_bits_rectangle, gx_default_map_color_rgb_alpha, gx_no_create_compositor, gx_forward_get_hardware_params, gx_default_text_begin, gx_default_finish_copydevice } }; /* * Define the packing of two target colors and a halftone level into * a gx_color_index. Since C doesn't let us cast between a structure * and a scalar, we have to use explicit shifting and masking. */ #define cx_color0(color) ((color) & htdev->color_mask) #define cx_color1(color) (((color) >> htdev->color_shift) & htdev->color_mask) #define cx_level(color) ((color) >> htdev->level_shift) #define cx_values(c0, c1, lev)\ ( ((lev) << htdev->level_shift) + ((c1) << htdev->color_shift) + (c0) ) /* ---------------- Non-drawing procedures ---------------- */ /* Open the device. Right now we just make some error checks. */ private int ht_open(gx_device * dev) { gx_device_ht *htdev = (gx_device_ht *) dev; const gx_device *target = htdev->target; const gx_device_halftone *pdht = htdev->dev_ht; int target_depth; uint num_levels; if (target == 0 || pdht == 0 || pdht->num_comp != 0) return_error(gs_error_rangecheck); /* * Make sure we can fit a halftone level and two target colors * into a gx_color_index. If we're lucky, we can pack them into * even less space. */ target_depth = target->color_info.depth; num_levels = pdht->order.num_levels; { int depth = target_depth * 2 + num_levels; if (depth > sizeof(gx_color_index) * 8) return_error(gs_error_rangecheck); /* * If there are too few halftone levels (fewer than 32), * we can't treat this device as contone. */ if (num_levels < 32) return_error(gs_error_rangecheck); /* Set up color information. */ htdev->color_info.num_components = target->color_info.num_components; htdev->color_info.depth = depth; htdev->color_info.max_gray = num_levels - 1; htdev->color_info.max_color = num_levels - 1; htdev->color_info.dither_grays = num_levels; htdev->color_info.dither_colors = num_levels; } htdev->color_shift = target_depth; htdev->level_shift = target_depth * 2; htdev->color_mask = ((gx_color_index) 1 << target_depth) - 1; htdev->phase.x = imod(-htdev->ht_phase.x, pdht->lcm_width); htdev->phase.y = imod(-htdev->ht_phase.y, pdht->lcm_height); return 0; } /* Map from RGB or CMYK colors to the packed representation. */ private gx_color_index ht_finish_map_color(const gx_device_ht * htdev, int code, const gx_device_color * pdevc) { if (code < 0) return gx_no_color_index; if (gx_dc_is_pure(pdevc)) return cx_values(pdevc->colors.pure, 0, 0); if (gx_dc_is_binary_halftone(pdevc)) return cx_values(pdevc->colors.binary.color[0], pdevc->colors.binary.color[1], pdevc->colors.binary.b_level); lprintf("bad type in ht color mapping!"); return gx_no_color_index; } private gx_color_index ht_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g, gx_color_value b) { return ht_map_rgb_alpha_color(dev, r, g, b, gx_max_color_value); } private gx_color_index ht_map_cmyk_color(gx_device * dev, gx_color_value c, gx_color_value m, gx_color_value y, gx_color_value k) { gx_device_ht *htdev = (gx_device_ht *) dev; gx_device_color devc; frac fc = cv2frac(k); frac fk = cv2frac(k); int code = (c == m && m == y ? gx_render_device_gray(color_cmyk_to_gray(fc, fc, fc, fk, NULL), gx_max_color_value, &devc, htdev->target, htdev->dev_ht, &htdev->ht_phase) : gx_render_device_color(fc, cv2frac(m), cv2frac(y), fk, false, gx_max_color_value, &devc, htdev->target, htdev->dev_ht, &htdev->ht_phase)); return ht_finish_map_color(htdev, code, &devc); } private gx_color_index ht_map_rgb_alpha_color(gx_device * dev, gx_color_value r, gx_color_value g, gx_color_value b, gx_color_value alpha) { gx_device_ht *htdev = (gx_device_ht *) dev; gx_device_color devc; int code = (r == g && g == b ? gx_render_device_gray(cv2frac(r), alpha, &devc, htdev->target, htdev->dev_ht, &htdev->ht_phase) : gx_render_device_color(cv2frac(r), cv2frac(g), cv2frac(b), frac_0, false, alpha, &devc, htdev->target, htdev->dev_ht, &htdev->ht_phase)); return ht_finish_map_color(htdev, code, &devc); } /* Map back to an RGB color. */ private int ht_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value prgb[3]) { gx_device_ht *htdev = (gx_device_ht *) dev; gx_device *tdev = htdev->target; gx_color_index color0 = cx_color0(color); uint level = cx_level(color); dev_proc_map_color_rgb((*map)) = dev_proc(tdev, map_color_rgb); if (level == 0) return (*map) (tdev, color0, prgb); { gx_color_index color1 = cx_color1(color); gx_color_value rgb0[3], rgb1[3]; uint num_levels = htdev->dev_ht->order.num_levels; int i; (*map) (tdev, color0, rgb0); (*map) (tdev, color1, rgb1); for (i = 0; i < 3; ++i) prgb[i] = rgb0[i] + (rgb1[i] - rgb0[i]) * (ulong) level / num_levels; return 0; } } /* ---------------- Drawing procedures ---------------- */ /* * Map a (pure) contone color into a gx_device_color, either pure or * halftoned. Return 0 if pure, 1 if halftoned, <0 if the original color * wasn't pure. (This is not a driver procedure.) */ private int ht_map_device_color(const gx_device_ht * htdev, gx_device_color * pdevc, const gx_device_color * pcdevc) { if (!gx_dc_is_pure(pcdevc)) return -1; { gx_color_index color = pcdevc->colors.pure; gx_color_index color0 = cx_color0(color); uint level = cx_level(color); if (level == 0) { color_set_pure(pdevc, color0); return 0; } else { color_set_binary_halftone(pdevc, htdev->dev_ht, color0, cx_color1(color), level); color_set_phase(pdevc, htdev->phase.x, htdev->phase.y); return 1; } } } /* Fill a rectangle by tiling with a halftone. */ private int ht_fill_rectangle(gx_device * dev, int x, int y, int w, int h, gx_color_index color) { gx_device_ht *htdev = (gx_device_ht *) dev; gx_device *tdev = htdev->target; gx_color_index color0 = cx_color0(color); uint level = cx_level(color); if (level == 0) return (*dev_proc(tdev, fill_rectangle)) (tdev, x, y, w, h, color0); { const gx_ht_order *porder = &htdev->dev_ht->order; gx_ht_cache *pcache = porder->cache; gx_ht_tile *tile; /* Ensure that the tile cache is current. */ if (pcache->order.bits != porder->bits) gx_ht_init_cache(pcache, porder); /* Ensure that the tile we want is cached. */ tile = gx_render_ht(pcache, level); if (tile == 0) return_error(gs_error_Fatal); /* Fill the rectangle with the tile. */ return (*dev_proc(tdev, strip_tile_rectangle)) (tdev, &tile->tiles, x, y, w, h, color0, cx_color1(color), htdev->phase.x, htdev->phase.y); } } /* * Create a halftoned color if necessary for the high-level drawing * operations. */ #define MAP_DRAWING_COLOR(proc, default_proc)\ gx_device_ht *htdev = (gx_device_ht *)dev;\ gx_device_color dcolor;\ gx_device *tdev;\ const gx_device_color *tdcolor;\ \ if ( ht_map_device_color(htdev, &dcolor, pdcolor) < 0 )\ tdev = dev, tdcolor = pdcolor, proc = default_proc;\ else\ tdev = htdev->target, tdcolor = &dcolor,\ proc = dev_proc(tdev, proc) private int ht_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath, const gx_fill_params * params, const gx_drawing_color * pdcolor, const gx_clip_path * pcpath) { dev_proc_fill_path((*fill_path)); MAP_DRAWING_COLOR(fill_path, gx_default_fill_path); return (*fill_path) (tdev, pis, ppath, params, tdcolor, pcpath); } private int ht_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath, const gx_stroke_params * params, const gx_drawing_color * pdcolor, const gx_clip_path * pcpath) { dev_proc_stroke_path((*stroke_path)); MAP_DRAWING_COLOR(stroke_path, gx_default_stroke_path); return (*stroke_path) (tdev, pis, ppath, params, tdcolor, pcpath); } private int ht_fill_mask(gx_device * dev, const byte * data, int data_x, int raster, gx_bitmap_id id, int x, int y, int width, int height, const gx_drawing_color * pdcolor, int depth, gs_logical_operation_t lop, const gx_clip_path * pcpath) { dev_proc_fill_mask((*fill_mask)); MAP_DRAWING_COLOR(fill_mask, gx_default_fill_mask); return (*fill_mask) (tdev, data, data_x, raster, id, x, y, width, height, tdcolor, depth, lop, pcpath); } private int ht_fill_trapezoid(gx_device * dev, const gs_fixed_edge * left, const gs_fixed_edge * right, fixed ybot, fixed ytop, bool swap_axes, const gx_drawing_color * pdcolor, gs_logical_operation_t lop) { dev_proc_fill_trapezoid((*fill_trapezoid)); MAP_DRAWING_COLOR(fill_trapezoid, gx_default_fill_trapezoid); return (*fill_trapezoid) (tdev, left, right, ybot, ytop, swap_axes, tdcolor, lop); } private int ht_fill_parallelogram(gx_device * dev, fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by, const gx_drawing_color * pdcolor, gs_logical_operation_t lop) { dev_proc_fill_parallelogram((*fill_parallelogram)); MAP_DRAWING_COLOR(fill_parallelogram, gx_default_fill_parallelogram); return (*fill_parallelogram) (tdev, px, py, ax, ay, bx, by, tdcolor, lop); } private int ht_fill_triangle(gx_device * dev, fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by, const gx_drawing_color * pdcolor, gs_logical_operation_t lop) { dev_proc_fill_triangle((*fill_triangle)); MAP_DRAWING_COLOR(fill_triangle, gx_default_fill_triangle); return (*fill_triangle) (tdev, px, py, ax, ay, bx, by, tdcolor, lop); } private int ht_draw_thin_line(gx_device * dev, fixed fx0, fixed fy0, fixed fx1, fixed fy1, const gx_drawing_color * pdcolor, gs_logical_operation_t lop) { dev_proc_draw_thin_line((*draw_thin_line)); MAP_DRAWING_COLOR(draw_thin_line, gx_default_draw_thin_line); return (*draw_thin_line) (tdev, fx0, fy0, fx1, fy1, tdcolor, lop); }