xref: /plan9/sys/src/cmd/gs/src/gdevm1.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1996, 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: gdevm1.c,v 1.6 2002/10/07 08:28:56 ghostgum Exp $ */
18 /* Monobit "memory" (stored bitmap) device */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gxdevice.h"
22 #include "gxdevmem.h"		/* semi-public definitions */
23 #include "gdevmem.h"		/* private definitions */
24 
25 /* Optionally, use the slow RasterOp implementations for testing. */
26 /*#define USE_COPY_ROP */
27 
28 #ifdef USE_COPY_ROP
29 #include "gsrop.h"
30 #endif
31 
32 /* ================ Standard (byte-oriented) device ================ */
33 
34 /* Procedures */
35 private dev_proc_map_rgb_color(mem_mono_map_rgb_color);
36 private dev_proc_map_color_rgb(mem_mono_map_color_rgb);
37 private dev_proc_copy_mono(mem_mono_copy_mono);
38 private dev_proc_fill_rectangle(mem_mono_fill_rectangle);
39 private dev_proc_strip_tile_rectangle(mem_mono_strip_tile_rectangle);
40 
41 /* The device descriptor. */
42 /* The instance is public. */
43 const gx_device_memory mem_mono_device =
44 mem_full_alpha_device("image1", 0, 1, mem_open,
45 		      mem_mono_map_rgb_color, mem_mono_map_color_rgb,
46 	 mem_mono_copy_mono, gx_default_copy_color, mem_mono_fill_rectangle,
47 		      gx_default_map_cmyk_color, gx_no_copy_alpha,
48 		      mem_mono_strip_tile_rectangle, mem_mono_strip_copy_rop,
49 		      mem_get_bits_rectangle);
50 
51 /* Map color to/from RGB.  This may be inverted. */
52 private gx_color_index
mem_mono_map_rgb_color(gx_device * dev,const gx_color_value cv[])53 mem_mono_map_rgb_color(gx_device * dev, const gx_color_value cv[])
54 {
55     gx_device_memory * const mdev = (gx_device_memory *)dev;
56     return (gx_default_w_b_map_rgb_color(dev, cv) ^ mdev->palette.data[0]) & 1;
57 }
58 
59 private int
mem_mono_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])60 mem_mono_map_color_rgb(gx_device * dev, gx_color_index color,
61 		       gx_color_value prgb[3])
62 {
63     gx_device_memory * const mdev = (gx_device_memory *)dev;
64     /* NB code doesn't make sense... map_color_rgb procedures return an error code */
65     return (gx_default_w_b_map_color_rgb(dev, color, prgb) ^ mdev->palette.data[0]) & 1;
66 }
67 
68 /* Fill a rectangle with a color. */
69 private int
mem_mono_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)70 mem_mono_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
71 			gx_color_index color)
72 {
73     gx_device_memory * const mdev = (gx_device_memory *)dev;
74 
75 #ifdef USE_COPY_ROP
76     return mem_mono_copy_rop(dev, NULL, 0, 0, gx_no_bitmap_id, NULL,
77 			     NULL, NULL,
78 			     x, y, w, h, 0, 0,
79 			     (color ? rop3_1 : rop3_0));
80 #else
81     fit_fill(dev, x, y, w, h);
82     bits_fill_rectangle(scan_line_base(mdev, y), x, mdev->raster,
83 			-(int)(mono_fill_chunk) color, w, h);
84     return 0;
85 #endif
86 }
87 
88 /* Convert x coordinate to byte offset in scan line. */
89 #define x_to_byte(x) ((x) >> 3)
90 
91 /* Copy a monochrome bitmap. */
92 #undef mono_masks
93 #define mono_masks mono_copy_masks
94 
95 /*
96  * Fetch a chunk from the source.
97  *
98  * Since source and destination are both always big-endian,
99  * fetching an aligned chunk never requires byte swapping.
100  */
101 #define CFETCH_ALIGNED(cptr)\
102   (*(const chunk *)(cptr))
103 
104 /*
105  * Note that the macros always cast cptr,
106  * so it doesn't matter what the type of cptr is.
107  */
108 /* cshift = chunk_bits - shift. */
109 #undef chunk
110 #if arch_is_big_endian
111 #  define chunk uint
112 #  define CFETCH_RIGHT(cptr, shift, cshift)\
113 	(CFETCH_ALIGNED(cptr) >> shift)
114 #  define CFETCH_LEFT(cptr, shift, cshift)\
115 	(CFETCH_ALIGNED(cptr) << shift)
116 #  define CFETCH_USES_CSKEW 0
117 /* Fetch a chunk that straddles a chunk boundary. */
118 #  define CFETCH2(cptr, cskew, skew)\
119     (CFETCH_LEFT(cptr, cskew, skew) +\
120      CFETCH_RIGHT((const chunk *)(cptr) + 1, skew, cskew))
121 #else /* little-endian */
122 #  define chunk bits16
123 private const bits16 right_masks2[9] =
124 {
125     0xffff, 0x7f7f, 0x3f3f, 0x1f1f, 0x0f0f, 0x0707, 0x0303, 0x0101, 0x0000
126 };
127 private const bits16 left_masks2[9] =
128 {
129     0xffff, 0xfefe, 0xfcfc, 0xf8f8, 0xf0f0, 0xe0e0, 0xc0c0, 0x8080, 0x0000
130 };
131 
132 #  define CCONT(cptr, off) (((const chunk *)(cptr))[off])
133 #  define CFETCH_RIGHT(cptr, shift, cshift)\
134 	((shift) < 8 ?\
135 	 ((CCONT(cptr, 0) >> (shift)) & right_masks2[shift]) +\
136 	  (CCONT(cptr, 0) << (cshift)) :\
137 	 ((chunk)*(const byte *)(cptr) << (cshift)) & 0xff00)
138 #  define CFETCH_LEFT(cptr, shift, cshift)\
139 	((shift) < 8 ?\
140 	 ((CCONT(cptr, 0) << (shift)) & left_masks2[shift]) +\
141 	  (CCONT(cptr, 0) >> (cshift)) :\
142 	 ((CCONT(cptr, 0) & 0xff00) >> (cshift)) & 0xff)
143 #  define CFETCH_USES_CSKEW 1
144 /* Fetch a chunk that straddles a chunk boundary. */
145 /* We can avoid testing the shift amount twice */
146 /* by expanding the CFETCH_LEFT/right macros in-line. */
147 #  define CFETCH2(cptr, cskew, skew)\
148 	((cskew) < 8 ?\
149 	 ((CCONT(cptr, 0) << (cskew)) & left_masks2[cskew]) +\
150 	  (CCONT(cptr, 0) >> (skew)) +\
151 	  (((chunk)(((const byte *)(cptr))[2]) << (cskew)) & 0xff00) :\
152 	 (((CCONT(cptr, 0) & 0xff00) >> (skew)) & 0xff) +\
153 	  ((CCONT(cptr, 1) >> (skew)) & right_masks2[skew]) +\
154 	   (CCONT(cptr, 1) << (cskew)))
155 #endif
156 
157 typedef enum {
158     COPY_OR = 0, COPY_STORE, COPY_AND, COPY_FUNNY
159 } copy_function;
160 typedef struct {
161     int invert;
162     copy_function op;
163 } copy_mode;
164 
165 /*
166  * Map from <color0,color1> to copy_mode.
167  * Logically, this is a 2-D array.
168  * The indexing is (transparent, 0, 1, unused). */
169 private const copy_mode copy_modes[16] = {
170     {~0, COPY_FUNNY},		/* NN */
171     {~0, COPY_AND},		/* N0 */
172     {0, COPY_OR},		/* N1 */
173     {0, 0},			/* unused */
174     {0, COPY_AND},		/* 0N */
175     {0, COPY_FUNNY},		/* 00 */
176     {0, COPY_STORE},		/* 01 */
177     {0, 0},			/* unused */
178     {~0, COPY_OR},		/* 1N */
179     {~0, COPY_STORE},		/* 10 */
180     {0, COPY_FUNNY},		/* 11 */
181     {0, 0},			/* unused */
182     {0, 0},			/* unused */
183     {0, 0},			/* unused */
184     {0, 0},			/* unused */
185     {0, 0},			/* unused */
186 };
187 
188 /* Handle the funny cases that aren't supposed to happen. */
189 #define FUNNY_CASE()\
190   (invert ? gs_note_error(-1) :\
191    mem_mono_fill_rectangle(dev, x, y, w, h, color0))
192 
193 private int
mem_mono_copy_mono(gx_device * dev,const byte * source_data,int source_x,int source_raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color0,gx_color_index color1)194 mem_mono_copy_mono(gx_device * dev,
195  const byte * source_data, int source_x, int source_raster, gx_bitmap_id id,
196    int x, int y, int w, int h, gx_color_index color0, gx_color_index color1)
197 {
198     gx_device_memory * const mdev = (gx_device_memory *)dev;
199 
200 #ifdef USE_COPY_ROP
201     return mem_mono_copy_rop(dev, source_data, source_x, source_raster,
202 			     id, NULL, NULL, NULL,
203 			     x, y, w, h, 0, 0,
204 			     ((color0 == gx_no_color_index ? rop3_D :
205 			       color0 == 0 ? rop3_0 : rop3_1) & ~rop3_S) |
206 			     ((color1 == gx_no_color_index ? rop3_D :
207 			       color1 == 0 ? rop3_0 : rop3_1) & rop3_S));
208 #else /* !USE_COPY_ROP */
209     register const byte *bptr;	/* actually chunk * */
210     int dbit, wleft;
211     uint mask;
212     copy_mode mode;
213 
214     DECLARE_SCAN_PTR_VARS(dbptr, byte *, dest_raster);
215 #define optr ((chunk *)dbptr)
216     register int skew;
217     register uint invert;
218 
219     fit_copy(dev, source_data, source_x, source_raster, id, x, y, w, h);
220 #if gx_no_color_index_value != -1	/* hokey! */
221     if (color0 == gx_no_color_index)
222 	color0 = -1;
223     if (color1 == gx_no_color_index)
224 	color1 = -1;
225 #endif
226     mode = copy_modes[((int)color0 << 2) + (int)color1 + 5];
227     invert = (uint)mode.invert;	/* load register */
228     SETUP_RECT_VARS(dbptr, byte *, dest_raster);
229     bptr = source_data + ((source_x & ~chunk_align_bit_mask) >> 3);
230     dbit = x & chunk_align_bit_mask;
231     skew = dbit - (source_x & chunk_align_bit_mask);
232 
233 /* Macros for writing partial chunks. */
234 /* The destination pointer is always named optr, */
235 /* and must be declared as chunk *. */
236 /* CINVERT may be temporarily redefined. */
237 #define CINVERT(bits) ((bits) ^ invert)
238 #define WRITE_OR_MASKED(bits, mask, off)\
239   optr[off] |= (CINVERT(bits) & mask)
240 #define WRITE_STORE_MASKED(bits, mask, off)\
241   optr[off] = ((optr[off] & ~mask) | (CINVERT(bits) & mask))
242 #define WRITE_AND_MASKED(bits, mask, off)\
243   optr[off] &= (CINVERT(bits) | ~mask)
244 /* Macros for writing full chunks. */
245 #define WRITE_OR(bits)  *optr |= CINVERT(bits)
246 #define WRITE_STORE(bits) *optr = CINVERT(bits)
247 #define WRITE_AND(bits) *optr &= CINVERT(bits)
248 /* Macro for incrementing to next chunk. */
249 #define NEXT_X_CHUNK()\
250   bptr += chunk_bytes; dbptr += chunk_bytes
251 /* Common macro for the end of each scan line. */
252 #define END_Y_LOOP(sdelta, ddelta)\
253   bptr += sdelta; dbptr += ddelta
254 
255     if ((wleft = w + dbit - chunk_bits) <= 0) {		/* The entire operation fits in one (destination) chunk. */
256 	set_mono_thin_mask(mask, w, dbit);
257 
258 #define WRITE_SINGLE(wr_op, src)\
259   for ( ; ; )\
260    { wr_op(src, mask, 0);\
261      if ( --h == 0 ) break;\
262      END_Y_LOOP(source_raster, dest_raster);\
263    }
264 
265 #define WRITE1_LOOP(src)\
266   switch ( mode.op ) {\
267     case COPY_OR: WRITE_SINGLE(WRITE_OR_MASKED, src); break;\
268     case COPY_STORE: WRITE_SINGLE(WRITE_STORE_MASKED, src); break;\
269     case COPY_AND: WRITE_SINGLE(WRITE_AND_MASKED, src); break;\
270     default: return FUNNY_CASE();\
271   }
272 
273 	if (skew >= 0) {	/* single -> single, right/no shift */
274 	    if (skew == 0) {	/* no shift */
275 		WRITE1_LOOP(CFETCH_ALIGNED(bptr));
276 	    } else {		/* right shift */
277 #if CFETCH_USES_CSKEW
278 		int cskew = chunk_bits - skew;
279 #endif
280 
281 		WRITE1_LOOP(CFETCH_RIGHT(bptr, skew, cskew));
282 	    }
283 	} else if (wleft <= skew) {	/* single -> single, left shift */
284 #if CFETCH_USES_CSKEW
285 	    int cskew = chunk_bits + skew;
286 #endif
287 
288 	    skew = -skew;
289 	    WRITE1_LOOP(CFETCH_LEFT(bptr, skew, cskew));
290 	} else {		/* double -> single */
291 	    int cskew = -skew;
292 
293 	    skew += chunk_bits;
294 	    WRITE1_LOOP(CFETCH2(bptr, cskew, skew));
295 	}
296 #undef WRITE1_LOOP
297 #undef WRITE_SINGLE
298     } else if (wleft <= skew) {	/* 1 source chunk -> 2 destination chunks. */
299 	/* This is an important special case for */
300 	/* both characters and halftone tiles. */
301 	uint rmask;
302 	int cskew = chunk_bits - skew;
303 
304 	set_mono_left_mask(mask, dbit);
305 	set_mono_right_mask(rmask, wleft);
306 #undef CINVERT
307 #define CINVERT(bits) (bits)	/* pre-inverted here */
308 
309 #if arch_is_big_endian		/* no byte swapping */
310 #  define WRITE_1TO2(wr_op)\
311   for ( ; ; )\
312    { register uint bits = CFETCH_ALIGNED(bptr) ^ invert;\
313      wr_op(bits >> skew, mask, 0);\
314      wr_op(bits << cskew, rmask, 1);\
315      if ( --h == 0 ) break;\
316      END_Y_LOOP(source_raster, dest_raster);\
317    }
318 #else /* byte swapping */
319 #  define WRITE_1TO2(wr_op)\
320   for ( ; ; )\
321    { wr_op(CFETCH_RIGHT(bptr, skew, cskew) ^ invert, mask, 0);\
322      wr_op(CFETCH_LEFT(bptr, cskew, skew) ^ invert, rmask, 1);\
323      if ( --h == 0 ) break;\
324      END_Y_LOOP(source_raster, dest_raster);\
325    }
326 #endif
327 
328 	switch (mode.op) {
329 	    case COPY_OR:
330 		WRITE_1TO2(WRITE_OR_MASKED);
331 		break;
332 	    case COPY_STORE:
333 		WRITE_1TO2(WRITE_STORE_MASKED);
334 		break;
335 	    case COPY_AND:
336 		WRITE_1TO2(WRITE_AND_MASKED);
337 		break;
338 	    default:
339 		return FUNNY_CASE();
340 	}
341 #undef CINVERT
342 #define CINVERT(bits) ((bits) ^ invert)
343 #undef WRITE_1TO2
344     } else {			/* More than one source chunk and more than one */
345 	/* destination chunk are involved. */
346 	uint rmask;
347 	int words = (wleft & ~chunk_bit_mask) >> 3;
348 	uint sskip = source_raster - words;
349 	uint dskip = dest_raster - words;
350 	register uint bits;
351 
352 	set_mono_left_mask(mask, dbit);
353 	set_mono_right_mask(rmask, wleft & chunk_bit_mask);
354 	if (skew == 0) {	/* optimize the aligned case */
355 
356 #define WRITE_ALIGNED(wr_op, wr_op_masked)\
357   for ( ; ; )\
358    { int count = wleft;\
359      /* Do first partial chunk. */\
360      wr_op_masked(CFETCH_ALIGNED(bptr), mask, 0);\
361      /* Do full chunks. */\
362      while ( (count -= chunk_bits) >= 0 )\
363       { NEXT_X_CHUNK(); wr_op(CFETCH_ALIGNED(bptr)); }\
364      /* Do last chunk */\
365      if ( count > -chunk_bits )\
366       { wr_op_masked(CFETCH_ALIGNED(bptr + chunk_bytes), rmask, 1); }\
367      if ( --h == 0 ) break;\
368      END_Y_LOOP(sskip, dskip);\
369    }
370 
371 	    switch (mode.op) {
372 		case COPY_OR:
373 		    WRITE_ALIGNED(WRITE_OR, WRITE_OR_MASKED);
374 		    break;
375 		case COPY_STORE:
376 		    WRITE_ALIGNED(WRITE_STORE, WRITE_STORE_MASKED);
377 		    break;
378 		case COPY_AND:
379 		    WRITE_ALIGNED(WRITE_AND, WRITE_AND_MASKED);
380 		    break;
381 		default:
382 		    return FUNNY_CASE();
383 	    }
384 #undef WRITE_ALIGNED
385 	} else {		/* not aligned */
386 	    int cskew = -skew & chunk_bit_mask;
387 	    bool case_right =
388 	    (skew >= 0 ? true :
389 	     ((bptr += chunk_bytes), false));
390 
391 	    skew &= chunk_bit_mask;
392 
393 #define WRITE_UNALIGNED(wr_op, wr_op_masked)\
394   /* Prefetch partial word. */\
395   bits =\
396     (case_right ? CFETCH_RIGHT(bptr, skew, cskew) :\
397      CFETCH2(bptr - chunk_bytes, cskew, skew));\
398   wr_op_masked(bits, mask, 0);\
399   /* Do full chunks. */\
400   while ( count >= chunk_bits )\
401     { bits = CFETCH2(bptr, cskew, skew);\
402       NEXT_X_CHUNK(); wr_op(bits); count -= chunk_bits;\
403     }\
404   /* Do last chunk */\
405   if ( count > 0 )\
406     { bits = CFETCH_LEFT(bptr, cskew, skew);\
407       if ( count > skew ) bits += CFETCH_RIGHT(bptr + chunk_bytes, skew, cskew);\
408       wr_op_masked(bits, rmask, 1);\
409     }
410 
411 	    switch (mode.op) {
412 		case COPY_OR:
413 		    for (;;) {
414 			int count = wleft;
415 
416 			WRITE_UNALIGNED(WRITE_OR, WRITE_OR_MASKED);
417 			if (--h == 0)
418 			    break;
419 			END_Y_LOOP(sskip, dskip);
420 		    }
421 		    break;
422 		case COPY_STORE:
423 		    for (;;) {
424 			int count = wleft;
425 
426 			WRITE_UNALIGNED(WRITE_STORE, WRITE_STORE_MASKED);
427 			if (--h == 0)
428 			    break;
429 			END_Y_LOOP(sskip, dskip);
430 		    }
431 		    break;
432 		case COPY_AND:
433 		    for (;;) {
434 			int count = wleft;
435 
436 			WRITE_UNALIGNED(WRITE_AND, WRITE_AND_MASKED);
437 			if (--h == 0)
438 			    break;
439 			END_Y_LOOP(sskip, dskip);
440 		    }
441 		    break;
442 		default /*case COPY_FUNNY */ :
443 		    return FUNNY_CASE();
444 	    }
445 #undef WRITE_UNALIGNED
446 	}
447     }
448 #undef END_Y_LOOP
449 #undef NEXT_X_CHUNK
450     return 0;
451 #undef optr
452 #endif /* !USE_COPY_ROP */
453 }
454 
455 /* Strip-tile with a monochrome halftone. */
456 /* This is a performance bottleneck for monochrome devices, */
457 /* so we re-implement it, even though it takes a lot of code. */
458 private int
mem_mono_strip_tile_rectangle(gx_device * dev,register const gx_strip_bitmap * tiles,int tx,int y,int tw,int th,gx_color_index color0,gx_color_index color1,int px,int py)459 mem_mono_strip_tile_rectangle(gx_device * dev,
460 			      register const gx_strip_bitmap * tiles,
461 int tx, int y, int tw, int th, gx_color_index color0, gx_color_index color1,
462 			      int px, int py)
463 {
464     gx_device_memory * const mdev = (gx_device_memory *)dev;
465 
466 #ifdef USE_COPY_ROP
467     return mem_mono_strip_copy_rop(dev, NULL, 0, 0, tile->id, NULL,
468 				   tiles, NULL,
469 				   tx, y, tw, th, px, py,
470 				   ((color0 == gx_no_color_index ? rop3_D :
471 				 color0 == 0 ? rop3_0 : rop3_1) & ~rop3_T) |
472 				   ((color1 == gx_no_color_index ? rop3_D :
473 				  color1 == 0 ? rop3_0 : rop3_1) & rop3_T));
474 #else /* !USE_COPY_ROP */
475     register uint invert;
476     int source_raster;
477     uint tile_bits_size;
478     const byte *source_data;
479     const byte *end;
480     int x, rw, w, h;
481     register const byte *bptr;	/* actually chunk * */
482     int dbit, wleft;
483     uint mask;
484     byte *dbase;
485 
486     DECLARE_SCAN_PTR_VARS(dbptr, byte *, dest_raster);
487 #define optr ((chunk *)dbptr)
488     register int skew;
489 
490     /* This implementation doesn't handle strips yet. */
491     if (color0 != (color1 ^ 1) || tiles->shift != 0)
492 	return gx_default_strip_tile_rectangle(dev, tiles, tx, y, tw, th,
493 					       color0, color1, px, py);
494     fit_fill(dev, tx, y, tw, th);
495     invert = (uint)(-(int) color0);
496     source_raster = tiles->raster;
497     source_data = tiles->data + ((y + py) % tiles->rep_height) * source_raster;
498     tile_bits_size = tiles->size.y * source_raster;
499     end = tiles->data + tile_bits_size;
500 #undef END_Y_LOOP
501 #define END_Y_LOOP(sdelta, ddelta)\
502   if ( end - bptr <= sdelta )	/* wrap around */\
503     bptr -= tile_bits_size;\
504   bptr += sdelta; dbptr += ddelta
505     dest_raster = mdev->raster;
506     dbase = scan_line_base(mdev, y);
507     x = tx;
508     rw = tw;
509     /*
510      * The outermost loop here works horizontally, one iteration per
511      * copy of the tile.  Note that all iterations except the first
512      * have source_x = 0.
513      */
514     {
515 	int source_x = (x + px) % tiles->rep_width;
516 
517 	w = tiles->size.x - source_x;
518 	bptr = source_data + ((source_x & ~chunk_align_bit_mask) >> 3);
519 	dbit = x & chunk_align_bit_mask;
520 	skew = dbit - (source_x & chunk_align_bit_mask);
521     }
522   outer:if (w > rw)
523 	w = rw;
524     h = th;
525     dbptr = dbase + ((x >> 3) & -chunk_align_bytes);
526     if ((wleft = w + dbit - chunk_bits) <= 0) {		/* The entire operation fits in one (destination) chunk. */
527 	set_mono_thin_mask(mask, w, dbit);
528 #define WRITE1_LOOP(src)\
529   for ( ; ; )\
530    { WRITE_STORE_MASKED(src, mask, 0);\
531      if ( --h == 0 ) break;\
532      END_Y_LOOP(source_raster, dest_raster);\
533    }
534 	if (skew >= 0) {	/* single -> single, right/no shift */
535 	    if (skew == 0) {	/* no shift */
536 		WRITE1_LOOP(CFETCH_ALIGNED(bptr));
537 	    } else {		/* right shift */
538 #if CFETCH_USES_CSKEW
539 		int cskew = chunk_bits - skew;
540 #endif
541 
542 		WRITE1_LOOP(CFETCH_RIGHT(bptr, skew, cskew));
543 	    }
544 	} else if (wleft <= skew) {	/* single -> single, left shift */
545 #if CFETCH_USES_CSKEW
546 	    int cskew = chunk_bits + skew;
547 #endif
548 
549 	    skew = -skew;
550 	    WRITE1_LOOP(CFETCH_LEFT(bptr, skew, cskew));
551 	} else {		/* double -> single */
552 	    int cskew = -skew;
553 
554 	    skew += chunk_bits;
555 	    WRITE1_LOOP(CFETCH2(bptr, cskew, skew));
556 	}
557 #undef WRITE1_LOOP
558     } else if (wleft <= skew) {	/* 1 source chunk -> 2 destination chunks. */
559 	/* This is an important special case for */
560 	/* both characters and halftone tiles. */
561 	uint rmask;
562 	int cskew = chunk_bits - skew;
563 
564 	set_mono_left_mask(mask, dbit);
565 	set_mono_right_mask(rmask, wleft);
566 #if arch_is_big_endian		/* no byte swapping */
567 #undef CINVERT
568 #define CINVERT(bits) (bits)	/* pre-inverted here */
569 	for (;;) {
570 	    register uint bits = CFETCH_ALIGNED(bptr) ^ invert;
571 
572 	    WRITE_STORE_MASKED(bits >> skew, mask, 0);
573 	    WRITE_STORE_MASKED(bits << cskew, rmask, 1);
574 	    if (--h == 0)
575 		break;
576 	    END_Y_LOOP(source_raster, dest_raster);
577 	}
578 #undef CINVERT
579 #define CINVERT(bits) ((bits) ^ invert)
580 #else /* byte swapping */
581 	for (;;) {
582 	    WRITE_STORE_MASKED(CFETCH_RIGHT(bptr, skew, cskew), mask, 0);
583 	    WRITE_STORE_MASKED(CFETCH_LEFT(bptr, cskew, skew), rmask, 1);
584 	    if (--h == 0)
585 		break;
586 	    END_Y_LOOP(source_raster, dest_raster);
587 	}
588 #endif
589     } else {			/* More than one source chunk and more than one */
590 	/* destination chunk are involved. */
591 	uint rmask;
592 	int words = (wleft & ~chunk_bit_mask) >> 3;
593 	uint sskip = source_raster - words;
594 	uint dskip = dest_raster - words;
595 	register uint bits;
596 
597 #define NEXT_X_CHUNK()\
598   bptr += chunk_bytes; dbptr += chunk_bytes
599 
600 	set_mono_right_mask(rmask, wleft & chunk_bit_mask);
601 	if (skew == 0) {	/* optimize the aligned case */
602 	    if (dbit == 0)
603 		mask = 0;
604 	    else
605 		set_mono_left_mask(mask, dbit);
606 	    for (;;) {
607 		int count = wleft;
608 
609 		/* Do first partial chunk. */
610 		if (mask)
611 		    WRITE_STORE_MASKED(CFETCH_ALIGNED(bptr), mask, 0);
612 		else
613 		    WRITE_STORE(CFETCH_ALIGNED(bptr));
614 		/* Do full chunks. */
615 		while ((count -= chunk_bits) >= 0) {
616 		    NEXT_X_CHUNK();
617 		    WRITE_STORE(CFETCH_ALIGNED(bptr));
618 		}
619 		/* Do last chunk */
620 		if (count > -chunk_bits) {
621 		    WRITE_STORE_MASKED(CFETCH_ALIGNED(bptr + chunk_bytes), rmask, 1);
622 		}
623 		if (--h == 0)
624 		    break;
625 		END_Y_LOOP(sskip, dskip);
626 	    }
627 	} else {		/* not aligned */
628 	    bool case_right =
629 	    (skew >= 0 ? true :
630 	     ((bptr += chunk_bytes), false));
631 	    int cskew = -skew & chunk_bit_mask;
632 
633 	    skew &= chunk_bit_mask;
634 	    set_mono_left_mask(mask, dbit);
635 	    for (;;) {
636 		int count = wleft;
637 
638 		if (case_right)
639 		    bits = CFETCH_RIGHT(bptr, skew, cskew);
640 		else
641 		    bits = CFETCH2(bptr - chunk_bytes, cskew, skew);
642 		WRITE_STORE_MASKED(bits, mask, 0);
643 		/* Do full chunks. */
644 		while (count >= chunk_bits) {
645 		    bits = CFETCH2(bptr, cskew, skew);
646 		    NEXT_X_CHUNK();
647 		    WRITE_STORE(bits);
648 		    count -= chunk_bits;
649 		}
650 		/* Do last chunk */
651 		if (count > 0) {
652 		    bits = CFETCH_LEFT(bptr, cskew, skew);
653 		    if (count > skew)
654 			bits += CFETCH_RIGHT(bptr + chunk_bytes, skew, cskew);
655 		    WRITE_STORE_MASKED(bits, rmask, 1);
656 		}
657 		if (--h == 0)
658 		    break;
659 		END_Y_LOOP(sskip, dskip);
660 	    }
661 	}
662     }
663 #undef END_Y_LOOP
664 #undef NEXT_X_CHUNK
665 #undef optr
666     if ((rw -= w) > 0) {
667 	x += w;
668 	w = tiles->size.x;
669 	bptr = source_data;
670 	skew = dbit = x & chunk_align_bit_mask;
671 	goto outer;
672     }
673     return 0;
674 #endif /* !USE_COPY_ROP */
675 }
676 
677 /* ================ "Word"-oriented device ================ */
678 
679 /* Note that on a big-endian machine, this is the same as the */
680 /* standard byte-oriented-device. */
681 
682 #if !arch_is_big_endian
683 
684 /* Procedures */
685 private dev_proc_copy_mono(mem1_word_copy_mono);
686 private dev_proc_fill_rectangle(mem1_word_fill_rectangle);
687 
688 #define mem1_word_strip_tile_rectangle gx_default_strip_tile_rectangle
689 
690 /* Here is the device descriptor. */
691 const gx_device_memory mem_mono_word_device =
692 mem_full_alpha_device("image1w", 0, 1, mem_open,
693 		      mem_mono_map_rgb_color, mem_mono_map_color_rgb,
694        mem1_word_copy_mono, gx_default_copy_color, mem1_word_fill_rectangle,
695 		      gx_default_map_cmyk_color, gx_no_copy_alpha,
696 		      mem1_word_strip_tile_rectangle, gx_no_strip_copy_rop,
697 		      mem_word_get_bits_rectangle);
698 
699 /* Fill a rectangle with a color. */
700 private int
mem1_word_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)701 mem1_word_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
702 			 gx_color_index color)
703 {
704     gx_device_memory * const mdev = (gx_device_memory *)dev;
705     byte *base;
706     uint raster;
707 
708     fit_fill(dev, x, y, w, h);
709     base = scan_line_base(mdev, y);
710     raster = mdev->raster;
711     mem_swap_byte_rect(base, raster, x, w, h, true);
712     bits_fill_rectangle(base, x, raster, -(int)(mono_fill_chunk) color, w, h);
713     mem_swap_byte_rect(base, raster, x, w, h, true);
714     return 0;
715 }
716 
717 /* Copy a bitmap. */
718 private int
mem1_word_copy_mono(gx_device * dev,const byte * source_data,int source_x,int source_raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color0,gx_color_index color1)719 mem1_word_copy_mono(gx_device * dev,
720  const byte * source_data, int source_x, int source_raster, gx_bitmap_id id,
721    int x, int y, int w, int h, gx_color_index color0, gx_color_index color1)
722 {
723     gx_device_memory * const mdev = (gx_device_memory *)dev;
724     byte *row;
725     uint raster;
726     bool store;
727 
728     fit_copy(dev, source_data, source_x, source_raster, id, x, y, w, h);
729     row = scan_line_base(mdev, y);
730     raster = mdev->raster;
731     store = (color0 != gx_no_color_index && color1 != gx_no_color_index);
732     mem_swap_byte_rect(row, raster, x, w, h, store);
733     mem_mono_copy_mono(dev, source_data, source_x, source_raster, id,
734 		       x, y, w, h, color0, color1);
735     mem_swap_byte_rect(row, raster, x, w, h, false);
736     return 0;
737 }
738 
739 #endif /* !arch_is_big_endian */
740