xref: /plan9/sys/src/cmd/gs/src/gdevpm.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000 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: gdevpm.c,v 1.10 2004/09/20 22:14:59 dan Exp $ */
18 /*
19  * OS/2 Presentation manager driver
20  *
21  * By Russell Lang (based on gdevmswn.c and gdevwdib.c)
22  *
23  * If Ghostscript is a PM application, stdin/stdout are not
24  * provided and so no text window is available.
25  * If Ghostscript is a windowed text application, a message queue
26  * can't be created so a PM window for graphics can't be created.
27  * The solution used here is to have two programs - gsos2.exe is a
28  * text application and the outboard PM driver is gspmdrv.exe.
29  * Another solution may be to make Ghostscript a PM application
30  * and use VIO calls to provide a text window.
31  *
32  * If PM GSview starts Ghostscript, PM GSview displays the
33  * bitmap instead of the PM driver (gspmdrv.exe).
34  *
35  * Since Ghostscript is not a PM application, this driver creates a
36  * BMP bitmap in a named shared memory block and a second process
37  * gspmdrv.exe reads this memory block and provides the PM window.
38  * Communication to gspmdrv.exe is via the shared memory block
39  * and semaphores.
40  */
41 
42 #define INCL_DOS
43 #define INCL_DOSERRORS
44 #define INCL_WINWINDOWMGR
45 #define INCL_DEV
46 #define INCL_GPIBITMAPS
47 
48 #include <os2.h>
49 #include "string_.h"
50 #include <stdlib.h>
51 #include "gx.h"
52 #include "gserrors.h"
53 #include "gxdevice.h"
54 
55 #include "gp.h"
56 #include "gpcheck.h"
57 #include "gsparam.h"
58 #include "gdevpccm.h"
59 
60 #include "gxdevmem.h"
61 #include "gdevpm.h"
62 #ifdef __DLL__
63 #include "gsdll.h"
64 #include "gsdllos2.h"
65 #endif
66 
67 #define MIN_COMMIT 4096		/* memory is committed in these size chunks */
68 #define ID_NAME "GSPMDRV_%u_%u"
69 
70 /* Initial values for width and height */
71 #define INITIAL_RESOLUTION 96
72 #define INITIAL_WIDTH ((INITIAL_RESOLUTION * 85 + 5) / 10)
73 #define INITIAL_HEIGHT ((INITIAL_RESOLUTION * 110 + 5) / 10)
74 
75 /* A macro for casting the device argument */
76 #define pmdev ((gx_device_pm *)dev)
77 
78 
79 #define pm_gsview_sizeof 80
80 typedef struct gx_device_pm_s gx_device_pm;
81 
82 #define gx_device_pm_common\
83 	int BitsPerPixel;\
84 	int UpdateInterval;\
85 	char GSVIEW[pm_gsview_sizeof];\
86 	BOOL dll;\
87 	int nColors;\
88 	BOOL updating;\
89 	HTIMER update_timer;\
90 	HEV sync_event;\
91 	HEV next_event;\
92 	HMTX bmp_mutex;\
93 	HQUEUE drv_queue;\
94 	HQUEUE term_queue;\
95 	ULONG session_id;\
96 	PID process_id;\
97 	PID gspid;\
98 	unsigned char *bitmap;\
99 	ULONG committed;\
100 	PBITMAPINFO2 bmi
101 
102 /* The device descriptor */
103 struct gx_device_pm_s {
104     gx_device_common;
105     gx_device_pm_common;
106     gx_device_memory mdev;
107 };
108 
109 /* Device procedures */
110 
111 /* See gxdevice.h for the definitions of the procedures. */
112 private dev_proc_open_device(pm_open);
113 private dev_proc_get_initial_matrix(pm_get_initial_matrix);
114 private dev_proc_sync_output(pm_sync_output);
115 private dev_proc_output_page(pm_output_page);
116 private dev_proc_close_device(pm_close);
117 private dev_proc_map_rgb_color(pm_map_rgb_color);
118 private dev_proc_map_color_rgb(pm_map_color_rgb);
119 private dev_proc_fill_rectangle(pm_fill_rectangle);
120 private dev_proc_copy_mono(pm_copy_mono);
121 private dev_proc_copy_color(pm_copy_color);
122 private dev_proc_get_bits(pm_get_bits);
123 private dev_proc_get_params(pm_get_params);
124 private dev_proc_put_params(pm_put_params);
125 
126 private gx_device_procs pm_procs =
127 {
128     pm_open,
129     pm_get_initial_matrix,
130     pm_sync_output,
131     pm_output_page,
132     pm_close,
133     pm_map_rgb_color,
134     pm_map_color_rgb,
135     pm_fill_rectangle,
136     NULL,			/* tile rectangle */
137     pm_copy_mono,
138     pm_copy_color,
139     NULL,			/* draw line */
140     pm_get_bits,
141     pm_get_params,
142     pm_put_params,
143     NULL,			/* map_cmyk_color */
144     gx_default_get_xfont_procs,
145     NULL,			/* get_xfont_device */
146     NULL,			/* map_rgb_alpha_color */
147     gx_page_device_get_page_device
148 };
149 
150 #ifdef __DLL__
151 gx_device_pm far_data gs_os2dll_device =
152 {
153     std_device_std_body(gx_device_pm, &pm_procs, "os2dll",
154 			INITIAL_WIDTH, INITIAL_HEIGHT,
155 			INITIAL_RESOLUTION, INITIAL_RESOLUTION),
156     {0},			/* std_procs */
157     24,				/* BitsPerPixel */
158     5000,			/* UpdateInterval */
159     "\0",			/* GSVIEW */
160     1				/* is DLL device */
161 };
162 
163 #endif
164 gx_device_pm far_data gs_os2pm_device =
165 {
166     std_device_std_body(gx_device_pm, &pm_procs, "os2pm",
167 			INITIAL_WIDTH, INITIAL_HEIGHT,
168 			INITIAL_RESOLUTION, INITIAL_RESOLUTION),
169     {0},			/* std_procs */
170     24,				/* BitsPerPixel */
171     5000,			/* UpdateInterval */
172     "\0",			/* GSVIEW */
173     0				/* is not DLL device */
174 };
175 
176 /* Compress a gx_color_value into an 8-bit PM color value, */
177 /* using only the high order 5 bits. */
178 #define pm_color_value(z)\
179   ((((z) >> (gx_color_value_bits - 5)) << 3) +\
180    ((z) >> (gx_color_value_bits - 3)))
181 
182 /* prototypes for internal procedures */
183 private void pm_makepalette(gx_device_pm *);
184 private void pm_update(gx_device_pm *);
185 private uint pm_set_bits_per_pixel(gx_device_pm *, int);
186 private uint pm_palette_size(gx_device_pm *);
187 private int pm_alloc_bitmap(gx_device_pm *, gx_device *);
188 private int pm_run_gspmdrv(gx_device_pm *);
189 private void pm_write_bmp(gx_device_pm *);
190 
191 /* Open the pm driver */
192 int
pm_open(gx_device * dev)193 pm_open(gx_device * dev)
194 {
195     int ccode;
196     CHAR id[128];
197     CHAR name[128];
198     PTIB pptib;
199     PPIB pppib;
200 
201     if (!pmdev->dll && (_osmode == DOS_MODE)) {
202 	eprintf("os2pm driver can't be used under DOS\n");
203 	return gs_error_limitcheck;
204     }
205     if (DosGetInfoBlocks(&pptib, &pppib)) {
206 	eprintf("\npm_open: Couldn't get pid\n");
207 	return gs_error_limitcheck;
208     }
209 #ifdef __DLL__
210     if (pppib->pib_ultype == 3)	/* if caller is PM app */
211 	pmdev->gspid = pppib->pib_ulpid;	/* use caller pid */
212     else
213 #endif
214 	pmdev->gspid = pppib->pib_ulppid;	/* use parent (CMD.EXE) pid */
215     sprintf(id, ID_NAME, pmdev->gspid, (ULONG) dev);
216 
217     /* Allocate, but don't commit, enough memory for the largest */
218     /* possible bitmap (13Mbytes = A3 x 150dpi x 24bits) */
219 #ifdef __DLL__
220     if (pmdev->dll) {
221 	/* We don't need to use shared memory for the DLL */
222 	if (DosAllocMem((PPVOID) & pmdev->bitmap,
223 			13 * 1024 * 1024, PAG_READ | PAG_WRITE)) {
224 	    eprintf("pm_open: failed allocating BMP memory\n");
225 	    return gs_error_limitcheck;
226 	}
227     } else
228 #endif
229     {
230 	/* Shared memory is common to all processes so we don't want to allocate too much */
231 	sprintf(name, SHARED_NAME, *pmdev->GSVIEW ? pmdev->GSVIEW : id);
232 	if (DosAllocSharedMem((PPVOID) & pmdev->bitmap, name,
233 			      13 * 1024 * 1024, PAG_READ | PAG_WRITE)) {
234 	    eprintf1("pm_open: failed allocating shared BMP memory %s\n", name);
235 	    return gs_error_limitcheck;
236 	}
237     }
238 
239     /* commit one page so there is enough storage for a */
240     /* bitmap header and palette */
241     if (DosSetMem(pmdev->bitmap, MIN_COMMIT, PAG_COMMIT | PAG_DEFAULT)) {
242 	DosFreeMem(pmdev->bitmap);
243 	eprintf("pm_open: failed committing BMP memory\n");
244 	return gs_error_limitcheck;
245     }
246     pmdev->committed = MIN_COMMIT;
247 
248     if (pmdev->dll) {
249 	/* Create mutex - used for preventing another thread from accessing */
250 	/* bitmap while we are changing the bitmap size. Initially unowned. */
251 	sprintf(name, MUTEX_NAME, id);
252 	if (DosCreateMutexSem(name, &(pmdev->bmp_mutex), 0, FALSE)) {
253 	    DosFreeMem(pmdev->bitmap);
254 	    DosCloseEventSem(pmdev->sync_event);
255 	    DosCloseQueue(pmdev->drv_queue);
256 	    eprintf1("pm_open: failed to create mutex semaphore %s\n", name);
257 	    return gs_error_limitcheck;
258 	}
259     } else {
260 	if (*pmdev->GSVIEW) {
261 	    APIRET rc;
262 
263 	    /* GSview has already created the necessary objects */
264 	    /* so we use Open instead of Create */
265 	    rc = 0;
266 	    if (!rc) {
267 		sprintf(name, NEXT_NAME, pmdev->GSVIEW);
268 		rc = DosOpenEventSem(name, &pmdev->next_event);
269 	    }
270 	    if (!rc) {
271 		sprintf(name, MUTEX_NAME, pmdev->GSVIEW);
272 		rc = DosOpenMutexSem(name, &pmdev->bmp_mutex);
273 	    }
274 	    if (!rc) {
275 		PID owner_pid;
276 
277 		sprintf(name, QUEUE_NAME, pmdev->GSVIEW);
278 		rc = DosOpenQueue(&owner_pid, &pmdev->drv_queue, name);
279 	    }
280 	    if (rc) {
281 		DosFreeMem(pmdev->bitmap);
282 		DosCloseEventSem(pmdev->next_event);
283 		eprintf2("pm_open: failed to open %s, rc = %u\n", name, rc);
284 		return gs_error_limitcheck;
285 	    }
286 	} else {		/* not GSVIEW */
287 	    /* Create update event semaphore */
288 	    sprintf(name, SYNC_NAME, id);
289 	    if (DosCreateEventSem(name, &(pmdev->sync_event), 0, FALSE)) {
290 		DosFreeMem(pmdev->bitmap);
291 		eprintf1("pm_open: failed to create event semaphore %s\n", name);
292 		return gs_error_limitcheck;
293 	    }
294 	    /* Create mutex - used for preventing gspmdrv from accessing */
295 	    /* bitmap while we are changing the bitmap size. Initially unowned. */
296 	    sprintf(name, MUTEX_NAME, id);
297 	    if (DosCreateMutexSem(name, &(pmdev->bmp_mutex), 0, FALSE)) {
298 		DosFreeMem(pmdev->bitmap);
299 		DosCloseEventSem(pmdev->sync_event);
300 		DosCloseQueue(pmdev->drv_queue);
301 		eprintf1("pm_open: failed to create mutex semaphore %s\n", name);
302 		return gs_error_limitcheck;
303 	    }
304 	}
305     }
306 
307     if ((pm_set_bits_per_pixel(pmdev, pmdev->BitsPerPixel) < 0) ||
308 	(gdev_mem_device_for_bits(dev->color_info.depth) == 0)) {
309 	if (!pmdev->dll) {
310 	    if (*pmdev->GSVIEW) {
311 		DosCloseQueue(pmdev->drv_queue);
312 		DosCloseEventSem(pmdev->next_event);
313 	    } else
314 		DosCloseEventSem(pmdev->sync_event);
315 	}
316 	DosCloseMutexSem(pmdev->bmp_mutex);
317 	DosFreeMem(pmdev->bitmap);
318 	return gs_error_limitcheck;
319     }
320     /* initialise bitmap header */
321     pmdev->bmi = (PBITMAPINFO2) pmdev->bitmap;
322     pmdev->bmi->cbFix = 40;	/* OS/2 2.0 and Windows 3.0 compatible */
323     pmdev->bmi->cx = dev->width;
324     pmdev->bmi->cy = dev->height;
325     pmdev->bmi->cPlanes = 1;
326     pmdev->bmi->cBitCount = dev->color_info.depth;
327     pmdev->bmi->ulCompression = BCA_UNCOMP;
328     pmdev->bmi->cbImage = 0;
329     pmdev->bmi->cxResolution = (ULONG) (dev->x_pixels_per_inch / 25.4 * 1000);
330     pmdev->bmi->cyResolution = (ULONG) (dev->y_pixels_per_inch / 25.4 * 1000);
331     if (pmdev->BitsPerPixel <= 8) {
332 	pmdev->bmi->cclrUsed = 1 << (pmdev->BitsPerPixel);
333 	pmdev->bmi->cclrImportant = pmdev->nColors;
334     } else {
335 	pmdev->bmi->cclrUsed = 0;
336 	pmdev->bmi->cclrImportant = 0;
337     }
338 
339     pm_makepalette(pmdev);
340 
341     /* commit pages */
342     ccode = pm_alloc_bitmap((gx_device_pm *) dev, dev);
343     if (ccode < 0) {
344 	if (!pmdev->dll) {
345 	    if (*pmdev->GSVIEW) {
346 		DosCloseQueue(pmdev->drv_queue);
347 		DosCloseEventSem(pmdev->next_event);
348 	    } else
349 		DosCloseEventSem(pmdev->sync_event);
350 	}
351 	DosCloseMutexSem(pmdev->bmp_mutex);
352 	DosFreeMem(pmdev->bitmap);
353 	return ccode;
354     }
355     if (*pmdev->GSVIEW)
356 	return 0;		/* GSview will handle displaying */
357 
358 #ifdef __DLL__
359     if (pmdev->dll && pgsdll_callback) {
360 	/* notify caller about new device */
361 	(*pgsdll_callback) (GSDLL_DEVICE, (unsigned char *)pmdev, 1);
362 	return 0;		/* caller will handle displaying */
363     }
364 #endif
365 
366     ccode = pm_run_gspmdrv(pmdev);
367     if (ccode < 0) {
368 	DosFreeMem(pmdev->bitmap);
369 	DosCloseEventSem(pmdev->sync_event);
370 	DosCloseMutexSem(pmdev->bmp_mutex);
371     }
372     return ccode;
373 }
374 
375 /* Get the initial matrix.  BMPs, unlike most displays, */
376 /* put (0,0) in the lower left corner. */
377 private void
pm_get_initial_matrix(gx_device * dev,gs_matrix * pmat)378 pm_get_initial_matrix(gx_device * dev, gs_matrix * pmat)
379 {
380     pmat->xx = dev->x_pixels_per_inch / 72.0;
381     pmat->xy = 0;
382     pmat->yx = 0;
383     pmat->yy = dev->y_pixels_per_inch / 72.0;
384     pmat->tx = 0;
385     pmat->ty = 0;
386     if (*pmdev->GSVIEW)
387 	pm_update((gx_device_pm *) dev);	/* let GSVIEW know we are drawing */
388 }
389 
390 /* Make the output appear on the screen. */
391 int
pm_sync_output(gx_device * dev)392 pm_sync_output(gx_device * dev)
393 {
394 #ifdef __DLL__
395     if (pmdev->dll && pgsdll_callback) {
396 	(*pgsdll_callback) (GSDLL_SYNC, (unsigned char *)dev, 0);
397 	return 0;
398     }
399 #endif
400 
401     /* tell gspmdrv or GSview process to update display */
402     if (*pmdev->GSVIEW) {
403 	APIRET rc;
404 
405 	rc = DosWriteQueue(pmdev->drv_queue, GS_SYNC, 0, NULL, 0);
406 	if (rc)
407 	    eprintf1("pm_sync_output: DosWriteQueue error %d\n", rc);
408     } else {
409 	if (pmdev->updating)
410 	    DosStopTimer(pmdev->update_timer);
411 	DosPostEventSem(pmdev->sync_event);
412     }
413     pmdev->updating = FALSE;
414     return (0);
415 }
416 
417 /* Make the output appear on the screen  */
418 /* and bring image window to foreground. */
419 private int
pm_do_output_page(gx_device * dev,int copies,int flush)420 pm_do_output_page(gx_device * dev, int copies, int flush)
421 {
422     int code;
423     APIRET rc;
424 
425 #ifdef DEBUG
426     pm_write_bmp(pmdev);
427 #endif
428 #ifdef __DLL__
429     if (pmdev->dll && pgsdll_callback) {
430 	(*pgsdll_callback) (GSDLL_PAGE, (unsigned char *)dev, 0);
431 	return 0;
432     }
433 #endif
434 
435     if (*pmdev->GSVIEW) {
436 	if (copies == -2) {
437 	    rc = DosWriteQueue(pmdev->drv_queue, GS_END, 0, NULL, 0);
438 	    if (rc)
439 		eprintf1("pm_output_page: DosWriteQueue error %d\n", rc);
440 	} else if (copies == -1) {
441 	    rc = DosWriteQueue(pmdev->drv_queue, GS_BEGIN, 0, NULL, 0);
442 	    if (rc)
443 		eprintf1("pm_output_page: DosWriteQueue error %d\n", rc);
444 	} else {
445 	    ULONG count;
446 
447 	    pmdev->updating = FALSE;
448 	    /* signal GSview that another page is ready */
449 	    rc = DosWriteQueue(pmdev->drv_queue, GS_PAGE, 0, NULL, 0);
450 	    if (rc)
451 		eprintf1("pm_output_page: DosWriteQueue error %d\n", rc);
452 	    /* wait for GSview to signal we can move on to next page */
453 	    DosWaitEventSem(pmdev->next_event, SEM_INDEFINITE_WAIT);
454 	    DosResetEventSem(pmdev->next_event, &count);
455 	}
456 	code = 0;
457     } else {
458 	code = pm_sync_output(dev);
459 	rc = DosSelectSession(pmdev->session_id);
460 	if (rc) {
461 	    DosSleep(2000);	/* give gspmdrv.exe a chance to run */
462 	    rc = DosSelectSession(pmdev->session_id);
463 	    if (rc == ERROR_SMG_NO_TARGET_WINDOW) {
464 		DosSleep(5000);	/* give gspmdrv.exe a chance to run */
465 		rc = DosSelectSession(pmdev->session_id);	/* try yet again */
466 	    }
467 	    if ((rc == ERROR_SMG_SESSION_NOT_FOUND) ||
468 		(rc == ERROR_SMG_INVALID_SESSION_ID)) {
469 		/* someone has killed the session */
470 		REQUESTDATA Request;
471 		ULONG DataLength;
472 		PVOID DataAddress;
473 		PULONG QueueEntry;
474 		BYTE ElemPriority;
475 
476 		/* Close gspmdrv driver */
477 		DosStopSession(STOP_SESSION_SPECIFIED, pmdev->session_id);
478 		Request.pid = pmdev->gspid;
479 		Request.ulData = 0;
480 		/* wait for termination queue, queue is then closed by session manager */
481 		DosReadQueue(pmdev->term_queue, &Request, &DataLength,
482 		     &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL);
483 		DosCloseQueue(pmdev->term_queue);
484 		pmdev->term_queue = (HQUEUE) 0;
485 		/* restart it */
486 		pm_run_gspmdrv(pmdev);
487 		DosSleep(2000);	/* give gspmdrv.exe a chance to run */
488 		rc = DosSelectSession(pmdev->session_id);
489 	    }
490 	    if (rc == ERROR_SMG_SESSION_NOT_FOREGRND)
491 		DosBeep(400, 50);
492 	    else if (rc)
493 		eprintf1("pm_output_page: Select Session error code %u\n", rc);
494 	}
495     }
496     return code;
497 }
498 int
pm_output_page(gx_device * dev,int copies,int flush)499 pm_output_page(gx_device * dev, int copies, int flush)
500 {
501     int code = pm_do_output_page(dev, copies, flush);
502 
503     if (code >= 0)
504 	code = gx_finish_output_page(dev, copies, flush);
505     return code;
506 }
507 
508 /* Close the pm driver */
509 int
pm_close(gx_device * dev)510 pm_close(gx_device * dev)
511 {
512     APIRET rc;
513 
514 #ifdef __DLL__
515     if (pmdev->dll) {
516 	DosRequestMutexSem(pmdev->bmp_mutex, 60000);
517 	if (pgsdll_callback)
518 	    (*pgsdll_callback) (GSDLL_DEVICE, (unsigned char *)dev, 0);
519 	DosReleaseMutexSem(pmdev->bmp_mutex);
520     } else
521 #endif
522     {
523 	if (*pmdev->GSVIEW) {
524 	    rc = DosWriteQueue(pmdev->drv_queue, GS_CLOSE, 0, NULL, 0);
525 	    if (rc)
526 		eprintf1("pm_close: DosWriteQueue error %d\n", rc);
527 	} else {
528 	    REQUESTDATA Request;
529 	    ULONG DataLength;
530 	    PVOID DataAddress;
531 	    PULONG QueueEntry;
532 	    BYTE ElemPriority;
533 
534 	    /* Close gspmdrv driver */
535 	    DosStopSession(STOP_SESSION_SPECIFIED, pmdev->session_id);
536 	    Request.pid = pmdev->gspid;
537 	    Request.ulData = 0;
538 	    /* wait for termination queue, queue is then closed by session manager */
539 	    DosReadQueue(pmdev->term_queue, &Request, &DataLength,
540 		     &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL);
541 	    /* queue needs to be closed by us */
542 	    DosCloseQueue(pmdev->term_queue);
543 	}
544     }
545     /* release memory */
546     DosFreeMem(pmdev->bitmap);
547     pmdev->bitmap = (unsigned char *)NULL;
548     pmdev->committed = 0;
549 
550     if (!pmdev->dll) {
551 	/* close objects */
552 	if (*pmdev->GSVIEW) {
553 	    DosCloseQueue(pmdev->drv_queue);
554 	    DosCloseEventSem(pmdev->next_event);
555 	} else {
556 	    DosCloseEventSem(pmdev->sync_event);
557 	    /* stop update timer */
558 	    if (pmdev->updating)
559 		DosStopTimer(pmdev->update_timer);
560 	    pmdev->updating = FALSE;
561 	}
562     }
563     DosCloseMutexSem(pmdev->bmp_mutex);
564     return (0);
565 }
566 
567 
568 /* Map a r-g-b color to the colors available under PM */
569 gx_color_index
pm_map_rgb_color(gx_device * dev,const gx_color_value cv[])570 pm_map_rgb_color(gx_device * dev, const gx_color_value cv[])
571 {
572     gx_color_value r = cv[0];
573     gx_color_value g = cv[1];
574     gx_color_value b = cv[2];
575     switch (dev->color_info.depth) {
576 	case 24:
577 	    return ((b >> (gx_color_value_bits - 8)) << 16) +
578 		((g >> (gx_color_value_bits - 8)) << 8) +
579 		((r >> (gx_color_value_bits - 8)));
580 	case 8:{
581 		int i;
582 		RGB2 *prgb;
583 		byte cr, cg, cb;
584 
585 		/* map colors to 0->255 in 32 steps */
586 		cr = pm_color_value(r);
587 		cg = pm_color_value(g);
588 		cb = pm_color_value(b);
589 
590 		prgb = (RGB2 *) ((PBYTE) pmdev->bmi + pmdev->bmi->cbFix);
591 		/* search in palette */
592 		for (i = 0; i < pmdev->nColors; i++, prgb++) {
593 		    if (!((cr ^ prgb->bRed) & 0xf8) &&
594 			!((cg ^ prgb->bGreen) & 0xf8) &&
595 			!((cb ^ prgb->bBlue) & 0xf8)
596 			)
597 			return ((gx_color_index) i);	/* found it */
598 		}
599 
600 		/* next try adding it to palette */
601 		if (i < 230) {	/* allow 26 for PM and other apps */
602 		    prgb->bRed = cr;
603 		    prgb->bGreen = cg;
604 		    prgb->bBlue = cb;
605 		    prgb->fcOptions = 0;
606 		    pmdev->nColors = i + 1;
607 		    pmdev->bmi->cclrImportant = pmdev->nColors;
608 		    if (*pmdev->GSVIEW) {
609 			APIRET rc;
610 
611 			rc = DosWriteQueue(pmdev->drv_queue, GS_PALCHANGE, 0, NULL, 0);
612 			if (rc)
613 			    eprintf1("pm_sync_output: DosWriteQueue error %d\n", rc);
614 		    }
615 		    return ((gx_color_index) i);	/* return new palette index */
616 		}
617 		return (gx_no_color_index);	/* not found - dither instead */
618 	    }
619 	case 4:
620 	    return pc_4bit_map_rgb_color(dev, cv);
621     }
622     return (gx_default_map_rgb_color(dev, cv));
623 }
624 
625 /* Map a color code to r-g-b. */
626 int
pm_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])627 pm_map_color_rgb(gx_device * dev, gx_color_index color,
628 		 gx_color_value prgb[3])
629 {
630     gx_color_value one;
631 
632     switch (dev->color_info.depth) {
633 	case 24:
634 	    one = (gx_color_value) (gx_max_color_value / 255);
635 	    prgb[0] = ((color) & 255) * one;
636 	    prgb[1] = ((color >> 8) & 255) * one;
637 	    prgb[2] = ((color >> 16) & 255) * one;
638 	    break;
639 	case 8:
640 	    if (!dev->is_open)
641 		return -1;
642 	    {
643 		RGB2 *argb = (RGB2 *) ((PBYTE) pmdev->bmi + pmdev->bmi->cbFix);
644 
645 		one = (gx_color_value) (gx_max_color_value / 255);
646 		prgb[0] = argb[(int)color].bRed * one;
647 		prgb[1] = argb[(int)color].bGreen * one;
648 		prgb[2] = argb[(int)color].bBlue * one;
649 	    }
650 	    break;
651 	case 4:
652 	    pc_4bit_map_color_rgb(dev, color, prgb);
653 	    break;
654 	default:
655 	    prgb[0] = prgb[1] = prgb[2] =
656 		(int)color ? gx_max_color_value : 0;
657     }
658     return 0;
659 }
660 
661 #define pmmdev ((gx_device *)&pmdev->mdev)
662 #define pmmproc(proc) (*dev_proc(&pmdev->mdev, proc))
663 
664 /* Fill a rectangle. */
665 private int
pm_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)666 pm_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
667 		  gx_color_index color)
668 {
669     pmmproc(fill_rectangle) (pmmdev, x, y, w, h, color);
670     pm_update((gx_device_pm *) dev);
671     return 0;
672 }
673 
674 /* Copy a monochrome bitmap.  The colors are given explicitly. */
675 /* Color = gx_no_color_index means transparent (no effect on the image). */
676 private int
pm_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 zero,gx_color_index one)677 pm_copy_mono(gx_device * dev,
678 	     const byte * base, int sourcex, int raster, gx_bitmap_id id,
679 	     int x, int y, int w, int h,
680 	     gx_color_index zero, gx_color_index one)
681 {
682     pmmproc(copy_mono) (pmmdev, base, sourcex, raster, id,
683 			x, y, w, h, zero, one);
684     pm_update((gx_device_pm *) dev);
685     return 0;
686 }
687 
688 /* Copy a color pixel map.  This is just like a bitmap, except that */
689 /* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */
690 private int
pm_copy_color(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)691 pm_copy_color(gx_device * dev,
692 	      const byte * base, int sourcex, int raster, gx_bitmap_id id,
693 	      int x, int y, int w, int h)
694 {
695     pmmproc(copy_color) (pmmdev, base, sourcex, raster, id,
696 			 x, y, w, h);
697     pm_update((gx_device_pm *) dev);
698     return 0;
699 }
700 
701 int
pm_get_bits(gx_device * dev,int y,byte * str,byte ** actual_data)702 pm_get_bits(gx_device * dev, int y, byte * str, byte ** actual_data)
703 {
704     return pmmproc(get_bits) (pmmdev, y, str, actual_data);
705 }
706 /* Get PM parameters */
707 int
pm_get_params(gx_device * dev,gs_param_list * plist)708 pm_get_params(gx_device * dev, gs_param_list * plist)
709 {
710     int code = gx_default_get_params(dev, plist);
711     gs_param_string gvs;
712 
713     gvs.data = pmdev->GSVIEW, gvs.size = strlen(gvs.data),
714 	gvs.persistent = false;
715     code < 0 ||
716 	(code = param_write_int(plist, "UpdateInterval", &pmdev->UpdateInterval)) < 0 ||
717 	(code = param_write_string(plist, "GSVIEW", &gvs)) < 0;
718     return code;
719 }
720 
721 /* Put parameters. */
722 
723 /* Set PM parameters -- size and resolution. */
724 /* We implement this ourselves so that we can do it without */
725 /* closing and opening the device. */
726 /* Also set BitsPerPixel and GSVIEW if device not open */
727 int
pm_put_params(gx_device * dev,gs_param_list * plist)728 pm_put_params(gx_device * dev, gs_param_list * plist)
729 {
730     int ecode = 0, code;
731     bool reopen = false;
732     bool is_open = dev->is_open;
733     int width = dev->width;
734     int height = dev->height;
735     int old_bpp = dev->color_info.depth;
736     int bpp = old_bpp;
737     int uii = pmdev->UpdateInterval;
738     gs_param_string gsvs;
739 
740     /* Handle extra parameters */
741     switch (code = param_read_string(plist, "GSVIEW", &gsvs)) {
742 	case 0:
743 	    if (gsvs.size == strlen(pmdev->GSVIEW) &&
744 		!memcmp(pmdev->GSVIEW, gsvs.data, gsvs.size)
745 		) {
746 		gsvs.data = 0;
747 		break;
748 	    }
749 	    if (dev->is_open)
750 		ecode = gs_error_rangecheck;
751 	    else if (gsvs.size >= pm_gsview_sizeof)
752 		ecode = gs_error_limitcheck;
753 	    else
754 		break;
755 	    goto gsve;
756 	default:
757 	    ecode = code;
758 	  gsve:param_signal_error(plist, "GSVIEW", ecode);
759 	case 1:
760 	    gsvs.data = 0;
761 	    break;
762     }
763 
764     switch (code = param_read_int(plist, "UpdateInterval", &uii)) {
765 	case 0:
766 	    if (uii < 0)
767 		ecode = gs_error_rangecheck;
768 	    else
769 		break;
770 	    goto uie;
771 	default:
772 	    ecode = code;
773 	  uie:param_signal_error(plist, "UpdateInterval", ecode);
774 	case 1:
775 	    break;
776     }
777 
778     switch (code = param_read_int(plist, "BitsPerPixel", &bpp)) {
779 	case 0:
780 	    if (dev->is_open && bpp != old_bpp)
781 		ecode = gs_error_rangecheck;
782 	    else {
783 		code = pm_set_bits_per_pixel(pmdev, bpp);
784 		if (code < 0)
785 		    ecode = code;
786 		else
787 		    break;
788 	    }
789 	    goto bppe;
790 	default:
791 	    ecode = code;
792 	  bppe:param_signal_error(plist, "BitsPerPixel", ecode);
793 	case 1:
794 	    break;
795     }
796 
797     if (ecode >= 0) {		/* Prevent gx_default_put_params from closing the device. */
798 	dev->is_open = false;
799 	ecode = gx_default_put_params(dev, plist);
800 	dev->is_open = is_open;
801     }
802     if (ecode < 0) {
803 	if (bpp != old_bpp)
804 	    pm_set_bits_per_pixel(pmdev, old_bpp);
805 	return ecode;
806     }
807     /* Hand off the change to the implementation. */
808     /* obtain mutex - to prevent gspmdrv from using bitmap */
809     /* while we change its size */
810     if (DosRequestMutexSem(pmdev->bmp_mutex, 20000) == ERROR_TIMEOUT)
811 	eprintf("pm_put_params: mutex timeout\n");
812     if (is_open && (old_bpp != bpp ||
813 		    dev->width != width || dev->height != height)
814 	) {
815 	int ccode;
816 
817 	ccode = pm_alloc_bitmap(pmdev, dev);
818 	if (ccode < 0) {	/* Bad news!  Some of the other device parameters */
819 	    /* may have changed.  We don't handle this. */
820 	    /* This is ****** WRONG ******. */
821 	    dev->width = width;
822 	    dev->height = height;
823 	    pm_set_bits_per_pixel(pmdev, old_bpp);
824 	    pm_alloc_bitmap(pmdev, dev);
825 	    DosReleaseMutexSem(pmdev->bmp_mutex);
826 	    return ccode;
827 	}
828 	reopen = true;
829     }
830     pmdev->UpdateInterval = uii;
831     if (gsvs.data != 0) {
832 	memcpy(pmdev->GSVIEW, gsvs.data, gsvs.size);
833 	pmdev->GSVIEW[gsvs.size] = 0;
834     }
835     if (dev->is_open && reopen) {
836 	/* need to update bitmap info header also */
837 	pmdev->bmi->cx = dev->width;
838 	pmdev->bmi->cy = dev->height;
839 	/* update bit count and palette */
840 	pmdev->bmi->cBitCount = dev->color_info.depth;
841 	pmdev->bmi->cclrUsed = 1 << (pmdev->BitsPerPixel);
842 	pmdev->bmi->cclrImportant = pmdev->nColors;
843 	pm_makepalette(pmdev);
844 	/* erase bitmap - before window gets redrawn */
845 	{
846 	    int i;
847 	    gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
848 	    for (i=0; i<GX_DEVICE_COLOR_MAX_COMPONENTS; i++)
849 		cv[i] = (pmdev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE)
850 		    ? gx_max_color_value : 0;
851 	    dev_proc(pmdev, fill_rectangle)((gx_device *)pmdev,
852 		     0, 0, pmdev->width, pmdev->height,
853 		     pmdev->procs.encode_color((gx_device *)pmdev, cv));
854 	}
855 
856 
857 	/* cause scroll bars to be redrawn */
858 	/* need to signal gspmdrv that bitmap size has changed */
859 	/* or perhaps gspmdrv can check if the bitmap size has */
860 	/* before each use */
861 
862 #ifdef __DLL__
863 	if (pmdev->dll && pgsdll_callback)
864 	    (*pgsdll_callback) (GSDLL_SIZE, (unsigned char *)dev,
865 		    (dev->width & 0xffff) + ((dev->height & 0xffff) << 16));
866 #endif
867     }
868     /* release bmp mutex */
869     DosReleaseMutexSem(pmdev->bmp_mutex);
870     return 0;
871 }
872 
873 #ifdef __DLL__
874 /* ------ DLL routines ------ */
875 /* store at pbitmap the address of the bitmap */
876 /* device is a pointer to Ghostscript device from GSDLL_DEVICE message */
877 unsigned long GSDLLAPI
gsdll_get_bitmap(unsigned char * device,unsigned char ** pbitmap)878 gsdll_get_bitmap(unsigned char *device, unsigned char **pbitmap)
879 {
880     gx_device *dev = (gx_device *) device;
881 
882     *pbitmap = (unsigned char *)(pmdev->bmi);
883     return 0;
884 }
885 
886 /* Lock the device (so it's size cannot be changed) if flag = TRUE */
887 /* or unlock the device if flag = FALSE */
888 /* device is a pointer to Ghostscript device from GSDLL_DEVICE message */
889 int GSDLLAPI
gsdll_lock_device(unsigned char * device,int flag)890 gsdll_lock_device(unsigned char *device, int flag)
891 {
892     gx_device *dev = (gx_device *) device;
893     APIRET rc;
894 
895     if (flag)
896 	rc = DosRequestMutexSem(pmdev->bmp_mutex, 60000);
897     else
898 	rc = DosReleaseMutexSem(pmdev->bmp_mutex);
899     return rc;
900 }
901 
902 #endif /* __DLL__ */
903 
904 /* ------ Internal routines ------ */
905 
906 #undef pmdev
907 
908 
909 /* start gspmdrv.exe */
910 private int
pm_run_gspmdrv(gx_device_pm * pmdev)911 pm_run_gspmdrv(gx_device_pm * pmdev)
912 {
913     int ccode;
914     PCHAR pdrvname = "gspmdrv.exe";
915     CHAR error_message[256];
916     CHAR term_queue_name[128];
917     CHAR id[128];
918     CHAR arg[1024];
919     STARTDATA sdata;
920     APIRET rc;
921     PTIB pptib;
922     PPIB pppib;
923     CHAR progname[256];
924     PCHAR tail;
925 
926     sprintf(id, ID_NAME, pmdev->gspid, (ULONG) pmdev);
927 
928     /* Create termination queue - used to find out when gspmdrv terminates */
929     sprintf(term_queue_name, "\\QUEUES\\TERMQ_%s", id);
930     if (DosCreateQueue(&(pmdev->term_queue), QUE_FIFO, term_queue_name)) {
931 	eprintf("pm_run_gspmdrv: failed to create termination queue\n");
932 	return gs_error_limitcheck;
933     }
934     /* get full path to gsos2.exe and hence path to gspmdrv.exe */
935     if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) {
936 	eprintf1("pm_run_gspmdrv: Couldn't get module handle, rc = %d\n", rc);
937 	return gs_error_limitcheck;
938     }
939     if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(progname) - 1, progname)) != 0) {
940 	eprintf1("pm_run_gspmdrv: Couldn't get module name, rc = %d\n", rc);
941 	return gs_error_limitcheck;
942     }
943     if ((tail = strrchr(progname, '\\')) != (PCHAR) NULL) {
944 	tail++;
945 	*tail = '\0';
946     } else
947 	tail = progname;
948     strcat(progname, pdrvname);
949 
950     /* Open the PM driver session gspmdrv.exe */
951     /* arguments are: */
952     /*  (1) -d (display) option */
953     /*  (2) id string */
954     sprintf(arg, "-d %s", id);
955 
956     /* because gspmdrv.exe is a different EXE type to gs.exe,
957      * we must use start session not DosExecPgm() */
958     sdata.Length = sizeof(sdata);
959     sdata.Related = SSF_RELATED_CHILD;	/* to be a child  */
960     sdata.FgBg = SSF_FGBG_BACK;	/* start in background */
961     sdata.TraceOpt = 0;
962     sdata.PgmTitle = "Ghostscript PM driver session";
963     sdata.PgmName = progname;
964     sdata.PgmInputs = arg;
965     sdata.TermQ = term_queue_name;
966     sdata.Environment = pppib->pib_pchenv;	/* use Parent's environment */
967     sdata.InheritOpt = 0;	/* Can't inherit from parent because different sesison type */
968     sdata.SessionType = SSF_TYPE_DEFAULT;	/* default is PM */
969     sdata.IconFile = NULL;
970     sdata.PgmHandle = 0;
971     sdata.PgmControl = 0;
972     sdata.InitXPos = 0;
973     sdata.InitYPos = 0;
974     sdata.InitXSize = 0;
975     sdata.InitYSize = 0;
976     sdata.ObjectBuffer = error_message;
977     sdata.ObjectBuffLen = sizeof(error_message);
978 
979     rc = DosStartSession(&sdata, &pmdev->session_id, &pmdev->process_id);
980     if (rc == ERROR_FILE_NOT_FOUND) {
981 	sdata.PgmName = pdrvname;
982 	rc = DosStartSession(&sdata, &pmdev->session_id, &pmdev->process_id);
983     }
984     if (rc) {
985 	eprintf2("pm_run_gspmdrv: failed to run %s, rc = %d\n", sdata.PgmName, rc);
986 	eprintf1("pm_run_gspmdrv: error_message: %s\n", error_message);
987 	return gs_error_limitcheck;
988     }
989     return 0;
990 
991 }
992 
993 /* Allocate the backing bitmap. */
994 private int
pm_alloc_bitmap(gx_device_pm * pmdev,gx_device * param_dev)995 pm_alloc_bitmap(gx_device_pm * pmdev, gx_device * param_dev)
996 {
997     gx_device_memory mdev;
998     byte *base;
999     ulong data_size;
1000     uint ptr_size;
1001     uint pal_size;
1002     uint raster;
1003     ULONG rc;
1004     ULONG needed;
1005 
1006     /* Finish initializing the bitmap. */
1007 
1008     gs_make_mem_device(&mdev, gdev_mem_device_for_bits(pmdev->color_info.depth), 0, 0, (gx_device *) pmdev);
1009     mdev.width = param_dev->width;
1010     mdev.height = param_dev->height;
1011     /* BMP files need width rounded up so that a scan line is */
1012     /* a multiple of 4 bytes. */
1013     /* This is currently done by gdev_mem_raster(). */
1014     /* It may be better to do it here explicitly in case */
1015     /* gdev_mem_raster changes. */
1016     raster = gdev_mem_raster(&mdev);
1017     data_size = (ulong) raster *mdev.height;
1018 
1019     ptr_size = sizeof(byte **) * mdev.height;
1020     pal_size = pm_palette_size(pmdev);
1021     needed = pmdev->bmi->cbFix + pal_size + data_size + ptr_size;
1022     /* round up to page boundary */
1023     needed = (needed + MIN_COMMIT - 1) & (~(MIN_COMMIT - 1));
1024     if (needed > pmdev->committed) {
1025 	/* commit more memory */
1026 	if (rc = DosSetMem(pmdev->bitmap + pmdev->committed,
1027 			   needed - pmdev->committed,
1028 			   PAG_COMMIT | PAG_DEFAULT)) {
1029 	    eprintf1("No memory in pm_alloc_bitmap, rc = %d\n", rc);
1030 	    return gs_error_limitcheck;
1031 	}
1032 	pmdev->committed = needed;
1033     }
1034     /* Shared memory can't be decommitted */
1035 #ifdef __DLL__
1036     if (pmdev->dll && (needed < pmdev->committed)) {
1037 	/* decommit memory */
1038 	if (rc = DosSetMem(pmdev->bitmap + needed,
1039 			   pmdev->committed - needed,
1040 			   PAG_DECOMMIT)) {
1041 	    eprintf1("Failed to decommit memory in pm_alloc_bitmap, rc = %d\n", rc);
1042 	    return gs_error_limitcheck;
1043 	}
1044 	pmdev->committed = needed;
1045     }
1046 #endif
1047 
1048     /* Nothing can go wrong now.... */
1049     base = pmdev->bitmap + pmdev->bmi->cbFix + pm_palette_size(pmdev);
1050     pmdev->mdev = mdev;
1051     pmdev->mdev.base = (byte *) base;
1052     pmmproc(open_device) ((gx_device *) & pmdev->mdev);
1053     pmdev->bmi->cbImage = data_size;
1054     return 0;
1055 }
1056 
1057 private void
pm_makepalette(gx_device_pm * pmdev)1058 pm_makepalette(gx_device_pm * pmdev)
1059 {
1060     int i, val;
1061     RGB2 *argb = (RGB2 *) ((PBYTE) pmdev->bmi + pmdev->bmi->cbFix);
1062 
1063     if (pmdev->BitsPerPixel > 8)
1064 	return;			/* these don't use a palette */
1065 
1066     for (i = 0; i < pmdev->nColors; i++) {
1067 	switch (pmdev->nColors) {
1068 	    case 64:
1069 		/* colors are rrggbb */
1070 		argb[i].bRed = ((i & 0x30) >> 4) * 85;
1071 		argb[i].bGreen = ((i & 0xC) >> 2) * 85;
1072 		argb[i].bBlue = (i & 3) * 85;
1073 		argb[i].fcOptions = 0;
1074 		/* zero unused entries */
1075 		argb[i + 64].bRed = argb[i + 64].bGreen = argb[i + 64].bBlue = 0;
1076 		argb[i + 64].fcOptions = 0;
1077 		argb[i + 128].bRed = argb[i + 128].bGreen = argb[i + 128].bBlue = 0;
1078 		argb[i + 128].fcOptions = 0;
1079 		argb[i + 192].bRed = argb[i + 192].bGreen = argb[i + 192].bBlue = 0;
1080 		argb[i + 192].fcOptions = 0;
1081 		break;
1082 	    case 16:
1083 		/* colors are irgb */
1084 		val = (i & 8 ? 255 : 128);
1085 		argb[i].bRed = i & 4 ? val : 0;
1086 		argb[i].bGreen = i & 2 ? val : 0;
1087 		argb[i].bBlue = i & 1 ? val : 0;
1088 		if (i == 8) {	/* light gray */
1089 		    argb[i].bRed =
1090 			argb[i].bGreen =
1091 			argb[i].bBlue = 192;
1092 		    argb[i].fcOptions = 0;
1093 		}
1094 		break;
1095 	    case 2:
1096 		argb[i].bRed =
1097 		    argb[i].bGreen =
1098 		    argb[i].bBlue = (i ? 255 : 0);
1099 		argb[i].fcOptions = 0;
1100 		break;
1101 	}
1102     }
1103 }
1104 
1105 
1106 /* Cause display to be updated periodically */
1107 private void
pm_update(gx_device_pm * pmdev)1108 pm_update(gx_device_pm * pmdev)
1109 {
1110     if (pmdev->updating)
1111 	return;
1112     if (!pmdev->UpdateInterval)
1113 	return;
1114     if (*pmdev->GSVIEW) {
1115 	APIRET rc;
1116 
1117 	rc = DosWriteQueue(pmdev->drv_queue, GS_UPDATING, 0, NULL, 0);
1118 	if (rc)
1119 	    eprintf1("pm_update: DosWriteQueue error %d\n", rc);
1120     } else {
1121 	DosStartTimer(pmdev->UpdateInterval, (HSEM) pmdev->sync_event,
1122 		      &pmdev->update_timer);
1123     }
1124     pmdev->updating = TRUE;
1125 }
1126 
1127 /*
1128  * This is a utility routine to build the display device's color_info
1129  * structure (except for the anti alias info).
1130  */
1131 private void
set_color_info(gx_device_color_info * pdci,int nc,int depth,int maxgray,int maxcolor)1132 set_color_info(gx_device_color_info * pdci, int nc, int depth, int maxgray, int maxcolor)
1133 {
1134     pdci->num_components = pdci->max_components = nc;
1135     pdci->depth = depth;
1136     pdci->gray_index = 0;
1137     pdci->max_gray = maxgray;
1138     pdci->max_color = maxcolor;
1139     pdci->dither_grays = maxgray + 1;
1140     pdci->dither_colors = maxcolor + 1;
1141     pdci->separable_and_linear = GX_CINFO_UNKNOWN_SEP_LIN;
1142     switch (nc) {
1143 	case 1:
1144 	    pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
1145 	    pdci->cm_name = "DeviceGray";
1146 	    break;
1147 	case 3:
1148 	    pdci->polarity = GX_CINFO_POLARITY_ADDITIVE;
1149 	    pdci->cm_name = "DeviceRGB";
1150 	    break;
1151 	case 4:
1152 	    pdci->polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
1153 	    pdci->cm_name = "DeviceCMYK";
1154 	    break;
1155 	default:
1156 	    break;
1157     }
1158 }
1159 
1160 /*
1161  * This is an utility routine to set up the color procs for the display
1162  * device.  The display device can change its setup.
1163  */
1164 private void
1165 set_color_procs(gx_device * pdev,
1166 	dev_t_proc_encode_color((*encode_color), gx_device),
1167 	dev_t_proc_decode_color((*decode_color), gx_device),
1168 	dev_t_proc_get_color_mapping_procs((*get_color_mapping_procs), gx_device),
1169 	dev_t_proc_get_color_comp_index((*get_color_comp_index), gx_device))
1170 {
1171 #if 0				/* These procs are no longer used */
1172     pdev->procs.map_rgb_color = encode_color;
1173     pdev->procs.map_color_rgb = decode_color;
1174 #endif
1175     pdev->procs.get_color_mapping_procs = get_color_mapping_procs;
1176     pdev->procs.get_color_comp_index = get_color_comp_index;
1177     pdev->procs.encode_color = encode_color;
1178     pdev->procs.decode_color = decode_color;
1179 }
1180 
1181 /*
1182  * This is an utility routine to set up the color procs for the display
1183  * device.  This routine is used when the display device is Gray.
1184  */
1185 private void
1186 set_gray_color_procs(gx_device * pdev,
1187 	dev_t_proc_encode_color((*encode_color), gx_device),
1188 	dev_t_proc_decode_color((*decode_color), gx_device))
1189 {
1190     set_color_procs(pdev, encode_color, decode_color,
1191 	gx_default_DevGray_get_color_mapping_procs,
1192 	gx_default_DevGray_get_color_comp_index);
1193 }
1194 
1195 /*
1196  * This is an utility routine to set up the color procs for the display
1197  * device.  This routine is used when the display device is RGB.
1198  */
1199 private void
1200 set_rgb_color_procs(gx_device * pdev,
1201 	dev_t_proc_encode_color((*encode_color), gx_device),
1202 	dev_t_proc_decode_color((*decode_color), gx_device))
1203 {
1204     set_color_procs(pdev, encode_color, decode_color,
1205 	gx_default_DevRGB_get_color_mapping_procs,
1206 	gx_default_DevRGB_get_color_comp_index);
1207 }
1208 
1209 private uint
pm_set_bits_per_pixel(gx_device_pm * pmdev,int bpp)1210 pm_set_bits_per_pixel(gx_device_pm * pmdev, int bpp)
1211 {
1212     gx_device * pdev = (gx_device *) pmdev;
1213     gx_device_color_info dci = pmdev->color_info;
1214 
1215     switch (bpp) {
1216 	case 24:
1217 	    set_color_info(&dci, 3, bpp, 255, 255);
1218 	    set_rgb_color_procs(pdev, pm_map_rgb_color, pm_map_color_rgb);
1219 	    pmdev->nColors = (1 << 24);
1220 	    break;
1221 	case 8:
1222 	    /* use 64 static colors and 166 dynamic colors from 8 planes */
1223 	    set_color_info(&dci, 3, 8, 7, 31);
1224 	    set_rgb_color_procs(pdev, pm_map_rgb_color, pm_map_color_rgb);
1225 	    pmdev->nColors = 64;
1226 	    break;
1227 	case 4:
1228 	    set_color_info(&dci, 3, 4, 1, 1);
1229 	    set_rgb_color_procs(pdev, pm_map_rgb_color, pm_map_color_rgb);
1230 	    pmdev->nColors = 16;
1231 	    break;
1232 	case 1:
1233 	    set_color_info(&dci, 1, 1, 1, 0);
1234 	    set_gray_color_procs(pdev, gx_default_gray_encode,
1235 					gx_default_w_b_map_color_rgb);
1236 	    pmdev->nColors = 2;
1237 	    break;
1238 	default:
1239 	    return (gs_error_rangecheck);
1240     }
1241     pmdev->BitsPerPixel = bpp;
1242     /* restore old anti_alias info */
1243     dci.anti_alias = pmdev->color_info.anti_alias;
1244     pmdev->color_info = dci;
1245     /* Set the mask bits, etc. even though we are setting linear: unknown */
1246     set_linear_color_bits_mask_shift(pdev);
1247 
1248     return 0;
1249 }
1250 
1251 /* return length of BMP palette in bytes */
1252 private uint
pm_palette_size(gx_device_pm * pmdev)1253 pm_palette_size(gx_device_pm * pmdev)
1254 {
1255     switch (pmdev->color_info.depth) {
1256 	case 24:
1257 	    return 0;
1258 	case 8:
1259 	    return 256 * sizeof(RGB2);
1260 	case 4:
1261 	    return 16 * sizeof(RGB2);
1262     }
1263     /* must be two color */
1264     return 2 * sizeof(RGB2);
1265 }
1266 
1267 /* This is used for testing */
1268 /* Write out a BMP file to "out.bmp" */
1269 private void
pm_write_bmp(gx_device_pm * pmdev)1270 pm_write_bmp(gx_device_pm * pmdev)
1271 {
1272     BITMAPFILEHEADER2 bmfh;
1273     uint bmfh_length = sizeof(BITMAPFILEHEADER2) - sizeof(BITMAPINFOHEADER2);
1274     uint length;		/* bitmap length */
1275     ULONG fh;			/* file handle */
1276     ULONG action;
1277     ULONG count;
1278 
1279     bmfh.usType = 0x4d42;	/* "BM" */
1280     length = pmdev->bmi->cbFix + pm_palette_size(pmdev)
1281 	+ ((gdev_mem_raster(&pmdev->mdev) * pmdev->mdev.height));
1282     bmfh.cbSize = bmfh_length + length;
1283     bmfh.xHotspot = bmfh.yHotspot = 0;
1284     bmfh.offBits = bmfh_length + pmdev->bmi->cbFix + pm_palette_size(pmdev);
1285     if (DosOpen("out.bmp",	/* filename */
1286 		&fh,		/* pointer to handle */
1287 		&action,	/* pointer to result */
1288 		0,		/* initial length */
1289 		FILE_NORMAL,	/* normal file */
1290 		OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS,
1291 		OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE,
1292 		0)) {
1293 	eprintf("error opening out.bmp\n");
1294 	return;
1295     }
1296     if (DosWrite(fh, (PBYTE) & bmfh, bmfh_length, &count))
1297 	eprintf("error writing header for out.bmp\n");
1298     if (DosWrite(fh, pmdev->bitmap, length, &count))
1299 	eprintf("error writing out.bmp\n");
1300     if (DosClose(fh))
1301 	eprintf("error closing out.bmp\n");
1302 }
1303