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