xref: /reactos/win32ss/gdi/gdi32/objects/bitmap.c (revision e4c48945)
1 #include <precomp.h>
2 
3 #include <pseh/pseh2.h>
4 
5 #define NDEBUG
6 #include <debug.h>
7 
8 /* Copied from win32ss/gdi/eng/surface.c */
9 ULONG
10 FASTCALL
BitmapFormat(ULONG cBits,ULONG iCompression)11 BitmapFormat(ULONG cBits, ULONG iCompression)
12 {
13     switch (iCompression)
14     {
15         case BI_RGB:
16             /* Fall through */
17         case BI_BITFIELDS:
18             if (cBits <= 1) return BMF_1BPP;
19             if (cBits <= 4) return BMF_4BPP;
20             if (cBits <= 8) return BMF_8BPP;
21             if (cBits <= 16) return BMF_16BPP;
22             if (cBits <= 24) return BMF_24BPP;
23             if (cBits <= 32) return BMF_32BPP;
24             return 0;
25 
26         case BI_RLE4:
27             return BMF_4RLE;
28 
29         case BI_RLE8:
30             return BMF_8RLE;
31 
32         default:
33             return 0;
34     }
35 }
36 
37 /* Copied from win32ss/gdi/eng/surface.c */
38 UCHAR
39 gajBitsPerFormat[11] =
40 {
41     0, /*  0: unused */
42     1, /*  1: BMF_1BPP */
43     4, /*  2: BMF_4BPP */
44     8, /*  3: BMF_8BPP */
45    16, /*  4: BMF_16BPP */
46    24, /*  5: BMF_24BPP */
47    32, /*  6: BMF_32BPP */
48     4, /*  7: BMF_4RLE */
49     8, /*  8: BMF_8RLE */
50     0, /*  9: BMF_JPEG */
51     0, /* 10: BMF_PNG */
52 };
53 
54 // From Yuan, ScanLineSize = (Width * bitcount + 31)/32
55 #define WIDTH_BYTES_ALIGN32(cx, bpp) ((((cx) * (bpp) + 31) & ~31) >> 3)
56 
57 /*
58  *           DIB_BitmapInfoSize
59  *
60  * Return the size of the bitmap info structure including color table.
61  * 11/16/1999 (RJJ) lifted from wine
62  */
63 
64 INT
DIB_BitmapInfoSize(const BITMAPINFO * info,WORD coloruse,BOOL max)65 FASTCALL DIB_BitmapInfoSize(
66     const BITMAPINFO * info,
67     WORD coloruse,
68     BOOL max)
69 {
70     unsigned int colors, size, masks = 0;
71 
72     if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
73     {
74         const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *) info;
75         size = sizeof(BITMAPCOREHEADER);
76         if (core->bcBitCount <= 8)
77         {
78             colors = 1 << core->bcBitCount;
79             if (coloruse == DIB_RGB_COLORS)
80                 size += colors * sizeof(RGBTRIPLE);
81             else
82                 size += colors * sizeof(WORD);
83         }
84         return size;
85     }
86     else /* assume BITMAPINFOHEADER */
87     {
88         colors = max ? (1 << info->bmiHeader.biBitCount) : info->bmiHeader.biClrUsed;
89         if (colors > 256)
90             colors = 256;
91         if (!colors && (info->bmiHeader.biBitCount <= 8))
92             colors = 1 << info->bmiHeader.biBitCount;
93         if (info->bmiHeader.biCompression == BI_BITFIELDS)
94             masks = 3;
95         size = max(info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD));
96         return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD));
97     }
98 }
99 
100 /*
101  * Return the full scan size for a bitmap.
102  *
103  * Based on Wine, Utils.c and Windows Graphics Prog pg 595, SDK amvideo.h.
104  */
105 UINT
106 FASTCALL
DIB_BitmapMaxBitsSize(PBITMAPINFO Info,UINT ScanLines)107 DIB_BitmapMaxBitsSize(
108     PBITMAPINFO Info,
109     UINT ScanLines)
110 {
111     UINT Ret;
112 
113     if (!Info)
114         return 0;
115 
116     if (Info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
117     {
118         PBITMAPCOREHEADER Core = (PBITMAPCOREHEADER) Info;
119         Ret = WIDTH_BYTES_ALIGN32(Core->bcWidth * Core->bcPlanes,
120                 Core->bcBitCount) * ScanLines;
121     }
122     else /* assume BITMAPINFOHEADER */
123     {
124         if ((Info->bmiHeader.biCompression == BI_RGB) || (Info->bmiHeader.biCompression == BI_BITFIELDS))
125         {
126             Ret = WIDTH_BYTES_ALIGN32(
127                     Info->bmiHeader.biWidth * Info->bmiHeader.biPlanes,
128                     Info->bmiHeader.biBitCount) * ScanLines;
129         }
130         else
131         {
132             Ret = Info->bmiHeader.biSizeImage;
133         }
134     }
135     return Ret;
136 }
137 
138 /*
139  * DIB_GetBitmapInfo is complete copy of wine cvs 2/9-2006
140  * from file dib.c from gdi32.dll or orginal version
141  * did not calc the info right for some headers.
142  */
143 INT
144 WINAPI
DIB_GetBitmapInfo(const BITMAPINFOHEADER * header,PLONG width,PLONG height,PWORD planes,PWORD bpp,PLONG compr,PLONG size)145 DIB_GetBitmapInfo(
146     const BITMAPINFOHEADER *header,
147     PLONG width,
148     PLONG height,
149     PWORD planes,
150     PWORD bpp,
151     PLONG compr,
152     PLONG size)
153 {
154     if (header->biSize == sizeof(BITMAPCOREHEADER))
155     {
156         BITMAPCOREHEADER *core = (BITMAPCOREHEADER *) header;
157         *width = core->bcWidth;
158         *height = core->bcHeight;
159         *planes = core->bcPlanes;
160         *bpp = core->bcBitCount;
161         *compr = 0;
162         *size = 0;
163         return 0;
164     }
165 
166     if (header->biSize == sizeof(BITMAPINFOHEADER))
167     {
168         *width = header->biWidth;
169         *height = header->biHeight;
170         *planes = header->biPlanes;
171         *bpp = header->biBitCount;
172         *compr = header->biCompression;
173         *size = header->biSizeImage;
174         return 1;
175     }
176 
177     if (header->biSize == sizeof(BITMAPV4HEADER))
178     {
179         BITMAPV4HEADER *v4hdr = (BITMAPV4HEADER *) header;
180         *width = v4hdr->bV4Width;
181         *height = v4hdr->bV4Height;
182         *planes = v4hdr->bV4Planes;
183         *bpp = v4hdr->bV4BitCount;
184         *compr = v4hdr->bV4V4Compression;
185         *size = v4hdr->bV4SizeImage;
186         return 4;
187     }
188 
189     if (header->biSize == sizeof(BITMAPV5HEADER))
190     {
191         BITMAPV5HEADER *v5hdr = (BITMAPV5HEADER *) header;
192         *width = v5hdr->bV5Width;
193         *height = v5hdr->bV5Height;
194         *planes = v5hdr->bV5Planes;
195         *bpp = v5hdr->bV5BitCount;
196         *compr = v5hdr->bV5Compression;
197         *size = v5hdr->bV5SizeImage;
198         return 5;
199     }
200     DPRINT("(%lu): wrong size for header\n", header->biSize);
201     return -1;
202 }
203 
204 /*
205  * @implemented
206  */
207 int
208 WINAPI
GdiGetBitmapBitsSize(BITMAPINFO * lpbmi)209 GdiGetBitmapBitsSize(
210     BITMAPINFO *lpbmi)
211 {
212     UINT Ret;
213 
214     if (!lpbmi)
215         return 0;
216 
217     if (lpbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
218     {
219         PBITMAPCOREHEADER Core = (PBITMAPCOREHEADER) lpbmi;
220         Ret =
221         WIDTH_BYTES_ALIGN32(Core->bcWidth * Core->bcPlanes,
222                 Core->bcBitCount) * Core->bcHeight;
223     }
224     else /* assume BITMAPINFOHEADER */
225     {
226         if (!(lpbmi->bmiHeader.biCompression) || (lpbmi->bmiHeader.biCompression == BI_BITFIELDS))
227         {
228             Ret = WIDTH_BYTES_ALIGN32(
229                     lpbmi->bmiHeader.biWidth * lpbmi->bmiHeader.biPlanes,
230                     lpbmi->bmiHeader.biBitCount) * abs(lpbmi->bmiHeader.biHeight);
231         }
232         else
233         {
234             Ret = lpbmi->bmiHeader.biSizeImage;
235         }
236     }
237     return Ret;
238 }
239 
240 /*
241  * @implemented
242  */
243 HBITMAP
244 WINAPI
CreateDIBSection(HDC hDC,CONST BITMAPINFO * BitmapInfo,UINT Usage,VOID ** Bits,HANDLE hSection,DWORD dwOffset)245 CreateDIBSection(
246     HDC hDC,
247     CONST BITMAPINFO *BitmapInfo,
248     UINT Usage,
249     VOID **Bits,
250     HANDLE hSection,
251     DWORD dwOffset)
252 {
253     PBITMAPINFO pConvertedInfo;
254     UINT ConvertedInfoSize;
255     HBITMAP hBitmap = NULL;
256     PVOID bmBits = NULL;
257 
258     pConvertedInfo = ConvertBitmapInfo(BitmapInfo,
259                                        Usage,
260                                        &ConvertedInfoSize,
261                                        FALSE);
262     if (pConvertedInfo)
263     {
264         // Verify header due to converted may == info.
265         if (pConvertedInfo->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER))
266         {
267             if (pConvertedInfo->bmiHeader.biCompression == BI_JPEG ||
268                 pConvertedInfo->bmiHeader.biCompression == BI_PNG)
269             {
270                 SetLastError(ERROR_INVALID_PARAMETER);
271                 return NULL;
272             }
273         }
274         hBitmap = NtGdiCreateDIBSection(hDC,
275                                         hSection,
276                                         dwOffset,
277                                         pConvertedInfo,
278                                         Usage,
279                                         ConvertedInfoSize,
280                                         0, // fl
281                                         0, // dwColorSpace
282                                         &bmBits);
283         if (!hBitmap)
284             bmBits = NULL;
285 
286         if (BitmapInfo != pConvertedInfo)
287             RtlFreeHeap(RtlGetProcessHeap(), 0, pConvertedInfo);
288     }
289 
290     if (Bits)
291         *Bits = bmBits;
292 
293     return hBitmap;
294 }
295 
296 
297 /*
298  * @implemented
299  */
300 HBITMAP
301 WINAPI
CreateBitmap(INT Width,INT Height,UINT Planes,UINT BitsPixel,CONST VOID * pUnsafeBits)302 CreateBitmap(
303     INT Width,
304     INT Height,
305     UINT Planes,
306     UINT BitsPixel,
307     CONST VOID* pUnsafeBits)
308 {
309     if (Width && Height)
310     {
311         return NtGdiCreateBitmap(Width, Height, Planes, BitsPixel, (LPBYTE) pUnsafeBits);
312     }
313     else
314     {
315         /* Return 1x1 bitmap */
316         return GetStockObject(DEFAULT_BITMAP);
317     }
318 }
319 
320 /*
321  * @implemented
322  */
323 HBITMAP
324 WINAPI
CreateBitmapIndirect(const BITMAP * pbm)325 CreateBitmapIndirect(
326     const BITMAP *pbm)
327 {
328     HBITMAP bitmap = NULL;
329 
330     /* Note windows xp/2003 does not check if pbm is NULL or not */
331     if ((pbm->bmWidthBytes != 0) && (!(pbm->bmWidthBytes & 1)))
332 
333     {
334         bitmap = CreateBitmap(pbm->bmWidth, pbm->bmHeight, pbm->bmPlanes, pbm->bmBitsPixel,
335             pbm->bmBits);
336     }
337     else
338     {
339         SetLastError(ERROR_INVALID_PARAMETER);
340     }
341 
342     return bitmap;
343 }
344 
345 HBITMAP
346 WINAPI
CreateDiscardableBitmap(HDC hDC,INT Width,INT Height)347 CreateDiscardableBitmap(
348     HDC hDC,
349     INT Width,
350     INT Height)
351 {
352     return CreateCompatibleBitmap(hDC, Width, Height);
353 }
354 
355 HBITMAP
356 WINAPI
CreateCompatibleBitmap(HDC hDC,INT Width,INT Height)357 CreateCompatibleBitmap(
358     HDC hDC,
359     INT Width,
360     INT Height)
361 {
362     PDC_ATTR pDc_Attr;
363 
364     if (!GdiGetHandleUserData(hDC, GDI_OBJECT_TYPE_DC, (PVOID) & pDc_Attr))
365         return NULL;
366 
367     if (!Width || !Height)
368         return GetStockObject(DEFAULT_BITMAP);
369 
370     if (!(pDc_Attr->ulDirty_ & DC_DIBSECTION))
371     {
372         return NtGdiCreateCompatibleBitmap(hDC, Width, Height);
373     }
374     else
375     {
376         HBITMAP hBmp = NULL;
377         struct
378         {
379             BITMAP bitmap;
380             BITMAPINFOHEADER bmih;
381             RGBQUAD rgbquad[256];
382         } buffer;
383         DIBSECTION* pDIBs = (DIBSECTION*) &buffer;
384         BITMAPINFO* pbmi = (BITMAPINFO*) &buffer.bmih;
385 
386         hBmp = NtGdiGetDCObject(hDC, GDI_OBJECT_TYPE_BITMAP);
387 
388         if (GetObjectA(hBmp, sizeof(DIBSECTION), pDIBs) != sizeof(DIBSECTION))
389             return NULL;
390 
391         if (pDIBs->dsBm.bmBitsPixel <= 8)
392             GetDIBColorTable(hDC, 0, 256, buffer.rgbquad);
393 
394         pDIBs->dsBmih.biWidth = Width;
395         pDIBs->dsBmih.biHeight = Height;
396 
397         return CreateDIBSection(hDC, pbmi, DIB_RGB_COLORS, NULL, NULL, 0);
398     }
399     return NULL;
400 }
401 
402 INT
403 WINAPI
GetDIBits(HDC hDC,HBITMAP hbmp,UINT uStartScan,UINT cScanLines,LPVOID lpvBits,LPBITMAPINFO lpbmi,UINT uUsage)404 GetDIBits(
405     HDC hDC,
406     HBITMAP hbmp,
407     UINT uStartScan,
408     UINT cScanLines,
409     LPVOID lpvBits,
410     LPBITMAPINFO lpbmi,
411     UINT uUsage)
412 {
413     UINT cjBmpScanSize;
414     UINT cjInfoSize;
415 
416     if (!hDC || !GdiValidateHandle((HGDIOBJ) hDC) || !lpbmi)
417     {
418         GdiSetLastError(ERROR_INVALID_PARAMETER);
419         return 0;
420     }
421 
422     cjBmpScanSize = DIB_BitmapMaxBitsSize(lpbmi, cScanLines);
423     /* Caller must provide maximum size possible */
424     cjInfoSize = DIB_BitmapInfoSize(lpbmi, uUsage, TRUE);
425 
426     if (lpvBits)
427     {
428         if (lpbmi->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER))
429         {
430             if (lpbmi->bmiHeader.biCompression == BI_JPEG
431                 || lpbmi->bmiHeader.biCompression == BI_PNG)
432             {
433                 SetLastError(ERROR_INVALID_PARAMETER);
434                 return 0;
435             }
436         }
437     }
438 
439     return NtGdiGetDIBitsInternal(hDC, hbmp, uStartScan, cScanLines, lpvBits, lpbmi, uUsage,
440         cjBmpScanSize, cjInfoSize);
441 }
442 
443 /*
444  * @implemented
445  */
446 HBITMAP
447 WINAPI
CreateDIBitmap(HDC hDC,const BITMAPINFOHEADER * Header,DWORD Init,LPCVOID Bits,const BITMAPINFO * Data,UINT ColorUse)448 CreateDIBitmap(
449     HDC hDC,
450     const BITMAPINFOHEADER *Header,
451     DWORD Init,
452     LPCVOID Bits,
453     const BITMAPINFO *Data,
454     UINT ColorUse)
455 {
456     LONG Width, Height, Compression, DibSize;
457     WORD Planes, BitsPerPixel;
458 //  PDC_ATTR pDc_Attr;
459     UINT cjBmpScanSize = 0;
460     HBITMAP hBitmap = NULL;
461     PBITMAPINFO pbmiConverted;
462     UINT cjInfoSize;
463 
464     /* Convert the BITMAPINFO if it is a COREINFO */
465     pbmiConverted = ConvertBitmapInfo(Data, ColorUse, &cjInfoSize, FALSE);
466 
467     /* Check for CBM_CREATDIB */
468     if (Init & CBM_CREATDIB)
469     {
470         if (cjInfoSize == 0)
471         {
472             goto Exit;
473         }
474         else if (Init & CBM_INIT)
475         {
476             if (Bits == NULL)
477             {
478                 goto Exit;
479             }
480         }
481         else
482         {
483             Bits = NULL;
484         }
485 
486         /* CBM_CREATDIB needs Data. */
487         if (pbmiConverted == NULL)
488         {
489             DPRINT1("CBM_CREATDIB needs a BITMAPINFO!\n");
490             goto Exit;
491         }
492 
493         /* It only works with PAL or RGB */
494         if (ColorUse > DIB_PAL_COLORS)
495         {
496             DPRINT1("Invalid ColorUse: %lu\n", ColorUse);
497             GdiSetLastError(ERROR_INVALID_PARAMETER);
498             goto Exit;
499         }
500 
501         /* Use the header from the data */
502         Header = &Data->bmiHeader;
503     }
504     else
505     {
506         if (Init & CBM_INIT)
507         {
508             if (Bits != NULL)
509             {
510                 if (cjInfoSize == 0)
511                 {
512                     goto Exit;
513                 }
514             }
515             else
516             {
517                 Init &= ~CBM_INIT;
518             }
519         }
520     }
521 
522     /* Header is required */
523     if (!Header)
524     {
525         DPRINT1("Header is NULL\n");
526         GdiSetLastError(ERROR_INVALID_PARAMETER);
527         goto Exit;
528     }
529 
530     /* Get the bitmap format and dimensions */
531     if (DIB_GetBitmapInfo(Header, &Width, &Height, &Planes, &BitsPerPixel, &Compression, &DibSize) == -1)
532     {
533         DPRINT1("DIB_GetBitmapInfo failed!\n");
534         GdiSetLastError(ERROR_INVALID_PARAMETER);
535         goto Exit;
536     }
537 
538     /* Check if the Compr is incompatible */
539     if ((Compression == BI_JPEG) || (Compression == BI_PNG))
540     {
541         DPRINT1("Invalid compression: %lu!\n", Compression);
542         goto Exit;
543     }
544 
545     /* Only DIB_RGB_COLORS (0), DIB_PAL_COLORS (1) and 2 are valid. */
546     if (ColorUse > DIB_PAL_COLORS + 1)
547     {
548         DPRINT1("Invalid compression: %lu!\n", Compression);
549         GdiSetLastError(ERROR_INVALID_PARAMETER);
550         goto Exit;
551     }
552 
553     /* If some Bits are given, only DIB_PAL_COLORS and DIB_RGB_COLORS are valid */
554     if (Bits && (ColorUse > DIB_PAL_COLORS))
555     {
556         DPRINT1("Invalid ColorUse: %lu\n", ColorUse);
557         GdiSetLastError(ERROR_INVALID_PARAMETER);
558         goto Exit;
559     }
560 
561     /* Negative width is not allowed */
562     if (Width < 0)
563     {
564         DPRINT1("Negative width: %li\n", Width);
565         goto Exit;
566     }
567 
568     /* Top-down DIBs have a negative height. */
569     Height = abs(Height);
570 
571 // For Icm support.
572 // GdiGetHandleUserData(hdc, GDI_OBJECT_TYPE_DC, (PVOID)&pDc_Attr))
573 
574     cjBmpScanSize = GdiGetBitmapBitsSize(pbmiConverted);
575 
576     DPRINT("pBMI %p, Size bpp %u, dibsize %d, Conv %u, BSS %u\n",
577            Data, BitsPerPixel, DibSize, cjInfoSize, cjBmpScanSize);
578 
579     if (!Width || !Height)
580     {
581         hBitmap = GetStockObject(DEFAULT_BITMAP);
582     }
583     else
584     {
585         hBitmap = NtGdiCreateDIBitmapInternal(hDC,
586                                               Width,
587                                               Height,
588                                               Init,
589                                               (LPBYTE)Bits,
590                                               (LPBITMAPINFO)pbmiConverted,
591                                               ColorUse,
592                                               cjInfoSize,
593                                               cjBmpScanSize,
594                                               0, 0);
595     }
596 
597 Exit:
598     /* Cleanup converted BITMAPINFO */
599     if ((pbmiConverted != NULL) && (pbmiConverted != Data))
600     {
601         RtlFreeHeap(RtlGetProcessHeap(), 0, pbmiConverted);
602     }
603 
604     return hBitmap;
605 }
606 
607 /*
608  * @implemented
609  */
610 INT
611 WINAPI
SetDIBits(HDC hDC,HBITMAP hBitmap,UINT uStartScan,UINT cScanLines,CONST VOID * lpvBits,CONST BITMAPINFO * lpbmi,UINT fuColorUse)612 SetDIBits(
613     HDC hDC,
614     HBITMAP hBitmap,
615     UINT uStartScan,
616     UINT cScanLines,
617     CONST VOID *lpvBits,
618     CONST BITMAPINFO *lpbmi,
619     UINT fuColorUse)
620 {
621     HDC hDCc, SavehDC, nhDC;
622     DWORD dwWidth, dwHeight;
623     HGDIOBJ hOldBitmap;
624     HPALETTE hPal = NULL;
625     INT LinesCopied = 0;
626     BOOL newDC = FALSE;
627 
628     if (fuColorUse != DIB_RGB_COLORS && fuColorUse != DIB_PAL_COLORS)
629         return 0;
630 
631     if (!lpvBits || (GDI_HANDLE_GET_TYPE(hBitmap) != GDI_OBJECT_TYPE_BITMAP))
632         return 0;
633 
634     if (lpbmi->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER))
635     {
636         if (lpbmi->bmiHeader.biCompression == BI_JPEG
637             || lpbmi->bmiHeader.biCompression == BI_PNG)
638         {
639             SetLastError(ERROR_INVALID_PARAMETER);
640             return 0;
641         }
642     }
643 
644     if (lpbmi->bmiHeader.biCompression == BI_BITFIELDS)
645     {
646         DWORD *masks = (DWORD *)lpbmi->bmiColors;
647         if (!masks[0] || !masks[1] || !masks[2])
648         {
649             SetLastError(ERROR_INVALID_PARAMETER);
650             return 0;
651         }
652     }
653 
654     hDCc = NtGdiGetDCforBitmap(hBitmap); // hDC can be NULL, so, get it from the bitmap.
655     SavehDC = hDCc;
656     if (!hDCc) // No DC associated with bitmap, Clone or Create one.
657     {
658         nhDC = CreateCompatibleDC(hDC);
659         if (!nhDC)
660             return 0;
661         newDC = TRUE;
662         SavehDC = nhDC;
663     }
664     else if (!SaveDC(hDCc))
665         return 0;
666 
667     hOldBitmap = SelectObject(SavehDC, hBitmap);
668 
669     if (hOldBitmap)
670     {
671         if (hDC)
672             hPal = SelectPalette(SavehDC, (HPALETTE) GetCurrentObject(hDC, OBJ_PAL), FALSE);
673 
674         if (lpbmi->bmiHeader.biSize < sizeof(BITMAPINFOHEADER))
675         {
676             PBITMAPCOREINFO pbci = (PBITMAPCOREINFO) lpbmi;
677             dwWidth = pbci->bmciHeader.bcWidth;
678             dwHeight = pbci->bmciHeader.bcHeight;
679         }
680         else
681         {
682             dwWidth = lpbmi->bmiHeader.biWidth;
683             dwHeight = abs(lpbmi->bmiHeader.biHeight);
684         }
685 
686         LinesCopied = SetDIBitsToDevice(SavehDC, 0, 0, dwWidth, dwHeight, 0, 0, uStartScan,
687             cScanLines, (void *) lpvBits, (LPBITMAPINFO) lpbmi, fuColorUse);
688 
689         if (hDC)
690             SelectPalette(SavehDC, hPal, FALSE);
691 
692         SelectObject(SavehDC, hOldBitmap);
693     }
694 
695     if (newDC)
696         DeleteDC(SavehDC);
697     else
698         RestoreDC(SavehDC, -1);
699 
700     return LinesCopied;
701 }
702 
703 /*
704  * @implemented
705  *
706  */
707 INT
708 WINAPI
SetDIBitsToDevice(HDC hdc,int XDest,int YDest,DWORD Width,DWORD Height,int XSrc,int YSrc,UINT StartScan,UINT ScanLines,CONST VOID * Bits,CONST BITMAPINFO * lpbmi,UINT ColorUse)709 SetDIBitsToDevice(
710     HDC hdc,
711     int XDest,
712     int YDest,
713     DWORD Width,
714     DWORD Height,
715     int XSrc,
716     int YSrc,
717     UINT StartScan,
718     UINT ScanLines,
719     CONST VOID *Bits,
720     CONST BITMAPINFO *lpbmi,
721     UINT ColorUse)
722 {
723     PDC_ATTR pDc_Attr;
724     PBITMAPINFO pConvertedInfo;
725     UINT ConvertedInfoSize;
726     INT LinesCopied = 0;
727     UINT cjBmpScanSize = 0;
728     BOOL Hit = FALSE;
729     PVOID pvSafeBits = (PVOID) Bits;
730     UINT bmiHeight;
731     BOOL top_down;
732     INT src_y = 0;
733     ULONG iFormat, cBitsPixel, cjBits, cjWidth;
734 
735     #define MaxScanLines 1000
736     #define MaxHeight 2000
737     #define MaxSourceHeight 2000
738     #define IS_ALIGNED(Pointer, Alignment) \
739         (((ULONG_PTR)(void *)(Pointer)) % (Alignment) == 0)
740 
741     if (!ScanLines || !lpbmi || !Bits)
742         return 0;
743 
744     DPRINT("ScanLines %d Height %d Width %d biHeight %d biWidth %d\n"
745            "    lpbmi '%p' ColorUse '%d' SizeImage '%d' StartScan %d\n"
746            "    biCompression '%d' biBitCount '%d'\n",
747            ScanLines, Height, Width, lpbmi->bmiHeader.biHeight,
748            lpbmi->bmiHeader.biWidth, lpbmi, ColorUse,
749            lpbmi->bmiHeader.biSizeImage, StartScan,
750            lpbmi->bmiHeader.biCompression, lpbmi->bmiHeader.biBitCount);
751 
752     if (lpbmi->bmiHeader.biWidth < 0)
753         return 0;
754 
755     if (ColorUse && ColorUse != DIB_PAL_COLORS && ColorUse != DIB_PAL_COLORS + 1)
756         return 0;
757 
758     pConvertedInfo = ConvertBitmapInfo(lpbmi, ColorUse, &ConvertedInfoSize, FALSE);
759     if (!pConvertedInfo)
760         return 0;
761 
762     if (ScanLines > MaxScanLines)
763     {
764         LinesCopied = 0;
765         goto Exit;
766     }
767 
768     bmiHeight = abs(pConvertedInfo->bmiHeader.biHeight);
769     top_down = (pConvertedInfo->bmiHeader.biHeight < 0);
770     if ((StartScan > bmiHeight) && (ScanLines > bmiHeight))
771     {
772         DPRINT("Returning ScanLines of '%d'\n", ScanLines);
773         LinesCopied = ScanLines;
774         goto Exit;
775     }
776 
777     if (pConvertedInfo->bmiHeader.biHeight == 0)
778     {
779         LinesCopied = 0;
780         goto Exit;
781     }
782 
783     if (!IS_ALIGNED(Bits, 2) && (ScanLines > Height))
784     {
785         LinesCopied = 0;
786         goto Exit;
787     }
788 
789     /* Below code modeled after Wine's nulldrv_SetDIBitsToDevice */
790     if (StartScan <= YSrc + bmiHeight)
791     {
792         if ((pConvertedInfo->bmiHeader.biCompression == BI_RLE8) ||
793             (pConvertedInfo->bmiHeader.biCompression == BI_RLE4))
794         {
795             StartScan = 0;
796             ScanLines = bmiHeight;
797         }
798         else
799         {
800             if (StartScan >= bmiHeight)
801             {
802                 LinesCopied = 0;
803                 goto Exit;
804             }
805             if (!top_down && ScanLines > bmiHeight - StartScan)
806             {
807                 ScanLines = bmiHeight - StartScan;
808             }
809             src_y = StartScan + ScanLines - (YSrc + Height);
810             if (!top_down)
811             {
812                 /* get rid of unnecessary lines */
813                 if ((src_y < 0 || src_y >= (INT)ScanLines) &&
814                     pConvertedInfo->bmiHeader.biCompression != BI_BITFIELDS)
815                 {
816                     LinesCopied = ScanLines;
817                     goto Exit;
818                 }
819                 if (YDest >= 0)
820                 {
821                     LinesCopied = ScanLines + StartScan;
822                     ScanLines -= src_y;
823                 }
824                 else
825                 {
826                     LinesCopied = ScanLines - src_y;
827                 }
828             }
829             else if (src_y < 0 || src_y >= (INT)ScanLines)
830             {
831                 if (lpbmi->bmiHeader.biHeight < 0 &&
832                     StartScan > MaxScanLines)
833                 {
834                     ScanLines = lpbmi->bmiHeader.biHeight - StartScan;
835                 }
836                 DPRINT("Returning ScanLines of '%d'\n", ScanLines);
837                 LinesCopied = ScanLines;
838                 goto Exit;
839             }
840             else
841             {
842                 LinesCopied = ScanLines;
843             }
844         }
845     }
846 
847     HANDLE_METADC(INT,
848                   SetDIBitsToDevice,
849                   0,
850                   hdc,
851                   XDest,
852                   YDest,
853                   Width,
854                   Height,
855                   XSrc,
856                   YSrc,
857                   StartScan,
858                   ScanLines,
859                   Bits,
860                   lpbmi,
861                   ColorUse);
862 
863     // Handle the "Special Case"!
864     {
865         PLDC pldc;
866         ULONG hType = GDI_HANDLE_GET_TYPE(hdc);
867         if (hType != GDILoObjType_LO_DC_TYPE && hType != GDILoObjType_LO_METADC16_TYPE)
868         {
869             pldc = GdiGetLDC(hdc);
870             if (pldc)
871             {
872                 if (pldc->Flags & LDC_STARTPAGE) StartPage(hdc);
873 
874                 if (pldc->Flags & LDC_SAPCALLBACK) GdiSAPCallback(pldc);
875 
876                 if (pldc->Flags & LDC_KILL_DOCUMENT)
877                 {
878                     LinesCopied = 0;
879                     goto Exit;
880                 }
881             }
882             else
883             {
884                 SetLastError(ERROR_INVALID_HANDLE);
885                 LinesCopied = 0;
886                 goto Exit;
887             }
888         }
889     }
890 
891     if ((pConvertedInfo->bmiHeader.biCompression == BI_RLE8) ||
892             (pConvertedInfo->bmiHeader.biCompression == BI_RLE4))
893     {
894         /* For compressed data, we must set the whole thing */
895         StartScan = 0;
896         ScanLines = pConvertedInfo->bmiHeader.biHeight;
897     }
898 
899     cjBmpScanSize = DIB_BitmapMaxBitsSize((LPBITMAPINFO) lpbmi, ScanLines);
900 
901     pvSafeBits = RtlAllocateHeap(GetProcessHeap(), 0, cjBmpScanSize);
902     if (pvSafeBits)
903     {
904         _SEH2_TRY
905         {
906             RtlCopyMemory(pvSafeBits, Bits, cjBmpScanSize);
907         }
908         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
909         {
910             Hit = TRUE;
911         }
912         _SEH2_END;
913 
914         if (Hit)
915         {
916             // We don't die, we continue on with a allocated safe pointer to kernel
917             // space.....
918 
919             if (!IS_ALIGNED(Bits, 2)) // If both Read Exception and mis-aligned
920             {
921                 LinesCopied = 0;
922                 goto Exit;
923             }
924 
925             DPRINT1("SetDIBitsToDevice fail to read BitMapInfo: %p or Bits: %p & Size: %u\n",
926                 pConvertedInfo, Bits, cjBmpScanSize);
927         }
928         DPRINT("SetDIBitsToDevice Allocate Bits %u!!!\n", cjBmpScanSize);
929     }
930 
931     if (!GdiGetHandleUserData(hdc, GDI_OBJECT_TYPE_DC, (PVOID) & pDc_Attr))
932     {
933         DPRINT1("SetDIBitsToDevice called on invalid DC %p (not owned?)\n", hdc);
934         SetLastError(ERROR_INVALID_PARAMETER);
935         LinesCopied = 0;
936         goto Exit;
937     }
938 
939     /* Calculation of ScanLines for NtGdiSetDIBitsToDeviceInternal */
940     if (YDest >= 0)
941     {
942         ScanLines = min(abs(Height), ScanLines);
943         if (YSrc > 0)
944             ScanLines += YSrc;
945     }
946     else
947     {
948          ScanLines = min(ScanLines,
949                          abs(pConvertedInfo->bmiHeader.biHeight) - StartScan);
950     }
951 
952     if (YDest >= 0 && YSrc > 0 && bmiHeight <= MaxHeight)
953     {
954         ScanLines += YSrc;
955     }
956 
957     /* Find Format from lpbmi which is now pConvertedInfo */
958     iFormat = BitmapFormat(pConvertedInfo->bmiHeader.biBitCount,
959                            pConvertedInfo->bmiHeader.biCompression);
960 
961     /* Get bits per pixel from the format */
962     cBitsPixel = gajBitsPerFormat[iFormat];
963 
964     cjWidth = WIDTH_BYTES_ALIGN32(pConvertedInfo->bmiHeader.biWidth, cBitsPixel);
965 
966     /* Calculate the correct bitmap size in bytes */
967     if (!NT_SUCCESS(RtlULongMult(cjWidth, max(ScanLines, LinesCopied), &cjBits)))
968     {
969         DPRINT1("Overflow calculating size: cjWidth %lu, ScanLines %lu\n",
970                 cjWidth, max(ScanLines, LinesCopied));
971         goto Exit;
972     }
973 
974     /* Make sure the buffer is large enough */
975     if (pConvertedInfo->bmiHeader.biSizeImage < cjBits)
976     {
977         DPRINT("Buffer is too small, required: %lu, got %lu\n",
978                cjBits, pConvertedInfo->bmiHeader.biSizeImage);
979         if (pConvertedInfo->bmiHeader.biCompression == BI_RGB)
980         {
981             pConvertedInfo->bmiHeader.biSizeImage = cjBits;
982         }
983     }
984 
985     /*
986      if ( !pDc_Attr || // DC is Public
987      ColorUse == DIB_PAL_COLORS ||
988      ((pConvertedInfo->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER)) &&
989      (pConvertedInfo->bmiHeader.biCompression == BI_JPEG ||
990      pConvertedInfo->bmiHeader.biCompression  == BI_PNG )) )*/
991     {
992         LinesCopied = NtGdiSetDIBitsToDeviceInternal(hdc, XDest, YDest,
993             Width, Height, XSrc, YSrc,
994             StartScan, ScanLines, (LPBYTE) pvSafeBits,
995             (LPBITMAPINFO) pConvertedInfo, ColorUse,
996             cjBmpScanSize, ConvertedInfoSize,
997             TRUE,
998             NULL);
999     }
1000 
1001     if (bmiHeight > MaxScanLines)
1002     {
1003         LinesCopied = ScanLines;
1004     }
1005 
1006     if (YDest < 0)
1007     {
1008         if (top_down)
1009             LinesCopied = ScanLines;
1010         else
1011             LinesCopied = ScanLines - src_y;
1012     }
1013 
1014     if (pConvertedInfo->bmiHeader.biCompression == BI_RLE8 ||
1015         pConvertedInfo->bmiHeader.biCompression == BI_RLE4)
1016     {
1017         LinesCopied = bmiHeight;
1018     }
1019 
1020     if (pConvertedInfo->bmiHeader.biHeight < 0)
1021     {
1022         if (pConvertedInfo->bmiHeader.biHeight < -MaxSourceHeight ||
1023             (YDest >= 0 && src_y < -ScanLines))
1024         {
1025             LinesCopied = ScanLines + src_y;
1026         }
1027         else
1028         {
1029             LinesCopied = ScanLines;
1030         }
1031     }
1032 
1033 Exit:
1034     if (Bits != pvSafeBits)
1035         RtlFreeHeap(RtlGetProcessHeap(), 0, pvSafeBits);
1036     if (lpbmi != pConvertedInfo)
1037         RtlFreeHeap(RtlGetProcessHeap(), 0, pConvertedInfo);
1038 
1039     return LinesCopied;
1040 }
1041 
1042 /*
1043  * @unimplemented
1044  */
1045 int
1046 WINAPI
StretchDIBits(HDC hdc,int XDest,int YDest,int nDestWidth,int nDestHeight,int XSrc,int YSrc,int nSrcWidth,int nSrcHeight,CONST VOID * lpBits,CONST BITMAPINFO * lpBitsInfo,UINT iUsage,DWORD dwRop)1047 StretchDIBits(
1048     HDC hdc,
1049     int XDest,
1050     int YDest,
1051     int nDestWidth,
1052     int nDestHeight,
1053     int XSrc,
1054     int YSrc,
1055     int nSrcWidth,
1056     int nSrcHeight,
1057     CONST VOID *lpBits,
1058     CONST BITMAPINFO *lpBitsInfo,
1059     UINT iUsage,
1060     DWORD dwRop)
1061 
1062 {
1063     PDC_ATTR pDc_Attr;
1064     PBITMAPINFO pConvertedInfo = NULL;
1065     UINT ConvertedInfoSize = 0;
1066     INT LinesCopied = 0;
1067     UINT cjBmpScanSize = 0;
1068     PVOID pvSafeBits = NULL;
1069     BOOL Hit = FALSE;
1070 
1071     DPRINT("StretchDIBits %p : %p : %u\n", lpBits, lpBitsInfo, iUsage);
1072 
1073     HANDLE_METADC( int,
1074                    StretchDIBits,
1075                    0,
1076                    hdc,
1077                    XDest,
1078                    YDest,
1079                    nDestWidth,
1080                    nDestHeight,
1081                    XSrc,
1082                    YSrc,
1083                    nSrcWidth,
1084                    nSrcHeight,
1085                    lpBits,
1086                    lpBitsInfo,
1087                    iUsage,
1088                    dwRop );
1089 
1090     if ( GdiConvertAndCheckDC(hdc) == NULL ) return 0;
1091 
1092     pConvertedInfo = ConvertBitmapInfo(lpBitsInfo, iUsage, &ConvertedInfoSize, FALSE);
1093     if (!pConvertedInfo)
1094     {
1095         return 0;
1096     }
1097 
1098     cjBmpScanSize = GdiGetBitmapBitsSize((BITMAPINFO *) pConvertedInfo);
1099 
1100     if (lpBits)
1101     {
1102         pvSafeBits = RtlAllocateHeap(GetProcessHeap(), 0, cjBmpScanSize);
1103         if (pvSafeBits)
1104         {
1105             _SEH2_TRY
1106             {
1107                 RtlCopyMemory(pvSafeBits, lpBits, cjBmpScanSize);
1108             }
1109             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1110             {
1111                 Hit = TRUE;
1112             }
1113             _SEH2_END
1114 
1115             if (Hit)
1116             {
1117                 // We don't die, we continue on with a allocated safe pointer to kernel
1118                 // space.....
1119                 DPRINT1("StretchDIBits fail to read BitMapInfo: %p or Bits: %p & Size: %u\n",
1120                     pConvertedInfo, lpBits, cjBmpScanSize);
1121             }
1122             DPRINT("StretchDIBits Allocate Bits %u!!!\n", cjBmpScanSize);
1123         }
1124     }
1125 
1126     if (!GdiGetHandleUserData(hdc, GDI_OBJECT_TYPE_DC, (PVOID) & pDc_Attr))
1127     {
1128         DPRINT1("StretchDIBits called on invalid DC %p (not owned?)\n", hdc);
1129         SetLastError(ERROR_INVALID_PARAMETER);
1130         LinesCopied = 0;
1131         goto Exit;
1132     }
1133     /*
1134      if ( !pDc_Attr ||
1135      iUsage == DIB_PAL_COLORS ||
1136      ((pConvertedInfo->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER)) &&
1137      (pConvertedInfo->bmiHeader.biCompression == BI_JPEG ||
1138      pConvertedInfo->bmiHeader.biCompression  == BI_PNG )) )*/
1139     {
1140         LinesCopied = NtGdiStretchDIBitsInternal( hdc,
1141                                                   XDest,
1142                                                   YDest,
1143                                                   nDestWidth,
1144                                                   nDestHeight,
1145                                                   XSrc,
1146                                                   YSrc,
1147                                                   nSrcWidth,
1148                                                   nSrcHeight,
1149                                                   pvSafeBits,
1150                                                   pConvertedInfo,
1151                                                   (DWORD) iUsage,
1152                                                   dwRop,
1153                                                   ConvertedInfoSize,
1154                                                   cjBmpScanSize,
1155                                                   NULL );
1156     }
1157 Exit:
1158     if (pvSafeBits)
1159         RtlFreeHeap(RtlGetProcessHeap(), 0, pvSafeBits);
1160     if (lpBitsInfo != pConvertedInfo)
1161         RtlFreeHeap(RtlGetProcessHeap(), 0, pConvertedInfo);
1162 
1163     return LinesCopied;
1164 }
1165 
1166 /*
1167  * @implemented
1168  */
1169 DWORD
1170 WINAPI
GetBitmapAttributes(HBITMAP hbm)1171 GetBitmapAttributes(HBITMAP hbm)
1172 {
1173     if ( GDI_HANDLE_IS_STOCKOBJ(hbm) )
1174     {
1175         return SC_BB_STOCKOBJ;
1176     }
1177     return 0;
1178 }
1179 
1180 /*
1181  * @implemented
1182  */
1183 HBITMAP
1184 WINAPI
SetBitmapAttributes(HBITMAP hbm,DWORD dwFlags)1185 SetBitmapAttributes(HBITMAP hbm, DWORD dwFlags)
1186 {
1187     if ( dwFlags & ~SC_BB_STOCKOBJ )
1188     {
1189         return NULL;
1190     }
1191     return NtGdiSetBitmapAttributes( hbm, dwFlags );
1192 }
1193 
1194 /*
1195  * @implemented
1196  */
1197 HBITMAP
1198 WINAPI
ClearBitmapAttributes(HBITMAP hbm,DWORD dwFlags)1199 ClearBitmapAttributes(HBITMAP hbm, DWORD dwFlags)
1200 {
1201     if ( dwFlags & ~SC_BB_STOCKOBJ )
1202     {
1203         return NULL;
1204     }
1205     return NtGdiClearBitmapAttributes( hbm, dwFlags );;
1206 }
1207 
1208 
1209