//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1992, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of TPalette, an encapsulation of the GDI Palette object
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/defs.h>
#include <owl/gdiobjec.h>
#include <owl/clipboar.h>
#include <owl/file.h>
#include <owl/filename.h>
#include <memory.h>
 
 
#if defined(__BORLANDC__)
# pragma option -w-ccc // Disable "Condition is always true/false"
#endif
 
using namespace std;
 
namespace owl {
 
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlGDI);        // General GDI diagnostic group
DIAG_DECLARE_GROUP(OwlGDIOrphan);  // Orphan control tracing group
 
//
// Constructors
//
 
//
/// Alias an existing palette handle. Assume ownership if autoDelete says so
//
/// Creates a TPalette object and sets the Handle data member to the given borrowed
/// handle. The ShouldDelete data member defaults to false, ensuring that the
/// borrowed handle will not be deleted when the C++ object is destroyed.
//
TPalette::TPalette(HPALETTE handle, TAutoDelete autoDelete)
:
  TGdiObject(handle, autoDelete)
{
  if (ShouldDelete)
    RefAdd(Handle, Palette);
}
 
//
/// Creates a TPalette object with values taken from the given clipboard.
//
TPalette::TPalette(const TClipboard& clipboard)
:
  TGdiObject(clipboard.GetClipboardData(CF_PALETTE))
{
  WARNX(OwlGDI, !Handle, 0, "Cannot create TPalette from the clipboard");
  CheckValid();
  RefAdd(Handle, Palette);
  RefInc(Handle);
}
 
//
/// This public copy constructor creates a complete copy of the given palette object
/// as in TPalette myPalette = yourPalette;
//
/// Always performs full, deep object copy
//
TPalette::TPalette(const TPalette& src)
{
  uint16  nColors;
  src.GetObject(nColors);
  if (nColors) {
    LOGPALETTE* logPal = (LOGPALETTE*) new
      uint8[sizeof(LOGPALETTE)+(nColors-1)*sizeof(PALETTEENTRY)];
 
    logPal->palVersion = 0x300;  // !CQ check to see if we should use 0x400
    logPal->palNumEntries = nColors;
    src.GetPaletteEntries(0, nColors, logPal->palPalEntry);
    Handle = ::CreatePalette(logPal);
    delete[] logPal;
  }
  else
    Handle = 0;  // Force a failure
 
  WARNX(OwlGDI, !Handle, 0, "Cannot create palette from palette " << static_cast<void*>(src.GetHandle()));
  CheckValid();
  RefAdd(Handle, Palette);
}
 
//
/// Creates a TPalette object from the given LOGPALETTE.
//
TPalette::TPalette(const LOGPALETTE& logPalette)
{
  Handle = ::CreatePalette(&logPalette);
  WARNX(OwlGDI, !Handle, 0, "Cannot create palette from LOGPALETTE @" << static_cast<const void*>(&logPalette));
  CheckValid();
  RefAdd(Handle, Palette);
}
 
#if defined(OWL5_COMPAT)
 
//
/// Creates a TPalette object from the given LOGPALETTE.
/// This overload is deprecated. Use the overload that takes a reference instead.
//
TPalette::TPalette(const LOGPALETTE * logPalette)
{
  PRECONDITION(logPalette);
  Handle = ::CreatePalette(logPalette);
  WARNX(OwlGDI, !Handle, 0, "Cannot create palette from LOGPALETTE @" << static_cast<const void*>(logPalette));
  CheckValid();
  RefAdd(Handle, Palette);
}
 
#endif
 
//
/// Creates a TPalette object with count entries from the given entries array.
//
TPalette::TPalette(const PALETTEENTRY* entries, int count)
{
  Init(entries, count);
}
 
//
// Initializes a TPalette object with count entries from the given entries array.
//
void TPalette::Init(const PALETTEENTRY* entries, int count)
{
  LOGPALETTE* logPal = (LOGPALETTE*)new uint8[
     sizeof(LOGPALETTE)+(count-1)*sizeof(PALETTEENTRY) ];
 
  logPal->palVersion  = 0x300;
  logPal->palNumEntries = (uint16)count;
  memcpy(logPal->palPalEntry, entries, count*sizeof(PALETTEENTRY));
  Handle = ::CreatePalette(logPal);
  delete[] logPal;
 
  WARNX(OwlGDI, !Handle, 0, "Cannot create palette from " << count <<
    "palette entries @" << static_cast<const void*>(entries));
  CheckValid();
  RefAdd(Handle, Palette);
}
 
