#include "precomp.h"

/**
 * @name CalculateColorTableSize
 *
 * Internal routine to calculate the number of color table entries.
 *
 * @param BitmapInfoHeader
 *        Input bitmap information header, can be any version of
 *        BITMAPINFOHEADER or BITMAPCOREHEADER.
 *
 * @param ColorSpec
 *        Pointer to variable which specifiing the color mode (DIB_RGB_COLORS
 *        or DIB_RGB_COLORS). On successful return this value is normalized
 *        according to the bitmap info.
 *
 * @param ColorTableSize
 *        On successful return this variable is filled with number of
 *        entries in color table for the image with specified parameters.
 *
 * @return
 *    TRUE if the input values together form a valid image, FALSE otherwise.
 */

BOOL STDCALL
CalculateColorTableSize(
   CONST BITMAPINFOHEADER *BitmapInfoHeader,
   UINT *ColorSpec,
   UINT *ColorTableSize)
{
   WORD BitCount;
   DWORD ClrUsed;
   DWORD Compression;

   /*
    * At first get some basic parameters from the passed BitmapInfoHeader
    * structure. It can have one of the following formats: 
    * - BITMAPCOREHEADER (the oldest one with totally different layout
    *                     from the others)
    * - BITMAPINFOHEADER (the standard and most common header)
    * - BITMAPV4HEADER (extension of BITMAPINFOHEADER)
    * - BITMAPV5HEADER (extension of BITMAPV4HEADER)
    */

   if (BitmapInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
   {
      BitCount = ((LPBITMAPCOREHEADER)BitmapInfoHeader)->bcBitCount;
      ClrUsed = 0;
      Compression = BI_RGB;
   }
   else
   {
      BitCount = BitmapInfoHeader->biBitCount;
      ClrUsed = BitmapInfoHeader->biClrUsed;
      Compression = BitmapInfoHeader->biCompression;
   }

   switch (Compression)
   {
      case BI_BITFIELDS:
         if (*ColorSpec == DIB_PAL_COLORS)
            *ColorSpec = DIB_RGB_COLORS;

         if (BitCount != 16 && BitCount != 32)
            return FALSE;

         /*
          * For BITMAPV4HEADER/BITMAPV5HEADER the masks are included in
          * the structure itself (bV4RedMask, bV4GreenMask, and bV4BlueMask).
          * For BITMAPINFOHEADER the color masks are stored in the palette.
          */

         if (BitmapInfoHeader->biSize > sizeof(BITMAPINFOHEADER))
            *ColorTableSize = 0;
         else
            *ColorTableSize = 3;
         
         return TRUE;

      case BI_RGB:
         switch (BitCount)
         {
            case 1:
               *ColorTableSize = ClrUsed ? min(ClrUsed, 2) : 2;
               return TRUE;

            case 4:
               *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
               return TRUE;

            case 8:
               *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
               return TRUE;

            default:
               if (*ColorSpec == DIB_PAL_COLORS)
                  *ColorSpec = DIB_RGB_COLORS;
               if (BitCount != 16 && BitCount != 24 && BitCount != 32)
                  return FALSE;
               *ColorTableSize = ClrUsed;
               return TRUE;
         }
         
      case BI_RLE4:
         if (BitCount == 4)
         {
            *ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
            return TRUE;
         }
         return FALSE;

      case BI_RLE8:
         if (BitCount == 8)
         {
            *ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
            return TRUE;
         }
         return FALSE;

      case BI_JPEG:
      case BI_PNG:
         *ColorTableSize = ClrUsed;
         return TRUE;

      default:
         return FALSE;      
   }
}

/**
 * @name ConvertBitmapInfo
 *
 * Internal routine to convert a user-passed BITMAPINFO structure into
 * unified BITMAPINFO structure.
 *
 * @param BitmapInfo
 *        Input bitmap info, can be any version of BITMAPINFO or
 *        BITMAPCOREINFO.
 * @param ColorSpec
 *        Specifies whether the bmiColors member of the BITMAPINFO structure
 *        contains a valid color table and, if so, whether the entries in
 *        this color table contain explicit red, green, blue (DIB_RGB_COLORS)
 *        values or palette indexes (DIB_PAL_COLORS).
 * @param BitmapInfoSize
 *        On successful return contains the size of the returned BITMAPINFO
 *        structure. If FollowedByData is TRUE the size includes the number
 *        of bytes occupied by the image data.
 * @param FollowedByData
 *        Specifies if the BITMAPINFO header is immediately followed
 *        by the actual bitmap data (eg. as passed to CreateDIBPatternBrush).
 *
 * @return
 *    Either the original BitmapInfo or newly allocated structure is
 *    returned. For the later case the caller is responsible for freeing the
 *    memory using RtlFreeHeap with the current process heap.
 *
 * @example
 *    PBITMAPINFO NewBitmapInfo;
 *    UINT NewBitmapInfoSize;
 *
 *    NewBitmapInfo = ConvertBitmapInfo(OldBitmapInfo, DIB_RGB_COLORS,
 *                                      &NewBitmapInfoSize, FALSE);
 *    if (NewBitmapInfo)
 *    {
 *       <do something with the bitmap info>
 *       if (NewBitmapInfo != OldBitmapInfo)
 *          RtlFreeHeap(RtlGetProcessHeap(), 0, NewBitmapInfo);
 *    }
 */

LPBITMAPINFO STDCALL
ConvertBitmapInfo(
   CONST BITMAPINFO *BitmapInfo,
   UINT ColorSpec,
   UINT *BitmapInfoSize,
   BOOL FollowedByData)
{
   LPBITMAPINFO NewBitmapInfo = (LPBITMAPINFO)BitmapInfo;
   LPBITMAPCOREINFO CoreBitmapInfo = (LPBITMAPCOREINFO)BitmapInfo;
   DWORD Size = 0;
   ULONG DataSize = 0;
   UINT PaletteEntryCount = 0;

   /*
    * At first check if the passed BitmapInfo structure has valid size. It
    * can have one of these headers: BITMAPCOREHEADER, BITMAPINFOHEADER,
    * BITMAPV4HEADER or BITMAPV5HEADER (see CalculateColorTableSize for
    * description).
    */

   if (BitmapInfo->bmiHeader.biSize != sizeof(BITMAPCOREHEADER) &&
       (BitmapInfo->bmiHeader.biSize < sizeof(BITMAPINFOHEADER) ||
        BitmapInfo->bmiHeader.biSize > sizeof(BITMAPV5HEADER)))
   {
      return NULL;
   }

   /*
    * Now calculate the color table size. Also if the bitmap info contains
    * invalid color information it's rejected here.
    */

   if (!CalculateColorTableSize(&BitmapInfo->bmiHeader, &ColorSpec,
                                &PaletteEntryCount))
   {
      return NULL;
   }

   /*
    * Calculate the size of image data if applicable. We must be careful
    * to do proper aligning on line ends.
    */

   if (FollowedByData)
   {
      if (BitmapInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
      {
         DataSize =
            CoreBitmapInfo->bmciHeader.bcHeight *
            CoreBitmapInfo->bmciHeader.bcWidth *               
            CoreBitmapInfo->bmciHeader.bcBitCount;
         DataSize = ((DataSize + 31) & ~31) / 8;
         DataSize *= CoreBitmapInfo->bmciHeader.bcPlanes;
      }
      else
      {
         if (BitmapInfo->bmiHeader.biCompression == BI_RGB ||
             BitmapInfo->bmiHeader.biCompression == BI_BITFIELDS)
         {
            DataSize =
               abs(BitmapInfo->bmiHeader.biHeight) *
               BitmapInfo->bmiHeader.biWidth *
               BitmapInfo->bmiHeader.biBitCount;
            DataSize = ((DataSize + 31) & ~31) / 8;
            DataSize *= BitmapInfo->bmiHeader.biPlanes;
         }
         else
         {
            DataSize = BitmapInfo->bmiHeader.biSizeImage;
         }
      }
   }

   /*
    * If BitmapInfo was originally BITMAPCOREINFO then we need to convert
    * it to the standard BITMAPINFO layout.
    */

   if (BitmapInfo->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
   {
      Size = sizeof(BITMAPINFOHEADER);
      if (ColorSpec == DIB_RGB_COLORS)
         Size += PaletteEntryCount * sizeof(RGBQUAD);
      else
         Size += PaletteEntryCount * sizeof(USHORT);
      Size += DataSize;

      NewBitmapInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
      if (NewBitmapInfo == NULL)
      {
         return NULL;
      }

      NewBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      NewBitmapInfo->bmiHeader.biWidth = CoreBitmapInfo->bmciHeader.bcWidth;
      NewBitmapInfo->bmiHeader.biHeight = CoreBitmapInfo->bmciHeader.bcHeight;
      NewBitmapInfo->bmiHeader.biPlanes = CoreBitmapInfo->bmciHeader.bcPlanes;
      NewBitmapInfo->bmiHeader.biBitCount = CoreBitmapInfo->bmciHeader.bcBitCount;
      NewBitmapInfo->bmiHeader.biCompression = BI_RGB;
      NewBitmapInfo->bmiHeader.biSizeImage = 0;
      NewBitmapInfo->bmiHeader.biXPelsPerMeter = 0;
      NewBitmapInfo->bmiHeader.biYPelsPerMeter = 0;
      NewBitmapInfo->bmiHeader.biClrUsed = 0;
      NewBitmapInfo->bmiHeader.biClrImportant = 0;

      if (PaletteEntryCount != 0)
      {
         if (ColorSpec == DIB_RGB_COLORS)
         {
            ULONG Index;

            for (Index = 0; Index < PaletteEntryCount; Index++)
            {
               NewBitmapInfo->bmiColors[Index].rgbRed =
                  CoreBitmapInfo->bmciColors[Index].rgbtRed;
               NewBitmapInfo->bmiColors[Index].rgbGreen =
                  CoreBitmapInfo->bmciColors[Index].rgbtGreen;
               NewBitmapInfo->bmiColors[Index].rgbBlue =
                  CoreBitmapInfo->bmciColors[Index].rgbtBlue;
               NewBitmapInfo->bmiColors[Index].rgbReserved = 0;
            }
         }
         else
         {
            RtlCopyMemory(NewBitmapInfo->bmiColors,
                          CoreBitmapInfo->bmciColors,
                          PaletteEntryCount * sizeof(USHORT));
         }
      }

      if (FollowedByData)
      {
         ULONG_PTR NewDataPtr, OldDataPtr;

         if (ColorSpec == DIB_RGB_COLORS)
         {
            NewDataPtr = (ULONG_PTR)(NewBitmapInfo->bmiColors +
                         PaletteEntryCount);
            OldDataPtr = (ULONG_PTR)(CoreBitmapInfo->bmciColors +
                         PaletteEntryCount);
         }
         else
         {
            NewDataPtr = (ULONG_PTR)(NewBitmapInfo->bmiColors) +
                         PaletteEntryCount * sizeof(USHORT);
            OldDataPtr = (ULONG_PTR)(CoreBitmapInfo->bmciColors) +
                         PaletteEntryCount * sizeof(USHORT);
         }

         RtlCopyMemory((PVOID)NewDataPtr, (PVOID)OldDataPtr, DataSize);
      }
   }

   Size = NewBitmapInfo->bmiHeader.biSize;
   if (ColorSpec == DIB_RGB_COLORS)
      Size += PaletteEntryCount * sizeof(RGBQUAD);
   else
      Size += PaletteEntryCount * sizeof(USHORT);
   Size += DataSize;
   *BitmapInfoSize = Size;

   return NewBitmapInfo;
}

VOID
STDCALL
LogFontA2W(LPLOGFONTW pW, CONST LOGFONTA *pA)
{
#define COPYS(f,len) MultiByteToWideChar ( CP_THREAD_ACP, 0, pA->f, len, pW->f, len )
#define COPYN(f) pW->f = pA->f

  COPYN(lfHeight);
  COPYN(lfWidth);
  COPYN(lfEscapement);
  COPYN(lfOrientation);
  COPYN(lfWeight);
  COPYN(lfItalic);
  COPYN(lfUnderline);
  COPYN(lfStrikeOut);
  COPYN(lfCharSet);
  COPYN(lfOutPrecision);
  COPYN(lfClipPrecision);
  COPYN(lfQuality);
  COPYN(lfPitchAndFamily);
  COPYS(lfFaceName,LF_FACESIZE);

#undef COPYN
#undef COPYS
}

VOID
STDCALL
LogFontW2A(LPLOGFONTA pA, CONST LOGFONTW *pW)
{
#define COPYS(f,len) WideCharToMultiByte ( CP_THREAD_ACP, 0, pW->f, len, pA->f, len, NULL, NULL )
#define COPYN(f) pA->f = pW->f

  COPYN(lfHeight);
  COPYN(lfWidth);
  COPYN(lfEscapement);
  COPYN(lfOrientation);
  COPYN(lfWeight);
  COPYN(lfItalic);
  COPYN(lfUnderline);
  COPYN(lfStrikeOut);
  COPYN(lfCharSet);
  COPYN(lfOutPrecision);
  COPYN(lfClipPrecision);
  COPYN(lfQuality);
  COPYN(lfPitchAndFamily);
  COPYS(lfFaceName,LF_FACESIZE);

#undef COPYN
#undef COPYS
}
