2 * COPYRIGHT: GNU GPL, See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Bitmap functions
5 * FILE: win32ss/gdi/ntgdi/bitmaps.c
6 * PROGRAMERS: Timo Kreuzer <timo.kreuzer@reactos.org>
7 * Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
21 /* Check if we have the correct object type */
22 if (GDI_HANDLE_GET_TYPE(hbmp
) != GDILoObjType_LO_BITMAP_TYPE
)
24 DPRINT1("Incorrect type for hbmp: %p\n", hbmp
);
28 /// FIXME: this is a hack and doesn't handle a race condition properly.
29 /// It needs to be done in GDIOBJ_vSetObjectOwner atomically.
31 /* Check if we set public or none */
32 if ((ulOwner
== GDI_OBJ_HMGR_PUBLIC
) ||
33 (ulOwner
== GDI_OBJ_HMGR_NONE
))
35 /* Only allow this for owned objects */
36 if (GreGetObjectOwner(hbmp
) != GDI_OBJ_HMGR_POWNED
)
38 DPRINT1("Cannot change owner for non-powned hbmp\n");
43 return GreSetObjectOwner(hbmp
, ulOwner
);
49 _Inout_ PSURFACE psurf
,
51 _In_
const VOID
*pvBits
)
55 LONG lDeltaDst
, lDeltaSrc
, lDeltaDstAbs
;
56 ULONG Y
, iSrc
, iDst
, cbSrc
, cbDst
, nWidth
, nHeight
, cBitsPixel
;
58 NT_ASSERT(psurf
->flags
& API_BITMAP
);
59 NT_ASSERT(psurf
->SurfObj
.iBitmapFormat
<= BMF_32BPP
);
61 nWidth
= psurf
->SurfObj
.sizlBitmap
.cx
;
62 nHeight
= labs(psurf
->SurfObj
.sizlBitmap
.cy
);
63 cBitsPixel
= BitsPerFormat(psurf
->SurfObj
.iBitmapFormat
);
65 pjDst
= psurf
->SurfObj
.pvScan0
;
67 lDeltaDst
= psurf
->SurfObj
.lDelta
;
68 lDeltaDstAbs
= labs(lDeltaDst
);
69 lDeltaSrc
= WIDTH_BYTES_ALIGN16(nWidth
, cBitsPixel
);
70 NT_ASSERT(lDeltaSrc
<= lDeltaDstAbs
);
72 cbDst
= lDeltaDstAbs
* nHeight
;
73 cbSrc
= lDeltaSrc
* nHeight
;
74 cjBits
= min(cjBits
, cbSrc
);
77 for (Y
= 0; Y
< nHeight
; Y
++)
79 if (iSrc
+ lDeltaSrc
> cjBits
|| iDst
+ lDeltaDstAbs
> cbDst
)
81 LONG lDelta
= min(cjBits
- iSrc
, cbDst
- iDst
);
82 NT_ASSERT(lDelta
>= 0);
83 RtlCopyMemory(pjDst
, pjSrc
, lDelta
);
89 RtlCopyMemory(pjDst
, pjSrc
, lDeltaSrc
);
104 _In_ ULONG cjWidthBytes
,
106 _In_ USHORT fjBitmap
,
107 _In_ ULONG cjSizeImage
,
108 _In_opt_ PVOID pvBits
,
113 PVOID pvCompressedBits
= NULL
;
116 if (iFormat
< BMF_1BPP
|| iFormat
> BMF_PNG
) return NULL
;
118 /* The infamous RLE hack */
119 if ((iFormat
== BMF_4RLE
) || (iFormat
== BMF_8RLE
))
121 pvCompressedBits
= pvBits
;
123 iFormat
= (iFormat
== BMF_4RLE
) ? BMF_4BPP
: BMF_8BPP
;
126 /* Allocate a surface */
127 psurf
= SURFACE_AllocSurface(STYPE_BITMAP
,
133 pvCompressedBits
? 0 : cjSizeImage
,
137 DPRINT1("SURFACE_AllocSurface failed.\n");
141 /* The infamous RLE hack */
142 if (pvCompressedBits
)
149 lDelta
= WIDTH_BYTES_ALIGN32(nWidth
, gajBitsPerFormat
[iFormat
]);
151 pvBits
= psurf
->SurfObj
.pvBits
;
152 DecompressBitmap(sizl
, pvCompressedBits
, pvBits
, lDelta
, iFormat
, cjSizeImage
);
155 /* Get the handle for the bitmap */
156 hbmp
= (HBITMAP
)psurf
->SurfObj
.hsurf
;
158 /* Mark as API bitmap */
159 psurf
->flags
|= (flags
| API_BITMAP
);
161 /* Unlock the surface and return */
162 SURFACE_UnlockSurface(psurf
);
166 /* Creates a DDB surface,
167 * as in CreateCompatibleBitmap or CreateBitmap.
168 * Note that each scanline must be 32bit aligned!
176 _In_ ULONG cBitsPixel
,
177 _In_opt_ PVOID pvBits
)
179 /* Call the extended function */
180 return GreCreateBitmapEx(nWidth
,
183 BitmapFormat(cBitsPixel
* cPlanes
, BI_RGB
),
184 0, /* No bitmap flags */
187 DDB_SURFACE
/* DDB */);
197 IN OPTIONAL LPBYTE pUnsafeBits
)
200 ULONG cRealBpp
, cjWidthBytes
, iFormat
;
204 /* Calculate bitmap format and real bits per pixel. */
205 iFormat
= BitmapFormat(cBitsPixel
* cPlanes
, BI_RGB
);
206 cRealBpp
= gajBitsPerFormat
[iFormat
];
208 /* Calculate width and image size in bytes */
209 cjWidthBytes
= WIDTH_BYTES_ALIGN16(nWidth
, cRealBpp
);
210 cjSize
= (ULONGLONG
)cjWidthBytes
* nHeight
;
212 /* Check parameters (possible overflow of cjSize!) */
213 if ((iFormat
== 0) || (nWidth
<= 0) || (nWidth
>= 0x8000000) || (nHeight
<= 0) ||
214 (cBitsPixel
> 32) || (cPlanes
> 32) || (cjSize
>= 0x100000000ULL
))
216 DPRINT1("Invalid bitmap format! Width=%d, Height=%d, Bpp=%u, Planes=%u\n",
217 nWidth
, nHeight
, cBitsPixel
, cPlanes
);
218 EngSetLastError(ERROR_INVALID_PARAMETER
);
222 /* Allocate the surface (but don't set the bits) */
223 psurf
= SURFACE_AllocSurface(STYPE_BITMAP
,
233 DPRINT1("SURFACE_AllocSurface failed.\n");
237 /* Mark as API and DDB bitmap */
238 psurf
->flags
|= (API_BITMAP
| DDB_SURFACE
);
240 /* Check if we have bits to set */
243 /* Protect with SEH and copy the bits */
246 ProbeForRead(pUnsafeBits
, (SIZE_T
)cjSize
, 1);
247 UnsafeSetBitmapBits(psurf
, cjSize
, pUnsafeBits
);
249 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
251 GDIOBJ_vDeleteObject(&psurf
->BaseObject
);
252 _SEH2_YIELD(return NULL
;)
259 RtlZeroMemory(psurf
->SurfObj
.pvBits
, psurf
->SurfObj
.cjBits
);
262 /* Get the handle for the bitmap */
263 hbmp
= (HBITMAP
)psurf
->SurfObj
.hsurf
;
265 /* Unlock the surface */
266 SURFACE_UnlockSurface(psurf
);
273 IntCreateCompatibleBitmap(
283 /* MS doc says if width or height is 0, return 1-by-1 pixel, monochrome bitmap */
284 if (0 == Width
|| 0 == Height
)
286 return NtGdiGetStockObject(DEFAULT_BITMAP
);
289 if (Dc
->dctype
!= DC_TYPE_MEMORY
)
293 Bmp
= GreCreateBitmap(abs(Width
),
296 Bpp
? Bpp
: Dc
->ppdev
->gdiinfo
.cBitsPixel
,
300 DPRINT1("Failed to allocate a bitmap!\n");
304 psurf
= SURFACE_ShareLockSurface(Bmp
);
307 /* Dereference old palette and set new palette */
308 ppal
= PALETTE_ShareLockPalette(Dc
->ppdev
->devinfo
.hpalDefault
);
310 SURFACE_vSetPalette(psurf
, ppal
);
311 PALETTE_ShareUnlockPalette(ppal
);
314 psurf
->flags
= API_BITMAP
;
315 psurf
->hdc
= NULL
; // FIXME:
316 psurf
->SurfObj
.hdev
= (HDEV
)Dc
->ppdev
;
317 SURFACE_ShareUnlockSurface(psurf
);
323 PSURFACE psurf
= Dc
->dclevel
.pSurface
;
324 if(!psurf
) psurf
= psurfDefaultBitmap
;
325 Count
= BITMAP_GetObject(psurf
, sizeof(dibs
), &dibs
);
327 if (Count
== sizeof(BITMAP
))
331 Bmp
= GreCreateBitmap(abs(Width
),
334 Bpp
? Bpp
: dibs
.dsBm
.bmBitsPixel
,
336 psurfBmp
= SURFACE_ShareLockSurface(Bmp
);
339 /* Dereference old palette and set new palette */
340 SURFACE_vSetPalette(psurfBmp
, psurf
->ppal
);
343 psurfBmp
->flags
= API_BITMAP
;
344 psurfBmp
->hdc
= NULL
; // FIXME:
345 psurf
->SurfObj
.hdev
= (HDEV
)Dc
->ppdev
;
346 SURFACE_ShareUnlockSurface(psurfBmp
);
348 else if (Count
== sizeof(DIBSECTION
))
350 /* A DIB section is selected in the DC */
351 BYTE buf
[sizeof(BITMAPINFOHEADER
) + 256*sizeof(RGBQUAD
)] = {0};
353 BITMAPINFO
* bi
= (BITMAPINFO
*)buf
;
355 bi
->bmiHeader
.biSize
= sizeof(bi
->bmiHeader
);
356 bi
->bmiHeader
.biWidth
= Width
;
357 bi
->bmiHeader
.biHeight
= Height
;
358 bi
->bmiHeader
.biPlanes
= Planes
? Planes
: dibs
.dsBmih
.biPlanes
;
359 bi
->bmiHeader
.biBitCount
= Bpp
? Bpp
: dibs
.dsBmih
.biBitCount
;
360 bi
->bmiHeader
.biCompression
= dibs
.dsBmih
.biCompression
;
361 bi
->bmiHeader
.biSizeImage
= 0;
362 bi
->bmiHeader
.biXPelsPerMeter
= dibs
.dsBmih
.biXPelsPerMeter
;
363 bi
->bmiHeader
.biYPelsPerMeter
= dibs
.dsBmih
.biYPelsPerMeter
;
364 bi
->bmiHeader
.biClrUsed
= dibs
.dsBmih
.biClrUsed
;
365 bi
->bmiHeader
.biClrImportant
= dibs
.dsBmih
.biClrImportant
;
367 if (bi
->bmiHeader
.biCompression
== BI_BITFIELDS
)
369 /* Copy the color masks */
370 RtlCopyMemory(bi
->bmiColors
, dibs
.dsBitfields
, 3*sizeof(RGBQUAD
));
372 else if (bi
->bmiHeader
.biBitCount
<= 8)
374 /* Copy the color table */
380 EngSetLastError(ERROR_INVALID_HANDLE
);
384 PalGDI
= psurf
->ppal
;
387 Index
< 256 && Index
< PalGDI
->NumColors
;
390 bi
->bmiColors
[Index
].rgbRed
= PalGDI
->IndexedColors
[Index
].peRed
;
391 bi
->bmiColors
[Index
].rgbGreen
= PalGDI
->IndexedColors
[Index
].peGreen
;
392 bi
->bmiColors
[Index
].rgbBlue
= PalGDI
->IndexedColors
[Index
].peBlue
;
393 bi
->bmiColors
[Index
].rgbReserved
= 0;
397 Bmp
= DIB_CreateDIBSection(Dc
,
411 NtGdiCreateCompatibleBitmap(
419 /* Check parameters */
420 if ((Width
<= 0) || (Height
<= 0) || ((Width
* Height
) > 0x3FFFFFFF))
422 EngSetLastError(ERROR_INVALID_PARAMETER
);
427 return GreCreateBitmap(Width
, Height
, 1, 1, 0);
431 DPRINT("NtGdiCreateCompatibleBitmap(%p,%d,%d, bpp:%u) = \n",
432 hDC
, Width
, Height
, Dc
->ppdev
->gdiinfo
.cBitsPixel
);
436 EngSetLastError(ERROR_INVALID_HANDLE
);
440 Bmp
= IntCreateCompatibleBitmap(Dc
, Width
, Height
, 0, 0);
448 GreGetBitmapDimension(
449 _In_ HBITMAP hBitmap
,
450 _Out_ LPSIZE psizDim
)
457 /* Lock the bitmap */
458 psurfBmp
= SURFACE_ShareLockSurface(hBitmap
);
459 if (psurfBmp
== NULL
)
461 EngSetLastError(ERROR_INVALID_HANDLE
);
465 *psizDim
= psurfBmp
->sizlDim
;
467 /* Unlock the bitmap */
468 SURFACE_ShareUnlockSurface(psurfBmp
);
475 NtGdiGetBitmapDimension(
481 if (!GreGetBitmapDimension(hBitmap
, &dim
))
484 /* Use SEH to copy the data to the caller */
487 ProbeForWrite(psizDim
, sizeof(*psizDim
), 1);
490 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
492 _SEH2_YIELD(return FALSE
);
508 LONG lDeltaDst
, lDeltaSrc
, lDeltaSrcAbs
;
509 ULONG Y
, iSrc
, iDst
, cbSrc
, cbDst
, nWidth
, nHeight
, cBitsPixel
;
511 nWidth
= psurf
->SurfObj
.sizlBitmap
.cx
;
512 nHeight
= labs(psurf
->SurfObj
.sizlBitmap
.cy
);
513 cBitsPixel
= BitsPerFormat(psurf
->SurfObj
.iBitmapFormat
);
516 pjSrc
= psurf
->SurfObj
.pvScan0
;
518 lDeltaSrc
= psurf
->SurfObj
.lDelta
;
519 lDeltaSrcAbs
= labs(lDeltaSrc
);
520 lDeltaDst
= WIDTH_BYTES_ALIGN16(nWidth
, cBitsPixel
);
521 NT_ASSERT(lDeltaSrcAbs
>= lDeltaDst
);
523 cbSrc
= nHeight
* lDeltaSrcAbs
;
524 cbDst
= nHeight
* lDeltaDst
;
525 Bytes
= min(Bytes
, cbDst
);
528 for (Y
= 0; Y
< nHeight
; Y
++)
530 if (iSrc
+ lDeltaSrcAbs
> cbSrc
|| iDst
+ lDeltaDst
> Bytes
)
532 LONG lDelta
= min(cbSrc
- iSrc
, Bytes
- iDst
);
533 NT_ASSERT(lDelta
>= 0);
534 RtlCopyMemory(pjDst
, pjSrc
, lDelta
);
540 RtlCopyMemory(pjDst
, pjSrc
, lDeltaDst
);
543 iSrc
+= lDeltaSrcAbs
;
555 OUT OPTIONAL PBYTE pUnsafeBits
)
561 /* Check parameters */
562 if (pUnsafeBits
!= NULL
&& cjBuffer
== 0)
567 /* Lock the bitmap */
568 psurf
= SURFACE_ShareLockSurface(hBitmap
);
571 EngSetLastError(ERROR_INVALID_HANDLE
);
575 /* Calculate the size of the bitmap in bytes */
576 cjSize
= WIDTH_BYTES_ALIGN16(psurf
->SurfObj
.sizlBitmap
.cx
,
577 BitsPerFormat(psurf
->SurfObj
.iBitmapFormat
)) *
578 abs(psurf
->SurfObj
.sizlBitmap
.cy
);
580 /* If the bits vector is null, the function should return the read size */
581 if (pUnsafeBits
== NULL
)
583 SURFACE_ShareUnlockSurface(psurf
);
587 /* Don't copy more bytes than the buffer has */
588 cjBuffer
= min(cjBuffer
, cjSize
);
590 // FIXME: Use MmSecureVirtualMemory
593 ProbeForWrite(pUnsafeBits
, cjBuffer
, 1);
594 ret
= UnsafeGetBitmapBits(psurf
, cjBuffer
, pUnsafeBits
);
596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
602 SURFACE_ShareUnlockSurface(psurf
);
612 IN PBYTE pUnsafeBits
)
617 if (pUnsafeBits
== NULL
|| Bytes
== 0)
622 if (GDI_HANDLE_IS_STOCKOBJ(hBitmap
))
627 psurf
= SURFACE_ShareLockSurface(hBitmap
);
630 EngSetLastError(ERROR_INVALID_HANDLE
);
634 if (((psurf
->flags
& API_BITMAP
) == 0) ||
635 (psurf
->SurfObj
.iBitmapFormat
> BMF_32BPP
))
637 DPRINT1("Invalid bitmap: iBitmapFormat = %lu, flags = 0x%lx\n",
638 psurf
->SurfObj
.iBitmapFormat
,
640 EngSetLastError(ERROR_INVALID_HANDLE
);
641 SURFACE_ShareUnlockSurface(psurf
);
647 /* NOTE: Win2k3 doesn't check WORD alignment here. */
648 ProbeForWrite(pUnsafeBits
, Bytes
, 1);
649 ret
= UnsafeSetBitmapBits(psurf
, Bytes
, pUnsafeBits
);
651 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
657 SURFACE_ShareUnlockSurface(psurf
);
663 NtGdiSetBitmapDimension(
675 psurf
= SURFACE_ShareLockSurface(hBitmap
);
678 EngSetLastError(ERROR_INVALID_HANDLE
);
686 ProbeForWrite(Size
, sizeof(SIZE
), 1);
687 *Size
= psurf
->sizlDim
;
689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
696 /* The dimension is changed even if writing the old value failed */
697 psurf
->sizlDim
.cx
= Width
;
698 psurf
->sizlDim
.cy
= Height
;
700 SURFACE_ShareUnlockSurface(psurf
);
705 /* Internal Functions */
709 BITMAP_CopyBitmap(HBITMAP hBitmap
)
712 SURFACE
*psurfSrc
, *psurfNew
;
714 /* Fail, if no source bitmap is given */
715 if (hBitmap
== NULL
) return 0;
717 /* Lock the source bitmap */
718 psurfSrc
= SURFACE_ShareLockSurface(hBitmap
);
719 if (psurfSrc
== NULL
)
724 /* Allocate a new bitmap with the same dimensions as the source bmp */
725 hbmNew
= GreCreateBitmapEx(psurfSrc
->SurfObj
.sizlBitmap
.cx
,
726 psurfSrc
->SurfObj
.sizlBitmap
.cy
,
727 abs(psurfSrc
->SurfObj
.lDelta
),
728 psurfSrc
->SurfObj
.iBitmapFormat
,
729 psurfSrc
->SurfObj
.fjBitmap
& BMF_TOPDOWN
,
730 psurfSrc
->SurfObj
.cjBits
,
736 /* Lock the new bitmap */
737 psurfNew
= SURFACE_ShareLockSurface(hbmNew
);
740 /* Copy the bitmap bits to the new bitmap buffer */
741 RtlCopyMemory(psurfNew
->SurfObj
.pvBits
,
742 psurfSrc
->SurfObj
.pvBits
,
743 psurfNew
->SurfObj
.cjBits
);
746 /* Reference the palette of the source bitmap and use it */
747 SURFACE_vSetPalette(psurfNew
, psurfSrc
->ppal
);
749 /* Unlock the new surface */
750 SURFACE_ShareUnlockSurface(psurfNew
);
754 /* Failed to lock the bitmap, shouldn't happen */
755 GreDeleteObject(hbmNew
);
760 /* Unlock the source bitmap and return the handle of the new bitmap */
761 SURFACE_ShareUnlockSurface(psurfSrc
);
766 BITMAP_GetObject(SURFACE
*psurf
, INT Count
, LPVOID buffer
)
770 if (!buffer
) return sizeof(BITMAP
);
771 if ((UINT
)Count
< sizeof(BITMAP
)) return 0;
773 /* Always fill a basic BITMAP structure */
776 pBitmap
->bmWidth
= psurf
->SurfObj
.sizlBitmap
.cx
;
777 pBitmap
->bmHeight
= psurf
->SurfObj
.sizlBitmap
.cy
;
778 pBitmap
->bmPlanes
= 1;
779 pBitmap
->bmBitsPixel
= BitsPerFormat(psurf
->SurfObj
.iBitmapFormat
);
780 pBitmap
->bmWidthBytes
= WIDTH_BYTES_ALIGN16(pBitmap
->bmWidth
, pBitmap
->bmBitsPixel
);
782 /* Check for DIB section */
785 /* Set bmBits in this case */
786 pBitmap
->bmBits
= psurf
->SurfObj
.pvBits
;
787 /* DIBs data are 32 bits aligned */
788 pBitmap
->bmWidthBytes
= WIDTH_BYTES_ALIGN32(pBitmap
->bmWidth
, pBitmap
->bmBitsPixel
);
790 if (Count
>= sizeof(DIBSECTION
))
792 /* Fill rest of DIBSECTION */
793 PDIBSECTION pds
= buffer
;
795 pds
->dsBmih
.biSize
= sizeof(BITMAPINFOHEADER
);
796 pds
->dsBmih
.biWidth
= pds
->dsBm
.bmWidth
;
797 pds
->dsBmih
.biHeight
= pds
->dsBm
.bmHeight
;
798 pds
->dsBmih
.biPlanes
= pds
->dsBm
.bmPlanes
;
799 pds
->dsBmih
.biBitCount
= pds
->dsBm
.bmBitsPixel
;
801 switch (psurf
->SurfObj
.iBitmapFormat
)
806 pds
->dsBmih
.biCompression
= BI_RGB
;
810 if (psurf
->ppal
->flFlags
& PAL_RGB16_555
)
811 pds
->dsBmih
.biCompression
= BI_RGB
;
813 pds
->dsBmih
.biCompression
= BI_BITFIELDS
;
818 /* 24/32bpp BI_RGB is actually BGR format */
819 if (psurf
->ppal
->flFlags
& PAL_BGR
)
820 pds
->dsBmih
.biCompression
= BI_RGB
;
822 pds
->dsBmih
.biCompression
= BI_BITFIELDS
;
826 pds
->dsBmih
.biCompression
= BI_RLE4
;
829 pds
->dsBmih
.biCompression
= BI_RLE8
;
832 pds
->dsBmih
.biCompression
= BI_JPEG
;
835 pds
->dsBmih
.biCompression
= BI_PNG
;
838 ASSERT(FALSE
); /* This shouldn't happen */
841 pds
->dsBmih
.biSizeImage
= psurf
->SurfObj
.cjBits
;
842 pds
->dsBmih
.biXPelsPerMeter
= 0;
843 pds
->dsBmih
.biYPelsPerMeter
= 0;
844 pds
->dsBmih
.biClrUsed
= psurf
->ppal
->NumColors
;
845 pds
->dsBmih
.biClrImportant
= psurf
->biClrImportant
;
846 pds
->dsBitfields
[0] = psurf
->ppal
->RedMask
;
847 pds
->dsBitfields
[1] = psurf
->ppal
->GreenMask
;
848 pds
->dsBitfields
[2] = psurf
->ppal
->BlueMask
;
849 pds
->dshSection
= psurf
->hDIBSection
;
850 pds
->dsOffset
= psurf
->dwOffset
;
852 return sizeof(DIBSECTION
);
857 /* Not set according to wine test, confirmed in win2k */
858 pBitmap
->bmBits
= NULL
;
861 return sizeof(BITMAP
);
873 PSURFACE psurf
= SURFACE_ShareLockSurface(hsurf
);
877 SURFACE_ShareUnlockSurface(psurf
);