//
/// Creates a TPalette object from the color table following the given BITMAPINFO
/// structure. This constructor works only for 2-color, 16-color, and 256-color
/// bitmaps. A handle with value 0 (zero) is returned for other bitmaps, including
/// 24-bit DIBs.
//
TPalette::TPalette(const BITMAPINFO& info, uint flags)
{
  Create(&info, flags);
}
 
#if defined(OWL5_COMPAT)
 
//
/// Creates a TPalette object from the color table following the given BITMAPINFO
/// structure. This constructor works only for 2-color, 16-color, and 256-color
/// bitmaps. A handle with value 0 (zero) is returned for other bitmaps, including
/// 24-bit DIBs.
/// This overload is deprecated. Use the overload that takes a reference instead.
//
TPalette::TPalette(const BITMAPINFO * info, uint flags)
{
  Create(info, flags);
}
 
#endif
 
//
/// Creates a TPalette object from the given DIB object. The flags argument
/// represents the values of the LOGPALETTE data structure used to create the
/// palette.
//
TPalette::TPalette(const TDib& dib, uint flags)
{
  Create(dib.GetInfo(), flags);
}
 
//
/// Read from file: *.dib,*.bmp,*.pal,*.aco,*.act
//
TPalette::TPalette(const tchar * fileName)
{
  PRECONDITION(fileName);
  Handle = 0;
 
  Read(fileName);
 
  WARNX(OwlGDI, !Handle, 0, "Cannot create palette from file: " << fileName);
 
  CheckValid(IDS_GDIFILEREADFAIL);
  ShouldDelete = true;
  RefAdd(Handle, Palette);
}
 
//
/// Accept a pointer to a BITMAPINFO structure and create a GDI logical
/// palette from the color table which follows it, for 2, 16 and 256 color
/// bitmaps. Fail for all others, including 24-bit DIB's
///
/// !CQ Don't fail for other formats!?
//
void
TPalette::Create(const BITMAPINFO * info, uint flags)
{
  PRECONDITION(info);
  const RGBQUAD * rgb = info->bmiColors;
 
  // if the ClrUsed field of the header is non-zero,
  // it means that we could have have a short color table.
  //
  uint16 nColors = uint16(info->bmiHeader.biClrUsed ?
           info->bmiHeader.biClrUsed :
           NColors(info->bmiHeader.biBitCount));
 
  if (nColors) {
    LOGPALETTE* logPal = (LOGPALETTE*)
       new uint8[sizeof(LOGPALETTE) + (nColors-1)*sizeof(PALETTEENTRY)];
 
    logPal->palVersion  = 0x300;      // Windows 3.0 version
    logPal->palNumEntries = nColors;
    for (uint16 n = 0; n < nColors; n++) {
      logPal->palPalEntry[n].peRed   = rgb[n].rgbRed;
      logPal->palPalEntry[n].peGreen = rgb[n].rgbGreen;
      logPal->palPalEntry[n].peBlue  = rgb[n].rgbBlue;
      logPal->palPalEntry[n].peFlags = (uint8)flags;
    }
    Handle = ::CreatePalette(logPal);
    delete[] logPal;
  }
  else
    Handle = 0;
 
  WARNX(OwlGDI, !Handle, 0, "Cannot create palette from bitmapinfo @" << static_cast<const void*>(info));
  CheckValid();
  RefAdd(Handle, Palette);
}
 
//
/// Moves this palette to the target Clipboard argument. If a copy is to be put on
/// the Clipboard, use TPalette(myPalette).ToClipboard; to make a copy first. The
/// handle in the temporary copy of the object is moved to the clipboard.
/// ToClipboard sets ShouldDelete to false so that the object on the clipboard is
/// not deleted. The handle will still be available for examination.
//
void
TPalette::ToClipboard(TClipboard& clipboard)
{
  if (Handle) {
    clipboard.SetClipboardData(CF_PALETTE, Handle);
    ShouldDelete = false; // GDI object now owned by Clipboard
    RefRemove(Handle);
  }
}
 
//
/// Read palette from file
//
bool
TPalette::Read(const tchar * filename)
{
  TRiffFile file(filename);
  if (!file.IsOpen())
    return false;
 
  const tchar* lp = _tcsrchr(filename, _T('.'));
  if(lp){
    if(_tcsicmp(lp, _T(".pal"))==0)  // check extension
      return Read_PAL(file);
    else if(_tcsicmp(lp,_T(".aco"))==0)
      return Read_ACO(file);
    else if(_tcsicmp(lp,_T(".act"))==0)
      return Read_ACT(file);
    else if(_tcsicmp(lp,_T(".bmp"))==0)
      return Read_BMP(file);
    else if(_tcsicmp(lp,_T(".dib"))==0)
      return Read_BMP(file);
  }
  return false;
}
 
