- Fix errors during optimized build.
[reactos.git] / reactos / subsys / win32k / objects / bitmaps.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: bitmaps.c,v 1.77 2004/07/03 17:40:27 navaraf Exp $ */
20 #include <w32k.h>
21
22 #define IN_RECT(r,x,y) \
23 ( \
24 (x) >= (r).left && \
25 (y) >= (r).top && \
26 (x) < (r).right && \
27 (y) < (r).bottom \
28 )
29
30 BOOL STDCALL
31 NtGdiBitBlt(
32 HDC hDCDest,
33 INT XDest,
34 INT YDest,
35 INT Width,
36 INT Height,
37 HDC hDCSrc,
38 INT XSrc,
39 INT YSrc,
40 DWORD ROP)
41 {
42 PDC DCDest = NULL;
43 PDC DCSrc = NULL;
44 BITMAPOBJ *BitmapDest, *BitmapSrc;
45 RECTL DestRect;
46 POINTL SourcePoint, BrushOrigin;
47 BOOL Status;
48 PPALGDI PalDestGDI, PalSourceGDI;
49 XLATEOBJ *XlateObj = NULL;
50 HPALETTE SourcePalette = 0, DestPalette = 0;
51 ULONG SourceMode, DestMode;
52 PGDIBRUSHOBJ BrushObj;
53 BOOL UsesSource = ((ROP & 0xCC0000) >> 2) != (ROP & 0x330000);
54 BOOL UsesPattern = TRUE;//((ROP & 0xF00000) >> 4) != (ROP & 0x0F0000);
55 HPALETTE Mono = NULL;
56
57 DCDest = DC_LockDc(hDCDest);
58 if (NULL == DCDest)
59 {
60 DPRINT1("Invalid destination dc handle (0x%08x) passed to NtGdiBitBlt\n", hDCDest);
61 SetLastWin32Error(ERROR_INVALID_HANDLE);
62 return FALSE;
63 }
64
65 if (UsesSource)
66 {
67 if (hDCSrc != hDCDest)
68 {
69 DCSrc = DC_LockDc(hDCSrc);
70 if (NULL == DCSrc)
71 {
72 DC_UnlockDc(hDCDest);
73 DPRINT1("Invalid source dc handle (0x%08x) passed to NtGdiBitBlt\n", hDCSrc);
74 SetLastWin32Error(ERROR_INVALID_HANDLE);
75 return FALSE;
76 }
77 }
78 else
79 {
80 DCSrc = DCDest;
81 }
82 }
83 else
84 {
85 DCSrc = NULL;
86 }
87
88 /* Offset the destination and source by the origin of their DCs. */
89 XDest += DCDest->w.DCOrgX;
90 YDest += DCDest->w.DCOrgY;
91 if (UsesSource)
92 {
93 XSrc += DCSrc->w.DCOrgX;
94 YSrc += DCSrc->w.DCOrgY;
95 }
96
97 DestRect.left = XDest;
98 DestRect.top = YDest;
99 DestRect.right = XDest+Width;
100 DestRect.bottom = YDest+Height;
101
102 SourcePoint.x = XSrc;
103 SourcePoint.y = YSrc;
104
105 BrushOrigin.x = 0;
106 BrushOrigin.y = 0;
107
108 /* Determine surfaces to be used in the bitblt */
109 BitmapDest = BITMAPOBJ_LockBitmap(DCDest->w.hBitmap);
110 if (UsesSource)
111 {
112 if (DCSrc->w.hBitmap == DCDest->w.hBitmap)
113 BitmapSrc = BitmapDest;
114 else
115 BitmapSrc = BITMAPOBJ_LockBitmap(DCSrc->w.hBitmap);
116 }
117 else
118 {
119 BitmapSrc = NULL;
120 }
121
122 if (UsesPattern)
123 {
124 BrushObj = BRUSHOBJ_LockBrush(DCDest->w.hBrush);
125 if (NULL == BrushObj)
126 {
127 if (UsesSource && hDCSrc != hDCDest)
128 {
129 DC_UnlockDc(hDCSrc);
130 }
131 DC_UnlockDc(hDCDest);
132 SetLastWin32Error(ERROR_INVALID_HANDLE);
133 return FALSE;
134 }
135 BrushOrigin = BrushObj->ptOrigin;
136 }
137 else
138 {
139 BrushObj = NULL;
140 }
141
142 if (DCDest->w.hPalette != 0)
143 DestPalette = DCDest->w.hPalette;
144
145 if (UsesSource && DCSrc->w.hPalette != 0)
146 SourcePalette = DCSrc->w.hPalette;
147
148 PalSourceGDI = PALETTE_LockPalette(SourcePalette);
149 if (NULL == PalSourceGDI)
150 {
151 if (UsesSource && hDCSrc != hDCDest)
152 {
153 DC_UnlockDc(hDCSrc);
154 }
155 DC_UnlockDc(hDCDest);
156 SetLastWin32Error(ERROR_INVALID_HANDLE);
157 return FALSE;
158 }
159 SourceMode = PalSourceGDI->Mode;
160 PALETTE_UnlockPalette(SourcePalette);
161
162 if (DestPalette == SourcePalette)
163 {
164 DestMode = SourceMode;
165 }
166 else
167 {
168 PalDestGDI = PALETTE_LockPalette(DestPalette);
169 if (NULL == PalDestGDI)
170 {
171 if (UsesSource && hDCSrc != hDCDest)
172 {
173 DC_UnlockDc(hDCSrc);
174 }
175 DC_UnlockDc(hDCDest);
176 SetLastWin32Error(ERROR_INVALID_HANDLE);
177 return FALSE;
178 }
179 DestMode = PalDestGDI->Mode;
180 PALETTE_UnlockPalette(DestPalette);
181 }
182
183 /* KB41464 details how to convert between mono and color */
184 if (DCDest->w.bitsPerPixel == 1)
185 {
186 XlateObj = (XLATEOBJ*)IntEngCreateMonoXlate(SourceMode, DestPalette,
187 SourcePalette, DCSrc->w.backgroundColor);
188 }
189 else if (UsesSource && 1 == DCSrc->w.bitsPerPixel)
190 {
191 ULONG Colors[2];
192
193 Colors[0] = DCSrc->w.textColor;
194 Colors[1] = DCSrc->w.backgroundColor;
195 Mono = PALETTE_AllocPaletteIndexedRGB(2, (RGBQUAD*)Colors);
196 if (NULL != Mono)
197 {
198 XlateObj = (XLATEOBJ*)IntEngCreateXlate(DestMode, PAL_INDEXED, DestPalette, Mono);
199 }
200 else
201 {
202 XlateObj = NULL;
203 }
204 }
205 else
206 {
207 XlateObj = (XLATEOBJ*)IntEngCreateXlate(DestMode, SourceMode, DestPalette, SourcePalette);
208 }
209 if (NULL == XlateObj)
210 {
211 if (NULL != Mono)
212 {
213 EngDeletePalette(Mono);
214 }
215 if (UsesSource && hDCSrc != hDCDest)
216 {
217 DC_UnlockDc(hDCSrc);
218 }
219 DC_UnlockDc(hDCDest);
220 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
221 return FALSE;
222 }
223
224 /* Perform the bitblt operation */
225 Status = IntEngBitBlt(BitmapDest, BitmapSrc, NULL, DCDest->CombinedClip, XlateObj,
226 &DestRect, &SourcePoint, NULL, &BrushObj->BrushObject, &BrushOrigin, ROP);
227
228 EngDeleteXlate(XlateObj);
229 BITMAPOBJ_UnlockBitmap(DCDest->w.hBitmap);
230 if (UsesSource && DCSrc->w.hBitmap != DCDest->w.hBitmap)
231 {
232 BITMAPOBJ_UnlockBitmap(DCSrc->w.hBitmap);
233 }
234 if (NULL != Mono)
235 {
236 EngDeletePalette(Mono);
237 }
238 if (UsesPattern)
239 {
240 BRUSHOBJ_UnlockBrush(DCDest->w.hBrush);
241 }
242 if (UsesSource && hDCSrc != hDCDest)
243 {
244 DC_UnlockDc(hDCSrc);
245 }
246 DC_UnlockDc(hDCDest);
247
248 return Status;
249 }
250
251 BOOL STDCALL
252 NtGdiTransparentBlt(
253 HDC hdcDst,
254 INT xDst,
255 INT yDst,
256 INT cxDst,
257 INT cyDst,
258 HDC hdcSrc,
259 INT xSrc,
260 INT ySrc,
261 INT cxSrc,
262 INT cySrc,
263 COLORREF TransColor)
264 {
265 PDC DCDest, DCSrc;
266 RECTL rcDest, rcSrc;
267 BITMAPOBJ *BitmapDest, *BitmapSrc;
268 XLATEOBJ *XlateObj;
269 HPALETTE SourcePalette = 0, DestPalette = 0;
270 PPALGDI PalDestGDI, PalSourceGDI;
271 USHORT PalDestMode, PalSrcMode;
272 ULONG TransparentColor = 0;
273 BOOL Ret = FALSE;
274
275 if(!(DCDest = DC_LockDc(hdcDst)))
276 {
277 DPRINT1("Invalid destination dc handle (0x%08x) passed to NtGdiTransparentBlt\n", hdcDst);
278 SetLastWin32Error(ERROR_INVALID_HANDLE);
279 return FALSE;
280 }
281
282 if((hdcDst != hdcSrc) && !(DCSrc = DC_LockDc(hdcSrc)))
283 {
284 DC_UnlockDc(hdcDst);
285 DPRINT1("Invalid source dc handle (0x%08x) passed to NtGdiTransparentBlt\n", hdcSrc);
286 SetLastWin32Error(ERROR_INVALID_HANDLE);
287 return FALSE;
288 }
289 if(hdcDst == hdcSrc)
290 {
291 DCSrc = DCDest;
292 }
293
294 /* Offset positions */
295 xDst += DCDest->w.DCOrgX;
296 yDst += DCDest->w.DCOrgY;
297 xSrc += DCSrc->w.DCOrgX;
298 ySrc += DCSrc->w.DCOrgY;
299
300 if(DCDest->w.hPalette)
301 DestPalette = DCDest->w.hPalette;
302
303 if(DCSrc->w.hPalette)
304 SourcePalette = DCSrc->w.hPalette;
305
306 if(!(PalSourceGDI = PALETTE_LockPalette(SourcePalette)))
307 {
308 DC_UnlockDc(hdcSrc);
309 DC_UnlockDc(hdcDst);
310 SetLastWin32Error(ERROR_INVALID_HANDLE);
311 return FALSE;
312 }
313 if((DestPalette != SourcePalette) && !(PalDestGDI = PALETTE_LockPalette(DestPalette)))
314 {
315 PALETTE_UnlockPalette(SourcePalette);
316 DC_UnlockDc(hdcSrc);
317 DC_UnlockDc(hdcDst);
318 SetLastWin32Error(ERROR_INVALID_HANDLE);
319 return FALSE;
320 }
321 if(DestPalette != SourcePalette)
322 {
323 PalDestMode = PalDestGDI->Mode;
324 PalSrcMode = PalSourceGDI->Mode;
325 PALETTE_UnlockPalette(DestPalette);
326 }
327 else
328 {
329 PalDestMode = PalSrcMode = PalSourceGDI->Mode;
330 }
331 PALETTE_UnlockPalette(SourcePalette);
332
333 /* Translate Transparent (RGB) Color to the source palette */
334 if((XlateObj = (XLATEOBJ*)IntEngCreateXlate(PalSrcMode, PAL_RGB, SourcePalette, NULL)))
335 {
336 TransparentColor = XLATEOBJ_iXlate(XlateObj, (ULONG)TransColor);
337 EngDeleteXlate(XlateObj);
338 }
339
340 /* Create the XLATE object to convert colors between source and destination */
341 XlateObj = (XLATEOBJ*)IntEngCreateXlate(PalDestMode, PalSrcMode, DestPalette, SourcePalette);
342
343 BitmapDest = BITMAPOBJ_LockBitmap(DCDest->w.hBitmap);
344 ASSERT(BitmapDest);
345 BitmapSrc = BITMAPOBJ_LockBitmap(DCSrc->w.hBitmap);
346 ASSERT(BitmapSrc);
347
348 rcDest.left = xDst;
349 rcDest.top = yDst;
350 rcDest.right = rcDest.left + cxDst;
351 rcDest.bottom = rcDest.top + cyDst;
352 rcSrc.left = xSrc;
353 rcSrc.top = ySrc;
354 rcSrc.right = rcSrc.left + cxSrc;
355 rcSrc.bottom = rcSrc.top + cySrc;
356
357 if((cxDst != cxSrc) || (cyDst != cySrc))
358 {
359 DPRINT1("TransparentBlt() does not support stretching at the moment!\n");
360 goto done;
361 }
362
363 Ret = IntEngTransparentBlt(BitmapDest, BitmapSrc, DCDest->CombinedClip, XlateObj, &rcDest, &rcSrc,
364 TransparentColor, 0);
365
366 done:
367 BITMAPOBJ_UnlockBitmap(DCDest->w.hBitmap);
368 BITMAPOBJ_UnlockBitmap(DCSrc->w.hBitmap);
369 DC_UnlockDc(hdcSrc);
370 if(hdcDst != hdcSrc)
371 {
372 DC_UnlockDc(hdcDst);
373 }
374 if(XlateObj)
375 {
376 EngDeleteXlate(XlateObj);
377 }
378 return Ret;
379 }
380
381 HBITMAP STDCALL
382 NtGdiCreateBitmap(
383 INT Width,
384 INT Height,
385 UINT Planes,
386 UINT BitsPerPel,
387 CONST VOID *Bits)
388 {
389 PBITMAPOBJ bmp;
390 HBITMAP hBitmap;
391 SIZEL Size;
392
393 /* NOTE: Windows also doesn't store nr. of planes separately! */
394 BitsPerPel = (BYTE)BitsPerPel * (BYTE)Planes;
395
396 /* Check parameters */
397 if (!Height || !Width)
398 {
399 Size.cx = Size.cy = 1;
400 }
401 else
402 {
403 Size.cx = Width;
404 Size.cy = abs(Height);
405 }
406
407 /* Create the bitmap object. */
408 hBitmap = IntCreateBitmap(Size, BITMAPOBJ_GetWidthBytes(Width, BitsPerPel),
409 BitmapFormat(BitsPerPel, BI_RGB),
410 (Height > 0 ? 0 : BMF_TOPDOWN) |
411 (Bits == NULL ? 0 : BMF_NOZEROINIT), NULL);
412 if (!hBitmap)
413 {
414 DPRINT("NtGdiCreateBitmap: IntCreateBitmap returned 0\n");
415 return 0;
416 }
417
418 DPRINT("NtGdiCreateBitmap:%dx%d, %d BPP colors returning %08x\n",
419 Size.cx, Size.cy, BitsPerPel, hBitmap);
420
421 bmp = BITMAPOBJ_LockBitmap( hBitmap );
422 bmp->flFlags = BITMAPOBJ_IS_APIBITMAP;
423 BITMAPOBJ_UnlockBitmap( hBitmap );
424
425 /*
426 * NOTE: It's ugly practice, but we are using the object even
427 * after unlocking. Since the handle is currently known only
428 * to us it should be safe.
429 */
430
431 if (Bits != NULL)
432 {
433 NtGdiSetBitmapBits(hBitmap, bmp->SurfObj.cjBits, Bits);
434 }
435
436 return hBitmap;
437 }
438
439 BOOL FASTCALL
440 Bitmap_InternalDelete( PBITMAPOBJ pBmp )
441 {
442 ASSERT( pBmp );
443
444 if (pBmp->SurfObj.pvBits != NULL &&
445 (pBmp->flFlags & BITMAPOBJ_IS_APIBITMAP))
446 {
447 if (pBmp->dib == NULL)
448 {
449 ExFreePool(pBmp->SurfObj.pvBits);
450 }
451 else
452 {
453 EngFreeUserMem(pBmp->SurfObj.pvBits);
454 }
455 }
456
457 return TRUE;
458 }
459
460
461 HBITMAP FASTCALL
462 IntCreateCompatibleBitmap(
463 PDC Dc,
464 INT Width,
465 INT Height)
466 {
467 HBITMAP Bmp;
468
469 Bmp = NULL;
470
471 if ((Width >= 0x10000) || (Height >= 0x10000))
472 {
473 DPRINT1("got bad width %d or height %d, please look for reason\n", Width, Height);
474 return NULL;
475 }
476
477 /* MS doc says if width or height is 0, return 1-by-1 pixel, monochrome bitmap */
478 if (0 == Width || 0 == Height)
479 {
480 Bmp = NtGdiCreateBitmap (1, 1, 1, 1, NULL);
481 }
482 else
483 {
484 Bmp = NtGdiCreateBitmap(Width, Height, 1, Dc->w.bitsPerPixel, NULL);
485 }
486
487 return Bmp;
488 }
489
490 HBITMAP STDCALL
491 NtGdiCreateCompatibleBitmap(
492 HDC hDC,
493 INT Width,
494 INT Height)
495 {
496 HBITMAP Bmp;
497 PDC Dc;
498
499 Dc = DC_LockDc(hDC);
500
501 DPRINT("NtGdiCreateCompatibleBitmap(%04x,%d,%d, bpp:%d) = \n", hDC, Width, Height, Dc->w.bitsPerPixel);
502
503 if (NULL == Dc)
504 {
505 SetLastWin32Error(ERROR_INVALID_HANDLE);
506 return NULL;
507 }
508
509 Bmp = IntCreateCompatibleBitmap(Dc, Width, Height);
510
511 DPRINT ("\t\t%04x\n", Bmp);
512 DC_UnlockDc(hDC);
513 return Bmp;
514 }
515
516 HBITMAP STDCALL
517 NtGdiCreateBitmapIndirect(CONST BITMAP *BM)
518 {
519 return NtGdiCreateBitmap (BM->bmWidth,
520 BM->bmHeight,
521 BM->bmPlanes,
522 BM->bmBitsPixel,
523 BM->bmBits);
524 }
525
526 HBITMAP STDCALL
527 NtGdiCreateDiscardableBitmap(
528 HDC hDC,
529 INT Width,
530 INT Height)
531 {
532 /* FIXME: this probably should do something else */
533 return NtGdiCreateCompatibleBitmap(hDC, Width, Height);
534 }
535
536 BOOL STDCALL
537 NtGdiExtFloodFill(
538 HDC hDC,
539 INT XStart,
540 INT YStart,
541 COLORREF Color,
542 UINT FillType)
543 {
544 UNIMPLEMENTED;
545 }
546
547 BOOL STDCALL
548 NtGdiFloodFill(
549 HDC hDC,
550 INT XStart,
551 INT YStart,
552 COLORREF Fill)
553 {
554 return NtGdiExtFloodFill(hDC, XStart, YStart, Fill, FLOODFILLBORDER );
555 }
556
557 BOOL STDCALL
558 NtGdiGetBitmapDimensionEx(
559 HBITMAP hBitmap,
560 LPSIZE Dimension)
561 {
562 PBITMAPOBJ bmp;
563
564 bmp = BITMAPOBJ_LockBitmap(hBitmap);
565 if (bmp == NULL)
566 {
567 return FALSE;
568 }
569
570 *Dimension = bmp->dimension;
571
572 BITMAPOBJ_UnlockBitmap(hBitmap);
573
574 return TRUE;
575 }
576
577 COLORREF STDCALL
578 NtGdiGetPixel(HDC hDC, INT XPos, INT YPos)
579 {
580 PDC dc = NULL;
581 COLORREF Result = (COLORREF)CLR_INVALID; // default to failure
582 BOOL bInRect = FALSE;
583 BITMAPOBJ *BitmapObject;
584 SURFOBJ *SurfaceObject;
585 HPALETTE Pal = 0;
586 PPALGDI PalGDI;
587 USHORT PalMode;
588 XLATEOBJ *XlateObj;
589
590 dc = DC_LockDc (hDC);
591
592 if ( !dc )
593 {
594 SetLastWin32Error(ERROR_INVALID_HANDLE);
595 return Result;
596 }
597 if ( IN_RECT(dc->CombinedClip->rclBounds,XPos,YPos) )
598 {
599 bInRect = TRUE;
600 BitmapObject = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
601 SurfaceObject = &BitmapObject->SurfObj;
602 if ( BitmapObject )
603 {
604 if ( dc->w.hPalette != 0 )
605 Pal = dc->w.hPalette;
606 PalGDI = PALETTE_LockPalette(Pal);
607 if ( PalGDI )
608 {
609 PalMode = PalGDI->Mode;
610 PALETTE_UnlockPalette(Pal);
611
612 /* FIXME: Verify if it shouldn't be PAL_BGR! */
613 XlateObj = (XLATEOBJ*)IntEngCreateXlate ( PAL_RGB, PalMode, NULL, Pal );
614 if ( XlateObj )
615 {
616 // check if this DC has a DIB behind it...
617 if ( SurfaceObject->pvScan0 ) // STYPE_BITMAP == SurfaceObject->iType
618 {
619 ASSERT ( SurfaceObject->lDelta );
620 Result = XLATEOBJ_iXlate(XlateObj,
621 DibFunctionsForBitmapFormat[SurfaceObject->iBitmapFormat].DIB_GetPixel ( SurfaceObject, XPos, YPos ) );
622 }
623 EngDeleteXlate(XlateObj);
624 }
625 }
626 }
627 BITMAPOBJ_UnlockBitmap(dc->w.hBitmap);
628 }
629 DC_UnlockDc(hDC);
630
631 // if Result is still CLR_INVALID, then the "quick" method above didn't work
632 if ( bInRect && Result == CLR_INVALID )
633 {
634 // FIXME: create a 1x1 32BPP DIB, and blit to it
635 HDC hDCTmp = NtGdiCreateCompatableDC(hDC);
636 if ( hDCTmp )
637 {
638 static const BITMAPINFOHEADER bih = { sizeof(BITMAPINFOHEADER), 1, 1, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
639 BITMAPINFO bi;
640 RtlMoveMemory ( &(bi.bmiHeader), &bih, sizeof(bih) );
641 HBITMAP hBmpTmp = NtGdiCreateDIBitmap ( hDC, &bi.bmiHeader, 0, NULL, &bi, DIB_RGB_COLORS );
642 //HBITMAP hBmpTmp = NtGdiCreateBitmap ( 1, 1, 1, 32, NULL);
643 if ( hBmpTmp )
644 {
645 HBITMAP hBmpOld = (HBITMAP)NtGdiSelectObject ( hDCTmp, hBmpTmp );
646 if ( hBmpOld )
647 {
648 PBITMAPOBJ bmpobj;
649
650 NtGdiBitBlt ( hDCTmp, 0, 0, 1, 1, hDC, XPos, YPos, SRCCOPY );
651 NtGdiSelectObject ( hDCTmp, hBmpOld );
652
653 // our bitmap is no longer selected, so we can access it's stuff...
654 bmpobj = BITMAPOBJ_LockBitmap ( hBmpTmp );
655 if ( bmpobj )
656 {
657 Result = *(COLORREF*)bmpobj->SurfObj.pvScan0;
658 BITMAPOBJ_UnlockBitmap ( hBmpTmp );
659 }
660 }
661 NtGdiDeleteObject ( hBmpTmp );
662 }
663 NtGdiDeleteDC ( hDCTmp );
664 }
665 }
666
667 return Result;
668 }
669
670 /***********************************************************************
671 * MaskBlt
672 * Ported from WINE by sedwards 11-4-03
673 *
674 * Someone thought it would be faster to do it here and then switch back
675 * to GDI32. I dunno. Write a test and let me know.
676 */
677
678 static inline BYTE
679 SwapROP3_SrcDst(BYTE bRop3)
680 {
681 return (bRop3 & 0x99) | ((bRop3 & 0x22) << 1) | ((bRop3 & 0x44) >> 1);
682 }
683
684 #define FRGND_ROP3(ROP4) ((ROP4) & 0x00FFFFFF)
685 #define BKGND_ROP3(ROP4) (ROP3Table[(SwapROP3_SrcDst((ROP4)>>24)) & 0xFF])
686 #define DSTCOPY 0x00AA0029
687 #define DSTERASE 0x00220326 /* dest = dest & (~src) : DSna */
688
689 BOOL STDCALL
690 NtGdiMaskBlt (
691 HDC hdcDest, INT nXDest, INT nYDest,
692 INT nWidth, INT nHeight, HDC hdcSrc,
693 INT nXSrc, INT nYSrc, HBITMAP hbmMask,
694 INT xMask, INT yMask, DWORD dwRop)
695 {
696 HBITMAP hOldMaskBitmap, hBitmap2, hOldBitmap2, hBitmap3, hOldBitmap3;
697 HDC hDCMask, hDC1, hDC2;
698 static const DWORD ROP3Table[256] =
699 {
700 0x00000042, 0x00010289,
701 0x00020C89, 0x000300AA,
702 0x00040C88, 0x000500A9,
703 0x00060865, 0x000702C5,
704 0x00080F08, 0x00090245,
705 0x000A0329, 0x000B0B2A,
706 0x000C0324, 0x000D0B25,
707 0x000E08A5, 0x000F0001,
708 0x00100C85, 0x001100A6,
709 0x00120868, 0x001302C8,
710 0x00140869, 0x001502C9,
711 0x00165CCA, 0x00171D54,
712 0x00180D59, 0x00191CC8,
713 0x001A06C5, 0x001B0768,
714 0x001C06CA, 0x001D0766,
715 0x001E01A5, 0x001F0385,
716 0x00200F09, 0x00210248,
717 0x00220326, 0x00230B24,
718 0x00240D55, 0x00251CC5,
719 0x002606C8, 0x00271868,
720 0x00280369, 0x002916CA,
721 0x002A0CC9, 0x002B1D58,
722 0x002C0784, 0x002D060A,
723 0x002E064A, 0x002F0E2A,
724 0x0030032A, 0x00310B28,
725 0x00320688, 0x00330008,
726 0x003406C4, 0x00351864,
727 0x003601A8, 0x00370388,
728 0x0038078A, 0x00390604,
729 0x003A0644, 0x003B0E24,
730 0x003C004A, 0x003D18A4,
731 0x003E1B24, 0x003F00EA,
732 0x00400F0A, 0x00410249,
733 0x00420D5D, 0x00431CC4,
734 0x00440328, 0x00450B29,
735 0x004606C6, 0x0047076A,
736 0x00480368, 0x004916C5,
737 0x004A0789, 0x004B0605,
738 0x004C0CC8, 0x004D1954,
739 0x004E0645, 0x004F0E25,
740 0x00500325, 0x00510B26,
741 0x005206C9, 0x00530764,
742 0x005408A9, 0x00550009,
743 0x005601A9, 0x00570389,
744 0x00580785, 0x00590609,
745 0x005A0049, 0x005B18A9,
746 0x005C0649, 0x005D0E29,
747 0x005E1B29, 0x005F00E9,
748 0x00600365, 0x006116C6,
749 0x00620786, 0x00630608,
750 0x00640788, 0x00650606,
751 0x00660046, 0x006718A8,
752 0x006858A6, 0x00690145,
753 0x006A01E9, 0x006B178A,
754 0x006C01E8, 0x006D1785,
755 0x006E1E28, 0x006F0C65,
756 0x00700CC5, 0x00711D5C,
757 0x00720648, 0x00730E28,
758 0x00740646, 0x00750E26,
759 0x00761B28, 0x007700E6,
760 0x007801E5, 0x00791786,
761 0x007A1E29, 0x007B0C68,
762 0x007C1E24, 0x007D0C69,
763 0x007E0955, 0x007F03C9,
764 0x008003E9, 0x00810975,
765 0x00820C49, 0x00831E04,
766 0x00840C48, 0x00851E05,
767 0x008617A6, 0x008701C5,
768 0x008800C6, 0x00891B08,
769 0x008A0E06, 0x008B0666,
770 0x008C0E08, 0x008D0668,
771 0x008E1D7C, 0x008F0CE5,
772 0x00900C45, 0x00911E08,
773 0x009217A9, 0x009301C4,
774 0x009417AA, 0x009501C9,
775 0x00960169, 0x0097588A,
776 0x00981888, 0x00990066,
777 0x009A0709, 0x009B07A8,
778 0x009C0704, 0x009D07A6,
779 0x009E16E6, 0x009F0345,
780 0x00A000C9, 0x00A11B05,
781 0x00A20E09, 0x00A30669,
782 0x00A41885, 0x00A50065,
783 0x00A60706, 0x00A707A5,
784 0x00A803A9, 0x00A90189,
785 0x00AA0029, 0x00AB0889,
786 0x00AC0744, 0x00AD06E9,
787 0x00AE0B06, 0x00AF0229,
788 0x00B00E05, 0x00B10665,
789 0x00B21974, 0x00B30CE8,
790 0x00B4070A, 0x00B507A9,
791 0x00B616E9, 0x00B70348,
792 0x00B8074A, 0x00B906E6,
793 0x00BA0B09, 0x00BB0226,
794 0x00BC1CE4, 0x00BD0D7D,
795 0x00BE0269, 0x00BF08C9,
796 0x00C000CA, 0x00C11B04,
797 0x00C21884, 0x00C3006A,
798 0x00C40E04, 0x00C50664,
799 0x00C60708, 0x00C707AA,
800 0x00C803A8, 0x00C90184,
801 0x00CA0749, 0x00CB06E4,
802 0x00CC0020, 0x00CD0888,
803 0x00CE0B08, 0x00CF0224,
804 0x00D00E0A, 0x00D1066A,
805 0x00D20705, 0x00D307A4,
806 0x00D41D78, 0x00D50CE9,
807 0x00D616EA, 0x00D70349,
808 0x00D80745, 0x00D906E8,
809 0x00DA1CE9, 0x00DB0D75,
810 0x00DC0B04, 0x00DD0228,
811 0x00DE0268, 0x00DF08C8,
812 0x00E003A5, 0x00E10185,
813 0x00E20746, 0x00E306EA,
814 0x00E40748, 0x00E506E5,
815 0x00E61CE8, 0x00E70D79,
816 0x00E81D74, 0x00E95CE6,
817 0x00EA02E9, 0x00EB0849,
818 0x00EC02E8, 0x00ED0848,
819 0x00EE0086, 0x00EF0A08,
820 0x00F00021, 0x00F10885,
821 0x00F20B05, 0x00F3022A,
822 0x00F40B0A, 0x00F50225,
823 0x00F60265, 0x00F708C5,
824 0x00F802E5, 0x00F90845,
825 0x00FA0089, 0x00FB0A09,
826 0x00FC008A, 0x00FD0A0A,
827 0x00FE02A9, 0x00FF0062,
828 };
829
830 if (!hbmMask)
831 return NtGdiBitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, FRGND_ROP3(dwRop));
832
833 /* 1. make mask bitmap's dc */
834 hDCMask = NtGdiCreateCompatableDC(hdcDest);
835 hOldMaskBitmap = (HBITMAP)NtGdiSelectObject(hDCMask, hbmMask);
836
837 /* 2. make masked Background bitmap */
838
839 /* 2.1 make bitmap */
840 hDC1 = NtGdiCreateCompatableDC(hdcDest);
841 hBitmap2 = NtGdiCreateCompatibleBitmap(hdcDest, nWidth, nHeight);
842 hOldBitmap2 = (HBITMAP)NtGdiSelectObject(hDC1, hBitmap2);
843
844 /* 2.2 draw dest bitmap and mask */
845 NtGdiBitBlt(hDC1, 0, 0, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, SRCCOPY);
846 NtGdiBitBlt(hDC1, 0, 0, nWidth, nHeight, hdcDest, nXDest, nYDest, BKGND_ROP3(dwRop));
847 NtGdiBitBlt(hDC1, 0, 0, nWidth, nHeight, hDCMask, xMask, yMask, DSTERASE);
848
849 /* 3. make masked Foreground bitmap */
850
851 /* 3.1 make bitmap */
852 hDC2 = NtGdiCreateCompatableDC(hdcDest);
853 hBitmap3 = NtGdiCreateCompatibleBitmap(hdcDest, nWidth, nHeight);
854 hOldBitmap3 = (HBITMAP)NtGdiSelectObject(hDC2, hBitmap3);
855
856 /* 3.2 draw src bitmap and mask */
857 NtGdiBitBlt(hDC2, 0, 0, nWidth, nHeight, hdcDest, nXDest, nYDest, SRCCOPY);
858 NtGdiBitBlt(hDC2, 0, 0, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, FRGND_ROP3(dwRop));
859 NtGdiBitBlt(hDC2, 0, 0, nWidth, nHeight, hDCMask, xMask, yMask, SRCAND);
860
861 /* 4. combine two bitmap and copy it to hdcDest */
862 NtGdiBitBlt(hDC1, 0, 0, nWidth, nHeight, hDC2, 0, 0, SRCPAINT);
863 NtGdiBitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hDC1, 0, 0, SRCCOPY);
864
865 /* 5. restore all object */
866 NtGdiSelectObject(hDCMask, hOldMaskBitmap);
867 NtGdiSelectObject(hDC1, hOldBitmap2);
868 NtGdiSelectObject(hDC2, hOldBitmap3);
869
870 /* 6. delete all temp object */
871 NtGdiDeleteObject(hBitmap2);
872 NtGdiDeleteObject(hBitmap3);
873
874 NtGdiDeleteDC(hDC1);
875 NtGdiDeleteDC(hDC2);
876 NtGdiDeleteDC(hDCMask);
877
878 return TRUE;
879 }
880
881 BOOL STDCALL
882 NtGdiPlgBlt(
883 HDC hDCDest,
884 CONST POINT *Point,
885 HDC hDCSrc,
886 INT XSrc,
887 INT YSrc,
888 INT Width,
889 INT Height,
890 HBITMAP hMaskBitmap,
891 INT xMask,
892 INT yMask)
893 {
894 UNIMPLEMENTED;
895 }
896
897 LONG STDCALL
898 NtGdiSetBitmapBits(
899 HBITMAP hBitmap,
900 DWORD Bytes,
901 CONST VOID *Bits)
902 {
903 LONG height, ret;
904 PBITMAPOBJ bmp;
905
906 bmp = BITMAPOBJ_LockBitmap(hBitmap);
907 if (bmp == NULL || Bits == NULL)
908 {
909 return 0;
910 }
911
912 if (Bytes < 0)
913 {
914 DPRINT ("(%ld): Negative number of bytes passed???\n", Bytes );
915 Bytes = -Bytes;
916 }
917
918 /* Only get entire lines */
919 height = Bytes / abs(bmp->SurfObj.lDelta);
920 if (height > bmp->SurfObj.sizlBitmap.cx)
921 {
922 height = bmp->SurfObj.sizlBitmap.cx;
923 }
924 Bytes = height * abs(bmp->SurfObj.lDelta);
925 DPRINT ("(%08x, bytes:%ld, bits:%p) %dx%d %d colors fetched height: %ld\n",
926 hBitmap,
927 Bytes,
928 Bits,
929 bmp->SurfObj.sizlBitmap.cx,
930 bmp->SurfObj.sizlBitmap.cy,
931 1 << BitsPerFormat(bmp->SurfObj.iBitmapFormat),
932 height);
933
934 #if 0
935 /* FIXME: call DDI specific function here if available */
936 if(bmp->DDBitmap)
937 {
938 DPRINT ("Calling device specific BitmapBits\n");
939 if (bmp->DDBitmap->funcs->pBitmapBits)
940 {
941 ret = bmp->DDBitmap->funcs->pBitmapBits(hBitmap, (void *) Bits, Bytes, DDB_SET);
942 }
943 else
944 {
945 DPRINT ("BitmapBits == NULL??\n");
946 ret = 0;
947 }
948 }
949 else
950 #endif
951 {
952 memcpy(bmp->SurfObj.pvBits, Bits, Bytes);
953 ret = Bytes;
954 }
955
956 BITMAPOBJ_UnlockBitmap(hBitmap);
957
958 return ret;
959 }
960
961 BOOL STDCALL
962 NtGdiSetBitmapDimensionEx(
963 HBITMAP hBitmap,
964 INT Width,
965 INT Height,
966 LPSIZE Size)
967 {
968 PBITMAPOBJ bmp;
969
970 bmp = BITMAPOBJ_LockBitmap(hBitmap);
971 if (bmp == NULL)
972 {
973 return FALSE;
974 }
975
976 if (Size)
977 {
978 *Size = bmp->dimension;
979 }
980 bmp->dimension.cx = Width;
981 bmp->dimension.cy = Height;
982
983 BITMAPOBJ_UnlockBitmap (hBitmap);
984
985 return TRUE;
986 }
987
988 COLORREF STDCALL
989 NtGdiSetPixel(
990 HDC hDC,
991 INT X,
992 INT Y,
993 COLORREF Color)
994 {
995 COLORREF cr = NtGdiGetPixel(hDC,X,Y);
996 if(cr != CLR_INVALID && NtGdiSetPixelV(hDC,X,Y,Color))
997 {
998 return(cr);
999 }
1000 return ((COLORREF) -1);
1001 }
1002
1003 BOOL STDCALL
1004 NtGdiSetPixelV(
1005 HDC hDC,
1006 INT X,
1007 INT Y,
1008 COLORREF Color)
1009 {
1010 HBRUSH NewBrush = NtGdiCreateSolidBrush(Color);
1011 HGDIOBJ OldBrush;
1012
1013 if (NewBrush == NULL)
1014 return(FALSE);
1015 OldBrush = NtGdiSelectObject(hDC, NewBrush);
1016 if (OldBrush == NULL)
1017 {
1018 NtGdiDeleteObject(NewBrush);
1019 return(FALSE);
1020 }
1021 NtGdiPatBlt(hDC, X, Y, 1, 1, PATCOPY);
1022 NtGdiSelectObject(hDC, OldBrush);
1023 NtGdiDeleteObject(NewBrush);
1024 return TRUE;
1025 }
1026
1027 BOOL STDCALL
1028 NtGdiStretchBlt(
1029 HDC hDCDest,
1030 INT XOriginDest,
1031 INT YOriginDest,
1032 INT WidthDest,
1033 INT HeightDest,
1034 HDC hDCSrc,
1035 INT XOriginSrc,
1036 INT YOriginSrc,
1037 INT WidthSrc,
1038 INT HeightSrc,
1039 DWORD ROP)
1040 {
1041 PDC DCDest = NULL;
1042 PDC DCSrc = NULL;
1043 BITMAPOBJ *BitmapDest, *BitmapSrc;
1044 RECTL DestRect;
1045 RECTL SourceRect;
1046 BOOL Status;
1047 PPALGDI PalDestGDI, PalSourceGDI;
1048 XLATEOBJ *XlateObj = NULL;
1049 HPALETTE SourcePalette = 0, DestPalette = 0;
1050 ULONG SourceMode, DestMode;
1051 PGDIBRUSHOBJ BrushObj;
1052 BOOL UsesSource = ((ROP & 0xCC0000) >> 2) != (ROP & 0x330000);
1053 BOOL UsesPattern = ((ROP & 0xF00000) >> 4) != (ROP & 0x0F0000);
1054
1055 if (0 == WidthDest || 0 == HeightDest || 0 == WidthSrc || 0 == HeightSrc)
1056 {
1057 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1058 return FALSE;
1059 }
1060 DCDest = DC_LockDc(hDCDest);
1061 if (NULL == DCDest)
1062 {
1063 DPRINT1("Invalid destination dc handle (0x%08x) passed to NtGdiStretchBlt\n", hDCDest);
1064 SetLastWin32Error(ERROR_INVALID_HANDLE);
1065 return FALSE;
1066 }
1067
1068 if (UsesSource)
1069 {
1070 if (hDCSrc != hDCDest)
1071 {
1072 DCSrc = DC_LockDc(hDCSrc);
1073 if (NULL == DCSrc)
1074 {
1075 DC_UnlockDc(hDCDest);
1076 DPRINT1("Invalid source dc handle (0x%08x) passed to NtGdiStretchBlt\n", hDCSrc);
1077 SetLastWin32Error(ERROR_INVALID_HANDLE);
1078 return FALSE;
1079 }
1080 }
1081 else
1082 {
1083 DCSrc = DCDest;
1084 }
1085 }
1086 else
1087 {
1088 DCSrc = NULL;
1089 }
1090
1091 /* Offset the destination and source by the origin of their DCs. */
1092 XOriginDest += DCDest->w.DCOrgX;
1093 YOriginDest += DCDest->w.DCOrgY;
1094 if (UsesSource)
1095 {
1096 XOriginSrc += DCSrc->w.DCOrgX;
1097 YOriginSrc += DCSrc->w.DCOrgY;
1098 }
1099
1100 DestRect.left = XOriginDest;
1101 DestRect.top = YOriginDest;
1102 DestRect.right = XOriginDest+WidthDest;
1103 DestRect.bottom = YOriginDest+HeightDest;
1104
1105 SourceRect.left = XOriginSrc;
1106 SourceRect.top = YOriginSrc;
1107 SourceRect.right = XOriginSrc+WidthSrc;
1108 SourceRect.bottom = YOriginSrc+HeightSrc;
1109
1110 /* Determine surfaces to be used in the bitblt */
1111 BitmapDest = BITMAPOBJ_LockBitmap(DCDest->w.hBitmap);
1112 if (UsesSource)
1113 {
1114 if (DCSrc->w.hBitmap == DCDest->w.hBitmap)
1115 BitmapSrc = BitmapDest;
1116 else
1117 BitmapSrc = BITMAPOBJ_LockBitmap(DCSrc->w.hBitmap);
1118 }
1119 else
1120 {
1121 BitmapSrc = NULL;
1122 }
1123
1124 if (UsesPattern)
1125 {
1126 BrushObj = BRUSHOBJ_LockBrush(DCDest->w.hBrush);
1127 if (NULL == BrushObj)
1128 {
1129 if (UsesSource && hDCSrc != hDCDest)
1130 {
1131 DC_UnlockDc(hDCSrc);
1132 }
1133 DC_UnlockDc(hDCDest);
1134 SetLastWin32Error(ERROR_INVALID_HANDLE);
1135 return FALSE;
1136 }
1137 }
1138 else
1139 {
1140 BrushObj = NULL;
1141 }
1142
1143 if (DCDest->w.hPalette != 0)
1144 DestPalette = DCDest->w.hPalette;
1145
1146 if (UsesSource && DCSrc->w.hPalette != 0)
1147 SourcePalette = DCSrc->w.hPalette;
1148
1149 PalSourceGDI = PALETTE_LockPalette(SourcePalette);
1150 if (NULL == PalSourceGDI)
1151 {
1152 if (UsesSource && hDCSrc != hDCDest)
1153 {
1154 DC_UnlockDc(hDCSrc);
1155 }
1156 DC_UnlockDc(hDCDest);
1157 SetLastWin32Error(ERROR_INVALID_HANDLE);
1158 return FALSE;
1159 }
1160 SourceMode = PalSourceGDI->Mode;
1161 PALETTE_UnlockPalette(SourcePalette);
1162
1163 if (DestPalette == SourcePalette)
1164 {
1165 DestMode = SourceMode;
1166 }
1167 else
1168 {
1169 PalDestGDI = PALETTE_LockPalette(DestPalette);
1170 if (NULL == PalDestGDI)
1171 {
1172 if (UsesSource && hDCSrc != hDCDest)
1173 {
1174 DC_UnlockDc(hDCSrc);
1175 }
1176 DC_UnlockDc(hDCDest);
1177 SetLastWin32Error(ERROR_INVALID_HANDLE);
1178 return FALSE;
1179 }
1180 DestMode = PalDestGDI->Mode;
1181 PALETTE_UnlockPalette(DestPalette);
1182 }
1183
1184 XlateObj = (XLATEOBJ*)IntEngCreateXlate(DestMode, SourceMode, DestPalette, SourcePalette);
1185 if (NULL == XlateObj)
1186 {
1187 if (UsesSource && hDCSrc != hDCDest)
1188 {
1189 DC_UnlockDc(hDCSrc);
1190 }
1191 DC_UnlockDc(hDCDest);
1192 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
1193 return FALSE;
1194 }
1195
1196 /* Perform the bitblt operation */
1197 Status = IntEngStretchBlt(BitmapDest, BitmapSrc, NULL, DCDest->CombinedClip,
1198 XlateObj, &DestRect, &SourceRect, NULL, NULL, NULL, COLORONCOLOR);
1199
1200 EngDeleteXlate(XlateObj);
1201 BITMAPOBJ_UnlockBitmap(DCDest->w.hBitmap);
1202 if (UsesSource && DCSrc->w.hBitmap != DCDest->w.hBitmap)
1203 {
1204 BITMAPOBJ_UnlockBitmap(DCSrc->w.hBitmap);
1205 }
1206 if (UsesPattern)
1207 {
1208 BRUSHOBJ_UnlockBrush(DCDest->w.hBrush);
1209 }
1210 if (UsesSource && hDCSrc != hDCDest)
1211 {
1212 DC_UnlockDc(hDCSrc);
1213 }
1214 DC_UnlockDc(hDCDest);
1215
1216 return Status;
1217 }
1218
1219 /* Internal Functions */
1220
1221 INT FASTCALL
1222 BITMAPOBJ_GetWidthBytes (INT bmWidth, INT bpp)
1223 {
1224 #if 0
1225 switch(bpp)
1226 {
1227 case 1:
1228 return 2 * ((bmWidth+15) >> 4);
1229
1230 case 24:
1231 bmWidth *= 3; /* fall through */
1232 case 8:
1233 return bmWidth + (bmWidth & 1);
1234
1235 case 32:
1236 return bmWidth * 4;
1237
1238 case 16:
1239 case 15:
1240 return bmWidth * 2;
1241
1242 case 4:
1243 return 2 * ((bmWidth+3) >> 2);
1244
1245 default:
1246 DPRINT ("stub");
1247 }
1248
1249 return -1;
1250 #endif
1251
1252 return ((bmWidth * bpp + 15) & ~15) >> 3;
1253 }
1254
1255 HBITMAP FASTCALL
1256 BITMAPOBJ_CopyBitmap(HBITMAP hBitmap)
1257 {
1258 HBITMAP res;
1259 BITMAP bm;
1260
1261 if (IntGdiGetObject(hBitmap, sizeof(BITMAP), &bm) == 0)
1262 {
1263 return 0;
1264 }
1265 bm.bmBits = NULL;
1266 res = NtGdiCreateBitmapIndirect(&bm);
1267 if(res)
1268 {
1269 char *buf;
1270
1271 buf = ExAllocatePoolWithTag (PagedPool, bm.bmWidthBytes * bm.bmHeight, TAG_BITMAP);
1272 NtGdiGetBitmapBits (hBitmap, bm.bmWidthBytes * bm.bmHeight, buf);
1273 NtGdiSetBitmapBits (res, bm.bmWidthBytes * bm.bmHeight, buf);
1274 ExFreePool (buf);
1275 }
1276
1277 return res;
1278 }
1279
1280 INT STDCALL
1281 BITMAP_GetObject(BITMAPOBJ * bmp, INT count, LPVOID buffer)
1282 {
1283 if(bmp->dib)
1284 {
1285 if(count < (INT) sizeof(DIBSECTION))
1286 {
1287 if (count > (INT) sizeof(BITMAP)) count = sizeof(BITMAP);
1288 }
1289 else
1290 {
1291 if (count > (INT) sizeof(DIBSECTION)) count = sizeof(DIBSECTION);
1292 }
1293 memcpy(buffer, bmp->dib, count);
1294 return count;
1295 }
1296 else
1297 {
1298 BITMAP Bitmap;
1299 if (count > (INT) sizeof(BITMAP)) count = sizeof(BITMAP);
1300 Bitmap.bmType = 0;
1301 Bitmap.bmWidth = bmp->SurfObj.sizlBitmap.cx;
1302 Bitmap.bmHeight = bmp->SurfObj.sizlBitmap.cy;
1303 Bitmap.bmWidthBytes = abs(bmp->SurfObj.lDelta);
1304 Bitmap.bmPlanes = 1;
1305 Bitmap.bmBitsPixel = BitsPerFormat(bmp->SurfObj.iBitmapFormat);
1306 Bitmap.bmBits = bmp->SurfObj.pvBits;
1307 memcpy(buffer, &Bitmap, count);
1308 return count;
1309 }
1310 }
1311 /* EOF */