/* Copyright (C) 1998, 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: gxclipm.c,v 1.3.6.1.2.1 2003/01/17 00:49:03 giles Exp $ */ /* Mask clipping device */ #include "memory_.h" #include "gx.h" #include "gsbittab.h" #include "gxdevice.h" #include "gxdevmem.h" #include "gxclipm.h" /* Device procedures */ private dev_proc_fill_rectangle(mask_clip_fill_rectangle); private dev_proc_copy_mono(mask_clip_copy_mono); private dev_proc_copy_color(mask_clip_copy_color); private dev_proc_copy_alpha(mask_clip_copy_alpha); private dev_proc_strip_tile_rectangle(mask_clip_strip_tile_rectangle); private dev_proc_strip_copy_rop(mask_clip_strip_copy_rop); private dev_proc_get_clipping_box(mask_clip_get_clipping_box); /* The device descriptor. */ const gx_device_mask_clip gs_mask_clip_device = {std_device_std_body_open(gx_device_mask_clip, 0, "mask clipper", 0, 0, 1, 1), {gx_default_open_device, gx_forward_get_initial_matrix, gx_default_sync_output, gx_default_output_page, gx_default_close_device, gx_forward_map_rgb_color, gx_forward_map_color_rgb, mask_clip_fill_rectangle, gx_default_tile_rectangle, mask_clip_copy_mono, mask_clip_copy_color, gx_default_draw_line, gx_forward_get_bits, gx_forward_get_params, gx_forward_put_params, gx_forward_map_cmyk_color, gx_forward_get_xfont_procs, gx_forward_get_xfont_device, gx_forward_map_rgb_alpha_color, gx_forward_get_page_device, gx_forward_get_alpha_bits, mask_clip_copy_alpha, gx_forward_get_band, gx_default_copy_rop, gx_default_fill_path, gx_default_stroke_path, gx_default_fill_mask, gx_default_fill_trapezoid, gx_default_fill_parallelogram, gx_default_fill_triangle, gx_default_draw_thin_line, gx_default_begin_image, gx_default_image_data, gx_default_end_image, mask_clip_strip_tile_rectangle, mask_clip_strip_copy_rop, mask_clip_get_clipping_box, gx_default_begin_typed_image, gx_forward_get_bits_rectangle, gx_forward_map_color_rgb_alpha, gx_no_create_compositor, gx_forward_get_hardware_params, gx_default_text_begin, gx_default_finish_copydevice } }; /* Fill a rectangle by painting through the mask. */ private int mask_clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h, gx_color_index color) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; gx_device *tdev = cdev->target; /* Clip the rectangle to the region covered by the mask. */ int mx0 = x + cdev->phase.x, my0 = y + cdev->phase.y; int mx1 = mx0 + w, my1 = my0 + h; if (mx0 < 0) mx0 = 0; if (my0 < 0) my0 = 0; if (mx1 > cdev->tiles.size.x) mx1 = cdev->tiles.size.x; if (my1 > cdev->tiles.size.y) my1 = cdev->tiles.size.y; return (*dev_proc(tdev, copy_mono)) (tdev, cdev->tiles.data + my0 * cdev->tiles.raster, mx0, cdev->tiles.raster, cdev->tiles.id, mx0 - cdev->phase.x, my0 - cdev->phase.y, mx1 - mx0, my1 - my0, gx_no_color_index, color); } /* * Clip the rectangle for a copy operation. * Sets m{x,y}{0,1} to the region in the mask coordinate system; * subtract cdev->phase.{x,y} to get target coordinates. * Sets sdata, sx to adjusted values of data, sourcex. * References cdev, data, sourcex, raster, x, y, w, h. */ #define DECLARE_MASK_COPY\ const byte *sdata;\ int sx, mx0, my0, mx1, my1 #define FIT_MASK_COPY(data, sourcex, raster, vx, vy, vw, vh)\ BEGIN\ sdata = data, sx = sourcex;\ mx0 = vx + cdev->phase.x, my0 = vy + cdev->phase.y;\ mx1 = mx0 + vw, my1 = my0 + vh;\ if ( mx0 < 0 )\ sx -= mx0, mx0 = 0;\ if ( my0 < 0 )\ sdata -= my0 * raster, my0 = 0;\ if ( mx1 > cdev->tiles.size.x )\ mx1 = cdev->tiles.size.x;\ if ( my1 > cdev->tiles.size.y )\ my1 = cdev->tiles.size.y;\ END /* Copy a monochrome bitmap by playing Boolean games. */ private int mask_clip_copy_mono(gx_device * dev, const byte * data, int sourcex, int raster, gx_bitmap_id id, int x, int y, int w, int h, gx_color_index color0, gx_color_index color1) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; gx_device *tdev = cdev->target; gx_color_index color, mcolor0, mcolor1; DECLARE_MASK_COPY; int cy, ny; int code; setup_mask_copy_mono(cdev, color, mcolor0, mcolor1); FIT_MASK_COPY(data, sourcex, raster, x, y, w, h); for (cy = my0; cy < my1; cy += ny) { int ty = cy - cdev->phase.y; int cx, nx; ny = my1 - cy; if (ny > cdev->mdev.height) ny = cdev->mdev.height; for (cx = mx0; cx < mx1; cx += nx) { int tx = cx - cdev->phase.x; nx = mx1 - cx; /* also should be min */ /* Copy a tile slice to the memory device buffer. */ memcpy(cdev->buffer.bytes, cdev->tiles.data + cy * cdev->tiles.raster, cdev->tiles.raster * ny); /* Intersect the tile with the source data. */ /* mcolor0 and mcolor1 invert the data if needed. */ /* This call can't fail. */ (*dev_proc(&cdev->mdev, copy_mono)) ((gx_device *) & cdev->mdev, sdata + (ty - y) * raster, sx + tx - x, raster, gx_no_bitmap_id, cx, 0, nx, ny, mcolor0, mcolor1); /* Now copy the color through the double mask. */ code = (*dev_proc(tdev, copy_mono)) (cdev->target, cdev->buffer.bytes, cx, cdev->tiles.raster, gx_no_bitmap_id, tx, ty, nx, ny, gx_no_color_index, color); if (code < 0) return code; } } return 0; } /* * Define the run enumerator for the other copying operations. We can't use * the BitBlt tricks: we have to scan for runs of 1s. There are obvious * ways to speed this up; we'll implement some if we need to. */ private int clip_runs_enumerate(gx_device_mask_clip * cdev, int (*process) (P5(clip_callback_data_t * pccd, int xc, int yc, int xec, int yec)), clip_callback_data_t * pccd) { DECLARE_MASK_COPY; int cy; const byte *tile_row; gs_int_rect prev; int code; FIT_MASK_COPY(pccd->data, pccd->sourcex, pccd->raster, pccd->x, pccd->y, pccd->w, pccd->h); tile_row = cdev->tiles.data + my0 * cdev->tiles.raster + (mx0 >> 3); prev.p.y = prev.q.y = -1; /* arbitrary */ for (cy = my0; cy < my1; cy++) { int cx = mx0; const byte *tp = tile_row; if_debug1('B', "[B]clip runs y=%d:", cy - cdev->phase.y); while (cx < mx1) { int len; int tx1, tx, ty; /* Skip a run of 0s. */ len = byte_bit_run_length[cx & 7][*tp ^ 0xff]; if (len < 8) { cx += len; if (cx >= mx1) break; } else { cx += len - 8; tp++; while (cx < mx1 && *tp == 0) cx += 8, tp++; if (cx >= mx1) break; cx += byte_bit_run_length_0[*tp ^ 0xff]; if (cx >= mx1) break; } tx1 = cx - cdev->phase.x; /* Scan a run of 1s. */ len = byte_bit_run_length[cx & 7][*tp]; if (len < 8) { cx += len; if (cx > mx1) cx = mx1; } else { cx += len - 8; tp++; while (cx < mx1 && *tp == 0xff) cx += 8, tp++; if (cx > mx1) cx = mx1; else { cx += byte_bit_run_length_0[*tp]; if (cx > mx1) cx = mx1; } } tx = cx - cdev->phase.x; if_debug2('B', " %d-%d,", tx1, tx); ty = cy - cdev->phase.y; /* Detect vertical rectangles. */ if (prev.p.x == tx1 && prev.q.x == tx && prev.q.y == ty) prev.q.y = ty + 1; else { if (prev.q.y > prev.p.y) { code = (*process)(pccd, tx1, ty, tx, ty + 1); if (code < 0) return code; } prev.p.x = tx1; prev.p.y = ty; prev.q.x = tx; prev.q.y = ty + 1; } } if_debug0('B', "\n"); tile_row += cdev->tiles.raster; } if (prev.q.y > prev.p.y) { code = (*process)(pccd, prev.p.x, prev.p.y, prev.q.x, prev.q.y); if (code < 0) return code; } return 0; } /* Copy a color rectangle */ private int mask_clip_copy_color(gx_device * dev, const byte * data, int sourcex, int raster, gx_bitmap_id id, int x, int y, int w, int h) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; clip_callback_data_t ccdata; ccdata.tdev = cdev->target; ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster; ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h; return clip_runs_enumerate(cdev, clip_call_copy_color, &ccdata); } /* Copy a rectangle with alpha */ private int mask_clip_copy_alpha(gx_device * dev, const byte * data, int sourcex, int raster, gx_bitmap_id id, int x, int y, int w, int h, gx_color_index color, int depth) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; clip_callback_data_t ccdata; ccdata.tdev = cdev->target; ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster; ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h; ccdata.color[0] = color, ccdata.depth = depth; return clip_runs_enumerate(cdev, clip_call_copy_alpha, &ccdata); } private int mask_clip_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles, int x, int y, int w, int h, gx_color_index color0, gx_color_index color1, int phase_x, int phase_y) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; clip_callback_data_t ccdata; ccdata.tdev = cdev->target; ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h; ccdata.tiles = tiles; ccdata.color[0] = color0, ccdata.color[1] = color1; ccdata.phase.x = phase_x, ccdata.phase.y = phase_y; return clip_runs_enumerate(cdev, clip_call_strip_tile_rectangle, &ccdata); } private int mask_clip_strip_copy_rop(gx_device * dev, const byte * data, int sourcex, uint raster, gx_bitmap_id id, const gx_color_index * scolors, const gx_strip_bitmap * textures, const gx_color_index * tcolors, int x, int y, int w, int h, int phase_x, int phase_y, gs_logical_operation_t lop) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; clip_callback_data_t ccdata; ccdata.tdev = cdev->target; ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h; ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster; ccdata.scolors = scolors, ccdata.textures = textures, ccdata.tcolors = tcolors; ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop; return clip_runs_enumerate(cdev, clip_call_strip_copy_rop, &ccdata); } private void mask_clip_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox) { gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev; gx_device *tdev = cdev->target; gs_fixed_rect tbox; (*dev_proc(tdev, get_clipping_box)) (tdev, &tbox); pbox->p.x = tbox.p.x - cdev->phase.x; pbox->p.y = tbox.p.y - cdev->phase.y; pbox->q.x = tbox.q.x - cdev->phase.x; pbox->q.y = tbox.q.y - cdev->phase.y; }