Don't lock unnecessarily and don't leak a bitmap object in case of failure
[reactos.git] / reactos / subsystems / win32 / 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$ */
20
21 #include <w32k.h>
22
23 #define NDEBUG
24 #include <debug.h>
25
26 #define IN_RECT(r,x,y) \
27 ( \
28 (x) >= (r).left && \
29 (y) >= (r).top && \
30 (x) < (r).right && \
31 (y) < (r).bottom \
32 )
33
34 HBITMAP STDCALL
35 IntGdiCreateBitmap(
36 INT Width,
37 INT Height,
38 UINT Planes,
39 UINT BitsPixel,
40 IN OPTIONAL LPBYTE pBits)
41 {
42 HBITMAP hBitmap;
43 SIZEL Size;
44 LONG WidthBytes;
45
46 /* NOTE: Windows also doesn't store nr. of planes separately! */
47 BitsPixel = BITMAPOBJ_GetRealBitsPixel(BitsPixel * Planes);
48
49 /* Check parameters */
50 if (BitsPixel == 0 || Width < 0)
51 {
52 DPRINT1("Width = %d, Height = %d BitsPixel = %d\n", Width, Height, BitsPixel);
53 SetLastWin32Error(ERROR_INVALID_PARAMETER);
54 return 0;
55 }
56
57 WidthBytes = BITMAPOBJ_GetWidthBytes(Width, BitsPixel);
58
59 Size.cx = Width;
60 Size.cy = abs(Height);
61
62 /* Create the bitmap object. */
63 hBitmap = IntCreateBitmap(Size, WidthBytes,
64 BitmapFormat(BitsPixel, BI_RGB),
65 (Height < 0 ? BMF_TOPDOWN : 0) |
66 (NULL == pBits ? 0 : BMF_NOZEROINIT), NULL);
67 if (!hBitmap)
68 {
69 DPRINT("IntGdiCreateBitmap: returned 0\n");
70 return 0;
71 }
72
73 if (NULL != pBits)
74 {
75 PBITMAPOBJ bmp = BITMAPOBJ_LockBitmap( hBitmap );
76 if (bmp == NULL)
77 {
78 NtGdiDeleteObject(hBitmap);
79 return NULL;
80 }
81
82 bmp->flFlags = BITMAPOBJ_IS_APIBITMAP;
83
84 IntSetBitmapBits(bmp, bmp->SurfObj.cjBits, pBits);
85
86
87 BITMAPOBJ_UnlockBitmap( bmp );
88 }
89
90 DPRINT("IntGdiCreateBitmap : %dx%d, %d BPP colors, topdown %d, returning %08x\n",
91 Size.cx, Size.cy, BitsPixel, (Height < 0 ? 1 : 0), hBitmap);
92
93 return hBitmap;
94 }
95
96
97 HBITMAP STDCALL
98 NtGdiCreateBitmap(
99 INT Width,
100 INT Height,
101 UINT Planes,
102 UINT BitsPixel,
103 IN OPTIONAL LPBYTE pUnsafeBits)
104 {
105 HBITMAP hBitmap;
106
107 _SEH_TRY
108 {
109 if (pUnsafeBits)
110 {
111 UINT cjBits = BITMAPOBJ_GetWidthBytes(Width, BitsPixel) * Height;
112 ProbeForRead(pUnsafeBits, cjBits, 1);
113 }
114
115 if (0 == Width || 0 == Height)
116 {
117 hBitmap = IntGdiCreateBitmap (1, 1, 1, 1, NULL);
118 }
119 else
120 {
121 hBitmap = IntGdiCreateBitmap(Width, Height, Planes, BitsPixel, pUnsafeBits);
122 }
123 }
124 _SEH_HANDLE
125 {
126 hBitmap = 0;
127 }
128 _SEH_END
129
130 return hBitmap;
131 }
132
133 BOOL INTERNAL_CALL
134 BITMAP_Cleanup(PVOID ObjectBody)
135 {
136 PBITMAPOBJ pBmp = (PBITMAPOBJ)ObjectBody;
137 if (pBmp->SurfObj.pvBits != NULL &&
138 (pBmp->flFlags & BITMAPOBJ_IS_APIBITMAP))
139 {
140 if (pBmp->dib == NULL)
141 {
142 if (pBmp->SurfObj.pvBits != NULL)
143 ExFreePool(pBmp->SurfObj.pvBits);
144 }
145 else
146 {
147 if (pBmp->SurfObj.pvBits != NULL)
148 EngFreeUserMem(pBmp->SurfObj.pvBits);
149 }
150 if (pBmp->hDIBPalette != NULL)
151 {
152 NtGdiDeleteObject(pBmp->hDIBPalette);
153 }
154 }
155
156 if (NULL != pBmp->BitsLock)
157 {
158 ExFreePoolWithTag(pBmp->BitsLock, TAG_BITMAPOBJ);
159 pBmp->BitsLock = NULL;
160 }
161
162 return TRUE;
163 }
164
165
166 HBITMAP FASTCALL
167 IntCreateCompatibleBitmap(
168 PDC Dc,
169 INT Width,
170 INT Height)
171 {
172 HBITMAP Bmp;
173
174 Bmp = NULL;
175
176 if ((Width >= 0x10000) || (Height >= 0x10000))
177 {
178 DPRINT1("got bad width %d or height %d, please look for reason\n", Width, Height);
179 return NULL;
180 }
181
182 /* MS doc says if width or height is 0, return 1-by-1 pixel, monochrome bitmap */
183 if (0 == Width || 0 == Height)
184 {
185 Bmp = IntGdiCreateBitmap (1, 1, 1, 1, NULL);
186 }
187 else
188 {
189 Bmp = IntGdiCreateBitmap(Width, Height, 1, Dc->w.bitsPerPixel, NULL);
190 }
191
192 return Bmp;
193 }
194
195 HBITMAP STDCALL
196 NtGdiCreateCompatibleBitmap(
197 HDC hDC,
198 INT Width,
199 INT Height)
200 {
201 HBITMAP Bmp;
202 PDC Dc;
203
204 Dc = DC_LockDc(hDC);
205
206 DPRINT("NtGdiCreateCompatibleBitmap(%04x,%d,%d, bpp:%d) = \n", hDC, Width, Height, Dc->w.bitsPerPixel);
207
208 if (NULL == Dc)
209 {
210 SetLastWin32Error(ERROR_INVALID_HANDLE);
211 return NULL;
212 }
213
214 Bmp = IntCreateCompatibleBitmap(Dc, Width, Height);
215
216 DPRINT ("\t\t%04x\n", Bmp);
217 DC_UnlockDc(Dc);
218 return Bmp;
219 }
220
221 BOOL STDCALL
222 NtGdiGetBitmapDimension(
223 HBITMAP hBitmap,
224 LPSIZE Dimension)
225 {
226 PBITMAPOBJ bmp;
227
228 bmp = BITMAPOBJ_LockBitmap(hBitmap);
229 if (bmp == NULL)
230 {
231 return FALSE;
232 }
233
234 *Dimension = bmp->dimension;
235
236 BITMAPOBJ_UnlockBitmap(bmp);
237
238 return TRUE;
239 }
240
241 COLORREF STDCALL
242 NtGdiGetPixel(HDC hDC, INT XPos, INT YPos)
243 {
244 PDC dc = NULL;
245 COLORREF Result = (COLORREF)CLR_INVALID; // default to failure
246 BOOL bInRect = FALSE;
247 BITMAPOBJ *BitmapObject;
248 SURFOBJ *SurfaceObject;
249 HPALETTE Pal = 0;
250 XLATEOBJ *XlateObj;
251 HBITMAP hBmpTmp;
252
253 dc = DC_LockDc (hDC);
254
255 if ( !dc )
256 {
257 SetLastWin32Error(ERROR_INVALID_HANDLE);
258 return Result;
259 }
260 if (dc->IsIC)
261 {
262 DC_UnlockDc(dc);
263 return Result;
264 }
265 XPos += dc->w.DCOrgX;
266 YPos += dc->w.DCOrgY;
267 if ( IN_RECT(dc->CombinedClip->rclBounds,XPos,YPos) )
268 {
269 bInRect = TRUE;
270 BitmapObject = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
271 SurfaceObject = &BitmapObject->SurfObj;
272 if ( BitmapObject )
273 {
274 if ( dc->w.hPalette != 0 )
275 Pal = dc->w.hPalette;
276 /* FIXME: Verify if it shouldn't be PAL_BGR! */
277 XlateObj = (XLATEOBJ*)IntEngCreateXlate ( PAL_RGB, 0, NULL, Pal );
278 if ( XlateObj )
279 {
280 // check if this DC has a DIB behind it...
281 if ( SurfaceObject->pvScan0 ) // STYPE_BITMAP == SurfaceObject->iType
282 {
283 ASSERT ( SurfaceObject->lDelta );
284 Result = XLATEOBJ_iXlate(XlateObj,
285 DibFunctionsForBitmapFormat[SurfaceObject->iBitmapFormat].DIB_GetPixel ( SurfaceObject, XPos, YPos ) );
286 }
287 EngDeleteXlate(XlateObj);
288 }
289 BITMAPOBJ_UnlockBitmap(BitmapObject);
290 }
291 }
292 DC_UnlockDc(dc);
293
294 // if Result is still CLR_INVALID, then the "quick" method above didn't work
295 if ( bInRect && Result == CLR_INVALID )
296 {
297 // FIXME: create a 1x1 32BPP DIB, and blit to it
298 HDC hDCTmp = NtGdiCreateCompatibleDC(hDC);
299 if ( hDCTmp )
300 {
301 static const BITMAPINFOHEADER bih = { sizeof(BITMAPINFOHEADER), 1, 1, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
302 BITMAPINFO bi;
303 RtlMoveMemory ( &(bi.bmiHeader), &bih, sizeof(bih) );
304 hBmpTmp = NtGdiCreateDIBitmap ( hDC, &bi.bmiHeader, 0, NULL, &bi, DIB_RGB_COLORS );
305 //HBITMAP hBmpTmp = IntGdiCreateBitmap ( 1, 1, 1, 32, NULL);
306 if ( hBmpTmp )
307 {
308 HBITMAP hBmpOld = (HBITMAP)NtGdiSelectObject ( hDCTmp, hBmpTmp );
309 if ( hBmpOld )
310 {
311 PBITMAPOBJ bmpobj;
312
313 NtGdiBitBlt ( hDCTmp, 0, 0, 1, 1, hDC, XPos, YPos, SRCCOPY, 0, 0 );
314 NtGdiSelectObject ( hDCTmp, hBmpOld );
315
316 // our bitmap is no longer selected, so we can access it's stuff...
317 bmpobj = BITMAPOBJ_LockBitmap ( hBmpTmp );
318 if ( bmpobj )
319 {
320 Result = *(COLORREF*)bmpobj->SurfObj.pvScan0;
321 BITMAPOBJ_UnlockBitmap ( bmpobj );
322 }
323 }
324 NtGdiDeleteObject ( hBmpTmp );
325 }
326 NtGdiDeleteObjectApp ( hDCTmp );
327 }
328 }
329
330 return Result;
331 }
332
333
334 LONG STDCALL
335 IntGetBitmapBits(
336 PBITMAPOBJ bmp,
337 DWORD Bytes,
338 OUT PBYTE Bits)
339 {
340 LONG ret;
341
342 ASSERT(Bits);
343
344 /* Don't copy more bytes than the buffer has */
345 Bytes = min(Bytes, bmp->SurfObj.cjBits);
346
347 #if 0
348 /* FIXME: Call DDI CopyBits here if available */
349 if(bmp->DDBitmap)
350 {
351 DPRINT("Calling device specific BitmapBits\n");
352 if(bmp->DDBitmap->funcs->pBitmapBits)
353 {
354 ret = bmp->DDBitmap->funcs->pBitmapBits(hbitmap, bits, count, DDB_GET);
355 }
356 else
357 {
358 ERR_(bitmap)("BitmapBits == NULL??\n");
359 ret = 0;
360 }
361 }
362 else
363 #endif
364 {
365 RtlCopyMemory(Bits, bmp->SurfObj.pvBits, Bytes);
366 ret = Bytes;
367 }
368 return ret;
369 }
370
371 LONG STDCALL
372 NtGdiGetBitmapBits(HBITMAP hBitmap,
373 ULONG Bytes,
374 OUT OPTIONAL PBYTE pUnsafeBits)
375 {
376 PBITMAPOBJ bmp;
377 LONG ret;
378
379 if (pUnsafeBits != NULL && Bytes == 0)
380 {
381 return 0;
382 }
383
384 bmp = BITMAPOBJ_LockBitmap (hBitmap);
385 if (!bmp)
386 {
387 SetLastWin32Error(ERROR_INVALID_HANDLE);
388 return 0;
389 }
390
391 /* If the bits vector is null, the function should return the read size */
392 if (pUnsafeBits == NULL)
393 {
394 ret = bmp->SurfObj.cjBits;
395 BITMAPOBJ_UnlockBitmap (bmp);
396 return ret;
397 }
398
399 /* Don't copy more bytes than the buffer has */
400 Bytes = min(Bytes, bmp->SurfObj.cjBits);
401
402 _SEH_TRY
403 {
404 ProbeForWrite(pUnsafeBits, Bytes, 1);
405 ret = IntGetBitmapBits(bmp, Bytes, pUnsafeBits);
406 }
407 _SEH_HANDLE
408 {
409 ret = 0;
410 }
411 _SEH_END
412
413 BITMAPOBJ_UnlockBitmap (bmp);
414
415 return ret;
416 }
417
418
419 LONG STDCALL
420 IntSetBitmapBits(
421 PBITMAPOBJ bmp,
422 DWORD Bytes,
423 IN PBYTE Bits)
424 {
425 LONG ret;
426
427 /* Don't copy more bytes than the buffer has */
428 Bytes = min(Bytes, bmp->SurfObj.cjBits);
429
430 #if 0
431 /* FIXME: call DDI specific function here if available */
432 if(bmp->DDBitmap)
433 {
434 DPRINT ("Calling device specific BitmapBits\n");
435 if (bmp->DDBitmap->funcs->pBitmapBits)
436 {
437 ret = bmp->DDBitmap->funcs->pBitmapBits(hBitmap, (void *) Bits, Bytes, DDB_SET);
438 }
439 else
440 {
441 DPRINT ("BitmapBits == NULL??\n");
442 ret = 0;
443 }
444 }
445 else
446 #endif
447 {
448 RtlCopyMemory(bmp->SurfObj.pvBits, Bits, Bytes);
449 ret = Bytes;
450 }
451
452 return ret;
453 }
454
455
456 LONG STDCALL
457 NtGdiSetBitmapBits(
458 HBITMAP hBitmap,
459 DWORD Bytes,
460 IN PBYTE pUnsafeBits)
461 {
462 LONG ret;
463 PBITMAPOBJ bmp;
464
465 if (pUnsafeBits == NULL || Bytes == 0)
466 {
467 return 0;
468 }
469
470 bmp = BITMAPOBJ_LockBitmap(hBitmap);
471 if (bmp == NULL)
472 {
473 SetLastWin32Error(ERROR_INVALID_HANDLE);
474 return 0;
475 }
476
477 _SEH_TRY
478 {
479 ProbeForRead(pUnsafeBits, Bytes, 1);
480 ret = IntSetBitmapBits(bmp, Bytes, pUnsafeBits);
481 }
482 _SEH_HANDLE
483 {
484 ret = 0;
485 }
486 _SEH_END
487
488 BITMAPOBJ_UnlockBitmap(bmp);
489
490 return ret;
491 }
492
493 BOOL STDCALL
494 NtGdiSetBitmapDimension(
495 HBITMAP hBitmap,
496 INT Width,
497 INT Height,
498 LPSIZE Size)
499 {
500 PBITMAPOBJ bmp;
501
502 bmp = BITMAPOBJ_LockBitmap(hBitmap);
503 if (bmp == NULL)
504 {
505 return FALSE;
506 }
507
508 if (Size)
509 {
510 *Size = bmp->dimension;
511 }
512 bmp->dimension.cx = Width;
513 bmp->dimension.cy = Height;
514
515 BITMAPOBJ_UnlockBitmap (bmp);
516
517 return TRUE;
518 }
519
520 BOOL STDCALL
521 GdiSetPixelV(
522 HDC hDC,
523 INT X,
524 INT Y,
525 COLORREF Color)
526 {
527 HBRUSH NewBrush = NtGdiCreateSolidBrush(Color, NULL);
528 HGDIOBJ OldBrush;
529
530 if (NewBrush == NULL)
531 return(FALSE);
532 OldBrush = NtGdiSelectObject(hDC, NewBrush);
533 if (OldBrush == NULL)
534 {
535 NtGdiDeleteObject(NewBrush);
536 return(FALSE);
537 }
538 NtGdiPatBlt(hDC, X, Y, 1, 1, PATCOPY);
539 NtGdiSelectObject(hDC, OldBrush);
540 NtGdiDeleteObject(NewBrush);
541 return TRUE;
542 }
543
544 COLORREF STDCALL
545 NtGdiSetPixel(
546 HDC hDC,
547 INT X,
548 INT Y,
549 COLORREF Color)
550 {
551 DPRINT("0 NtGdiSetPixel X %ld Y %ld C %ld\n",X,Y,Color);
552
553 if (GdiSetPixelV(hDC,X,Y,Color))
554 {
555 Color = NtGdiGetPixel(hDC,X,Y);
556 DPRINT("1 NtGdiSetPixel X %ld Y %ld C %ld\n",X,Y,Color);
557 return Color;
558 }
559
560 Color = ((COLORREF) CLR_INVALID);
561 DPRINT("2 NtGdiSetPixel X %ld Y %ld C %ld\n",X,Y,Color);
562 return Color;
563 }
564
565
566 /* Internal Functions */
567
568 INT FASTCALL
569 BITMAPOBJ_GetRealBitsPixel(INT nBitsPixel)
570 {
571 if (nBitsPixel < 0)
572 return 0;
573 if (nBitsPixel <= 1)
574 return 1;
575 if (nBitsPixel <= 2)
576 return 2;
577 if (nBitsPixel <= 4)
578 return 4;
579 if (nBitsPixel <= 8)
580 return 8;
581 if (nBitsPixel <= 16)
582 return 16;
583 if (nBitsPixel <= 24)
584 return 24;
585 if (nBitsPixel <= 32)
586 return 32;
587
588 return 0;
589 }
590
591 INT FASTCALL
592 BITMAPOBJ_GetWidthBytes (INT bmWidth, INT bpp)
593 {
594 #if 0
595 switch(bpp)
596 {
597 case 1:
598 return 2 * ((bmWidth+15) >> 4);
599
600 case 24:
601 bmWidth *= 3; /* fall through */
602 case 8:
603 return bmWidth + (bmWidth & 1);
604
605 case 32:
606 return bmWidth * 4;
607
608 case 16:
609 case 15:
610 return bmWidth * 2;
611
612 case 4:
613 return 2 * ((bmWidth+3) >> 2);
614
615 default:
616 DPRINT ("stub");
617 }
618
619 return -1;
620 #endif
621
622 return ((bmWidth * bpp + 15) & ~15) >> 3;
623 }
624
625 HBITMAP FASTCALL
626 BITMAPOBJ_CopyBitmap(HBITMAP hBitmap)
627 {
628 HBITMAP res;
629 BITMAP bm;
630 BITMAPOBJ *Bitmap, *resBitmap;
631 SIZEL Size;
632
633 if (hBitmap == NULL)
634 {
635 return 0;
636 }
637
638 Bitmap = GDIOBJ_LockObj(GdiHandleTable, hBitmap, GDI_OBJECT_TYPE_BITMAP);
639 if (Bitmap == NULL)
640 {
641 return 0;
642 }
643
644 BITMAP_GetObject(Bitmap, sizeof(BITMAP), &bm);
645 bm.bmBits = NULL;
646 if (Bitmap->SurfObj.lDelta >= 0)
647 bm.bmHeight = -bm.bmHeight;
648
649 Size.cx = abs(bm.bmWidth);
650 Size.cy = abs(bm.bmHeight);
651 res = IntCreateBitmap(Size,
652 bm.bmWidthBytes,
653 BitmapFormat(bm.bmBitsPixel * bm.bmPlanes, BI_RGB),
654 (bm.bmHeight < 0 ? BMF_TOPDOWN : 0) | BMF_NOZEROINIT,
655 NULL);
656
657 if(res)
658 {
659 PBYTE buf;
660
661 resBitmap = GDIOBJ_LockObj(GdiHandleTable, res, GDI_OBJECT_TYPE_BITMAP);
662 if (resBitmap)
663 {
664 buf = ExAllocatePoolWithTag (PagedPool, bm.bmWidthBytes * abs(bm.bmHeight), TAG_BITMAP);
665 IntGetBitmapBits (Bitmap, bm.bmWidthBytes * abs(bm.bmHeight), buf);
666 IntSetBitmapBits (resBitmap, bm.bmWidthBytes * abs(bm.bmHeight), buf);
667 ExFreePool (buf);
668 GDIOBJ_UnlockObjByPtr(GdiHandleTable, resBitmap);
669 }
670 }
671
672 GDIOBJ_UnlockObjByPtr(GdiHandleTable, Bitmap);
673
674 return res;
675 }
676
677 INT STDCALL
678 BITMAP_GetObject(BITMAPOBJ * bmp, INT Count, LPVOID buffer)
679 {
680 if ((UINT)Count < sizeof(BITMAP)) return 0;
681
682 if(bmp->dib)
683 {
684 if((UINT)Count < sizeof(DIBSECTION))
685 {
686 Count = sizeof(BITMAP);
687 }
688 else
689 {
690 Count = sizeof(DIBSECTION);
691 }
692 if (buffer)
693 {
694 memcpy(buffer, bmp->dib, Count);
695 }
696 return Count;
697 }
698 else
699 {
700 Count = sizeof(BITMAP);
701 if (buffer)
702 {
703 BITMAP Bitmap;
704
705 Count = sizeof(BITMAP);
706 Bitmap.bmType = 0;
707 Bitmap.bmWidth = bmp->SurfObj.sizlBitmap.cx;
708 Bitmap.bmHeight = bmp->SurfObj.sizlBitmap.cy;
709 Bitmap.bmWidthBytes = abs(bmp->SurfObj.lDelta);
710 Bitmap.bmPlanes = 1;
711 Bitmap.bmBitsPixel = BitsPerFormat(bmp->SurfObj.iBitmapFormat);
712 //Bitmap.bmBits = bmp->SurfObj.pvBits;
713 Bitmap.bmBits = NULL; /* not set accoring wine test confirm in win2k */
714 memcpy(buffer, &Bitmap, Count);
715 }
716 return Count;
717 }
718 }
719 /* EOF */