//
/// Write this palette into file: *.dib,*.bmp,*.pal,*.aco,*.act
//
bool
TPalette::Write(const tchar * filename)
{
  TRiffFile file(filename, TRiffFile::WriteOnly|TRiffFile::PermExclusive|TRiffFile::CreateAlways);
  if (!file.IsOpen())
    return false;
 
  bool bOk = false;
  const tchar* lp = _tcsrchr(filename, _T('.'));
  if(lp){
    if(_tcsicmp(lp, _T(".pal"))==0)  // check extension
      bOk = Write_PAL(file);
    else if(_tcsicmp(lp, _T(".aco"))==0)
      bOk = Write_ACO(file);
    else if(_tcsicmp(lp, _T(".act"))==0)
      bOk = Write_ACT(file);
    else if(_tcsicmp(lp, _T(".bmp"))==0)
      bOk = Write_BMP(file);
    else if(_tcsicmp(lp, _T(".dib"))==0)
      bOk = Write_BMP(file);
  }
  if(!bOk)
    TFileName(filename).Remove();
 
  return bOk;
}
//------------------------------------------------------------------------------
static TBinField PALHDR_Fields[] = {
  {varSHORT,2, 1},   /* uint16  Version;  */
  {varSHORT,2, 1},   /* uint16   Count;    */
  {varEnd,  0, 0},
};
 
//
/// read Microsoft *.pal format, return true if success
//
bool
TPalette::Read_PAL(TRiffFile& file)
{
  // Check whether it's a RIFF PAL file.
  TCkInfo  ckFile;
  ckFile.Type = owlFCC('P','A','L',' ');
  if(!file.Descent(ckFile, 0, TRiffFile::ffFindRiff)) {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_PAL() Not a RIFF or PAL file");
    TXBadFormat::Raise();
    return false;
  }
  // Find the 'data' chunk.
  TCkInfo  ckChunk;
  ckChunk.CkId = owlFCC('d','a','t','a');
  if (!file.Descent(ckChunk, &ckFile, TRiffFile::ffFindChunk)) {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_PAL() No data chunk in file");
    TXBadFormat::Raise();
    return false;
  }
  // Allocate some memory for the data chunk.
  int iSize = (int)ckChunk.Size;
  TAPointer<char> pdata(new char[iSize]);
  LOGPALETTE* pLogPal = (LOGPALETTE*)(char*)pdata;
  // Read the data chunk.
  if(!file.ReadStruct((char*)pLogPal, PALHDR_Fields, boLittle_Endian)){
    WARNX(OwlGDI, 1, 0, "TPalette::Read_PAL() Failed to PAL Colors, line" << __LINE__);
    return false;
  }
  uint count = (iSize - sizeof(uint16)*2);
  if (file.Read(pLogPal->palPalEntry, count) == TFILE_ERROR) {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_PAL() Failed to PAL Colors, line" << __LINE__);
    return false;
  }
  // The data chunk should be a LOGPALETTE structure
  // that we can create a palette from.
  if (pLogPal->palVersion != 0x300) {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_PAL(): Invalid version number");
    TXBadFormat::Raise();
    return false;
  }
  // Get the number of entries.
  if (pLogPal->palNumEntries <= 0){
    WARNX(OwlGDI, 1, 0, "TPalette::Read_PAL(): No colors in palette");
    TXBadFormat::Raise();
    return false;
  }
  Handle = ::CreatePalette(pLogPal);
  return true;
}
 
