xref: /plan9/sys/src/cmd/gs/src/gdevwddb.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989, 1995, 1996, 1997 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: gdevwddb.c,v 1.7 2004/08/10 13:02:36 stefan Exp $ */
18 /*
19  * Microsoft Windows 3.n driver for Ghostscript,
20  * using device-dependent bitmap.
21  *
22  * Original version by Russell Lang and Maurice Castro with help from
23  * Programming Windows, 2nd Ed., Charles Petzold, Microsoft Press;
24  * created from gdevbgi.c and gnuplot/term/win.trm 5th June 1992.
25  * Extensively modified by L. Peter Deutsch, Aladdin Enterprises.
26  */
27 #include "gdevmswn.h"
28 
29 /* Make sure we cast to the correct structure type. */
30 typedef struct gx_device_win_ddb_s gx_device_win_ddb;
31 
32 #undef wdev
33 #define wdev ((gx_device_win_ddb *)dev)
34 
35 /* Forward references */
36 private void near win_addtool(gx_device_win_ddb *, int);
37 private void near win_maketools(gx_device_win_ddb *, HDC);
38 private void near win_destroytools(gx_device_win_ddb *);
39 
40 /* Device procedures */
41 
42 /* See gxdevice.h for the definitions of the procedures. */
43 private dev_proc_open_device(win_ddb_open);
44 private dev_proc_close_device(win_ddb_close);
45 private dev_proc_map_rgb_color(win_ddb_map_rgb_color);
46 private dev_proc_fill_rectangle(win_ddb_fill_rectangle);
47 private dev_proc_tile_rectangle(win_ddb_tile_rectangle);
48 private dev_proc_copy_mono(win_ddb_copy_mono);
49 private dev_proc_copy_color(win_ddb_copy_color);
50 
51 /* Windows-specific procedures */
52 private win_proc_copy_to_clipboard(win_ddb_copy_to_clipboard);
53 private win_proc_repaint(win_ddb_repaint);
54 private win_proc_alloc_bitmap(win_ddb_alloc_bitmap);
55 private win_proc_free_bitmap(win_ddb_free_bitmap);
56 
57 /* The device descriptor */
58 struct gx_device_win_ddb_s {
59     gx_device_common;
60     gx_device_win_common;
61 
62     /* Handles */
63 
64     HBITMAP FAR hbitmap;
65     HDC FAR hdcbit;
66     HPEN hpen, *hpens;
67     uint hpensize;
68     HBRUSH hbrush, *hbrushs;
69     uint hbrushsize;
70 #define select_brush(color)\
71   if (wdev->hbrush != wdev->hbrushs[color])\
72    {	wdev->hbrush = wdev->hbrushs[color];\
73 	SelectObject(wdev->hdcbit,wdev->hbrush);\
74    }
75     HPALETTE hpalette;
76     LPLOGPALETTE lpalette;
77 
78     /* A staging bitmap for copy_mono. */
79     /* We want one big enough to handle the standard 16x16 halftone; */
80     /* this is also big enough for ordinary-size characters. */
81 
82 #define bmWidthBytes 4		/* must be even */
83 #define bmWidthBits (bmWidthBytes * 8)
84 #define bmHeight 32
85     HBITMAP FAR hbmmono;
86     HDC FAR hdcmono;
87     gx_bitmap_id bm_id;
88 };
89 private const gx_device_procs win_ddb_procs =
90 {
91     win_ddb_open,
92     NULL,			/* get_initial_matrix */
93     win_sync_output,
94     win_output_page,
95     win_ddb_close,
96     win_ddb_map_rgb_color,
97     win_map_color_rgb,
98     win_ddb_fill_rectangle,
99     win_ddb_tile_rectangle,
100     win_ddb_copy_mono,
101     win_ddb_copy_color,
102     NULL,			/* draw_line */
103     NULL,			/* get_bits */
104     win_get_params,
105     win_put_params,
106     NULL,			/* map_cmyk_color */
107     win_get_xfont_procs
108 };
109 gx_device_win_ddb far_data gs_mswin_device =
110 {
111     std_device_std_body(gx_device_win_ddb, &win_ddb_procs, "mswin",
112 			INITIAL_WIDTH, INITIAL_HEIGHT,	/* win_open() fills these in later */
113 			INITIAL_RESOLUTION, INITIAL_RESOLUTION	/* win_open() fills these in later */
114     ),
115     {0},			/* std_procs */
116     0,				/* BitsPerPixel - not used */
117     5000,			/* UpdateInterval (in milliseconds) */
118     "\0",			/* GSVIEW_STR */
119     0,				/* not a DLL device */
120     2,				/* nColors */
121     0,				/* mapped_color_flags */
122     win_ddb_copy_to_clipboard,
123     win_ddb_repaint,
124     win_ddb_alloc_bitmap,
125     win_ddb_free_bitmap
126 };
127 
128 /* Open the win_ddb driver */
129 private int
win_ddb_open(gx_device * dev)130 win_ddb_open(gx_device * dev)
131 {
132     int code = win_open(dev);
133     HDC hdc;
134 
135     if (code < 0)
136 	return code;
137 
138     if (wdev->BitsPerPixel > 8)
139 	return gs_error_limitcheck;	/* don't support 24 bit/pixel */
140 
141     /* Create the backing bitmap. */
142     code = win_ddb_alloc_bitmap((gx_device_win *) dev, dev);
143     if (code < 0)
144 	return code;
145 
146     /* Create the bitmap and DC for copy_mono. */
147     hdc = GetDC(wdev->hwndimg);
148     wdev->hbmmono = CreateBitmap(bmWidthBits, bmHeight, 1, 1, NULL);
149     wdev->hdcmono = CreateCompatibleDC(hdc);
150     if (wdev->hbmmono == NULL || wdev->hdcmono == NULL) {
151 	win_ddb_free_bitmap((gx_device_win *) dev);
152 	ReleaseDC(wdev->hwndimg, hdc);
153 	return win_nomemory();
154     }
155     SetMapMode(wdev->hdcmono, GetMapMode(hdc));
156     SelectObject(wdev->hdcmono, wdev->hbmmono);
157     wdev->bm_id = gx_no_bitmap_id;
158     ReleaseDC(wdev->hwndimg, hdc);
159 
160     /* create palette and tools for bitmap */
161     if ((wdev->lpalette = win_makepalette((gx_device_win *) dev))
162 	== (LPLOGPALETTE) NULL)
163 	return win_nomemory();
164     wdev->hpalette = CreatePalette(wdev->lpalette);
165     (void)SelectPalette(wdev->hdcbit, wdev->hpalette, NULL);
166     RealizePalette(wdev->hdcbit);
167     win_maketools(wdev, wdev->hdcbit);
168 
169     wdev->hdctext = wdev->hdcbit;	/* draw text here */
170 
171     return 0;
172 }
173 
174 /* Close the win_ddb driver */
175 private int
win_ddb_close(gx_device * dev)176 win_ddb_close(gx_device * dev)
177 {
178     /* Free resources */
179 
180     win_destroytools(wdev);
181     DeleteDC(wdev->hdcmono);
182     win_ddb_free_bitmap((gx_device_win *) dev);
183     DeleteObject(wdev->hpalette);
184     DeleteObject(wdev->hbmmono);
185     gs_free((char *)(wdev->lpalette), 1, sizeof(LOGPALETTE) +
186 	    (1 << (wdev->color_info.depth)) * sizeof(PALETTEENTRY),
187 	    "win_ddb_close");
188 
189     return win_close(dev);
190 }
191 
192 /* Map a r-g-b color to the colors available under Windows */
193 private gx_color_index
win_ddb_map_rgb_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b)194 win_ddb_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g,
195 		      gx_color_value b)
196 {
197     int i = wdev->nColors;
198     gx_color_index color = win_map_rgb_color(dev, r, g, b);
199     LPLOGPALETTE lipal = wdev->limgpalette;
200     LPLOGPALETTE lpal = wdev->lpalette;
201 
202     if (color != i)
203 	return color;
204 
205     /* We just added a color to the window palette. */
206     /* Add it to the bitmap palette as well. */
207 
208     DeleteObject(wdev->hpalette);
209     lpal->palPalEntry[i].peFlags = NULL;
210     lpal->palPalEntry[i].peRed = lipal->palPalEntry[i].peRed;
211     lpal->palPalEntry[i].peGreen = lipal->palPalEntry[i].peGreen;
212     lpal->palPalEntry[i].peBlue = lipal->palPalEntry[i].peBlue;
213     lpal->palNumEntries = i + 1;
214     wdev->hpalette = CreatePalette(lpal);
215     (void)SelectPalette(wdev->hdcbit, wdev->hpalette, NULL);
216     RealizePalette(wdev->hdcbit);
217     win_addtool(wdev, i);
218 
219     return color;
220 }
221 
222 /* Macro for filling a rectangle with a color. */
223 /* Note that it starts with a declaration. */
224 #define fill_rect(x, y, w, h, color)\
225 RECT rect;\
226 rect.left = x, rect.top = y;\
227 rect.right = x + w, rect.bottom = y + h;\
228 FillRect(wdev->hdcbit, &rect, wdev->hbrushs[(int)color])
229 
230 
231 /* Fill a rectangle. */
232 private int
win_ddb_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)233 win_ddb_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
234 		       gx_color_index color)
235 {
236     fit_fill(dev, x, y, w, h);
237     /* Use PatBlt for filling.  Special-case black. */
238     if (color == 0)
239 	PatBlt(wdev->hdcbit, x, y, w, h, rop_write_0s);
240     else {
241 	select_brush((int)color);
242 	PatBlt(wdev->hdcbit, x, y, w, h, rop_write_pattern);
243     }
244     win_update((gx_device_win *) dev);
245 
246     return 0;
247 }
248 
249 /* Tile a rectangle.  If neither color is transparent, */
250 /* pre-clear the rectangle to color0 and just tile with color1. */
251 /* This is faster because of how win_copy_mono is implemented. */
252 /* Note that this also does the right thing for colored tiles. */
253 private int
win_ddb_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)254 win_ddb_tile_rectangle(gx_device * dev, const gx_tile_bitmap * tile,
255       int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
256 		       int px, int py)
257 {
258     fit_fill(dev, x, y, w, h);
259     if (czero != gx_no_color_index && cone != gx_no_color_index) {
260 	fill_rect(x, y, w, h, czero);
261 	czero = gx_no_color_index;
262     }
263     if (tile->raster == bmWidthBytes && tile->size.y <= bmHeight &&
264 	(px | py) == 0 && cone != gx_no_color_index
265 	) {			/* We can do this much more efficiently */
266 	/* by using the internal algorithms of copy_mono */
267 	/* and gx_default_tile_rectangle. */
268 	int width = tile->size.x;
269 	int height = tile->size.y;
270 	int rwidth = tile->rep_width;
271 	int irx = ((rwidth & (rwidth - 1)) == 0 ?	/* power of 2 */
272 		   x & (rwidth - 1) :
273 		   x % rwidth);
274 	int ry = y % tile->rep_height;
275 	int icw = width - irx;
276 	int ch = height - ry;
277 	int ex = x + w, ey = y + h;
278 	int fex = ex - width, fey = ey - height;
279 	int cx, cy;
280 
281 	select_brush((int)cone);
282 
283 	if (tile->id != wdev->bm_id || tile->id == gx_no_bitmap_id) {
284 	    wdev->bm_id = tile->id;
285 	    SetBitmapBits(wdev->hbmmono,
286 			  (DWORD) (bmWidthBytes * tile->size.y),
287 			  (BYTE *) tile->data);
288 	}
289 #define copy_tile(srcx, srcy, tx, ty, tw, th)\
290   BitBlt(wdev->hdcbit, tx, ty, tw, th, wdev->hdcmono, srcx, srcy, rop_write_at_1s)
291 
292 	if (ch > h)
293 	    ch = h;
294 	for (cy = y;;) {
295 	    if (w <= icw)
296 		copy_tile(irx, ry, x, cy, w, ch);
297 	    else {
298 		copy_tile(irx, ry, x, cy, icw, ch);
299 		cx = x + icw;
300 		while (cx <= fex) {
301 		    copy_tile(0, ry, cx, cy, width, ch);
302 		    cx += width;
303 		}
304 		if (cx < ex) {
305 		    copy_tile(0, ry, cx, cy, ex - cx, ch);
306 		}
307 	    }
308 	    if ((cy += ch) >= ey)
309 		break;
310 	    ch = (cy > fey ? ey - cy : height);
311 	    ry = 0;
312 	}
313 
314 	win_update((gx_device_win *) dev);
315 	return 0;
316     }
317     return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
318 }
319 
320 
321 /* Copy a monochrome bitmap.  The colors are given explicitly. */
322 /* Color = gx_no_color_index means transparent (no effect on the image). */
323 private int
win_ddb_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)324 win_ddb_copy_mono(gx_device * dev,
325 		const byte * base, int sourcex, int raster, gx_bitmap_id id,
326 		  int x, int y, int w, int h,
327 		  gx_color_index zero, gx_color_index one)
328 {
329     int endx;
330     const byte *ptr_line;
331     int width_bytes, height;
332     DWORD rop = rop_write_at_1s;
333     int color;
334     BYTE aBit[bmWidthBytes * bmHeight];
335     BYTE *aptr = aBit;
336 
337     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
338 
339     if (sourcex & ~7) {
340 	base += sourcex >> 3;
341 	sourcex &= 7;
342     }
343     /* Break up large transfers into smaller ones. */
344     while ((endx = sourcex + w) > bmWidthBits) {
345 	int lastx = (endx - 1) & -bmWidthBits;
346 	int subw = endx - lastx;
347 	int code = win_ddb_copy_mono(dev, base, lastx,
348 				     raster, gx_no_bitmap_id,
349 				     x + lastx - sourcex, y,
350 				     subw, h, zero, one);
351 
352 	if (code < 0)
353 	    return code;
354 	w -= subw;
355     }
356     while (h > bmHeight) {
357 	int code;
358 
359 	h -= bmHeight;
360 	code = win_ddb_copy_mono(dev, base + h * raster, sourcex,
361 				 raster, gx_no_bitmap_id,
362 				 x, y + h, w, bmHeight, zero, one);
363 	if (code < 0)
364 	    return code;
365     }
366 
367     width_bytes = (sourcex + w + 7) >> 3;
368     ptr_line = base;
369 
370     if (zero == gx_no_color_index) {
371 	if (one == gx_no_color_index)
372 	    return 0;
373 	color = (int)one;
374 	if (color == 0)
375 	    rop = rop_write_0_at_1s;
376 	else
377 	    select_brush(color);
378     } else {
379 	if (one == gx_no_color_index) {
380 	    color = (int)zero;
381 	    rop = rop_write_at_0s;
382 	} else {		/* Pre-clear the rectangle to zero */
383 	    fill_rect(x, y, w, h, zero);
384 	    color = (int)one;
385 	}
386 	select_brush(color);
387     }
388 
389     if (id != wdev->bm_id || id == gx_no_bitmap_id) {
390 	wdev->bm_id = id;
391 	if (raster == bmWidthBytes) {	/* We can do the whole thing in a single transfer! */
392 	    SetBitmapBits(wdev->hbmmono,
393 			  (DWORD) (bmWidthBytes * h),
394 			  (BYTE *) base);
395 	} else {
396 	    for (height = h; height--;
397 		 ptr_line += raster, aptr += bmWidthBytes
398 		) {		/* Pack the bits into the bitmap. */
399 		switch (width_bytes) {
400 		    default:
401 			memcpy(aptr, ptr_line, width_bytes);
402 			break;
403 		    case 4:
404 			aptr[3] = ptr_line[3];
405 		    case 3:
406 			aptr[2] = ptr_line[2];
407 		    case 2:
408 			aptr[1] = ptr_line[1];
409 		    case 1:
410 			aptr[0] = ptr_line[0];
411 		}
412 	    }
413 	    SetBitmapBits(wdev->hbmmono,
414 			  (DWORD) (bmWidthBytes * h),
415 			  &aBit[0]);
416 	}
417     }
418     BitBlt(wdev->hdcbit, x, y, w, h, wdev->hdcmono, sourcex, 0, rop);
419     win_update((gx_device_win *) dev);
420     return 0;
421 }
422 
423 
424 /* Copy a color pixel map.  This is just like a bitmap, except that */
425 /* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */
426 private int
win_ddb_copy_color(gx_device * dev,const byte * base,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)427 win_ddb_copy_color(gx_device * dev,
428 		const byte * base, int sourcex, int raster, gx_bitmap_id id,
429 		   int x, int y, int w, int h)
430 {
431     fit_copy(dev, base, sourcex, raster, id, x, y, w, h);
432 
433     if (gx_device_has_color(dev)) {
434 	switch (dev->color_info.depth) {
435 	    case 8:
436 		{
437 		    int xi, yi;
438 		    int skip = raster - w;
439 		    const byte *sptr = base + sourcex;
440 
441 		    if (w <= 0)
442 			return 0;
443 		    if (x < 0 || x + w > dev->width)
444 			return_error(gs_error_rangecheck);
445 		    for (yi = y; yi - y < h; yi++) {
446 			for (xi = x; xi - x < w; xi++) {
447 			    int color = *sptr++;
448 
449 			    SetPixel(wdev->hdcbit, xi, yi, PALETTEINDEX(color));
450 			}
451 			sptr += skip;
452 		    }
453 		}
454 		break;
455 	    case 4:
456 		{		/* color device, four bits per pixel */
457 		    const byte *line = base + (sourcex >> 1);
458 		    int dest_y = y, end_x = x + w;
459 
460 		    if (w <= 0)
461 			return 0;
462 		    while (h--) {	/* for each line */
463 			const byte *source = line;
464 			register int dest_x = x;
465 
466 			if (sourcex & 1) {	/* odd nibble first */
467 			    int color = *source++ & 0xf;
468 
469 			    SetPixel(wdev->hdcbit, dest_x, dest_y, PALETTEINDEX(color));
470 			    dest_x++;
471 			}
472 			/* Now do full bytes */
473 			while (dest_x < end_x) {
474 			    int color = *source >> 4;
475 
476 			    SetPixel(wdev->hdcbit, dest_x, dest_y, PALETTEINDEX(color));
477 			    dest_x++;
478 			    if (dest_x < end_x) {
479 				color = *source++ & 0xf;
480 				SetPixel(wdev->hdcbit, dest_x, dest_y, PALETTEINDEX(color));
481 				dest_x++;
482 			    }
483 			}
484 			dest_y++;
485 			line += raster;
486 		    }
487 		}
488 		break;
489 	    default:
490 		return (-1);	/* panic */
491 	}
492     } else
493 	/* monochrome device: one bit per pixel */
494     {				/* bitmap is the same as win_copy_mono: one bit per pixel */
495 	win_ddb_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
496 			  (gx_color_index) 0,
497 			  (gx_color_index) (dev->color_info.depth == 8 ? 63 : dev->color_info.max_gray));
498     }
499     win_update((gx_device_win *) dev);
500     return 0;
501 }
502 
503 /* ------ Windows-specific device procedures ------ */
504 
505 
506 /* Copy the bitmap to the clipboard. */
507 private void
win_ddb_copy_to_clipboard(gx_device_win * dev)508 win_ddb_copy_to_clipboard(gx_device_win * dev)
509 {				/* make somewhere to put it and copy */
510     HDC hdcbit = wdev->hdcbit;
511     HBITMAP bitmap = CreateCompatibleBitmap(hdcbit, dev->width,
512 					    dev->height);
513 
514     if (bitmap) {
515 	/* there is enough memory and the bitmaps OK */
516 	HDC mem = CreateCompatibleDC(hdcbit);
517 
518 	SelectObject(mem, bitmap);
519 	BitBlt(mem, 0, 0, dev->width, dev->height,
520 	       hdcbit, 0, 0, SRCCOPY);
521 	DeleteDC(mem);
522 	/* copy it to the clipboard */
523 	OpenClipboard(wdev->hwndimg);
524 	EmptyClipboard();
525 	SetClipboardData(CF_BITMAP, bitmap);
526 	SetClipboardData(CF_PALETTE, CreatePalette(wdev->limgpalette));
527 	CloseClipboard();
528     }
529 }
530 
531 
532 /* Repaint a section of the window. */
533 private void
win_ddb_repaint(gx_device_win * dev,HDC hdc,int dx,int dy,int wx,int wy,int sx,int sy)534 win_ddb_repaint(gx_device_win * dev, HDC hdc, int dx, int dy, int wx, int wy,
535 		int sx, int sy)
536 {
537     BitBlt(hdc, dx, dy, wx, wy, wdev->hdcbit, sx, sy, SRCCOPY);
538 }
539 
540 
541 /* Allocate the backing bitmap. */
542 private int
win_ddb_alloc_bitmap(gx_device_win * dev,gx_device * param_dev)543 win_ddb_alloc_bitmap(gx_device_win * dev, gx_device * param_dev)
544 {
545     HDC hdc;
546     int i;
547 
548     hdc = GetDC(wdev->hwndimg);
549     for (i = 0;; i++) {
550 	wdev->hbitmap = CreateCompatibleBitmap(hdc,
551 				       param_dev->width, param_dev->height);
552 	if (wdev->hbitmap != (HBITMAP) NULL)
553 	    break;
554 	if (i >= 4) {
555 	    ReleaseDC(wdev->hwndimg, hdc);
556 	    return win_nomemory();
557 	}
558 	errprintf("\nNot enough memory for bitmap.  Halving resolution... ");
559 	param_dev->x_pixels_per_inch /= 2;
560 	param_dev->y_pixels_per_inch /= 2;
561 	param_dev->width /= 2;
562 	param_dev->height /= 2;
563     }
564 
565     wdev->hdcbit = CreateCompatibleDC(hdc);	/* create Device Context for drawing */
566     SelectObject(wdev->hdcbit, wdev->hbitmap);
567     ReleaseDC(wdev->hwndimg, hdc);
568     return 0;
569 }
570 
571 
572 /* Free the backing bitmap. */
573 private void
win_ddb_free_bitmap(gx_device_win * dev)574 win_ddb_free_bitmap(gx_device_win * dev)
575 {
576     DeleteDC(wdev->hdcbit);	/* must do this first */
577     DeleteObject(wdev->hbitmap);
578 }
579 
580 
581 /* ------ Internal routines ------ */
582 
583 #undef wdev
584 
585 
586 private void near
win_addtool(gx_device_win_ddb * wdev,int i)587 win_addtool(gx_device_win_ddb * wdev, int i)
588 {
589     wdev->hpens[i] = CreatePen(PS_SOLID, 1, PALETTEINDEX(i));
590     wdev->hbrushs[i] = CreateSolidBrush(PALETTEINDEX(i));
591 }
592 
593 
594 private void near
win_maketools(gx_device_win_ddb * wdev,HDC hdc)595 win_maketools(gx_device_win_ddb * wdev, HDC hdc)
596 {
597     int i;
598 
599     wdev->hpensize = (1 << (wdev->color_info.depth)) * sizeof(HPEN);
600     wdev->hpens = (HPEN *) gs_malloc(wdev->memory, 1, wdev->hpensize,
601 				     "win_maketools(pens)");
602     wdev->hbrushsize = (1 << (wdev->color_info.depth)) * sizeof(HBRUSH);
603     wdev->hbrushs = (HBRUSH *) gs_malloc(wdev->memory, 1, wdev->hbrushsize,
604 					 "win_maketools(brushes)");
605     if (wdev->hpens && wdev->hbrushs) {
606 	for (i = 0; i < wdev->nColors; i++)
607 	    win_addtool(wdev, i);
608 
609 	wdev->hpen = wdev->hpens[0];
610 	SelectObject(hdc, wdev->hpen);
611 
612 	wdev->hbrush = wdev->hbrushs[0];
613 	SelectObject(hdc, wdev->hbrush);
614     }
615 }
616 
617 
618 private void near
win_destroytools(gx_device_win_ddb * wdev)619 win_destroytools(gx_device_win_ddb * wdev)
620 {
621     int i;
622 
623     for (i = 0; i < wdev->nColors; i++) {
624 	DeleteObject(wdev->hpens[i]);
625 	DeleteObject(wdev->hbrushs[i]);
626     }
627     gs_free(wdev->memory, (char *)wdev->hbrushs, 1, wdev->hbrushsize,
628 	    "win_destroytools(brushes)");
629     gs_free(wdev->memory, (char *)wdev->hpens, 1, wdev->hpensize,
630 	    "win_destroytools(pens)");
631 }
632