+FORCEINLINE
+DWORD
+GetBMIColor(CONST BITMAPINFO* pbmi, INT i)
+{
+ DWORD dwRet = 0;
+ INT size;
+ if(pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
+ {
+ /* BITMAPCOREINFO holds RGBTRIPLE values */
+ size = sizeof(RGBTRIPLE);
+ }
+ else
+ {
+ size = sizeof(RGBQUAD);
+ }
+ memcpy(&dwRet, (PBYTE)pbmi + pbmi->bmiHeader.biSize + i*size, size);
+ return dwRet;
+}
+
+FORCEINLINE
+VOID
+SetBMIColor(CONST BITMAPINFO* pbmi, DWORD* color, INT i)
+{
+ if(pbmi->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
+ {
+ *(RGBTRIPLE*)((PBYTE)pbmi + pbmi->bmiHeader.biSize + i*sizeof(RGBTRIPLE)) = *(RGBTRIPLE*)color;
+ }
+ else
+ {
+ *(RGBQUAD*)((PBYTE)pbmi + pbmi->bmiHeader.biSize + i*sizeof(RGBQUAD)) = *(RGBQUAD*)color;
+ }
+}
+
+NTSTATUS
+FASTCALL
+ProbeAndConvertToBitmapV5Info(
+ OUT PBITMAPV5INFO pbmiDst,
+ IN CONST BITMAPINFO* pbmiUnsafe,
+ IN DWORD dwColorUse)
+{
+ DWORD dwSize;
+ ULONG ulWidthBytes;
+ PBITMAPV5HEADER pbmhDst = &pbmiDst->bmiHeader;
+
+ /* Get the size and probe */
+ ProbeForRead(&pbmiUnsafe->bmiHeader.biSize, sizeof(DWORD), 1);
+ dwSize = pbmiUnsafe->bmiHeader.biSize;
+ ProbeForRead(pbmiUnsafe, dwSize, 1);
+
+ /* Check the size */
+ // FIXME: are intermediate sizes allowed? As what are they interpreted?
+ // make sure we don't use a too big dwSize later
+ if (dwSize != sizeof(BITMAPCOREHEADER) &&
+ dwSize != sizeof(BITMAPINFOHEADER) &&
+ dwSize != sizeof(BITMAPV4HEADER) &&
+ dwSize != sizeof(BITMAPV5HEADER))
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (dwSize == sizeof(BITMAPCOREHEADER))
+ {
+ PBITMAPCOREHEADER pbch = (PBITMAPCOREHEADER)pbmiUnsafe;
+
+ /* Manually copy the fields that are present */
+ pbmhDst->bV5Width = pbch->bcWidth;
+ pbmhDst->bV5Height = pbch->bcHeight;
+ pbmhDst->bV5Planes = pbch->bcPlanes;
+ pbmhDst->bV5BitCount = pbch->bcBitCount;
+
+ /* Set some default values */
+ pbmhDst->bV5Compression = BI_RGB;
+ pbmhDst->bV5SizeImage = 0;
+ pbmhDst->bV5XPelsPerMeter = 72;
+ pbmhDst->bV5YPelsPerMeter = 72;
+ pbmhDst->bV5ClrUsed = 0;
+ pbmhDst->bV5ClrImportant = 0;
+ }
+ else
+ {
+ /* Copy valid fields */
+ memcpy(pbmiDst, pbmiUnsafe, dwSize);
+
+ if(dwSize < sizeof(BITMAPV5HEADER))
+ {
+ /* Zero out the rest of the V5 header */
+ memset((char*)pbmiDst + dwSize, 0, sizeof(BITMAPV5HEADER) - dwSize);
+ }
+ }
+ pbmhDst->bV5Size = sizeof(BITMAPV5HEADER);
+
+
+ if (dwSize < sizeof(BITMAPV4HEADER))
+ {
+ if (pbmhDst->bV5Compression == BI_BITFIELDS)
+ {
+ pbmhDst->bV5RedMask = GetBMIColor(pbmiUnsafe, 0);
+ pbmhDst->bV5GreenMask = GetBMIColor(pbmiUnsafe, 1);
+ pbmhDst->bV5BlueMask = GetBMIColor(pbmiUnsafe, 2);
+ pbmhDst->bV5AlphaMask = 0;
+ pbmhDst->bV5ClrUsed = 0;
+ }
+
+// pbmhDst->bV5CSType;
+// pbmhDst->bV5Endpoints;
+// pbmhDst->bV5GammaRed;
+// pbmhDst->bV5GammaGreen;
+// pbmhDst->bV5GammaBlue;
+ }
+
+ if (dwSize < sizeof(BITMAPV5HEADER))
+ {
+// pbmhDst->bV5Intent;
+// pbmhDst->bV5ProfileData;
+// pbmhDst->bV5ProfileSize;
+// pbmhDst->bV5Reserved;
+ }
+
+ ulWidthBytes = ((pbmhDst->bV5Width * pbmhDst->bV5Planes *
+ pbmhDst->bV5BitCount + 31) & ~31) / 8;
+
+ if (pbmhDst->bV5SizeImage == 0)
+ pbmhDst->bV5SizeImage = abs(ulWidthBytes * pbmhDst->bV5Height);
+
+ if (pbmhDst->bV5ClrUsed == 0)
+ {
+ switch(pbmhDst->bV5BitCount)
+ {
+ case 1:
+ pbmhDst->bV5ClrUsed = 2;
+ break;
+ case 4:
+ pbmhDst->bV5ClrUsed = 16;
+ break;
+ case 8:
+ pbmhDst->bV5ClrUsed = 256;
+ break;
+ default:
+ pbmhDst->bV5ClrUsed = 0;
+ break;
+ }
+ }
+
+ if (pbmhDst->bV5Planes != 1)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (pbmhDst->bV5BitCount != 0 && pbmhDst->bV5BitCount != 1 &&
+ pbmhDst->bV5BitCount != 4 && pbmhDst->bV5BitCount != 8 &&
+ pbmhDst->bV5BitCount != 16 && pbmhDst->bV5BitCount != 24 &&
+ pbmhDst->bV5BitCount != 32)
+ {
+ DPRINT("Invalid bit count: %d\n", pbmhDst->bV5BitCount);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ((pbmhDst->bV5BitCount == 0 &&
+ pbmhDst->bV5Compression != BI_JPEG && pbmhDst->bV5Compression != BI_PNG))
+ {
+ DPRINT("Bit count 0 is invalid for compression %d.\n", pbmhDst->bV5Compression);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (pbmhDst->bV5Compression == BI_BITFIELDS &&
+ pbmhDst->bV5BitCount != 16 && pbmhDst->bV5BitCount != 32)
+ {
+ DPRINT("Bit count %d is invalid for compression BI_BITFIELDS.\n", pbmhDst->bV5BitCount);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Copy Colors */
+ if(pbmhDst->bV5ClrUsed)
+ {
+ INT i;
+ if(dwColorUse == DIB_PAL_COLORS)
+ {
+ RtlCopyMemory(pbmiDst->bmiColors,
+ pbmiUnsafe->bmiColors,
+ pbmhDst->bV5ClrUsed * sizeof(WORD));
+ }
+ else
+ {
+ for(i = 0; i < pbmhDst->bV5ClrUsed; i++)
+ {
+ ((DWORD*)pbmiDst->bmiColors)[i] = GetBMIColor(pbmiUnsafe, i);
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+FASTCALL
+GetBMIFromBitmapV5Info(IN PBITMAPV5INFO pbmiSrc,
+ OUT PBITMAPINFO pbmiDst,
+ IN DWORD dwColorUse)
+{
+ if(pbmiDst->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
+ {
+ /* Manually set value */
+ BITMAPCOREHEADER* pbmhCore = (BITMAPCOREHEADER*)&pbmiDst->bmiHeader;
+ pbmhCore->bcWidth = pbmiSrc->bmiHeader.bV5Width;
+ pbmhCore->bcHeight = pbmiSrc->bmiHeader.bV5Height;
+ pbmhCore->bcPlanes = pbmiSrc->bmiHeader.bV5Planes;
+ pbmhCore->bcBitCount = pbmiSrc->bmiHeader.bV5BitCount;
+ }
+ else
+ {
+ /* Copy valid Fields, keep bmiHeader.biSize safe */
+ RtlCopyMemory((PBYTE)pbmiDst + sizeof(DWORD), pbmiSrc, pbmiDst->bmiHeader.biSize - sizeof(DWORD));
+ }
+ if((pbmiDst->bmiHeader.biSize < sizeof(BITMAPV4HEADER)) &&
+ (pbmiSrc->bmiHeader.bV5Compression == BI_BITFIELDS))
+ {
+ /* Masks are already set in V4 and V5 headers */
+ SetBMIColor(pbmiDst, &pbmiSrc->bmiHeader.bV5RedMask, 0);
+ SetBMIColor(pbmiDst, &pbmiSrc->bmiHeader.bV5GreenMask, 1);
+ SetBMIColor(pbmiDst, &pbmiSrc->bmiHeader.bV5BlueMask, 2);
+ }
+ else
+ {
+ INT i;
+ if(dwColorUse == DIB_PAL_COLORS)
+ {
+ RtlCopyMemory(pbmiDst->bmiColors,
+ pbmiSrc->bmiColors,
+ pbmiSrc->bmiHeader.bV5ClrUsed * sizeof(WORD));
+ }
+ else
+ {
+ for(i = 0; i < pbmiSrc->bmiHeader.bV5ClrUsed; i++)
+ {
+ SetBMIColor(pbmiDst, (DWORD*)pbmiSrc->bmiColors + i, i);
+ }
+ }
+ }
+}
+