/// write Microsoft *.pal format
///
/// return true if success
bool
TPalette::Write_PAL(TRiffFile& file)
{
  // Create a RIFF chunk for a PAL file.
  TCkInfo  ckFile;
  ckFile.Size = 0; // Corrected later
  ckFile.Type = owlFCC('P','A','L',' ');
  if (!file.CreateChunk(ckFile, TRiffFile::cfCreateRiff)) {
    WARNX(OwlGDI, 1, 0, "TPalette::Write_PAL() Failed to create RIFF-PAL chunk");
    return false;
  }
  // Create the LOGPALETTE data which will become
  // the data chunk.
  uint16  nColors;
  GetObject(nColors);
  if (!nColors){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_PAL() no colors in palette");
    return false;
  }
  int iSize = sizeof(LOGPALETTE)+(nColors-1)*sizeof(PALETTEENTRY);
  TAPointer<char> pdata(new char[iSize]);
  LOGPALETTE* logPal = (LOGPALETTE*)(char*)pdata;
  logPal->palVersion = 0x300;
  logPal->palNumEntries = nColors;
  GetPaletteEntries(0, nColors, logPal->palPalEntry);
 
  // create the data chunk.
  TCkInfo  ckData;
  ckData.Size = iSize;
  ckData.CkId = owlFCC('d','a','t','a');
  if (!file.CreateChunk(ckData)) {
    WARNX(OwlGDI, 1, 0, "TPalette::Write_PAL() Failed to create data chunk");
    return false;
  }
  // Write the data chunk.
  if(!file.Write((char*)pdata, iSize)) {
    WARNX(OwlGDI, 1, 0, "TPalette::Write_PAL() Failed to write data chunk");
    return false;
  }
  // Ascend from the data chunk which will correct the length.
  file.Ascent(ckData);
  // Ascend from the RIFF-PAL chunk.
  file.Ascent(ckFile);
 
  return true;
}
/* -------------------------------------------------------------------------- */
/// convert HSL to RGB
///
/// given h,s,l on [0..1],return r,g,b on [0..1]
void HSL_to_RGB(double& h, double& sl, double& l, double* r, double* g, double* b)
{
  double v;
  v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
  if (v <= 0)
    *r = *g = *b = 0.0;
  else {
    double m;
    double sv;
    int sextant;
    double fract, vsf, mid1, mid2;
 
    m = l + l - v;
    sv = (v - m ) / v;
    h *= 6.0;
    sextant = static_cast<int>(h); //JJH added static cast
    fract = h - sextant;
    vsf = v * sv * fract;
    mid1 = m + vsf;
    mid2 = v - vsf;
    switch (sextant) {
      case 0: *r = v; *g = mid1; *b = m; break;
      case 1: *r = mid2; *g = v; *b = m; break;
      case 2: *r = m; *g = v; *b = mid1; break;
      case 3: *r = m; *g = mid2; *b = v; break;
      case 4: *r = mid1; *g = m; *b = v; break;
      case 5: *r = v; *g = m; *b = mid2; break;
    }
  }
}
 
/// \cond NoSuppressDoxygenWarning
#include <pshpack1.h>
/// \endcond
 
/// ACO header
struct ACO_Hdr{
  uint16 Version; ///< Version== 1
  uint16 Count;   ///< number of color
};
static TBinField ACO_HdrFields[] = {
  {varSHORT,2, 1},   /* uint16  Version;  */
  {varSHORT,2, 1},   /* uint16   Count;    */
  {varEnd,  0, 0},
};
/// ACO color structure
struct ACO_Color{
  uint16 clrSpace;  ///<  color space
  uint16 Color1;     ///<  color value1
  uint16 Color2;    ///<  color value2
  uint16 Color3;    ///<  color value3
  uint16 Color4;    ///<  color value4
};
static TBinField ACO_ColorFields[] = {
  {varSHORT,2, 1},   /* uint16 clrSpace; */
  {varSHORT,2, 1},   /* uint16 Color1;   */
  {varSHORT,2, 1},   /* uint16 Color2;   */
  {varSHORT,2, 1},   /* uint16 Color3;   */
  {varSHORT,2, 1},   /* uint16 Color4;   */
  {varEnd,  0, 0},
};
struct MLOGPALETTE { // lgpl
    WORD         palVersion;
    WORD         palNumEntries;
    PALETTEENTRY palPalEntry[256];
};
 
/// \cond NoSuppressDoxygenWarning
#include <poppack.h>
/// \endcond
 
#if !defined(CMYK)
#define CMYK(c,m,y,k)       ((COLORREF)((((BYTE)(c)|((WORD)((BYTE)(m))<<8))|(((DWORD)(BYTE)(y))<<16))|(((DWORD)(BYTE)(k))<<24)))
/* Macros to retrieve CMYK values from a COLORREF */
#define GetCValue(cmyk)      ((BYTE)(cmyk))
#define GetMValue(cmyk)      ((BYTE)((cmyk)>> 8))
#define GetYValue(cmyk)      ((BYTE)((cmyk)>>16))
#define GetKValue(cmyk)      ((BYTE)((cmyk)>>24))
#endif
 
