1 /* Copyright (C) 1993, 1994, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. 2 3 This software is provided AS-IS with no warranty, either express or 4 implied. 5 6 This software is distributed under license and may not be copied, 7 modified or distributed except as expressly authorized under the terms 8 of the license contained in the file LICENSE in this distribution. 9 10 For more information about licensing, please refer to 11 http://www.ghostscript.com/licensing/. For information on 12 commercial licensing, go to http://www.artifex.com/licensing/ or 13 contact Artifex Software, Inc., 101 Lucas Valley Road #110, 14 San Rafael, CA 94903, U.S.A., +1(415)492-9861. 15 */ 16 17 /* $Id: gdevmpla.c,v 1.5 2002/09/21 00:26:04 dan Exp $ */ 18 /* Any-depth planar "memory" (stored bitmap) device */ 19 #include "memory_.h" 20 #include "gx.h" 21 #include "gserrors.h" 22 #include "gsbitops.h" 23 #include "gxdevice.h" 24 #include "gxdevmem.h" /* semi-public definitions */ 25 #include "gxgetbit.h" 26 #include "gdevmem.h" /* private definitions */ 27 #include "gdevmpla.h" /* interface */ 28 29 /* procedures */ 30 private dev_proc_open_device(mem_planar_open); 31 declare_mem_procs(mem_planar_copy_mono, mem_planar_copy_color, mem_planar_fill_rectangle); 32 private dev_proc_strip_tile_rectangle(mem_planar_strip_tile_rectangle); 33 private dev_proc_get_bits_rectangle(mem_planar_get_bits_rectangle); 34 35 /* 36 * Set up a planar memory device, after calling gs_make_mem_device but 37 * before opening the device. The pre-existing device provides the color 38 * mapping procedures, but not the drawing procedures. Requires: num_planes 39 * > 0, plane_depths[0 .. num_planes - 1] > 0, sum of plane depths = 40 * mdev->color_info.depth. 41 * 42 * Note that this is the only public procedure in this file, and the only 43 * sanctioned way to set up a planar memory device. 44 */ 45 int 46 gdev_mem_set_planar(gx_device_memory * mdev, int num_planes, 47 const gx_render_plane_t *planes /*[num_planes]*/) 48 { 49 int total_depth; 50 int same_depth = planes[0].depth; 51 gx_color_index covered = 0; 52 int pi; 53 54 if (num_planes < 1 || num_planes > GX_DEVICE_COLOR_MAX_COMPONENTS) 55 return_error(gs_error_rangecheck); 56 for (pi = 0, total_depth = 0; pi < num_planes; ++pi) { 57 int shift = planes[pi].shift; 58 int plane_depth = planes[pi].depth; 59 gx_color_index mask; 60 61 if (shift < 0 || plane_depth > 16 || 62 !gdev_mem_device_for_bits(plane_depth)) 63 return_error(gs_error_rangecheck); 64 mask = (((gx_color_index)1 << plane_depth) - 1) << shift; 65 if (covered & mask) 66 return_error(gs_error_rangecheck); 67 covered |= mask; 68 if (plane_depth != same_depth) 69 same_depth = 0; 70 total_depth += plane_depth; 71 } 72 if (total_depth > mdev->color_info.depth) 73 return_error(gs_error_rangecheck); 74 mdev->num_planes = num_planes; 75 memcpy(mdev->planes, planes, num_planes * sizeof(planes[0])); 76 mdev->plane_depth = same_depth; 77 /* Change the drawing procedures. */ 78 set_dev_proc(mdev, open_device, mem_planar_open); 79 set_dev_proc(mdev, fill_rectangle, mem_planar_fill_rectangle); 80 set_dev_proc(mdev, copy_mono, mem_planar_copy_mono); 81 set_dev_proc(mdev, copy_color, mem_planar_copy_color); 82 set_dev_proc(mdev, copy_alpha, gx_default_copy_alpha); 83 set_dev_proc(mdev, strip_tile_rectangle, mem_planar_strip_tile_rectangle); 84 set_dev_proc(mdev, strip_copy_rop, gx_default_strip_copy_rop); 85 set_dev_proc(mdev, get_bits_rectangle, mem_planar_get_bits_rectangle); 86 return 0; 87 } 88 89 /* Open a planar memory device. */ 90 private int 91 mem_planar_open(gx_device * dev) 92 { 93 gx_device_memory *const mdev = (gx_device_memory *)dev; 94 95 /* Check that we aren't trying to open a chunky device as planar. */ 96 if (mdev->num_planes == 0) 97 return_error(gs_error_rangecheck); 98 return gdev_mem_open_scan_lines(mdev, dev->height); 99 } 100 101 /* 102 * We execute drawing operations by patching a few parameters in the 103 * device structure and then calling the procedure appropriate to the 104 * plane depth. 105 */ 106 typedef struct mem_save_params_s { 107 int depth; /* color_info.depth */ 108 byte *base; 109 byte **line_ptrs; 110 } mem_save_params_t; 111 #define MEM_SAVE_PARAMS(mdev, msp)\ 112 (msp.depth = mdev->color_info.depth,\ 113 msp.base = mdev->base,\ 114 msp.line_ptrs = mdev->line_ptrs) 115 #define MEM_SET_PARAMS(mdev, plane_depth)\ 116 (mdev->color_info.depth = plane_depth, /* maybe not needed */\ 117 mdev->base = mdev->line_ptrs[0],\ 118 mdev->raster = bitmap_raster(mdev->width * plane_depth)) 119 #define MEM_RESTORE_PARAMS(mdev, msp)\ 120 (mdev->color_info.depth = msp.depth,\ 121 mdev->base = msp.base,\ 122 mdev->line_ptrs = msp.line_ptrs) 123 124 /* Fill a rectangle with a color. */ 125 private int 126 mem_planar_fill_rectangle(gx_device * dev, int x, int y, int w, int h, 127 gx_color_index color) 128 { 129 gx_device_memory * const mdev = (gx_device_memory *)dev; 130 mem_save_params_t save; 131 int pi; 132 133 MEM_SAVE_PARAMS(mdev, save); 134 for (pi = 0; pi < mdev->num_planes; ++pi) { 135 int plane_depth = mdev->planes[pi].depth; 136 gx_color_index mask = ((gx_color_index)1 << plane_depth) - 1; 137 const gx_device_memory *mdproto = 138 gdev_mem_device_for_bits(plane_depth); 139 140 MEM_SET_PARAMS(mdev, plane_depth); 141 dev_proc(mdproto, fill_rectangle)(dev, x, y, w, h, 142 (color >> mdev->planes[pi].shift) & 143 mask); 144 mdev->line_ptrs += mdev->height; 145 } 146 MEM_RESTORE_PARAMS(mdev, save); 147 return 0; 148 } 149 150 /* Copy a bitmap. */ 151 private int 152 mem_planar_copy_mono(gx_device * dev, const byte * base, int sourcex, 153 int sraster, gx_bitmap_id id, int x, int y, int w, int h, 154 gx_color_index color0, gx_color_index color1) 155 { 156 gx_device_memory * const mdev = (gx_device_memory *)dev; 157 mem_save_params_t save; 158 int pi; 159 160 MEM_SAVE_PARAMS(mdev, save); 161 for (pi = 0; pi < mdev->num_planes; ++pi) { 162 int plane_depth = mdev->planes[pi].depth; 163 int shift = mdev->planes[pi].shift; 164 gx_color_index mask = ((gx_color_index)1 << plane_depth) - 1; 165 const gx_device_memory *mdproto = 166 gdev_mem_device_for_bits(plane_depth); 167 gx_color_index c0 = 168 (color0 == gx_no_color_index ? gx_no_color_index : 169 (color0 >> shift) & mask); 170 gx_color_index c1 = 171 (color1 == gx_no_color_index ? gx_no_color_index : 172 (color1 >> shift) & mask); 173 174 MEM_SET_PARAMS(mdev, plane_depth); 175 if (c0 == c1) 176 dev_proc(mdproto, fill_rectangle)(dev, x, y, w, h, c0); 177 else 178 dev_proc(mdproto, copy_mono) 179 (dev, base, sourcex, sraster, id, x, y, w, h, c0, c1); 180 mdev->line_ptrs += mdev->height; 181 } 182 MEM_RESTORE_PARAMS(mdev, save); 183 return 0; 184 } 185 186 /* Copy a color bitmap. */ 187 /* This is slow and messy. */ 188 private int 189 mem_planar_copy_color(gx_device * dev, const byte * base, int sourcex, 190 int sraster, gx_bitmap_id id, 191 int x, int y, int w, int h) 192 { 193 gx_device_memory * const mdev = (gx_device_memory *)dev; 194 #define BUF_LONGS 100 /* arbitrary, >= 1 */ 195 #define BUF_BYTES (BUF_LONGS * ARCH_SIZEOF_LONG) 196 union b_ { 197 ulong l[BUF_LONGS]; 198 byte b[BUF_BYTES]; 199 } buf; 200 int source_depth = dev->color_info.depth; 201 mem_save_params_t save; 202 int pi; 203 204 fit_copy(dev, base, sourcex, sraster, id, x, y, w, h); 205 MEM_SAVE_PARAMS(mdev, save); 206 for (pi = 0; pi < mdev->num_planes; ++pi) { 207 int plane_depth = mdev->planes[pi].depth; 208 int shift = mdev->planes[pi].shift; 209 gx_color_index mask = ((gx_color_index)1 << plane_depth) - 1; 210 const gx_device_memory *mdproto = 211 gdev_mem_device_for_bits(plane_depth); 212 /* 213 * Divide up the transfer into chunks that can be assembled 214 * within the fixed-size buffer. This code can be simplified 215 * a lot if all planes have the same depth, by simply using 216 * copy_color to transfer one column at a time, but it might 217 * be very inefficient. 218 */ 219 uint plane_raster = bitmap_raster(plane_depth * w); 220 int br, bw, bh, cx, cy, cw, ch, ix, iy; 221 222 MEM_SET_PARAMS(mdev, plane_depth); 223 if (plane_raster > BUF_BYTES) { 224 br = BUF_BYTES; 225 bw = BUF_BYTES * 8 / plane_depth; 226 bh = 1; 227 } else { 228 br = plane_raster; 229 bw = w; 230 bh = BUF_BYTES / plane_raster; 231 } 232 /* 233 * We could do the extraction with get_bits_rectangle 234 * selecting a single plane, but this is critical enough 235 * code that we more or less replicate it here. 236 */ 237 for (cy = y; cy < y + h; cy += ch) { 238 ch = min(bh, y + h - cy); 239 for (cx = x; cx < x + w; cx += cw) { 240 int sx = sourcex + cx - x; 241 const byte *source_base = base + sraster * (cy - y); 242 int source_bit = 0; 243 244 cw = min(bw, x + w - cx); 245 if (sx) { 246 int xbit = sx * source_depth; 247 248 source_base += xbit >> 3; 249 source_bit = xbit & 7; 250 } 251 for (iy = 0; iy < ch; ++iy) { 252 sample_load_declare_setup(sptr, sbit, source_base, 253 source_bit, source_depth); 254 sample_store_declare_setup(dptr, dbit, dbbyte, 255 buf.b + br * iy, 256 0, plane_depth); 257 258 for (ix = 0; ix < cw; ++ix) { 259 gx_color_index value; 260 261 sample_load_next_any(value, sptr, sbit, source_depth); 262 value = (value >> shift) & mask; 263 sample_store_next16(value, dptr, dbit, plane_depth, 264 dbbyte); 265 } 266 sample_store_flush(dptr, dbit, plane_depth, dbbyte); 267 source_base += sraster; 268 } 269 /* 270 * Detect and bypass the possibility that copy_color is 271 * defined in terms of copy_mono. 272 */ 273 if (plane_depth == 1) 274 dev_proc(mdproto, copy_mono) 275 (dev, buf.b, 0, br, gx_no_bitmap_id, cx, cy, cw, ch, 276 (gx_color_index)0, (gx_color_index)1); 277 else 278 dev_proc(mdproto, copy_color) 279 (dev, buf.b, 0, br, gx_no_bitmap_id, cx, cy, cw, ch); 280 } 281 } 282 mdev->line_ptrs += mdev->height; 283 } 284 MEM_RESTORE_PARAMS(mdev, save); 285 return 0; 286 #undef BUF_BYTES 287 #undef BUF_LONGS 288 } 289 290 private int 291 mem_planar_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles, 292 int x, int y, int w, int h, 293 gx_color_index color0, gx_color_index color1, 294 int px, int py) 295 { 296 gx_device_memory * const mdev = (gx_device_memory *)dev; 297 mem_save_params_t save; 298 int pi; 299 300 /* We can't split up the transfer if the tile is colored. */ 301 if (color0 == gx_no_color_index && color1 == gx_no_color_index) 302 return gx_default_strip_tile_rectangle 303 (dev, tiles, x, y, w, h, color0, color1, px, py); 304 MEM_SAVE_PARAMS(mdev, save); 305 for (pi = 0; pi < mdev->num_planes; ++pi) { 306 int plane_depth = mdev->planes[pi].depth; 307 int shift = mdev->planes[pi].shift; 308 gx_color_index mask = ((gx_color_index)1 << plane_depth) - 1; 309 const gx_device_memory *mdproto = 310 gdev_mem_device_for_bits(plane_depth); 311 gx_color_index c0 = 312 (color0 == gx_no_color_index ? gx_no_color_index : 313 (color0 >> shift) & mask); 314 gx_color_index c1 = 315 (color1 == gx_no_color_index ? gx_no_color_index : 316 (color1 >> shift) & mask); 317 318 MEM_SET_PARAMS(mdev, plane_depth); 319 if (c0 == c1) 320 dev_proc(mdproto, fill_rectangle)(dev, x, y, w, h, c0); 321 else { 322 /* 323 * Temporarily replace copy_mono in case strip_tile_rectangle is 324 * defined in terms of it. 325 */ 326 set_dev_proc(dev, copy_mono, dev_proc(mdproto, copy_mono)); 327 dev_proc(mdproto, strip_tile_rectangle) 328 (dev, tiles, x, y, w, h, c0, c1, px, py); 329 } 330 mdev->line_ptrs += mdev->height; 331 } 332 MEM_RESTORE_PARAMS(mdev, save); 333 set_dev_proc(dev, copy_mono, mem_planar_copy_mono); 334 return 0; 335 } 336 337 /* 338 * Repack planar into chunky format. This is an internal procedure that 339 * implements the straightforward chunky case of get_bits_rectangle, and 340 * is also used for the general cases. 341 */ 342 private int 343 planar_to_chunky(gx_device_memory *mdev, int x, int y, int w, int h, 344 int offset, uint draster, byte *dest) 345 { 346 int num_planes = mdev->num_planes; 347 sample_load_declare(sptr[GX_DEVICE_COLOR_MAX_COMPONENTS], 348 sbit[GX_DEVICE_COLOR_MAX_COMPONENTS]); 349 sample_store_declare(dptr, dbit, dbbyte); 350 int ddepth = mdev->color_info.depth; 351 int direct = 352 (mdev->color_info.depth != num_planes * mdev->plane_depth ? 0 : 353 mdev->planes[0].shift == 0 ? -mdev->plane_depth : mdev->plane_depth); 354 int pi, ix, iy; 355 356 /* Check whether the planes are of equal size and sequential. */ 357 /* If direct != 0, we already know they exactly fill the depth. */ 358 if (direct < 0) { 359 for (pi = 0; pi < num_planes; ++pi) 360 if (mdev->planes[pi].shift != pi * -direct) { 361 direct = 0; break; 362 } 363 } else if (direct > 0) { 364 for (pi = 0; pi < num_planes; ++pi) 365 if (mdev->planes[num_planes - 1 - pi].shift != pi * direct) { 366 direct = 0; break; 367 } 368 } 369 for (iy = y; iy < y + h; ++iy) { 370 byte **line_ptr = mdev->line_ptrs + iy; 371 372 for (pi = 0; pi < num_planes; ++pi, line_ptr += mdev->height) { 373 int plane_depth = mdev->planes[pi].depth; 374 int xbit = x * plane_depth; 375 376 sptr[pi] = *line_ptr + (xbit >> 3); 377 sample_load_setup(sbit[pi], xbit & 7, plane_depth); 378 } 379 { 380 int xbit = offset * ddepth; 381 382 dptr = dest + (iy - y) * draster + (xbit >> 3); 383 sample_store_setup(dbit, xbit & 7, ddepth); 384 } 385 if (direct == -8) { 386 /* 1 byte per component, lsb first. */ 387 switch (num_planes) { 388 case 3: { 389 const byte *p0 = sptr[2]; 390 const byte *p1 = sptr[1]; 391 const byte *p2 = sptr[0]; 392 393 for (ix = w; ix > 0; --ix, dptr += 3) { 394 dptr[0] = *p0++; 395 dptr[1] = *p1++; 396 dptr[2] = *p2++; 397 } 398 } 399 continue; 400 case 4: 401 for (ix = w; ix > 0; --ix, dptr += 4) { 402 dptr[0] = *sptr[3]++; 403 dptr[1] = *sptr[2]++; 404 dptr[2] = *sptr[1]++; 405 dptr[3] = *sptr[0]++; 406 } 407 continue; 408 default: 409 break; 410 } 411 } 412 sample_store_preload(dbbyte, dptr, dbit, ddepth); 413 for (ix = w; ix > 0; --ix) { 414 gx_color_index color = 0; 415 416 for (pi = 0; pi < num_planes; ++pi) { 417 int plane_depth = mdev->planes[pi].depth; 418 uint value; 419 420 sample_load_next16(value, sptr[pi], sbit[pi], plane_depth); 421 color |= (gx_color_index)value << mdev->planes[pi].shift; 422 } 423 sample_store_next_any(color, dptr, dbit, ddepth, dbbyte); 424 } 425 sample_store_flush(dptr, dbit, ddepth, dbbyte); 426 } 427 return 0; 428 } 429 430 /* Copy bits back from a planar memory device. */ 431 private int 432 mem_planar_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect, 433 gs_get_bits_params_t * params, 434 gs_int_rect ** unread) 435 { 436 /* This duplicates most of mem_get_bits_rectangle. Tant pis. */ 437 gx_device_memory * const mdev = (gx_device_memory *)dev; 438 gs_get_bits_options_t options = params->options; 439 int x = prect->p.x, w = prect->q.x - x, y = prect->p.y, h = prect->q.y - y; 440 int num_planes = mdev->num_planes; 441 gs_get_bits_params_t copy_params; 442 int code; 443 444 if (options == 0) { 445 /* 446 * Unfortunately, as things stand, we have to support 447 * GB_PACKING_CHUNKY. In fact, we can't even claim to support 448 * GB_PACKING_PLANAR, because there is currently no way to 449 * describe the particular planar packing format that the device 450 * actually stores. 451 */ 452 params->options = 453 (GB_ALIGN_STANDARD | GB_ALIGN_ANY) | 454 (GB_RETURN_COPY | GB_RETURN_POINTER) | 455 (GB_OFFSET_0 | GB_OFFSET_SPECIFIED | GB_OFFSET_ANY) | 456 (GB_RASTER_STANDARD | GB_RASTER_SPECIFIED | GB_RASTER_ANY) | 457 /* 458 (mdev->num_planes == mdev->color_info.depth ? 459 GB_PACKING_CHUNKY | GB_PACKING_PLANAR | GB_PACKING_BIT_PLANAR : 460 GB_PACKING_CHUNKY | GB_PACKING_PLANAR) 461 */ 462 GB_PACKING_CHUNKY | 463 GB_COLORS_NATIVE | GB_ALPHA_NONE; 464 return_error(gs_error_rangecheck); 465 } 466 if ((w <= 0) | (h <= 0)) { 467 if ((w | h) < 0) 468 return_error(gs_error_rangecheck); 469 return 0; 470 } 471 if (x < 0 || w > dev->width - x || 472 y < 0 || h > dev->height - y 473 ) 474 return_error(gs_error_rangecheck); 475 476 /* 477 * If the request is for exactly one plane, hand it off to a device 478 * temporarily tweaked to return just that plane. 479 */ 480 if (!(~options & (GB_PACKING_PLANAR | GB_SELECT_PLANES))) { 481 /* Check that only a single plane is being requested. */ 482 int pi; 483 484 for (pi = 0; pi < num_planes; ++pi) 485 if (params->data[pi] != 0) 486 break; 487 if (pi < num_planes) { 488 int plane = pi++; 489 490 for (; pi < num_planes; ++pi) 491 if (params->data[pi] != 0) 492 break; 493 if (pi == num_planes) { 494 mem_save_params_t save; 495 496 copy_params = *params; 497 copy_params.options = 498 (options & ~(GB_PACKING_ALL | GB_SELECT_PLANES)) | 499 GB_PACKING_CHUNKY; 500 copy_params.data[0] = copy_params.data[plane]; 501 MEM_SAVE_PARAMS(mdev, save); 502 mdev->line_ptrs += mdev->height * plane; 503 MEM_SET_PARAMS(mdev, mdev->planes[plane].depth); 504 code = mem_get_bits_rectangle(dev, prect, ©_params, 505 unread); 506 MEM_RESTORE_PARAMS(mdev, save); 507 if (code >= 0) { 508 params->data[plane] = copy_params.data[0]; 509 return code; 510 } 511 } 512 } 513 } 514 /* 515 * We can't return the requested plane by itself. Fall back to 516 * chunky format. This is somewhat painful. 517 * 518 * The code here knows how to produce just one chunky format: 519 * GB_COLORS_NATIVE, GB_ALPHA_NONE, GB_RETURN_COPY. 520 * For any other format, we generate this one in a buffer and 521 * hand it off to gx_get_bits_copy. This is *really* painful. 522 */ 523 if (!(~options & (GB_COLORS_NATIVE | GB_ALPHA_NONE | 524 GB_PACKING_CHUNKY | GB_RETURN_COPY))) { 525 int offset = (options & GB_OFFSET_SPECIFIED ? params->x_offset : 0); 526 uint draster = 527 (options & GB_RASTER_SPECIFIED ? params->raster : 528 bitmap_raster((offset + w) * mdev->color_info.depth)); 529 530 planar_to_chunky(mdev, x, y, w, h, offset, draster, params->data[0]); 531 } else { 532 /* 533 * Do the transfer through an intermediate buffer. 534 * The buffer must be large enough to hold at least one pixel, 535 * i.e., GX_DEVICE_COLOR_MAX_COMPONENTS 16-bit values. 536 * The algorithms are very similar to those in copy_color. 537 */ 538 #define BUF_LONGS\ 539 max(100, (GX_DEVICE_COLOR_MAX_COMPONENTS * 2 + sizeof(long) - 1) /\ 540 sizeof(long)) 541 #define BUF_BYTES (BUF_LONGS * ARCH_SIZEOF_LONG) 542 union b_ { 543 ulong l[BUF_LONGS]; 544 byte b[BUF_BYTES]; 545 } buf; 546 int br, bw, bh, cx, cy, cw, ch; 547 int ddepth = mdev->color_info.depth; 548 uint raster = bitmap_raster(ddepth * mdev->width); 549 gs_get_bits_params_t dest_params; 550 551 if (raster > BUF_BYTES) { 552 br = BUF_BYTES; 553 bw = BUF_BYTES * 8 / ddepth; 554 bh = 1; 555 } else { 556 br = raster; 557 bw = w; 558 bh = BUF_BYTES / raster; 559 } 560 copy_params.options = 561 GB_COLORS_NATIVE | GB_PACKING_CHUNKY | GB_ALPHA_NONE | 562 GB_RASTER_STANDARD; 563 copy_params.raster = raster; 564 dest_params = *params; 565 for (cy = y; cy < y + h; cy += ch) { 566 ch = min(bh, y + h - cy); 567 for (cx = x; cx < x + w; cx += cw) { 568 cw = min(bw, x + w - cx); 569 planar_to_chunky(mdev, cx, cy, cw, ch, 0, br, buf.b); 570 dest_params.x_offset = params->x_offset + cx - x; 571 code = gx_get_bits_copy(dev, 0, cw, ch, &dest_params, 572 ©_params, buf.b, br); 573 if (code < 0) 574 return code; 575 } 576 dest_params.data[0] += ch * raster; 577 } 578 #undef BUF_BYTES 579 #undef BUF_LONGS 580 } 581 return 0; 582 } 583