xref: /plan9/sys/src/cmd/gs/src/gdevpcfb.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989-2005 artofcode LLC.  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: gdevpcfb.c,v 1.7 2005/08/09 20:23:07 giles Exp $ */
18 /* IBM PC frame buffer (EGA/VGA) drivers */
19 #include "memory_.h"
20 #include "gconfigv.h"		/* for USE_ASM */
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsparam.h"
24 #include "gxdevice.h"
25 #include "gdevpccm.h"
26 #include "gdevpcfb.h"
27 
28 /* We may compile this in a non-segmented environment.... */
29 #ifndef _ss
30 #define _ss
31 #endif
32 
33 /* Macro for casting gx_device argument */
34 #define fb_dev ((gx_device_ega *)dev)
35 
36 /* Procedure record */
37 private dev_proc_map_rgb_color(ega0_map_rgb_color);
38 private dev_proc_map_rgb_color(ega1_map_rgb_color);
39 #define ega2_map_rgb_color pc_4bit_map_rgb_color
40 private dev_proc_map_color_rgb(ega01_map_color_rgb);
41 #define ega2_map_color_rgb pc_4bit_map_color_rgb
42 #if ega_bits_of_color == 0
43 #   define ega_map_rgb_color ega0_map_rgb_color
44 #   define ega_map_color_rgb ega01_map_color_rgb
45 #else
46 # if ega_bits_of_color == 1
47 #   define ega_map_rgb_color ega1_map_rgb_color
48 #   define ega_map_color_rgb ega01_map_color_rgb
49 # else
50 #   define ega_map_rgb_color ega2_map_rgb_color
51 #   define ega_map_color_rgb ega2_map_color_rgb
52 # endif
53 #endif
54 #define ega_std_procs(get_params, put_params)\
55 	ega_open,\
56 	NULL,			/* get_initial_matrix */\
57 	NULL,			/* sync_output */\
58 	NULL,			/* output_page */\
59 	ega_close,\
60 	ega_map_rgb_color,\
61 	ega_map_color_rgb,\
62 	ega_fill_rectangle,\
63 	ega_tile_rectangle,\
64 	ega_copy_mono,\
65 	ega_copy_color,\
66 	NULL,			/* draw_line */\
67 	ega_get_bits,\
68 	get_params,\
69 	put_params,\
70 	NULL,			/* map_cmyk_color */\
71 	NULL,			/* get_xfont_procs */\
72 	NULL,			/* get_xfont_device */\
73 	NULL,			/* map_rgb_alpha_color */\
74 	gx_page_device_get_page_device
75 
76 private const gx_device_procs ega_procs =
77 {
78     ega_std_procs(NULL, NULL)
79 };
80 
81 private dev_proc_get_params(svga16_get_params);
82 private dev_proc_put_params(svga16_put_params);
83 private const gx_device_procs svga16_procs =
84 {
85     ega_std_procs(svga16_get_params, svga16_put_params)
86 };
87 
88 /* All the known instances */
89 		/* EGA */
90 gx_device_ega far_data gs_ega_device =
91 ega_device("ega", ega_procs, 80, 350, 48.0 / 35.0, 0x10);
92 
93 		/* VGA */
94 gx_device_ega far_data gs_vga_device =
95 ega_device("vga", ega_procs, 80, 480, 1.0, 0x12);
96 
97 		/* Generic SuperVGA, 800x600, 16-color mode */
98 gx_device_ega far_data gs_svga16_device =
99 ega_device("svga16", svga16_procs, 100, 600, 1.0, 0x29 /*Tseng */ );
100 
101 /* Save the BIOS state */
102 private pcfb_bios_state pcfb_save_state =
103 {-1};
104 
105 /* Initialize the EGA for graphics mode */
106 int
ega_open(gx_device * dev)107 ega_open(gx_device * dev)
108 {				/* Adjust the device resolution. */
109     /* This is a hack, pending refactoring of the put_params machinery. */
110     switch (fb_dev->video_mode) {
111 	case 0x10:		/* EGA */
112 	    gx_device_adjust_resolution(dev, 640, 350, 1);
113 	    break;
114 	case 0x12:		/* VGA */
115 	    gx_device_adjust_resolution(dev, 640, 480, 1);
116 	    break;
117 	default:		/* 800x600 SuperVGA */
118 	    gx_device_adjust_resolution(dev, 800, 600, 1);
119 	    break;
120     }
121     if (pcfb_save_state.display_mode < 0)
122 	pcfb_get_state(&pcfb_save_state);
123     /* Do implementation-specific initialization */
124     pcfb_set_signals(dev);
125     pcfb_set_mode(fb_dev->video_mode);
126     set_s_map(-1);		/* enable all maps */
127     return 0;
128 }
129 
130 /* Reinitialize the EGA for text mode */
131 int
ega_close(gx_device * dev)132 ega_close(gx_device * dev)
133 {
134     if (pcfb_save_state.display_mode >= 0)
135 	pcfb_set_state(&pcfb_save_state);
136     return 0;
137 }
138 
139 /* Get/put the display mode parameter. */
140 private int
svga16_get_params(gx_device * dev,gs_param_list * plist)141 svga16_get_params(gx_device * dev, gs_param_list * plist)
142 {
143     int code = gx_default_get_params(dev, plist);
144 
145     if (code < 0)
146 	return code;
147     return param_write_int(plist, "DisplayMode", &fb_dev->video_mode);
148 }
149 private int
svga16_put_params(gx_device * dev,gs_param_list * plist)150 svga16_put_params(gx_device * dev, gs_param_list * plist)
151 {
152     int ecode = 0;
153     int code;
154     int imode = fb_dev->video_mode;
155     const char *param_name;
156 
157     switch (code = param_read_int(plist, (param_name = "DisplayMode"), &imode)) {
158 	default:
159 	    ecode = code;
160 	    param_signal_error(plist, param_name, ecode);
161 	case 0:
162 	case 1:
163 	    break;
164     }
165 
166     if (ecode < 0)
167 	return ecode;
168     code = gx_default_put_params(dev, plist);
169     if (code < 0)
170 	return code;
171 
172     if (imode != fb_dev->video_mode) {
173 	if (dev->is_open)
174 	    gs_closedevice(dev);
175 	fb_dev->video_mode = imode;
176     }
177     return 0;
178 }
179 
180 /* Map a r-g-b color to an EGA color code. */
181 private gx_color_index
ega0_map_rgb_color(gx_device * dev,const gx_color_value cv[])182 ega0_map_rgb_color(gx_device * dev, const gx_color_value cv[])
183 {
184     return pc_4bit_map_rgb_color(dev, cv);
185 }
186 private gx_color_index
ega1_map_rgb_color(gx_device * dev,const gx_color_value cv[])187 ega1_map_rgb_color(gx_device * dev, const gx_color_value cv[])
188 {
189     const gx_color_value cvtop = (1 << (gx_color_value_bits - 1));
190     gx_color_value cvt[3];
191     cvt[0] = cv[0] & cvtop;
192     cvt[1] = cv[1] & cvtop;
193     cvt[2] = cv[2] & cvtop;
194     return pc_4bit_map_rgb_color(dev, cvt);
195 }
196 
197 /* Map a color code to r-g-b. */
198 #define icolor (int)color
199 private int
ega01_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])200 ega01_map_color_rgb(gx_device * dev, gx_color_index color,
201 		    gx_color_value prgb[3])
202 {
203 #define one (gx_max_color_value / 2 + 1)
204     prgb[0] = (icolor & 4 ? one : 0);
205     prgb[1] = (icolor & 2 ? one : 0);
206     prgb[2] = (icolor & 1 ? one : 0);
207     return 0;
208 #undef one
209 }
210 #undef icolor
211 
212 /* ------ Internal routines ------ */
213 
214 /* Structure for operation parameters. */
215 /* Note that this structure is known to assembly code. */
216 /* Not all parameters are used for every operation. */
217 typedef struct rop_params_s {
218     fb_ptr dest;		/* pointer to frame buffer */
219     int draster;		/* raster of frame buffer */
220     const byte *src;		/* pointer to source data */
221     int sraster;		/* source raster */
222     int width;			/* width in bytes */
223     int height;			/* height in scan lines */
224     int shift;			/* amount to right shift source */
225     int invert;			/* 0 or -1 to invert source */
226     int data;			/* data for fill */
227 } rop_params;
228 typedef rop_params _ss *rop_ptr;
229 
230 /* Assembly language routines */
231 
232 #if USE_ASM
233 void memsetcol(rop_ptr);	/* dest, draster, height, data */
234 #else
235 #define memsetcol cmemsetcol
236 private void
cmemsetcol(rop_ptr rop)237 cmemsetcol(rop_ptr rop)
238 {
239     byte *addr = rop->dest;
240     int yc = rop->height;
241     byte data = rop->data;
242     int draster = rop->draster;
243 
244     while (yc--) {
245 	byte_discard(*addr);
246 	*addr = data;
247 	addr += draster;
248     }
249 }
250 #endif
251 
252 #if USE_ASM
253 void memsetrect(rop_ptr);	/* dest, draster, width, height, data */
254 #else
255 #define memsetrect cmemsetrect
256 private void
cmemsetrect(rop_ptr rop)257 cmemsetrect(rop_ptr rop)
258 {
259     int yc = rop->height;
260     int width = rop->width;
261 
262     if (yc <= 0 || width <= 0)
263 	return;
264     {
265 	byte *addr = rop->dest;
266 	byte data = rop->data;
267 
268 	if (width > 5) {	/* use memset */
269 	    int skip = rop->draster;
270 
271 	    do {
272 		memset(addr, data, width);
273 		addr += skip;
274 	    }
275 	    while (--yc);
276 	} else {		/* avoid the fixed overhead */
277 	    int skip = rop->draster - width;
278 
279 	    do {
280 		int cnt = width;
281 
282 		do {
283 		    *addr++ = data;
284 		} while (--cnt);
285 		addr += skip;
286 	    }
287 	    while (--yc);
288 	}
289     }
290 }
291 #endif
292 
293 #if USE_ASM
294 void memrwcol(rop_ptr);	/* dest, draster, src, sraster, height, shift, invert */
295 #  define memrwcol0(rop) memrwcol(rop)	/* same except shift = 0 */
296 #else
297 #  define memrwcol cmemrwcol
298 #  define memrwcol0 cmemrwcol0
299 private void
cmemrwcol(rop_ptr rop)300 cmemrwcol(rop_ptr rop)
301 {
302     byte *dp = rop->dest;
303     const byte *sp = rop->src;
304     int yc = rop->height;
305     int shift = rop->shift;
306     byte invert = rop->invert;
307     int sraster = rop->sraster, draster = rop->draster;
308 
309     while (yc--) {
310 	byte_discard(*dp);
311 	*dp = ((*sp >> shift) + (*sp << (8 - shift))) ^ invert;
312 	dp += draster, sp += sraster;
313     }
314 }
315 private void
cmemrwcol0(rop_ptr rop)316 cmemrwcol0(rop_ptr rop)
317 {
318     byte *dp = rop->dest;
319     const byte *sp = rop->src;
320     int yc = rop->height;
321     byte invert = rop->invert;
322     int sraster = rop->sraster, draster = rop->draster;
323 
324     if (yc > 0)
325 	do {
326 	    byte_discard(*dp);
327 	    *dp = *sp ^ invert;
328 	    dp += draster, sp += sraster;
329 	}
330 	while (--yc);
331 }
332 #endif
333 
334 #if USE_ASM
335 void memrwcol2(rop_ptr);	/* dest, draster, src, sraster, height, shift, invert */
336 #else
337 #define memrwcol2 cmemrwcol2
338 private void
cmemrwcol2(rop_ptr rop)339 cmemrwcol2(rop_ptr rop)
340 {
341     byte *dp = rop->dest;
342     const byte *sp = rop->src;
343     int yc = rop->height;
344     int shift = rop->shift;
345     byte invert = rop->invert;
346     int sraster = rop->sraster, draster = rop->draster;
347 
348     while (yc--) {
349 	byte_discard(*dp);
350 	*dp = ((sp[1] >> shift) + (*sp << (8 - shift))) ^ invert;
351 	dp += draster, sp += sraster;
352     }
353 }
354 #endif
355 
356 /* Forward definitions */
357 int ega_write_dot(gx_device *, int, int, gx_color_index);
358 private void fill_rectangle(rop_ptr, int, int, int);
359 private void fill_row_only(byte *, int, int, int);
360 
361 /* Clean up after writing */
362 #define dot_end()\
363   set_g_mask(0xff)		/* all bits on */
364 
365 /* Write a dot using the EGA color codes. */
366 /* This doesn't have to be efficient. */
367 int
ega_write_dot(gx_device * dev,int x,int y,gx_color_index color)368 ega_write_dot(gx_device * dev, int x, int y, gx_color_index color)
369 {
370     byte data[4];
371 
372     data[0] = (byte) color;
373     return ega_copy_color(dev, data, 1, 4, gx_no_bitmap_id, x, y, 1, 1);
374 }
375 
376 /* Macro for testing bit-inclusion */
377 #define bit_included_in(x,y) !((x)&~(y))
378 
379 /* Copy a monochrome bitmap.  The colors are given explicitly. */
380 /* Color = gx_no_color_index means transparent (no effect on the image). */
381 int
ega_copy_mono(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index izero,gx_color_index ione)382 ega_copy_mono(gx_device * dev,
383 	      const byte * base, int sourcex, int raster, gx_bitmap_id id,
384       int x, int y, int w, int h, gx_color_index izero, gx_color_index ione)
385 {
386     rop_params params;
387 
388 #define czero (int)izero
389 #define cone (int)ione
390     int dleft, count;
391     byte mask, rmask;
392     fb_ptr save_dest;
393     int other_color = -1;
394 
395     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
396     params.dest = mk_fb_ptr(x, y);
397     params.draster = fb_dev->raster;
398     params.src = base + (sourcex >> 3);
399     params.sraster = raster;
400     params.height = h;
401     params.shift = (x - sourcex) & 7;
402     /* Analyze the 16 possible cases: each of izero and ione may be */
403     /* 0, 0xf, transparent, or some other color. */
404     switch (czero) {
405 	case no_color:
406 	    switch (cone) {
407 		default:	/* (T, other) */
408 		    /* Must do 2 passes */
409 		    other_color = cone;
410 		    save_dest = params.dest;
411 		    /* falls through */
412 		case 0:	/* (T, 0) */
413 		    set_g_function(gf_AND);
414 		    params.invert = -1;
415 		    break;
416 		case 0xf:	/* (T, 0xf) */
417 		    set_g_function(gf_OR);
418 		    params.invert = 0;
419 		    break;
420 		case no_color:	/* (T, T) */
421 		    return 0;	/* nothing to do */
422 	    }
423 	    break;
424 	case 0:
425 	    params.invert = 0;
426 	    switch (cone) {
427 		default:	/* (0, other) */
428 		    set_g_const(0);
429 		    set_g_const_map(cone ^ 0xf);
430 		    /* falls through */
431 		case 0xf:	/* (0, 0xf) */
432 		    break;
433 		case no_color:	/* (0, T) */
434 		    set_g_function(gf_AND);
435 		    break;
436 	    }
437 	    break;
438 	case 0xf:
439 	    params.invert = -1;
440 	    switch (cone) {
441 		case 0:	/* (0xf, 0) */
442 		    break;
443 		default:	/* (0xf, other) */
444 		    set_g_const(0xf);
445 		    set_g_const_map(cone);
446 		    break;
447 		case no_color:	/* (0xf, T) */
448 		    set_g_function(gf_OR);
449 		    /* falls through */
450 	    }
451 	    break;
452 	default:
453 	    switch (cone) {
454 		default:	/* (other, not T) */
455 		    if (bit_included_in(czero, cone)) {
456 			set_g_const(czero);
457 			set_g_const_map(czero ^ cone ^ 0xf);
458 			params.invert = 0;
459 			break;
460 		    } else if (bit_included_in(cone, czero)) {
461 			set_g_const(cone);
462 			set_g_const_map(cone ^ czero ^ 0xf);
463 			params.invert = -1;
464 			break;
465 		    }
466 		    /* No way around it, fill with one color first. */
467 		    save_dest = params.dest;
468 		    fill_rectangle((rop_ptr) & params, x & 7, w, cone);
469 		    params.dest = save_dest;
470 		    set_g_function(gf_XOR);
471 		    set_s_map(czero ^ cone);
472 		    other_color = -2;	/* must reset s_map at end */
473 		    params.invert = -1;
474 		    break;
475 		case no_color:	/* (other, T) */
476 		    /* Must do 2 passes */
477 		    other_color = czero;
478 		    save_dest = params.dest;
479 		    set_g_function(gf_AND);
480 		    params.invert = 0;
481 		    break;
482 	    }
483 	    break;
484     }
485     /* Actually copy the bits. */
486     dleft = 8 - (x & 7);
487     mask = 0xff >> (8 - dleft);
488     count = w - dleft;
489     if (count < 0)
490 	mask -= mask >> w,
491 	    rmask = 0;
492     else
493 	rmask = 0xff00 >> (count & 7);
494     /* params: dest, src, sraster, height, shift, invert */
495     /* Smashes params.src, params.dest, count. */
496   copy:set_g_mask(mask);
497     if (params.shift == 0) {	/* optimize the aligned case *//* Do left column */
498 	memrwcol0((rop_ptr) & params);
499 	/* Do center */
500 	if ((count -= 8) >= 0) {
501 	    out_g_mask(0xff);
502 	    do {
503 		params.src++, params.dest++;
504 		memrwcol0((rop_ptr) & params);
505 	    }
506 	    while ((count -= 8) >= 0);
507 	}
508 	/* Do right column */
509 	if (rmask) {
510 	    params.src++, params.dest++;
511 	    out_g_mask(rmask);
512 	    memrwcol0((rop_ptr) & params);
513 	}
514     } else {			/* Do left column */
515 	int sleft = 8 - (sourcex & 7);
516 
517 	if (sleft >= dleft) {	/* Source fits in one byte */
518 	    memrwcol((rop_ptr) & params);
519 	} else if (w <= sleft) {	/* Source fits in one byte, thin case */
520 	    memrwcol((rop_ptr) & params);
521 	    goto fin;
522 	} else {
523 	    memrwcol2((rop_ptr) & params);
524 	    params.src++;
525 	}
526 	/* Do center */
527 	if ((count -= 8) >= 0) {
528 	    out_g_mask(0xff);
529 	    do {
530 		params.dest++;
531 		memrwcol2((rop_ptr) & params);
532 		params.src++;
533 	    }
534 	    while ((count -= 8) >= 0);
535 	}
536 	/* Do right column */
537 	if (rmask) {
538 	    out_g_mask(rmask);
539 	    params.dest++;
540 	    if (count + 8 <= params.shift)
541 		memrwcol((rop_ptr) & params);
542 	    else
543 		memrwcol2((rop_ptr) & params);
544 	}
545     }
546   fin:if (other_color != -1) {
547 	if (other_color >= 0) {	/* Do the second pass on (T, other) or (other, T). */
548 	    count = w - dleft;
549 	    params.src = base + (sourcex >> 3);
550 	    params.dest = save_dest;
551 	    params.invert ^= -1;
552 	    set_s_map(other_color);
553 	    set_g_function(gf_OR);
554 	    other_color = -2;
555 	    goto copy;
556 	} else {		/* Finished second pass, restore s_map */
557 	    set_s_map(-1);
558 	}
559     }
560     set_g_function(gf_WRITE);
561     set_g_const_map(0);
562     dot_end();
563     return 0;
564 #undef czero
565 #undef cone
566 }
567 
568 /* Copy a color pixelmap.  This is just like a bitmap, */
569 /* except that each pixel takes 4 bits instead of 1. */
570 int
ega_copy_color(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)571 ega_copy_color(gx_device * dev,
572 	       const byte * base, int sourcex, int raster, gx_bitmap_id id,
573 	       int x, int y, int w, int h)
574 {
575     const byte *line = base + (sourcex >> 1);
576     unsigned mask = 0x80 >> (x & 7);
577     int px = sourcex & 1;
578     fb_ptr fb_line;
579     int fb_raster = fb_dev->raster;
580 
581     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
582     fb_line = mk_fb_ptr(x, y);
583     set_g_mode(gm_FILL);
584     select_g_mask();
585     for (;; px++) {
586 	const byte *bptr = line;
587 	fb_ptr fbptr = fb_line;
588 	int py = h;
589 
590 	out_g_mask(mask);
591 	if (px & 1) {
592 	    do {
593 		byte_discard(*fbptr);	/* latch frame buffer data */
594 		*fbptr = *bptr;
595 		bptr += raster;
596 		fbptr += fb_raster;
597 	    }
598 	    while (--py);
599 	    line++;
600 	} else {
601 	    do {
602 		byte_discard(*fbptr);	/* latch frame buffer data */
603 		*fbptr = *bptr >> 4;
604 		bptr += raster;
605 		fbptr += fb_raster;
606 	    }
607 	    while (--py);
608 	}
609 	if (!--w)
610 	    break;
611 	if ((mask >>= 1) == 0)
612 	    mask = 0x80, fb_line++;
613     }
614     set_g_mode(gm_DATA);
615     dot_end();
616     return 0;
617 }
618 
619 /* Fill a rectangle. */
620 int
ega_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)621 ega_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
622 		   gx_color_index color)
623 {
624     rop_params params;
625 
626     fit_fill(dev, x, y, w, h);
627     params.dest = mk_fb_ptr(x, y);
628     if (h == 1)
629 	fill_row_only(params.dest, x & 7, w, (int)color);
630     else {
631 	params.draster = fb_dev->raster;
632 	params.height = h;
633 	fill_rectangle((rop_ptr) & params, x & 7, w, (int)color);
634 	dot_end();
635     }
636     return 0;
637 }
638 
639 /* Tile a rectangle.  Note that the two colors must both be supplied, */
640 /* i.e. neither one can be gx_no_color_index (transparent): */
641 /* a transparent color means that the tile is colored, not a mask. */
642 int
ega_tile_rectangle(gx_device * dev,const gx_tile_bitmap * tile,int x,int y,int w,int h,gx_color_index czero,gx_color_index cone,int px,int py)643 ega_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
644       int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
645 		   int px, int py)
646 #define zero (int)czero
647 #define one (int)cone
648 {
649     rop_params params;
650     int xmod, width_bytes;
651     int tile_height = tile->size.y;
652     int xbit;
653     int lcount;
654     int mask, rmask;
655     byte narrow;
656     byte again;
657     int const_bits, maps;
658     int ymod, yleft;
659 
660     fit_fill(dev, x, y, w, h);
661     /* We only handle the easiest cases directly. */
662     if ((tile->size.x & 7) || one == -1 || zero == -1 || px || py)
663 	return gx_default_tile_rectangle(dev, tile, x, y, w, h,
664 					 czero, cone, px, py);
665     /* Following is similar to aligned case of copy_mono */
666     params.dest = mk_fb_ptr(x, y);
667     params.draster = fb_dev->raster;
668     params.sraster = tile->raster;
669     params.shift = 0;
670     xbit = x & 7;
671     /* Set up the graphics registers */
672     const_bits = (zero ^ one) ^ 0xf;
673     if (const_bits) {
674 	set_g_const(zero);	/* either color will do */
675 	set_g_const_map(const_bits);
676     }
677     if ((maps = zero & ~one) != 0) {
678 	set_s_map(maps += const_bits);
679 	params.invert = -1;
680 	again = one & ~zero;
681     } else {
682 	maps = one & ~zero;
683 	set_s_map(maps += const_bits);
684 	params.invert = 0;
685 	again = 0;
686     }
687     xmod = (x % tile->size.x) >> 3;
688     width_bytes = tile->size.x >> 3;
689     mask = 0xff >> xbit;
690     if (w + xbit <= 8)
691 	mask -= mask >> w,
692 	    rmask = 0,
693 	    narrow = 1;
694     else {
695 	rmask = (0xff00 >> ((w + x) & 7)) & 0xff;
696 	if (xbit)
697 	    w += xbit - 8;
698 	else
699 	    mask = 0, --xmod, --params.dest;
700 	narrow = 0;
701     }
702     ymod = y % tile_height;
703   tile:yleft = tile_height - ymod;
704     params.src = tile->data + ymod * params.sraster + xmod;
705     lcount = h;
706     if (narrow) {		/* Optimize narrow case */
707 	set_g_mask(mask);
708 	if (lcount > yleft) {
709 	    params.height = yleft;
710 	    memrwcol0((rop_ptr) & params);
711 	    params.dest += yleft * params.draster;
712 	    params.src = tile->data + xmod;
713 	    params.height = tile_height;
714 	    lcount -= yleft;
715 	    while (lcount >= tile_height) {
716 		memrwcol0((rop_ptr) & params);
717 		params.dest += tile_height * params.draster;
718 		lcount -= tile_height;
719 	    }
720 	}
721 	if (lcount) {
722 	    params.height = lcount;
723 	    memrwcol0((rop_ptr) & params);
724 	}
725     } else {
726 	fb_ptr line = params.dest;
727 	int xpos = width_bytes - xmod;
728 
729 	while (1) {
730 	    int xleft = xpos;
731 	    int count = w;
732 
733 	    params.height = (lcount > yleft ? yleft : lcount);
734 	    /* Do first byte, if not a full byte. */
735 	    if (mask) {
736 		set_g_mask(mask);
737 		memrwcol0((rop_ptr) & params);
738 	    }
739 	    /* Do full bytes */
740 	    if ((count -= 8) >= 0) {
741 		set_g_mask(0xff);
742 		do {
743 		    if (!--xleft)
744 			xleft = width_bytes,
745 			    params.src -= width_bytes;
746 		    ++params.src, ++params.dest;
747 		    memrwcol0((rop_ptr) & params);
748 		}
749 		while ((count -= 8) >= 0);
750 	    }
751 	    /* Do last byte */
752 	    if (rmask) {
753 		if (!--xleft)
754 		    xleft = width_bytes,
755 			params.src -= width_bytes;
756 		set_g_mask(rmask);
757 		++params.src, ++params.dest;
758 		memrwcol0((rop_ptr) & params);
759 	    }
760 	    if ((lcount -= params.height) == 0)
761 		break;
762 	    params.dest = line += params.height * params.draster;
763 	    params.src = tile->data + xmod;
764 	    yleft = tile_height;
765 	}
766     }
767     /* Now do the second color if needed */
768     if (again) {
769 	maps = again + const_bits;
770 	set_s_map(maps);
771 	again = 0;
772 	params.dest = mk_fb_ptr(x, y);
773 	if (mask == 0)
774 	    params.dest--;
775 	params.invert = 0;
776 	goto tile;
777     }
778     if (maps != 0xf)
779 	set_s_map(-1);
780     if (const_bits)
781 	set_g_const_map(0);
782     dot_end();
783     return 0;
784 }
785 
786 /* Read scan lines back from the frame buffer. */
787 int
ega_get_bits(gx_device * dev,int y,byte * data,byte ** actual_data)788 ega_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
789 {				/* The maximum width for an EGA/VGA device is 800 pixels.... */
790     int width_bytes = (dev->width + 7) >> 3;
791     int i;
792     bits32 *dest;
793     const byte *src;
794     const byte *end;
795     byte planes[100 * 4];
796 
797     /* Plane 0 is the least significant plane. */
798     /* We know we're on a little-endian machine.... */
799 #define spread4(v)\
800  v+0x00000000, v+0x08000000, v+0x80000000, v+0x88000000,\
801  v+0x00080000, v+0x08080000, v+0x80080000, v+0x88080000,\
802  v+0x00800000, v+0x08800000, v+0x80800000, v+0x88800000,\
803  v+0x00880000, v+0x08880000, v+0x80880000, v+0x88880000
804     static const bits32 spread8[256] =
805     {spread4(0x0000), spread4(0x0800),
806      spread4(0x8000), spread4(0x8800),
807      spread4(0x0008), spread4(0x0808),
808      spread4(0x8008), spread4(0x8808),
809      spread4(0x0080), spread4(0x0880),
810      spread4(0x8080), spread4(0x8880),
811      spread4(0x0088), spread4(0x0888),
812      spread4(0x8088), spread4(0x8888)
813     };
814 
815     if (y < 0 || y >= dev->height || dev->width > 800)
816 	return_error(gs_error_rangecheck);
817     /* Read 4 planes into the holding buffer. */
818     for (i = 0; i < 4; ++i) {
819 	set_g_read_plane(i);
820 	memcpy(planes + 100 * i, mk_fb_ptr(0, y), width_bytes);
821     }
822     /* Now assemble the final data from the planes. */
823     for (dest = (bits32 *) data, src = planes, end = src + width_bytes;
824 	 src < end; ++dest, ++src
825 	)
826 	*dest = (((((spread8[src[0]] >> 1) | spread8[src[100]]) >> 1) |
827 		  spread8[src[200]]) >> 1) | spread8[src[300]];
828     if (actual_data != 0)
829 	*actual_data = data;
830     return 0;
831 }
832 
833 /* ------ Internal routines ------ */
834 
835 /* Mask table for rectangle fill. */
836 static const byte rmask_tab[9] =
837 {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
838 };
839 
840 /* Fill a rectangle specified by pointer into frame buffer, */
841 /* starting bit within byte, width, and height. */
842 /* Smashes rop->dest. */
843 private void
fill_rectangle(register rop_ptr rop,int bit,int w,int color)844 fill_rectangle(register rop_ptr rop, int bit, int w, int color)
845   /* rop: dest, draster, height */
846 {
847     set_g_const(color);
848     set_g_const_map(0xf);
849     select_g_mask();
850     if (bit + w <= 8) {		/* Less than one byte */
851 	out_g_mask(rmask_tab[w] >> bit);
852 	memsetcol(rop);
853     } else {
854 	byte right_mask;
855 
856 	if (bit) {
857 	    out_g_mask(0xff >> bit);
858 	    memsetcol(rop);
859 	    rop->dest++;
860 	    w += bit - 8;
861 	}
862 	if (w >= 8) {
863 	    out_g_mask(0xff);	/* all bits */
864 	    rop->width = w >> 3;
865 	    memsetrect(rop);
866 	    rop->dest += rop->width;
867 	    w &= 7;
868 	}
869 	if ((right_mask = rmask_tab[w]) != 0) {
870 	    out_g_mask(right_mask);
871 	    memsetcol(rop);
872 	}
873     }
874     set_g_const_map(0);
875 }
876 
877 /* Fill a single row specified by pointer into frame buffer, */
878 /* starting bit within byte, and width; clean up afterwards. */
879 #define r_m_w(ptr) (*(ptr))++	/* read & write, data irrelevant */
880 private void
fill_row_only(byte * dest,int bit,int w,int color)881 fill_row_only(byte * dest, int bit, int w, int color)
882   /* rop: dest */
883 {
884     if (bit + w <= 8) {		/* Less than one byte. */
885 	/* Optimize filling with black or white. */
886 	switch (color) {
887 	    case 0:
888 		set_g_mask(rmask_tab[w] >> bit);
889 		*dest &= color;	/* read, then write 0s; */
890 		/* some compilers optimize &= 0 to a store. */
891 		out_g_mask(0xff);	/* dot_end */
892 		break;
893 	    case 0xf:
894 		set_g_mask(rmask_tab[w] >> bit);
895 		*dest |= 0xff;	/* read, then write 1s; */
896 		/* some compilers optimize &= 0 to a store. */
897 		out_g_mask(0xff);	/* dot_end */
898 		break;
899 	    default:
900 		set_g_const(color);
901 		set_g_const_map(0xf);
902 		set_g_mask(rmask_tab[w] >> bit);
903 		r_m_w(dest);
904 		out_g_mask(0xff);	/* dot_end */
905 		set_g_const_map(0);
906 	}
907     } else {
908 	byte right_mask;
909 	int byte_count;
910 
911 	set_g_const(color);
912 	set_g_const_map(0xf);
913 	select_g_mask();
914 	if (bit) {
915 	    out_g_mask(0xff >> bit);
916 	    r_m_w(dest);
917 	    dest++;
918 	    w += bit - 8;
919 	}
920 	byte_count = w >> 3;
921 	if ((right_mask = rmask_tab[w & 7]) != 0) {
922 	    out_g_mask(right_mask);
923 	    r_m_w(dest + byte_count);
924 	}
925 	out_g_mask(0xff);
926 	if (byte_count) {
927 	    memset(dest, 0, byte_count);	/* data irrelevant */
928 	}
929 	set_g_const_map(0);
930     }
931 }
932