/* -------------------------------------------------------------------------- */
/// Read Adobe Photoshop *.ACO color table
///
/// return true if success
//
bool TPalette::Read_ACO(TRiffFile& file)
{
  ACO_Hdr hdr;
  // Read the Header.
  if(!file.ReadStruct(&hdr, ACO_HdrFields, boBig_Endian)){
    WARNX(OwlGDI, 1, 0, "TPalette::Read_ACO() Failed to read ACO header");
    return false;
  }
  TRACEX(OwlGDI, 1, "TPalette::Read_ACO() Data for header: hdr.Version = " << dec << hdr.Version <<", hdr.Count   = " << hdr.Count);
  if(hdr.Version != 1){ // equial to 1    ///////////////////////////////
    WARNX(OwlGDI, 1, 0, "TPalette::Read_ACO() Invalid version number");
    TXBadFormat::Raise();
    return false;
  }
 
  TAPointer<ACO_Color> clr(new ACO_Color[hdr.Count]);
 
  // Read the Colors.
  for(int i = 0; i < hdr.Count; i++){
    if(!file.ReadStruct((char*)&clr[i], ACO_ColorFields, boBig_Endian)){
      WARNX(OwlGDI, 1, 0, "TPalette::Read_ACO() Failed to ACO Colors, line" << __LINE__);
      return false;
    }
  }
#if defined(__TRACE)
  TRACEX(OwlGDI, 1, "TPalette::Read_ACO() Data for Colors:");
  for (int j= 0; j < hdr.Count; j++){
    TRACEX(OwlGDI, 1, "TPalette::Read_ACO() index("<< dec << j << "), clrSpace("
           << clr[j].clrSpace << "), Color1("<< clr[j].Color1<< "), Color2(" <<
           clr[j].Color2<< "), Color3("<< clr[j].Color3<<"), Color4("<<
           clr[j].Color4<< ")");
  }
#endif
  MLOGPALETTE LogPal;
  LogPal.palVersion = 0x300;
  LogPal.palNumEntries = 256;
  memset(&LogPal.palPalEntry, 0, sizeof(LogPal.palPalEntry));
 
  //////////////////////////////////////////////////////////////////////////////
  // copy color values from ACO_Clr to logical palette
  uint colors = 0;
 
  // only 256 colors use !!!!!!!!!!!!!!!!!!
  if(hdr.Count > 256)
    hdr.Count = 256;
 
  for (int k= 0; k < hdr.Count; k++){
    switch(clr[k].clrSpace){
      default:
        break;
 
      case 0:    // RGB
        LogPal.palPalEntry[k].peRed   = uint8(clr[k].Color1*255ul/65535u);
        LogPal.palPalEntry[k].peGreen = uint8(clr[k].Color2*255ul/65535u);
        LogPal.palPalEntry[k].peBlue   = uint8(clr[k].Color3*255ul/65535u);
        LogPal.palPalEntry[k].peFlags = 0;
        break;
 
      // not checked
      case 1: {   // HSB (HSL)          ?????????????????????????????????????
          double h,s,l,r,g,b;
          h = clr[k].Color1/65535.0;
          s = clr[k].Color2/65535.0;
          l = clr[k].Color3/65535.0;
          HSL_to_RGB(h, s, l, &r, &g, &b);
 
          LogPal.palPalEntry[k].peRed     = uint8(r*255u);
          LogPal.palPalEntry[k].peGreen   = uint8(g*255u);
          LogPal.palPalEntry[k].peBlue     = uint8(b*255u);
          LogPal.palPalEntry[k].peFlags   = 0;
        }
        break;
 
      // not checked
      case 2: {   // CMYK
          COLORREF clrRef = CMYK( uint8(clr[k].Color1*255ul/65535u),
                                  uint8(clr[k].Color2*255ul/65535u),
                                  uint8(clr[k].Color3*255ul/65535u),
                                  uint8(clr[k].Color4*255ul/65535u)
                                );
          LogPal.palPalEntry[k].peRed     = GetRValue(clrRef);
          LogPal.palPalEntry[k].peGreen   = GetGValue(clrRef);
          LogPal.palPalEntry[k].peBlue     = GetBValue(clrRef);
          LogPal.palPalEntry[k].peFlags   = 0;
        break;
      }
 
      // if I must do it also ??????????????????
      //case 7:   // LAB
      //  LogPal.palPalEntry[k].peRed     = uint8(clr[k].Color1*255ul/65535u);
      //  LogPal.palPalEntry[k].peGreen   = uint8(clr[k].Color2*255ul/65535u);
      //  LogPal.palPalEntry[k].peBlue     = uint8(clr[k].Color3*255ul/65535u);
      //  LogPal.palPalEntry[k].peFlags   = 0;
      //  break;
 
      // not checked
      case 8:    // GrayScale
        LogPal.palPalEntry[k].peRed     = uint8(clr[k].Color1*255ul/10000u);
        LogPal.palPalEntry[k].peGreen   = uint8(clr[k].Color1*255ul/10000u);
        LogPal.palPalEntry[k].peBlue     = uint8(clr[k].Color1*255ul/10000u);
        LogPal.palPalEntry[k].peFlags   = 0;
        break;
    }
    colors++;
  }
  if(!colors){
    WARNX(OwlGDI, 1, 0, "TPalette::Read_ACO() Error no colors found.");
    //TXGdi::Raise(IDS_BADFORMAT);
    return false;
  }
  Handle = ::CreatePalette((LOGPALETTE*)&LogPal);
  return true;
}
/* -------------------------------------------------------------------------- */
/// Write Adobe Photoshop *.ACO color table
///
/// return true if success
bool TPalette::Write_ACO(TRiffFile& file)
{
  // Create the LOGPALETTE data which will become
  // the data chunk.
  uint16  nColors;
  GetObject(nColors);
  if (!nColors){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_ACO() Error no colors in palette");
    TXBadFormat::Raise();
    return false;
  }
  TAPointer<PALETTEENTRY> pColors(new PALETTEENTRY[nColors]);
  GetPaletteEntries(0, nColors, (PALETTEENTRY*)pColors);
 
  ACO_Hdr hdr;
  hdr.Version = 0x1;
  hdr.Count   = nColors;
 
  // Write the Header.
  if(!file.WriteStruct(&hdr, ACO_HdrFields, boBig_Endian)){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_ACO() Failed to write ACO header");
    return false;
  }
 
  ACO_Color clr[256];
  for (int i= 0; i < nColors; i++){
    clr[i].clrSpace = 0; // RGB
    clr[i].Color1  = uint16(pColors[i].peRed*65535ul/255);
    clr[i].Color2 = uint16(pColors[i].peGreen * 65535ul/255);
    clr[i].Color3 = uint16(pColors[i].peBlue * 65535ul/255);
    clr[i].Color4 = 0;
  }
  // Read the Colors.
  for(int j = 0; j < nColors; j++){
    if(!file.WriteStruct((char*)&clr[j], ACO_ColorFields, boBig_Endian)){
      WARNX(OwlGDI, 1, 0, "TPalette::Write_ACO() Failed to write ACO Colors, line" << __LINE__);
      return false;
    }
  }
  return true;
}
// -----------------------------------------------------------------------------
/// ACT color structure
struct ACT_Color{
  uint8 Red;    ///< Red compponent
  uint8 Green;  ///< Green compponent
  uint8 Blue;   ///< Blue compponent
};
// -----------------------------------------------------------------------------
/// Read Adobe Photoshop *.ACT color table
///
/// return true if success
bool TPalette::Read_ACT(TRiffFile& file)
{
  MLOGPALETTE LogPal;
  LogPal.palVersion = 0x300;
  LogPal.palNumEntries = 256;
  memset(&LogPal.palPalEntry, 0, sizeof(LogPal.palPalEntry));
 
  ACT_Color clr[256];
 
  // Read the Color table.
  if(file.Read((char*)&clr, sizeof(clr)) == TFILE_ERROR){
    WARNX(OwlGDI, 1, 0, "TPalette::Read_ACT() Failed to read ACT colors");
    return false;
  }
  for (int i= 0; i < 256; i++){
    LogPal.palPalEntry[i].peRed     = clr[i].Red;
    LogPal.palPalEntry[i].peGreen   = clr[i].Green;
    LogPal.palPalEntry[i].peBlue     = clr[i].Blue;
    LogPal.palPalEntry[i].peFlags   = 0;
  }
  Handle = ::CreatePalette((LOGPALETTE*)&LogPal);
  return true;
}
/* -------------------------------------------------------------------------- */
/// Read Adobe Photoshop *.ACT color table
///
/// return true if success
bool TPalette::Write_ACT(TRiffFile& file)
{
  // Create the LOGPALETTE data which will become
  uint16  nColors;
  GetObject(nColors);
  if (!nColors){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_ACT() Error no colors in palette");
    return false;
  }
  TAPointer<PALETTEENTRY> pColors(new PALETTEENTRY[nColors]);
  GetPaletteEntries(0, nColors, (PALETTEENTRY*)pColors);
 
  ACT_Color clr[256];
  int i = 0;
  for (; i < nColors; i++){
    clr[i].Red     = pColors[i].peRed;
    clr[i].Green   = pColors[i].peGreen;
    clr[i].Blue   = pColors[i].peBlue;
  }
  for (; i < 256; i++){
    clr[i].Red     = 0;
    clr[i].Green   = 0;
    clr[i].Blue   = 0;
  }
 
  // Write the data
  if(!file.Write((char*)&clr, sizeof(clr))) {
    WARNX(OwlGDI, 1, 0, "TPalette::Write_ACT() Failed to write ACT data");
    return false;
  }
  return true;
}
/* -------------------------------------------------------------------------- */
/// Read color table from *.DIB
///
/// return true if success
bool TPalette::Read_BMP(TRiffFile& file)
{
  // Read the Header.
  BITMAPFILEHEADER bmf;
  // Read the Header.
  if(file.Read(&bmf, sizeof bmf) == TFILE_ERROR){
    WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Failed to read BMP header");
    return false;
  }
  uint16 bmp_id = 0x4D42; // 'BM'
  if(bmf.bfType != bmp_id) {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Not a Windows 3.x or PM 1.x bitmap file");
    TXBadFormat::Raise();
    return false;
  }
  // Read bitmap header size & check it. It must be one of the two known header
  // sizes.
  // Will add BITMAPV4HEADER support when available
  uint32 headerSize;
  if (file.Read((char*)&headerSize, sizeof headerSize) == TFILE_ERROR ||
    (headerSize != sizeof(BITMAPCOREHEADER) && headerSize != sizeof(BITMAPINFOHEADER)))
  {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Not a Windows 3.x or PM 1.x bitmap file");
    TXBadFormat::Raise();
    return false;
  }
  uint16 BitCount, biPlanes;
  int ClrUsed;
 
  // If this is a PM 1.x DIB, read the core header & copy over to the Info header
  bool   isCore = headerSize == sizeof(BITMAPCOREHEADER);
  if (isCore) {
    // Read in the rest of the core header, aborting if it is truncated
    BITMAPCOREHEADER coreHeader;
    if (file.Read((char*)&coreHeader.bcWidth,
        (int)headerSize-sizeof(uint32))==TFILE_ERROR) {
      WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Invalid PM 1.x DIB Header");
      TXBadFormat::Raise();
      return false;
    }
    BitCount = coreHeader.bcBitCount;
    biPlanes = coreHeader.bcPlanes;
    ClrUsed = 0;
  }
  else {
    BITMAPINFOHEADER infoHeader;
    infoHeader.biSize = sizeof(BITMAPINFOHEADER);
    // Read in the rest of the info header, aborting if it is truncated
    if (file.Read((char*)&infoHeader.biWidth,
         (int)headerSize-sizeof(uint32))==TFILE_ERROR){
      WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Invalid Win 3.x DIB Header");
      TXBadFormat::Raise();
      return false;
    }
    BitCount   = infoHeader.biBitCount;
    biPlanes   = infoHeader.biPlanes;
    ClrUsed    = infoHeader.biClrUsed;
  }
  // Check number of planes. Windows supports only 1 plane DIBs
  if (biPlanes != 1) {
    WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Invalid number of planes in DIB");
    TXBadFormat::Raise();
    return false;
  }
 
  // Fill in the default value for biClrsUsed, if not supplied, using the
  // bit count. Will remain 0 for 16bpp or greater.
  if (!ClrUsed)
    ClrUsed = NColors(BitCount);
 
  // Read color table directly into allocated memory
  // Walk backwards & expand to RGBQUADs if it is a PM Core DIB
  int colorRead = isCore ?          // Color table size on disk
        (int)ClrUsed * sizeof(RGBTRIPLE) :
        (int)ClrUsed * sizeof(RGBQUAD);
 
  RGBQUAD Colors[256];
  if(ClrUsed){
    if (file.Read((char*)&Colors, colorRead)==TFILE_ERROR) {
      WARNX(OwlGDI, 1, 0, "TPalette::Read_BMP() Could not read DIB color table");
      return false;
    }
    if(isCore) {
      for (int i = int(ClrUsed-1); i >= 0; i--) {
        Colors[i].rgbRed   = ((RGBTRIPLE*)Colors)[i].rgbtRed;
        Colors[i].rgbGreen = ((RGBTRIPLE*)Colors)[i].rgbtGreen;
        Colors[i].rgbBlue  = ((RGBTRIPLE*)Colors)[i].rgbtBlue;
      }
    }
  }
  MLOGPALETTE LogPal;
  LogPal.palVersion = 0x300;
  LogPal.palNumEntries = 256;
  memset(&LogPal.palPalEntry, 0, sizeof(LogPal.palPalEntry));
  int i = 0;
  for(; i < ClrUsed; i++){
    LogPal.palPalEntry[i].peRed     = Colors[i].rgbRed;
    LogPal.palPalEntry[i].peGreen   = Colors[i].rgbGreen;
    LogPal.palPalEntry[i].peBlue     = Colors[i].rgbBlue;
    LogPal.palPalEntry[i].peFlags   = 0;
  }
  for(; i < 256; i++){
    LogPal.palPalEntry[i].peRed     = 0;
    LogPal.palPalEntry[i].peGreen   = 0;
    LogPal.palPalEntry[i].peBlue     = 0;
    LogPal.palPalEntry[i].peFlags   = 0;
  }
  Handle = ::CreatePalette((LOGPALETTE*)&LogPal);
  return true;
}
/* -------------------------------------------------------------------------- */
/// Write Color table to *.DIB with dimensions 1x1 pixel
///
/// return true if success
bool TPalette::Write_BMP(TRiffFile& file)
{
  BITMAPFILEHEADER bmf;
  bmf.bfType = 0x4D42; // 'BM'
  bmf.bfSize = sizeof(bmf) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + sizeof(uint8);
  bmf.bfReserved1 = 0;
  bmf.bfReserved2 = 0;
  bmf.bfOffBits = sizeof bmf + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256;
  if(!file.Write((char*)&bmf, sizeof bmf)){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_BMP() Error write header");
    return false;
  }
 
  BITMAPINFOHEADER infoHeader;
  infoHeader.biSize = sizeof(BITMAPINFOHEADER);
  infoHeader.biWidth = 1;
  infoHeader.biHeight = 1;
  infoHeader.biPlanes = 1;
  infoHeader.biBitCount = 8;
  infoHeader.biCompression = BI_RGB;  // No compression
  infoHeader.biSizeImage = 1;         // Calculate this below
  infoHeader.biXPelsPerMeter = 0;     // Zero is OK
  infoHeader.biYPelsPerMeter = 0;     // Zero is OK
  infoHeader.biClrUsed = 256;           // Calculate this below
  infoHeader.biClrImportant = 256;      // Zero is OK
 
  // Write the data
  if(!file.Write((char*)&infoHeader, sizeof(infoHeader))){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_BMP() Failed to write BMP Info header");
    return false;
  }
 
  uint16  nColors;
  GetObject(nColors);
  if (!nColors){
    WARNX(OwlGDI, 1, 0, "TPalette::Write_BMP() no colors in palette");
    return false;
  }
  TAPointer<PALETTEENTRY> pColors(new PALETTEENTRY[nColors]);
  GetPaletteEntries(0, nColors, (PALETTEENTRY*)pColors);
 
  RGBQUAD clr[256];
  int i = 0;
  for (; i < nColors; i++){
    clr[i].rgbRed       = pColors[i].peRed;
    clr[i].rgbGreen     = pColors[i].peGreen;
    clr[i].rgbBlue       = pColors[i].peBlue;
    clr[i].rgbReserved   = 0;
  }
  for (; i < 256; i++){
    clr[i].rgbRed       = 0;
    clr[i].rgbGreen     = 0;
    clr[i].rgbBlue       = 0;
    clr[i].rgbReserved   = 0;
  }
 
  // Write the Colors
  if(!file.Write((char*)&clr, sizeof(clr))) {
    WARNX(OwlGDI, 1, 0, "TPalette::Write_BMP() Failed to write BMP Colors");
    return false;
  }
  // Write the 1x1 bitmap
  uint32 pixel = 0;
  if(!file.Write((char*)&pixel, sizeof(pixel))) {
    WARNX(OwlGDI, 1, 0, "TPalette::Write_BMP() Failed to write BMP 1x1 bitmap");
    return false;
  }
  return true;
}
 
} // OWL namespace

V614 Uninitialized variable 'coreHeader.bcBitCount' used.

V614 Uninitialized variable 'coreHeader.bcPlanes' used.

V614 Uninitialized variable 'infoHeader.biBitCount' used.

V614 Uninitialized variable 'infoHeader.biPlanes' used.

V614 Uninitialized variable 'infoHeader.biClrUsed' used.

V1004 The 'info' pointer was used unsafely after it was verified against nullptr. Check lines: 209, 210.

V641 The size of the 'Colors' buffer is not a multiple of the element size of the type 'RGBTRIPLE'.

V641 The size of the 'Colors' buffer is not a multiple of the element size of the type 'RGBTRIPLE'.

V641 The size of the 'Colors' buffer is not a multiple of the element size of the type 'RGBTRIPLE'.

V669 The 'sl', 'l' arguments are non-constant references. The analyzer is unable to determine the position at which this argument is being modified. It is possible that the function contains an error.