Release 950817
[wine/multimedia.git] / objects / dib.c
bloba023280ccf13e54cef71919b6394749c90e270af
1 /*
2 * GDI device-independent bitmaps
4 * Copyright 1993,1994 Alexandre Julliard
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <X11/Xlib.h>
10 #include <X11/Xutil.h>
11 #include "dc.h"
12 #include "bitmap.h"
13 #include "callback.h"
14 #include "palette.h"
15 #include "icon.h"
16 #include "stackframe.h"
17 #include "stddebug.h"
18 #include "color.h"
19 #include "debug.h"
22 /***********************************************************************
23 * DIB_GetImageWidthBytes
25 * Return the width of an X image in bytes
27 int DIB_GetImageWidthBytes( int width, int depth )
29 int words;
31 switch(depth)
33 case 1: words = (width + 31) / 32; break;
34 case 4: words = (width + 7) / 8; break;
35 case 8: words = (width + 3) / 4; break;
36 case 15:
37 case 16: words = (width + 1) / 2; break;
38 case 24: words = width; break;
39 default:
40 fprintf(stderr, "DIB: unsupported depth %d.\n", depth );
41 exit(1);
43 return 4 * words;
47 /***********************************************************************
48 * DIB_BitmapInfoSize
50 * Return the size of the bitmap info structure.
52 int DIB_BitmapInfoSize( BITMAPINFO * info, WORD coloruse )
54 int size = info->bmiHeader.biClrUsed;
55 if (!size && (info->bmiHeader.biBitCount != 24))
56 size = 1 << info->bmiHeader.biBitCount;
57 if (coloruse == DIB_RGB_COLORS)
58 size = info->bmiHeader.biSize + size * sizeof(RGBQUAD);
59 else
60 size = info->bmiHeader.biSize + size * sizeof(WORD);
61 return size;
65 /***********************************************************************
66 * DIB_DIBmpToImage
68 * Create an XImage pointing to the bitmap data.
70 static XImage *DIB_DIBmpToImage( BITMAPINFOHEADER * bmp, void * bmpData )
72 extern void _XInitImageFuncPtrs( XImage* );
73 XImage * image;
75 image = XCreateImage(display, DefaultVisualOfScreen( screen ),
76 bmp->biBitCount, ZPixmap, 0, bmpData,
77 bmp->biWidth, bmp->biHeight, 32,
78 DIB_GetImageWidthBytes(bmp->biWidth,bmp->biBitCount));
79 if (!image) return 0;
80 image->byte_order = MSBFirst;
81 image->bitmap_bit_order = MSBFirst;
82 image->bitmap_unit = 16;
83 _XInitImageFuncPtrs(image);
84 return image;
88 /***********************************************************************
89 * DIB_SetImageBits_1
91 * SetDIBits for a 1-bit deep DIB.
93 static void DIB_SetImageBits_1( WORD lines, BYTE *bits, WORD width,
94 int *colors, XImage *bmpImage )
96 WORD i, x;
97 BYTE pad, pix;
99 if (!(width & 31)) pad = 0;
100 else pad = ((32 - (width & 31)) + 7) / 8;
102 while (lines--)
104 for (i = width/8, x = 0; (i > 0); i--)
106 pix = *bits++;
107 XPutPixel( bmpImage, x++, lines, colors[pix >> 7] );
108 XPutPixel( bmpImage, x++, lines, colors[(pix >> 6) & 1] );
109 XPutPixel( bmpImage, x++, lines, colors[(pix >> 5) & 1] );
110 XPutPixel( bmpImage, x++, lines, colors[(pix >> 4) & 1] );
111 XPutPixel( bmpImage, x++, lines, colors[(pix >> 3) & 1] );
112 XPutPixel( bmpImage, x++, lines, colors[(pix >> 2) & 1] );
113 XPutPixel( bmpImage, x++, lines, colors[(pix >> 1) & 1] );
114 XPutPixel( bmpImage, x++, lines, colors[pix & 1] );
116 pix = *bits;
117 switch(width & 7)
119 case 7: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] ); pix <<= 1;
120 case 6: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] ); pix <<= 1;
121 case 5: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] ); pix <<= 1;
122 case 4: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] ); pix <<= 1;
123 case 3: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] ); pix <<= 1;
124 case 2: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] ); pix <<= 1;
125 case 1: XPutPixel( bmpImage, x++, lines, colors[pix >> 7] );
127 bits += pad;
132 /***********************************************************************
133 * DIB_SetImageBits_4
135 * SetDIBits for a 4-bit deep DIB.
137 static void DIB_SetImageBits_4( WORD lines, BYTE *bits, WORD width,
138 int *colors, XImage *bmpImage )
140 WORD i, x;
141 BYTE pad;
143 if (!(width & 7)) pad = 0;
144 else pad = ((8 - (width & 7)) + 1) / 2;
146 while (lines--)
148 for (i = width/2, x = 0; i > 0; i--)
150 BYTE pix = *bits++;
151 XPutPixel( bmpImage, x++, lines, colors[pix >> 4] );
152 XPutPixel( bmpImage, x++, lines, colors[pix & 0x0f] );
154 if (width & 1) XPutPixel( bmpImage, x, lines, colors[*bits >> 4] );
155 bits += pad;
159 #define check_xy(x,y) \
160 if (x > width) { \
161 x = 0; \
162 if (lines) \
163 lines--; \
166 /***********************************************************************
167 * DIB_SetImageBits_RLE4
169 * SetDIBits for a 4-bit deep compressed DIB.
171 static void DIB_SetImageBits_RLE4( WORD lines, BYTE *bits, WORD width,
172 int *colors, XImage *bmpImage )
174 int x = 0, c, length;
175 BYTE *begin = bits;
177 lines--;
178 while ((INT)lines >= 0)
180 length = *bits++;
181 if (length) { /* encoded */
182 c = *bits++;
183 while (length--) {
184 XPutPixel(bmpImage, x++, lines, colors[c >> 4]);
185 check_xy(x, y);
186 if (length) {
187 length--;
188 XPutPixel(bmpImage, x++, lines, colors[c & 0xf]);
189 check_xy(x, y);
192 } else {
193 length = *bits++;
194 switch (length) {
195 case 0: /* eol */
196 x = 0;
197 lines--;
198 continue;
200 case 1: /* eopicture */
201 return;
203 case 2: /* delta */
204 x += *bits++;
205 lines -= *bits++;
206 continue;
208 default: /* absolute */
209 while (length--) {
210 c = *bits++;
211 XPutPixel(bmpImage, x++, lines, colors[c >> 4]);
212 check_xy(x, y);
213 if (length) {
214 length--;
215 XPutPixel(bmpImage, x++, lines, colors[c & 0xf]);
216 check_xy(x, y);
219 if ((bits - begin) & 1)
220 bits++;
226 /***********************************************************************
227 * DIB_SetImageBits_8
229 * SetDIBits for an 8-bit deep DIB.
231 static void DIB_SetImageBits_8( WORD lines, BYTE *bits, WORD width,
232 int *colors, XImage *bmpImage )
234 WORD x;
235 BYTE pad = (4 - (width & 3)) & 3;
237 while (lines--)
239 for (x = 0; x < width; x++)
240 XPutPixel( bmpImage, x, lines, colors[*bits++] );
241 bits += pad;
245 /***********************************************************************
246 * DIB_SetImageBits_RLE8
248 * SetDIBits for an 8-bit deep compressed DIB.
250 * This function rewritten 941113 by James Youngman. WINE blew out when I
251 * first ran it because my desktop wallpaper is a (large) RLE8 bitmap.
253 * This was because the algorithm assumed that all RLE8 bitmaps end with the
254 * 'End of bitmap' escape code. This code is very much laxer in what it
255 * allows to end the expansion. Possibly too lax. See the note by
256 * case RleDelta. BTW, MS's documentation implies that a correct RLE8
257 * bitmap should end with RleEnd, but on the other hand, software exists
258 * that produces ones that don't and Windows 3.1 doesn't complain a bit
259 * about it.
261 * (No) apologies for my English spelling. [Emacs users: c-indent-level=4].
262 * James A. Youngman <mbcstjy@afs.man.ac.uk>
263 * [JAY]
266 enum Rle8_EscapeCodes
269 * Apologies for polluting your file's namespace...
271 RleEol = 0, /* End of line */
272 RleEnd = 1, /* End of bitmap */
273 RleDelta = 2 /* Delta */
276 static void DIB_SetImageBits_RLE8(WORD lines,
277 BYTE *bits,
278 WORD width,
279 int *colors,
280 XImage *bmpImage)
282 int x; /* X-positon on each line. Increases. */
283 int line; /* Line #. Starts at lines-1, decreases */
284 BYTE *pIn = bits; /* Pointer to current position in bits */
285 BYTE length; /* The length pf a run */
286 BYTE color_index; /* index into colors[] as read from bits */
287 BYTE escape_code; /* See enum Rle8_EscapeCodes.*/
288 WORD color; /* value of colour[color_index] */
290 if (lines == 0) /* Let's hope this doesn't happen. */
291 return;
294 * Note that the bitmap data is stored by Windows starting at the
295 * bottom line of the bitmap and going upwards. Within each line,
296 * the data is stored left-to-right. That's the reason why line
297 * goes from lines-1 to 0. [JAY]
300 x = 0;
301 line = lines-1;
304 length = *pIn++;
307 * If the length byte is not zero (which is the escape value),
308 * We have a run of length pixels all the same colour. The colour
309 * index is stored next.
311 * If the length byte is zero, we need to read the next byte to
312 * know what to do. [JAY]
314 if (length != 0)
317 * [Run-Length] Encoded mode
319 color_index = (*pIn++); /* Get the colour index. */
320 color = colors[color_index];
322 while(length--)
323 XPutPixel(bmpImage, x++, line, color);
325 else
328 * Escape codes (may be an absolute sequence though)
330 escape_code = (*pIn++);
331 switch(escape_code)
333 case RleEol: /* =0, end of line */
335 x = 0;
336 line--;
337 break;
340 case RleEnd: /* =1, end of bitmap */
343 * Not all RLE8 bitmaps end with this
344 * code. For example, Paint Shop Pro
345 * produces some that don't. That's (I think)
346 * what caused the previous implementation to
347 * fail. [JAY]
349 line=0; /* Cause exit from do loop. */
350 break;
353 case RleDelta: /* =2, a delta */
356 * Note that deltaing to line 0
357 * will cause an exit from the loop,
358 * which may not be what is intended.
359 * The fact that there is a delta in the bits
360 * almost certainly implies that there is data
361 * to follow. You may feel that we should
362 * jump to the top of the loop to avoid exiting
363 * in this case.
365 * TODO: Decide what to do here in that case. [JAY]
367 x += (*pIn++);
368 line -= (*pIn++);
369 if (line == 0)
371 dprintf_bitmap(stddeb,
372 "DIB_SetImageBits_RLE8(): "
373 "Delta to last line of bitmap "
374 "(wrongly?) causes loop exit\n");
376 break;
379 default: /* >2, switch to absolute mode */
382 * Absolute Mode
384 length = escape_code;
385 while(length--)
387 color_index = (*pIn++);
388 XPutPixel(bmpImage, x++, line,
389 colors[color_index]);
393 * If you think for a moment you'll realise that the
394 * only time we could ever possibly read an odd
395 * number of bytes is when there is a 0x00 (escape),
396 * a value >0x02 (absolute mode) and then an odd-
397 * length run. Therefore this is the only place we
398 * need to worry about it. Everywhere else the
399 * bytes are always read in pairs. [JAY]
401 if (escape_code & 1)
402 pIn++; /* Throw away the pad byte. */
403 break;
405 } /* switch (escape_code) : Escape sequence */
406 } /* process either an encoded sequence or an escape sequence */
408 /* We expect to come here more than once per line. */
409 } while (line > 0); /* Do this until the bitmap is filled */
412 * Everybody comes here at the end.
413 * Check how we exited the loop and print a message if it's a bit odd.
414 * [JAY]
416 if ( (*(pIn-2) != 0/*escape*/) || (*(pIn-1)!= RleEnd) )
418 dprintf_bitmap(stddeb, "DIB_SetImageBits_RLE8(): End-of-bitmap "
419 "without (strictly) proper escape code. Last two "
420 "bytes were: %02X %02X.\n",
421 (int)*(pIn-2),
422 (int)*(pIn-1));
427 /***********************************************************************
428 * DIB_SetImageBits_24
430 * SetDIBits for a 24-bit deep DIB.
432 static void DIB_SetImageBits_24( WORD lines, BYTE *bits, WORD width,
433 DC *dc, XImage *bmpImage )
435 WORD x;
436 BYTE pad = (4 - ((width*3) & 3)) & 3;
438 while (lines--)
440 for (x = 0; x < width; x++, bits += 3)
442 XPutPixel( bmpImage, x, lines,
443 COLOR_ToPhysical( dc, RGB(bits[0],bits[1],bits[2]) ));
445 bits += pad;
450 /***********************************************************************
451 * DIB_SetImageBits
453 * Transfer the bits to an X image.
454 * Helper function for SetDIBits() and SetDIBitsToDevice().
456 static int DIB_SetImageBits( DC *dc, WORD lines, WORD depth, LPSTR bits,
457 BITMAPINFO *info, WORD coloruse,
458 Drawable drawable, GC gc, int xSrc, int ySrc,
459 int xDest, int yDest, int width, int height )
461 int *colorMapping;
462 XImage *bmpImage;
463 int i, colors;
465 /* Build the color mapping table */
467 if (info->bmiHeader.biBitCount == 24) colorMapping = NULL;
468 else
470 colors = info->bmiHeader.biClrUsed;
471 if (!colors) colors = 1 << info->bmiHeader.biBitCount;
472 if (!(colorMapping = (int *)malloc( colors * sizeof(int) )))
473 return 0;
474 if (coloruse == DIB_RGB_COLORS)
476 RGBQUAD * rgbPtr = info->bmiColors;
477 for (i = 0; i < colors; i++, rgbPtr++)
478 colorMapping[i] = COLOR_ToPhysical( dc, RGB(rgbPtr->rgbRed,
479 rgbPtr->rgbGreen,
480 rgbPtr->rgbBlue) );
482 else
484 WORD * index = (WORD *)info->bmiColors;
485 for (i = 0; i < colors; i++, index++)
486 colorMapping[i] = COLOR_ToPhysical( dc, PALETTEINDEX(*index) );
490 /* Transfer the pixels */
491 XCREATEIMAGE(bmpImage, info->bmiHeader.biWidth, lines, depth );
493 switch(info->bmiHeader.biBitCount)
495 case 1:
496 DIB_SetImageBits_1( lines, bits, info->bmiHeader.biWidth,
497 colorMapping, bmpImage );
498 break;
499 case 4:
500 if (info->bmiHeader.biCompression)
501 DIB_SetImageBits_RLE4( lines, bits, info->bmiHeader.biWidth,
502 colorMapping, bmpImage );
503 else
504 DIB_SetImageBits_4( lines, bits, info->bmiHeader.biWidth,
505 colorMapping, bmpImage );
506 break;
507 case 8:
508 if (info->bmiHeader.biCompression)
509 DIB_SetImageBits_RLE8( lines, bits, info->bmiHeader.biWidth,
510 colorMapping, bmpImage );
511 else
512 DIB_SetImageBits_8( lines, bits, info->bmiHeader.biWidth,
513 colorMapping, bmpImage );
514 break;
515 case 24:
516 DIB_SetImageBits_24( lines, bits, info->bmiHeader.biWidth,
517 dc, bmpImage );
518 break;
520 if (colorMapping) free(colorMapping);
521 XPutImage( display, drawable, gc, bmpImage, xSrc, ySrc,
522 xDest, yDest, width, height );
523 XDestroyImage( bmpImage );
524 return lines;
528 /***********************************************************************
529 * StretchDIBits (GDI.439)
531 int StretchDIBits( HDC hdc,
532 WORD xDest, WORD yDest, WORD wDestWidth, WORD wDestHeight,
533 WORD xSrc, WORD ySrc, WORD wSrcWidth, WORD wSrcHeight,
534 LPSTR bits, LPBITMAPINFO info, WORD wUsage, DWORD dwRop )
536 HBITMAP hBitmap, hOldBitmap;
537 HDC hdcMem;
539 hBitmap = CreateDIBitmap( hdc, &info->bmiHeader, CBM_INIT,
540 bits, info, wUsage );
541 hdcMem = CreateCompatibleDC( hdc );
542 hOldBitmap = SelectObject( hdcMem, hBitmap );
543 StretchBlt( hdc, xDest, yDest, wDestWidth, wDestHeight,
544 hdcMem, xSrc, ySrc, wSrcWidth, wSrcHeight, dwRop );
545 SelectObject( hdcMem, hOldBitmap );
546 DeleteDC( hdcMem );
547 DeleteObject( hBitmap );
548 return wSrcHeight;
551 /***********************************************************************
552 * SetDIBits (GDI.440)
554 int SetDIBits( HDC hdc, HBITMAP hbitmap, WORD startscan, WORD lines,
555 LPSTR bits, BITMAPINFO * info, WORD coloruse )
557 DC * dc;
558 BITMAPOBJ * bmp;
560 /* Check parameters */
562 if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
563 if (!(bmp = (BITMAPOBJ *)GDI_GetObjPtr( hbitmap, BITMAP_MAGIC )))
564 return 0;
565 if (!lines || (startscan >= (WORD)info->bmiHeader.biHeight)) return 0;
566 if (startscan+lines > info->bmiHeader.biHeight)
567 lines = info->bmiHeader.biHeight - startscan;
569 return CallTo32_LargeStack( (int(*)())DIB_SetImageBits, 14,
570 dc, lines, bmp->bitmap.bmBitsPixel,
571 bits, info, coloruse, bmp->pixmap,
572 BITMAP_GC(bmp), 0, 0, 0, startscan,
573 bmp->bitmap.bmWidth, lines );
577 /***********************************************************************
578 * SetDIBitsToDevice (GDI.443)
580 int SetDIBitsToDevice( HDC hdc, short xDest, short yDest, WORD cx, WORD cy,
581 WORD xSrc, WORD ySrc, WORD startscan, WORD lines,
582 LPSTR bits, BITMAPINFO * info, WORD coloruse )
584 DC * dc;
586 /* Check parameters */
588 if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
589 if (!lines || (startscan >= info->bmiHeader.biHeight)) return 0;
590 if (startscan+lines > info->bmiHeader.biHeight)
591 lines = info->bmiHeader.biHeight - startscan;
592 if (ySrc < startscan) ySrc = startscan;
593 else if (ySrc >= startscan+lines) return 0;
594 if (xSrc >= info->bmiHeader.biWidth) return 0;
595 if (ySrc+cy >= startscan+lines) cy = startscan + lines - ySrc;
596 if (xSrc+cx >= info->bmiHeader.biWidth) cx = info->bmiHeader.biWidth-xSrc;
597 if (!cx || !cy) return 0;
599 DC_SetupGCForText( dc ); /* To have the correct colors */
600 XSetFunction( display, dc->u.x.gc, DC_XROPfunction[dc->w.ROPmode-1] );
601 return CallTo32_LargeStack( (int(*)())DIB_SetImageBits, 14,
602 dc, lines, dc->w.bitsPerPixel, bits, info,
603 coloruse, dc->u.x.drawable, dc->u.x.gc,
604 xSrc, ySrc - startscan,
605 dc->w.DCOrgX + XLPTODP( dc, xDest ),
606 dc->w.DCOrgY + YLPTODP( dc, yDest ),
607 cx, cy );
612 /***********************************************************************
613 * GetDIBits (GDI.441)
615 int GetDIBits( HDC hdc, HBITMAP hbitmap, WORD startscan, WORD lines,
616 LPSTR bits, BITMAPINFO * info, WORD coloruse )
618 DC * dc;
619 BITMAPOBJ * bmp;
620 PALETTEENTRY * palEntry;
621 PALETTEOBJ * palette;
622 XImage * bmpImage, * dibImage;
623 int i, x, y;
625 if (!lines) return 0;
626 if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
627 if (!(bmp = (BITMAPOBJ *)GDI_GetObjPtr( hbitmap, BITMAP_MAGIC )))
628 return 0;
629 if (!(palette = (PALETTEOBJ*)GDI_GetObjPtr( dc->w.hPalette, PALETTE_MAGIC )))
630 return 0;
632 /* Transfer color info */
634 palEntry = palette->logpalette.palPalEntry;
635 for (i = 0; i < info->bmiHeader.biClrUsed; i++, palEntry++)
637 if (coloruse == DIB_RGB_COLORS)
639 info->bmiColors[i].rgbRed = palEntry->peRed;
640 info->bmiColors[i].rgbGreen = palEntry->peGreen;
641 info->bmiColors[i].rgbBlue = palEntry->peBlue;
642 info->bmiColors[i].rgbReserved = 0;
644 else ((WORD *)info->bmiColors)[i] = (WORD)i;
647 /* Transfer the pixels (very slow...) */
649 if (bits)
651 bmpImage = (XImage *)CallTo32_LargeStack( (int (*)())XGetImage, 8,
652 display, bmp->pixmap, 0, 0, bmp->bitmap.bmWidth,
653 bmp->bitmap.bmHeight, AllPlanes, ZPixmap );
654 dibImage = DIB_DIBmpToImage( &info->bmiHeader, bits );
656 for (y = 0; y < lines; y++)
658 for (x = 0; x < info->bmiHeader.biWidth; x++)
660 XPutPixel( dibImage, x, y,
661 XGetPixel(bmpImage, x, bmp->bitmap.bmHeight-startscan-y-1) );
666 dibImage->data = NULL;
667 XDestroyImage( dibImage );
668 XDestroyImage( bmpImage );
670 info->bmiHeader.biCompression = 0;
671 return lines;
675 /***********************************************************************
676 * CreateDIBitmap (GDI.442)
678 HBITMAP CreateDIBitmap( HDC hdc, BITMAPINFOHEADER * header, DWORD init,
679 LPSTR bits, BITMAPINFO * data, WORD coloruse )
681 HBITMAP handle;
683 handle = CreateCompatibleBitmap( hdc, header->biWidth, header->biHeight );
684 /* handle = CreateBitmap( header->biWidth, header->biHeight,
685 1, header->biBitCount, NULL );
687 if (!handle) return 0;
688 if (init == CBM_INIT) SetDIBits( hdc, handle, 0, header->biHeight,
689 bits, data, coloruse );
690 return handle;
693 /***********************************************************************
694 * DrawIcon (USER.84)
696 BOOL DrawIcon(HDC hDC, short x, short y, HICON hIcon)
698 ICONALLOC *lpico;
699 BITMAP bm;
700 HBITMAP hBitTemp;
701 HDC hMemDC;
702 COLORREF oldFg, oldBg;
704 oldFg = SetTextColor( hDC, RGB(0,0,0) );
705 oldBg = SetBkColor( hDC, RGB(255,255,255) );
706 dprintf_icon(stddeb,"DrawIcon(%04X, %d, %d, %04X) \n", hDC, x, y, hIcon);
707 if (hIcon == (HICON)NULL) return FALSE;
708 lpico = (ICONALLOC *)GlobalLock(hIcon);
709 GetObject(lpico->hBitmap, sizeof(BITMAP), (LPSTR)&bm);
710 hMemDC = CreateCompatibleDC(hDC);
711 if (lpico->hBitMask)
713 hBitTemp = SelectObject(hMemDC, lpico->hBitMask);
714 BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCAND);
715 SelectObject(hMemDC, lpico->hBitmap);
716 BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCINVERT);
718 else /* no mask -> everything is masked; so use SRCCOPY as it's faster */
720 hBitTemp = SelectObject(hMemDC, lpico->hBitmap);
721 BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);
723 SelectObject( hMemDC, hBitTemp );
724 DeleteDC(hMemDC);
725 GlobalUnlock( hIcon );
726 SetTextColor( hDC, oldFg );
727 SetBkColor( hDC, oldBg );
728 return TRUE;