Storing an Image

MFC/Bitmap 2013. 8. 16. 13:18

http://msdn.microsoft.com/en-us/library/windows/desktop/dd145170%28v=vs.85%29.aspx

 

PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{ 
    BITMAP bmp; 
    PBITMAPINFO pbmi; 
    WORD    cClrBits; 

    // Retrieve the bitmap color format, width, and height.  
    if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) 
        errhandler("GetObject", hwnd); 

    // Convert the color format to a count of bits.  
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); 
    if (cClrBits == 1) 
        cClrBits = 1; 
    else if (cClrBits <= 4) 
        cClrBits = 4; 
    else if (cClrBits <= 8) 
        cClrBits = 8; 
    else if (cClrBits <= 16) 
        cClrBits = 16; 
    else if (cClrBits <= 24) 
        cClrBits = 24; 
    else cClrBits = 32; 

    // Allocate memory for the BITMAPINFO structure. (This structure  
    // contains a BITMAPINFOHEADER structure and an array of RGBQUAD  
    // data structures.)  

     if (cClrBits < 24) 
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
                    sizeof(BITMAPINFOHEADER) + 
                    sizeof(RGBQUAD) * (1<< cClrBits)); 

     // There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel 

     else 
         pbmi = (PBITMAPINFO) LocalAlloc(LPTR, 
                    sizeof(BITMAPINFOHEADER)); 

    // Initialize the fields in the BITMAPINFO structure.  

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    pbmi->bmiHeader.biWidth = bmp.bmWidth; 
    pbmi->bmiHeader.biHeight = bmp.bmHeight; 
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes; 
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; 
    if (cClrBits < 24) 
        pbmi->bmiHeader.biClrUsed = (1<<cClrBits); 

    // If the bitmap is not compressed, set the BI_RGB flag.  
    pbmi->bmiHeader.biCompression = BI_RGB; 

    // Compute the number of bytes in the array of color  
    // indices and store the result in biSizeImage.  
    // The width must be DWORD aligned unless the bitmap is RLE 
    // compressed. 
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
                                  * pbmi->bmiHeader.biHeight; 
    // Set biClrImportant to 0, indicating that all of the  
    // device colors are important.  
     pbmi->bmiHeader.biClrImportant = 0; 
     return pbmi; 
 } 
------------------------------------------------------
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, 
                  HBITMAP hBMP, HDC hDC) 
 { 
     HANDLE hf;                 // file handle  
    BITMAPFILEHEADER hdr;       // bitmap file-header  
    PBITMAPINFOHEADER pbih;     // bitmap info-header  
    LPBYTE lpBits;              // memory pointer  
    DWORD dwTotal;              // total count of bytes  
    DWORD cb;                   // incremental count of bytes  
    BYTE *hp;                   // byte pointer  
    DWORD dwTmp; 

    pbih = (PBITMAPINFOHEADER) pbi; 
    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

    if (!lpBits) 
         errhandler("GlobalAlloc", hwnd); 

    // Retrieve the color table (RGBQUAD array) and the bits  
    // (array of palette indices) from the DIB.  
    if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, 
        DIB_RGB_COLORS)) 
    {
        errhandler("GetDIBits", hwnd); 
    }

    // Create the .BMP file.  
    hf = CreateFile(pszFile, 
                   GENERIC_READ | GENERIC_WRITE, 
                   (DWORD) 0, 
                    NULL, 
                   CREATE_ALWAYS, 
                   FILE_ATTRIBUTE_NORMAL, 
                   (HANDLE) NULL); 
    if (hf == INVALID_HANDLE_VALUE) 
        errhandler("CreateFile", hwnd); 
    hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"  
    // Compute the size of the entire file.  
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + 
                 pbih->biSize + pbih->biClrUsed 
                 * sizeof(RGBQUAD) + pbih->biSizeImage); 
    hdr.bfReserved1 = 0; 
    hdr.bfReserved2 = 0; 

    // Compute the offset to the array of color indices.  
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + 
                    pbih->biSize + pbih->biClrUsed 
                    * sizeof (RGBQUAD); 

    // Copy the BITMAPFILEHEADER into the .BMP file.  
    if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), 
        (LPDWORD) &dwTmp,  NULL)) 
    {
       errhandler("WriteFile", hwnd); 
    }

    // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.  
    if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) 
                  + pbih->biClrUsed * sizeof (RGBQUAD), 
                  (LPDWORD) &dwTmp, ( NULL)))
        errhandler("WriteFile", hwnd); 

    // Copy the array of color indices into the .BMP file.  
    dwTotal = cb = pbih->biSizeImage; 
    hp = lpBits; 
    if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)) 
           errhandler("WriteFile", hwnd); 

    // Close the .BMP file.  
     if (!CloseHandle(hf)) 
           errhandler("CloseHandle", hwnd); 

    // Free memory.  
    GlobalFree((HGLOBAL)lpBits);
}
 

'MFC > Bitmap' 카테고리의 다른 글

핸들변환  (0) 2013.08.16
Bitmap.h  (0) 2013.08.16
Bitmap.cpp  (0) 2013.08.16
리소스에서 BMP 이미지 불러오기  (0) 2013.08.09
Writing a window image to a BMP file  (0) 2013.08.08
Posted by 곰돌이짱
,

Bitmap.h

MFC/Bitmap 2013. 8. 16. 11:24

 /*
Bitmap.h
Written by Matthew Fisher

A bitmap class (a 2D array of RGBColor's)
Rather self-explanitory, can only save and load a few primitive formats.
*/


#pragma once

struct BitmapSaveOptions
{
    BitmapSaveOptions()
    {
        //BGREncoding = false;
        //VerticalFlip = false;

        Region = Rectangle2i(Vec2i::Origin, Vec2i::Origin);
        ScratchSpace = NULL;
        ScratchSpaceSize = 0;
        SaveAlpha = false;
        UseBGR = false;
    }
    //bool BGREncoding;
    //bool VerticalFlip;

    Rectangle2i Region;
    BYTE *ScratchSpace;
    UINT ScratchSpaceSize;
    bool SaveAlpha;
    bool UseBGR;
};

class Bitmap
{
public:
    Bitmap();
    Bitmap(const Bitmap &B);
    Bitmap(const Bitmap &B, UINT x, UINT y, UINT Width, UINT Height);
    Bitmap(UINT Width, UINT Height);

    ~Bitmap();

    //
    // Memory
    //
    void FreeMemory();
    void Allocate(UINT Width, UINT Height);
    Bitmap& operator = (const Bitmap &Bmp);

    //
    // Accessors
    //
    __forceinline RGBColor* operator [] (UINT Row) {return &_Data[Row * _Width];}
    __forceinline const RGBColor* operator [] (UINT Row) const {return &_Data[Row * _Width];}
    __forceinline UINT Width() const {return _Width;}
    __forceinline UINT Height() const {return _Height;}
    __forceinline Vec2i Dimensions() const {return Vec2i(_Width, _Height);}
    __forceinline RGBColor SampleNearest(const Vec2f &Pos)
    {
        Vec2i SamplePos(Utility::Bound(Math::Round(Pos.x * (_Width - 1)), 0, int(_Width) - 1),
                        Utility::Bound(Math::Round(Pos.y * (_Height - 1)), 0, int(_Height) - 1));
        return _Data[SamplePos.y * _Width + SamplePos.x];
    }

    //
    // File Functions
    //
    void SaveBMP(const String &Filename) const; //saves bitmap to Filename in 32-bit *.BMP format
    void SavePPM(const String &Filename) const; //saves bitmap to Filename in *.PPM format
    void SavePGM(const String &Filename, int Channel) const;
    void SavePNG(const String &Filename) const; //saves bitmap to Filename in 24-bit *.PNG format
    void SavePNG(const String &Filename, const BitmapSaveOptions &Options) const;
    void SavePNGToMemory(Vector<BYTE> &Buffer) const; //save bitmap to Buffer in *.PNG format
    void SavePNGToMemory(Vector<BYTE> &Buffer, const BitmapSaveOptions &Options) const;
    UINT SaveDelta(const Bitmap &Bmp, const String &Filename) const;
    void LoadBMP(const String &Filename); //loads bitmap from Filename in *.BMP format
    void LoadPNG(const String &Filename); //loads bitmap from Filename in *.PNG format
    void LoadPNGFromMemory(const Vector<BYTE> &Buffer); //loads bitmap from Buffer in *.PNG format
    void LoadSDL(const String &Filename); //loads bitmap from most file formats using SDL
    void LoadDelta(const Bitmap &Bmp, const String &Filename);

#ifdef USE_D3D9
    void LoadFromSurface(LPDIRECT3DSURFACE9 Surface);
    static void SaveSurfaceToPNG(LPDIRECT3DSURFACE9 Surface, const String &Filename, const BitmapSaveOptions &Options);
#endif

    //
    // Transfer Functions
    //
    __forceinline void BltTo(Bitmap &B, const Vec2i &Target) const
    {
        BltTo(B, Target.x, Target.y);
    }
    void BltTo(Bitmap &B, int TargetX, int TargetY) const;
    void BltToNoClipMemcpy(Bitmap &B, UINT TargetX, UINT TargetY) const;
    void BltTo(Bitmap &B, int TargetX, int TargetY, int SourceX, int SourceY, int Width, int Height) const;
   
    enum SamplingType
    {
        SamplingPoint,
        SamplingLinear,
    };
    void StretchBltTo(Bitmap &B, const Rectangle2i &TargetRect, const Rectangle2i &SourceRect, SamplingType Sampling) const;
    void StretchBltTo(Bitmap &B, int TargetX, int TargetY, int TargetWidth, int TargetHeight, int SourceX, int SourceY, int SourceWidth, int SourceHeight, SamplingType Sampling) const;
   
    void LoadGrid(const Grid<float> &Values);
    void LoadGrid(const Grid<float> &Values, float Min, float Max);
    void FlipHorizontal();
    void FlipVertical();
   
    //
    // Query
    //
    UINT Hash32() const;
    UINT64 Hash64() const;
    bool PerfectMatch(const Bitmap &Bmp) const;
    bool Monochrome(RGBColor Color) const;
    bool MonochromeIncludingAlpha(RGBColor Color) const;
    UINT CountPixelsWithColor(RGBColor Color) const;

    //
    // Modifiers
    //
    void ReplaceColor(RGBColor SourceColor, RGBColor NewColor);
    void Clear(const RGBColor &Color);    //clears all pixels to Color
    void Clear();                         //clears all pixels to RGBColor::Black
    void LoadAlphaChannelAsGrayscale();
    void FlipBlueAndRed();

    //
    // Query
    //
    RGBColor AverageColorOverRegion(const Rectangle2f &Region) const;
   
private:
#ifdef USE_PNG
    static void __cdecl PNGReadFromBuffer(png_structp png_ptr, png_bytep data, png_size_t length);
    static void __cdecl PNGWriteToBuffer(png_structp png_ptr, png_bytep data, png_size_t length);
    static void __cdecl PNGFlushBuffer(png_structp png_ptr);
    void PNGCompleteRead(png_structp PngRead, png_infop PngInfo, const String &Filename);
    void PNGCompleteWrite(png_structp PngWrite, png_infop PngInfo, const BitmapSaveOptions &Options) const;
#endif

    UINT _Width, _Height;   //width and height of the bitmap
    RGBColor* _Data;        //Raw RGBColor data in one big array
};

namespace Utility
{
    __forceinline bool PointInsideBitmap(const Bitmap &Bmp, int x, int y)
    {
        return (between(x, 0, int(Bmp.Width()) - 1) &&
                between(y, 0, int(Bmp.Height()) - 1));
    }
}

'MFC > Bitmap' 카테고리의 다른 글

핸들변환  (0) 2013.08.16
Storing an Image  (0) 2013.08.16
Bitmap.cpp  (0) 2013.08.16
리소스에서 BMP 이미지 불러오기  (0) 2013.08.09
Writing a window image to a BMP file  (0) 2013.08.08
Posted by 곰돌이짱
,

Bitmap.cpp

MFC/Bitmap 2013. 8. 16. 11:24

 /*
Bitmap.cpp
Written by Matthew Fisher

A bitmap class (a 2D array of RGBColor's)
Rather self-explanitory, can only save and load a few primitive formats.
See Bitmap.h for a complete description
*/


Bitmap::Bitmap()
{
    _Data = 0;
    _Width = 0;
    _Height = 0;
}

Bitmap::Bitmap(UINT Width, UINT Height)
{
    _Data = 0;
    Allocate(Width, Height);
}

Bitmap::~Bitmap()
{
    FreeMemory();
}

Bitmap::Bitmap(const Bitmap &B)
{
    _Data = NULL;
    Allocate(B._Width,B._Height);
    memcpy(_Data,B._Data,sizeof(RGBColor)*_Width*_Height);
}

Bitmap::Bitmap(const Bitmap &B, UINT x, UINT y, UINT Width, UINT Height)
{
    _Data = NULL;
    Allocate(Width, Height);
    Clear();
    for(UINT CurY = 0; CurY < _Height; CurY++)
    {
        for(UINT CurX = 0; CurX < _Width; CurX++)
        {
            _Data[CurY * _Width + CurX] = B[CurY + y][CurX + x];
        }
    }
}

Bitmap& Bitmap::operator = (const Bitmap &B)
{
    Allocate(B._Width,B._Height);
    memcpy(_Data,B._Data,sizeof(RGBColor)*_Width*_Height);
    return *this;
}

void Bitmap::FreeMemory()
{
    if(_Data)
    {
        delete[] _Data;
    }
    _Data = NULL;
    _Width = 0;
    _Height = 0;
}

void Bitmap::Allocate(UINT Width, UINT Height)
{
    if(_Data == NULL || _Width != Width || _Height != Height)
    {
        FreeMemory();
        _Width = Width;
        _Height = Height;
        _Data = new RGBColor[Width * Height];
    }
}

void Bitmap::SaveBMP(const String &Filename) const
{
    BITMAPFILEHEADER    bmfh;  //stores information about the file format
    BITMAPINFOHEADER    bmih;  //stores information about the bitmap
    FILE                *file; //stores file pointer

    //create bitmap file header
    ((unsigned char *)&bmfh.bfType)[0] = 'B';
    ((unsigned char *)&bmfh.bfType)[1] = 'M';
    bmfh.bfSize = 54 + _Height * _Width * 4;
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = 54;

    //create bitmap information header
    bmih.biSize = 40;
    bmih.biWidth = _Width;
    bmih.biHeight = _Height;
    bmih.biPlanes = 1;
    bmih.biBitCount = 32;
    bmih.biCompression = 0;
    bmih.biSizeImage = 0;
    bmih.biXPelsPerMeter = 3800;
    bmih.biYPelsPerMeter = 3800;
    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

    //save all header and bitmap information into file
    file = Utility::CheckedFOpen(Filename.CString(), "wb");
    fwrite(&bmfh, sizeof(BITMAPFILEHEADER), 1, file);
    fwrite(&bmih, sizeof(BITMAPINFOHEADER), 1, file);
    fwrite(_Data, sizeof(RGBColor), _Width * _Height, file);
    fclose(file);
}

void Bitmap::LoadBMP(const String &Filename)
{
    BITMAPFILEHEADER    bmfh;  //stores information about the file format
    BITMAPINFOHEADER    bmih;  //stores information about the bitmap
    FILE                *file; //stores file pointer

    //open the file and read in the headers
    file = Utility::CheckedFOpen(Filename.CString(), "rb");

    fread(&bmfh, sizeof(BITMAPFILEHEADER), 1, file);
    fread(&bmih, sizeof(BITMAPINFOHEADER), 1, file);

    _Width = bmih.biWidth;
    _Height = abs(bmih.biHeight);
    if(bmih.biBitCount == 32)
    {
        //Allocate space for the read operation
        Allocate(_Width, _Height);

        //save all header and bitmap information into file
        fread(_Data, sizeof(RGBColor), _Width * _Height, file);
    }
    else if(bmih.biBitCount == 24)
    {
        //Allocate space for the read operation
        Allocate(_Width, _Height);

        UINT Pitch = _Width * 3;
        UINT ExcessPitch = 0;
        while(double(Pitch / 4) != double(Pitch) / 4.0)
        {
            Pitch++;
            ExcessPitch++;
        }

        unsigned char *DataStore = new unsigned char[Pitch*_Height];
        fread(DataStore, 1, Pitch*_Height, file);

        UINT CurDataPos = 0;
        for(UINT i=0;i<_Height;i++)
        {
            for(UINT i2=0;i2<_Width;i2++)
            {
                RGBColor CurColor;
                CurColor.b = DataStore[CurDataPos++];
                CurColor.g = DataStore[CurDataPos++];
                CurColor.r = DataStore[CurDataPos++];
                CurColor.a = 0;
                _Data[i*_Width+i2] = CurColor;
            }
            CurDataPos += ExcessPitch;
        }

        delete[] DataStore;
    }
    else
    {
        SignalError("Invalid bit count");
    }
    fclose(file);
}

void Bitmap::LoadSDL(const String &Filename)
{
#ifdef USE_SDL
    SDL_Surface *Image = IMG_Load(Filename.CString());
    PersistentAssert(Image != NULL, String("SDL IMG_Load failed on ") + Filename);
   
    //SDL_Surface *FormattedImage = SDL_CreateRGBSurface(SDL_SWSURFACE, Image->w, Image->h, 32, 0, 0, 0, 0);
    SDL_Surface *FormattedImage = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, 1, 32, 0, 0, 0, 0);

    /*SDL_PixelFormat Format;
    Format.palette = NULL;
    Format.BitsPerPixel = 32;
    Format.BytesPerPixel = 4;
    Format.*/


    Utility::Swap(FormattedImage->format->Rmask, FormattedImage->format->Bmask);
   
    SDL_Surface *ConvertedImage = SDL_ConvertSurface(Image, FormattedImage->format, SDL_SWSURFACE);
    PersistentAssert(ConvertedImage != NULL, String("SDL_ConvertSurface failed on ") + Filename);

    Allocate(ConvertedImage->w, ConvertedImage->h);
    //memcpy(_Data, ConvertedImage->pixels, sizeof(RGBColor) * _Width * _Height);
    for(UINT y = 0; y < _Height; y++)
    {
        memcpy(_Data + _Width * y, ((RGBColor *)ConvertedImage->pixels) + _Width * (_Height - 1 - y), sizeof(RGBColor) * _Width);
    }

    SDL_FreeSurface(ConvertedImage);
    SDL_FreeSurface(FormattedImage);
    SDL_FreeSurface(Image);
#else
    PersistentSignalError("LoadSDL called without USE_SDL");
#endif
}

#ifdef USE_PNG
struct PNGDirectMemoryIORead
{
    const Vector<BYTE> *Buffer;
    UINT Offset;
};

void Bitmap::PNGReadFromBuffer(png_structp png_ptr, png_bytep data, png_size_t length)
{
    PNGDirectMemoryIORead *ReadInfo = (PNGDirectMemoryIORead *)png_get_io_ptr(png_ptr);
    memcpy(data, ReadInfo->Buffer->CArray() + ReadInfo->Offset, length);
    ReadInfo->Offset += length;
}

struct PNGDirectMemoryIOWrite
{
    Vector<BYTE> *Buffer;
};

void Bitmap::PNGWriteToBuffer(png_structp png_ptr, png_bytep data, png_size_t length)
{
    PNGDirectMemoryIOWrite *WriteInfo = (PNGDirectMemoryIOWrite *)png_get_io_ptr(png_ptr);
    UINT StartLength = WriteInfo->Buffer->Length();
    WriteInfo->Buffer->ReSize(StartLength + length);
    memcpy(WriteInfo->Buffer->CArray() + StartLength, data, length);
}

void Bitmap::PNGFlushBuffer(png_structp png_ptr)
{
   
}
#endif

void Bitmap::LoadPNG(const String &Filename)
{
#ifdef USE_PNG
    FILE *File;
    File = fopen(Filename.CString(), "rb");
    PersistentAssert(File != NULL, String("File open for LoadPNG failed: ") + String(Filename));

    png_structp PngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    PersistentAssert(PngRead != NULL, "png_create_read_struct failed.");

    png_infop PngInfo = png_create_info_struct(PngRead);
    PersistentAssert(PngInfo != NULL, "png_create_info_struct failed.");

    png_init_io(PngRead, File);
    PNGCompleteRead(PngRead, PngInfo, Filename);

    fclose(File);
#else
    SignalError("LoadPNG called without USE_PNG");
#endif
}

#ifdef USE_PNG
void Bitmap::LoadPNGFromMemory(const Vector<BYTE> &Buffer)
{
    png_structp PngRead = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    Assert(PngRead != NULL, "png_create_read_struct failed.");

    png_infop PngInfo = png_create_info_struct(PngRead);
    Assert(PngInfo != NULL, "png_create_info_struct failed.");

    PNGDirectMemoryIORead ReadInfo;
    ReadInfo.Buffer = &Buffer;
    ReadInfo.Offset = 0;

    png_set_read_fn(PngRead, (void *)&ReadInfo, Bitmap::PNGReadFromBuffer);
    PNGCompleteRead(PngRead, PngInfo, "Memory");
}

void Bitmap::PNGCompleteRead(png_structp PngRead, png_infop PngInfo, const String &Filename)
{
    png_read_png(PngRead, PngInfo, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
    png_bytepp RowPointers = png_get_rows(PngRead, PngInfo);
    Assert(RowPointers != NULL, "png_get_rows failed.");

    Allocate(PngRead->width, PngRead->height);
    if(PngRead->channels == 3 && PngRead->pixel_depth == 24)
    {
        for(UINT y = 0; y < _Height; y++)
        {
            png_bytep CurRow = RowPointers[_Height - 1 - y];
            for(UINT x = 0; x < _Width; x++)
            {
                (*this)[y][x].r = CurRow[x * 3 + 0];
                (*this)[y][x].g = CurRow[x * 3 + 1];
                (*this)[y][x].b = CurRow[x * 3 + 2];
                (*this)[y][x].a = 0;
            }
        }
    }
    else if(PngRead->channels == 4 && PngRead->pixel_depth == 32)
    {
        for(UINT y = 0; y < _Height; y++)
        {
            png_bytep CurRow = RowPointers[_Height - 1 - y];
            for(UINT x = 0; x < _Width; x++)
            {
                (*this)[y][x].r = CurRow[x * 4 + 0];
                (*this)[y][x].g = CurRow[x * 4 + 1];
                (*this)[y][x].b = CurRow[x * 4 + 2];
                (*this)[y][x].a = CurRow[x * 4 + 3];
            }
        }
    }
    else if(PngRead->channels == 1 && PngRead->pixel_depth == 8)
    {
        for(UINT y = 0; y < _Height; y++)
        {
            png_bytep CurRow = RowPointers[_Height - 1 - y];
            for(UINT x = 0; x < _Width; x++)
            {
                BYTE C = CurRow[x];
                (*this)[y][x] = RGBColor(C, C, C, 0);
            }
        }
    }
    else
    {
        /*PersistentAssert(NULL, String("Unsupported channel # / pixel depth in ") + Filename + String("; ") +
                                      String(PngRead->channels) + String(" channels ") + String(PngRead->pixel_depth) +
                                      String(" bits per pixel"));*/

        Console::WriteLine(String("Unsupported channel # / pixel depth in ") + Filename + String("; ") +
                                      String(PngRead->channels) + String(" channels ") + String(PngRead->pixel_depth) +
                                      String(" bits per pixel"));
        Clear(RGBColor::Magenta);
    }

    png_destroy_read_struct(&PngRead, &PngInfo, NULL);
}
#endif

void Bitmap::SavePNG(const String &Filename) const
{
#ifdef USE_PNG
    BitmapSaveOptions Options;
    SavePNG(Filename, Options);
#else
    SignalError("SavePNG called without USE_PNG");
#endif
}

#ifdef USE_PNG
void Bitmap::SavePNG(const String &Filename, const BitmapSaveOptions &Options) const
{
    PersistentAssert(_Width > 0 && _Height > 0, "Saving empty image");

    FILE *File;
    File = fopen(Filename.CString(), "wb");
    PersistentAssert(File != NULL, String("File open for SavePNG failed: ") + Filename);

    png_structp PngWrite = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    PersistentAssert(PngWrite != NULL, "png_create_write_struct failed.");

    png_infop PngInfo = png_create_info_struct(PngWrite);
    PersistentAssert(PngInfo != NULL, "png_create_info_struct failed.");

    png_init_io(PngWrite, File);

    PNGCompleteWrite(PngWrite, PngInfo, Options);

    fclose(File);
}

void Bitmap::SavePNGToMemory(Vector<BYTE> &Buffer) const
{
    BitmapSaveOptions Options;
    SavePNGToMemory(Buffer, Options);
}

void Bitmap::SavePNGToMemory(Vector<BYTE> &Buffer, const BitmapSaveOptions &Options) const
{
    Buffer.FreeMemory();
    png_structp PngWrite = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    Assert(PngWrite != NULL, "png_create_write_struct failed");

    png_infop PngInfo = png_create_info_struct(PngWrite);
    Assert(PngInfo != NULL, "png_create_info_struct failed");

    PNGDirectMemoryIORead WriteInfo;
    WriteInfo.Buffer = &Buffer;

    png_set_write_fn(PngWrite, (void *)&WriteInfo, Bitmap::PNGWriteToBuffer, Bitmap::PNGFlushBuffer);
    PNGCompleteWrite(PngWrite, PngInfo, Options);
}

void Bitmap::PNGCompleteWrite(png_structp PngWrite, png_infop PngInfo, const BitmapSaveOptions &Options) const
{
    UINT LocalWidth = _Width;
    UINT LocalHeight = _Height;
    if(Options.Region.Max != Vec2i::Origin)
    {
        LocalWidth = Options.Region.Dimensions().x;
        LocalHeight = Options.Region.Dimensions().y;
    }

    if(Options.SaveAlpha)
    {
        png_set_IHDR(PngWrite, PngInfo, LocalWidth, LocalHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    }
    else
    {
        png_set_IHDR(PngWrite, PngInfo, LocalWidth, LocalHeight, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    }
    png_set_PLTE(PngWrite, PngInfo, NULL, 0);
    png_set_gAMA(PngWrite, PngInfo, 0.0);
    //png_set_sRGB_gAMA_and_cHRM(PngWrite, PngInfo, PNG_sRGB_INTENT_PERCEPTUAL);
   
    png_color_8 ColorInfo;

    ColorInfo.alpha = 0;
    UINT BytesPerPixel = 3;
    if(Options.SaveAlpha)
    {
        ColorInfo.alpha = 8;
        BytesPerPixel = 4;
    }
    ColorInfo.red = 8;
    ColorInfo.green = 8;
    ColorInfo.blue = 8;
    ColorInfo.gray = 0;
    png_set_sBIT(PngWrite, PngInfo, &ColorInfo);
   
   
    UINT ScratchSizeNeeded = (sizeof(png_bytep) + sizeof(png_byte) * BytesPerPixel * LocalWidth) * LocalHeight;
    BYTE *ScratchSpace = Options.ScratchSpace;
    BYTE *ScratchSpaceStart = NULL;
    if(Options.ScratchSpaceSize < ScratchSizeNeeded)
    {
        ScratchSpace = new BYTE[ScratchSizeNeeded];
        ScratchSpaceStart = ScratchSpace;
    }

    png_bytep *RowPointers = (png_bytep *)ScratchSpace;
    ScratchSpace += sizeof(png_bytep) * LocalHeight;
    for(UINT y = 0; y < LocalHeight; y++)
    {
        BYTE *DestRowStart = ScratchSpace;
        ScratchSpace += sizeof(png_byte) * BytesPerPixel * LocalWidth;
        RowPointers[LocalHeight - 1 - y] = DestRowStart;
       
        const RGBColor *SrcRowStart = (*this)[y + Options.Region.Min.y];
        UINT DestRowStartOffset = 0;
       
        if(Options.UseBGR)
        {
            if(Options.SaveAlpha)
            {
                for(UINT x = 0; x < LocalWidth; x++)
                {
                    RGBColor Color = SrcRowStart[x + Options.Region.Min.x];
                    DestRowStart[DestRowStartOffset++] = Color.b;
                    DestRowStart[DestRowStartOffset++] = Color.g;
                    DestRowStart[DestRowStartOffset++] = Color.r;
                    DestRowStart[DestRowStartOffset++] = Color.a;
                }
            }
            else
            {
                for(UINT x = 0; x < LocalWidth; x++)
                {
                    RGBColor Color = SrcRowStart[x + Options.Region.Min.x];
                    DestRowStart[DestRowStartOffset++] = Color.b;
                    DestRowStart[DestRowStartOffset++] = Color.g;
                    DestRowStart[DestRowStartOffset++] = Color.r;
                }
            }
        }
        else
        {
            if(Options.SaveAlpha)
            {
                for(UINT x = 0; x < LocalWidth; x++)
                {
                    RGBColor Color = SrcRowStart[x + Options.Region.Min.x];
                    DestRowStart[DestRowStartOffset++] = Color.r;
                    DestRowStart[DestRowStartOffset++] = Color.g;
                    DestRowStart[DestRowStartOffset++] = Color.b;
                    DestRowStart[DestRowStartOffset++] = Color.a;
                }
            }
            else
            {
                for(UINT x = 0; x < LocalWidth; x++)
                {
                    RGBColor Color = SrcRowStart[x + Options.Region.Min.x];
                    DestRowStart[DestRowStartOffset++] = Color.r;
                    DestRowStart[DestRowStartOffset++] = Color.g;
                    DestRowStart[DestRowStartOffset++] = Color.b;
                }
            }
        }
    }

    png_set_rows(PngWrite, PngInfo, RowPointers);
    png_write_png(PngWrite, PngInfo, NULL, NULL);

    png_destroy_write_struct(&PngWrite, &PngInfo);

    if(ScratchSpaceStart != NULL)
    {
        delete[] ScratchSpaceStart;
    }
}
#endif

void Bitmap::SavePPM(const String &Filename) const
{
    //PPM is a very simple ASCII format
    String FilenameCopy = Filename;
    ofstream file(FilenameCopy.CString());
    file << "P3" << endl;
    file << "# PPM saved to " << FilenameCopy.CString() << endl;
    file << _Width << ' ' << _Height << endl;
    file << 255 << endl;    //maximum value of a component

    for(UINT i2=0;i2<_Height;i2++)
    {
        for(UINT i=0;i<_Width;i++)
        {
            file << int((*this)[i2][i].r) << ' ' << int((*this)[i2][i].g) << ' ' << int((*this)[i2][i].b) << endl;
        }
    }
}

void Bitmap::SavePGM(const String &Filename, int Channel) const
{
    FILE *File;
    File = fopen(Filename.CString(), "wb");
    PersistentAssert(File != NULL, "File open for SavePNG failed.");

    fprintf(File, "P5\n%d %d\n255\n", _Width, _Height);

    for (UINT y = 0; y < _Height; y++)
    {
        for (UINT x = 0; x < _Width; x++)
        {
            unsigned char Value;
            RGBColor Color = (*this)[y][x];
            switch(Channel)
            {
            case 0:
                Value = Color.r;
                break;
            case 1:
                Value = Color.g;
                break;
            case 2:
                Value = Color.b;
                break;
            case 3:
                Value = Color.a;
                break;
            case 4:
            default:
                Value = Utility::BoundToByte((int(Color.r) + int(Color.g) + int(Color.b)) / 3);
            }
            fputc(Value, File);
        }
    }
}

void Bitmap::LoadAlphaChannelAsGrayscale()
{
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            RGBColor &C = (*this)[y][x];
            C.r = C.a;
            C.g = C.a;
            C.b = C.a;
        }
    }
}

void Bitmap::FlipBlueAndRed()
{
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            RGBColor &C = (*this)[y][x];
            unsigned char Temp = C.r;
            C.r = C.b;
            C.b = Temp;
        }
    }
}

void Bitmap::Clear()
{
    memset(_Data, 0, sizeof(RGBColor) * _Width * _Height);
}

void Bitmap::Clear(const RGBColor &Color)
{
    for(UINT i=0;i<_Width;i++)
    {
        _Data[i] = Color;
    }

    for(UINT i2=1;i2<_Height;i2++)
    {
        memcpy(&_Data[i2*_Width], &_Data[0], sizeof(RGBColor) * _Width);
    }
}

void Bitmap::BltTo(Bitmap &B, int TargetX, int TargetY) const
{
    BltTo(B, TargetX, TargetY, 0, 0, _Width, _Height);
}

void Bitmap::BltToNoClipMemcpy(Bitmap &B, UINT TargetX, UINT TargetY) const
{
    const UINT Height = _Height;
    const UINT Width = _Width;
    for(UINT y = 0; y < Height; y++)
    {
        const UINT CurTargetY = y + TargetY;
        memcpy(B._Data + (CurTargetY * B._Width + TargetX), _Data + y * _Width, sizeof(RGBColor) * Width);
    }
}

void Bitmap::BltTo(Bitmap &B, int TargetX, int TargetY, int SourceX, int SourceY, int Width, int Height) const
{
    const int BHeight = B._Height;
    const int BWidth = B._Width;
    for(int y = 0; y < Height; y++)
    {
        const int CurTargetY = y + TargetY;
        const int CurSourceY = y + SourceY;
        if(CurTargetY >= 0 && CurTargetY < BHeight &&
           CurSourceY >= 0 && CurSourceY < int(_Height))
        {
            for(int x = 0; x < Width; x++)
            {
                const int CurTargetX = x + TargetX;
                const int CurSourceX = x + SourceX;
                if(CurTargetX >= 0 && CurTargetX < BWidth &&
                   CurSourceX >= 0 && CurSourceX < int(_Width))
                {
                    B[CurTargetY][CurTargetX] = (*this)[CurSourceY][CurSourceX];
                }
            }
        }
    }
}

void Bitmap::StretchBltTo(Bitmap &B, const Rectangle2i &TargetRect, const Rectangle2i &SourceRect, SamplingType Sampling) const
{
    StretchBltTo(B,
        TargetRect.Min.x, TargetRect.Min.y, TargetRect.Width(), TargetRect.Height(),
        SourceRect.Min.x, SourceRect.Min.y, SourceRect.Width(), SourceRect.Height(), Sampling);
}

void Bitmap::StretchBltTo(Bitmap &B, int TargetX, int TargetY, int TargetWidth, int TargetHeight, int SourceX, int SourceY, int SourceWidth, int SourceHeight, SamplingType Sampling) const
{
    if(Sampling == SamplingPoint)
    {
        const float RatioX = float(SourceWidth) / float(TargetWidth);
        const float RatioY = float(SourceHeight) / float(TargetHeight);
        const float SourceXOffset = float(SourceX + 0.5f * RatioX) + 0.5f;
        const float SourceYOffset = float(SourceY + 0.5f * RatioY) + 0.5f;
        const int Width = _Width;
        const int Height = _Height;
        for(int y = 0; y < TargetHeight; y++)
        {
            for(int x = 0; x < TargetWidth; x++)
            {
                int XMiddle = int(x * RatioX + SourceXOffset);
                int YMiddle = int(y * RatioY + SourceYOffset);
                if(XMiddle >= 0 && XMiddle < Width && YMiddle >= 0 && YMiddle < Height)
                {
                    B._Data[(y + TargetY) * B._Width + x + TargetX] = _Data[YMiddle * _Width + XMiddle];
                }
            }
        }
    }
    else if(Sampling == SamplingLinear)
    {
        for(int y = 0; y < TargetHeight; y++)
        {
            for(int x = 0; x < TargetWidth; x++)
            {
                float XStart = Math::LinearMap(0.0f, float(TargetWidth),  float(SourceX), float(SourceX + SourceWidth ), float(x + 0));
                float XEnd =   Math::LinearMap(0.0f, float(TargetWidth),  float(SourceX), float(SourceX + SourceWidth ), float(x + 1));
                float YStart = Math::LinearMap(0.0f, float(TargetHeight), float(SourceY), float(SourceY + SourceHeight), float(y + 0));
                float YEnd =   Math::LinearMap(0.0f, float(TargetHeight), float(SourceY), float(SourceY + SourceHeight), float(y + 1));

                B[y + TargetY][x + TargetX] = AverageColorOverRegion(Rectangle2f(Vec2f(XStart, YStart), Vec2f(XEnd, YEnd)));
            }
        }
    }
}

RGBColor Bitmap::AverageColorOverRegion(const Rectangle2f &Region) const
{
    Vec3f Result = Vec3f::Origin;
    int XStart = Math::Max(Math::Floor(Region.Min.x), 0);
    int YStart = Math::Max(Math::Floor(Region.Min.y), 0);
    int XEnd = Math::Min(Math::Ceiling(Region.Max.x), int(_Width) - 1);
    int YEnd = Math::Min(Math::Ceiling(Region.Max.y), int(_Height) - 1);

    const RGBColor *Data = _Data;
    const UINT Width = _Width;
    UINT Count = 0;
    for(int y = YStart; y <= YEnd; y++)
    {
        for(int x = XStart; x <= XEnd; x++)
        {
            Count++;
            Result += Vec3f(Data[y * Width + x]);
        }
    }
    return RGBColor(Result * (1.0f / float(Count)));
}

void Bitmap::LoadGrid(const Grid<float> &Values)
{
    float Min = Values(0, 0);
    float Max = Values(0, 0);
    for(UINT y = 0; y < Values.Rows(); y++)
    {
        for(UINT x = 0; x < Values.Cols(); x++)
        {
            if(Values(y, x) != 0.0f)
            {
                if(Min == 0.0f)
                {
                    Min = Values(y, x);
                }
                Min = Math::Min(Min, Values(y, x));
            }
            Max = Math::Max(Max, Values(y, x));
        }
    }
    LoadGrid(Values, Min, Max);
}

void Bitmap::LoadGrid(const Grid<float> &Values, float Min, float Max)
{
    Allocate(Values.Cols(), Values.Rows());
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            BYTE Intensity = 0;
            if(Max != Min)
            {
                Intensity = Utility::BoundToByte(Math::LinearMap(Min, Max, 0.0f, 255.0f, Values(y, x)));
            }
            if(Values(y, x) == 0.0f)
            {
                (*this)[y][x] = RGBColor::Magenta;
            }
            else
            {
                (*this)[y][x] = RGBColor(Intensity, Intensity, Intensity);
            }
        }
    }
}

/*BITMAP_HASH Bitmap::UnorientedHash() const
{
    BITMAP_HASH Result = _Width + _Height + _Width * _Height + _Width * _Width * _Height;
    for(UINT y=0;y<_Height;y++)
    {
        for(UINT x=0;x<_Width;x++)
        {
            RGBColor C = (*this)[y][x];
            Result += int(C.r) * 541 +
                      int(C.g) * 978 +
                      int(C.b) * 1024 +
                      int(C.r) * int(C.g);
        }
    }
    return Result;
}*/


void Bitmap::ReplaceColor(RGBColor CurrentColor, RGBColor NewColor)
{
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            if((*this)[y][x] == CurrentColor)
            {
                (*this)[y][x] = NewColor;
            }
        }
    }
}

UINT Bitmap::Hash32() const
{
    return Utility::Hash32((BYTE *)_Data, sizeof(RGBColor) * _Width * _Height) + (_Width * 785 + _Height * 97);
}

UINT64 Bitmap::Hash64() const
{
    return Utility::Hash64((BYTE *)_Data, sizeof(RGBColor) * _Width * _Height) + (_Width * 785 + _Height * 97);
}

UINT Bitmap::CountPixelsWithColor(RGBColor Color) const
{
    UINT Result = 0;
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            if(_Data[y * _Width + x] == Color)
            {
                Result++;
            }
        }
    }
    return Result;
}

bool Bitmap::Monochrome(RGBColor Color) const
{
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            if(_Data[y * _Width + x] != Color)
            {
                return false;
            }
        }
    }
    return true;
}

bool Bitmap::MonochromeIncludingAlpha(RGBColor Color) const
{
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            const RGBColor C = _Data[y * _Width + x];
            if(C.r != Color.r || C.g != Color.g || C.b != Color.b || C.a != Color.a)
            {
                return false;
            }
        }
    }
    return true;
}

bool Bitmap::PerfectMatch(const Bitmap &Bmp) const
{
    if(Bmp.Dimensions() != Dimensions())
    {
        return false;
    }
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            if ( (*this)[y][x].r != Bmp[y][x].r ||
                 (*this)[y][x].g != Bmp[y][x].g ||
                 (*this)[y][x].b != Bmp[y][x].b)
            {
                return false;
            }
        }
    }
    return true;
}

void Bitmap::FlipHorizontal()
{
    Bitmap BTemp = *this;
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            (*this)[y][x] = BTemp[y][_Width - 1 - x];
        }
    }
}

void Bitmap::FlipVertical()
{
    Bitmap BTemp = *this;
    for(UINT y = 0; y < _Height; y++)
    {
        for(UINT x = 0; x < _Width; x++)
        {
            (*this)[y][x] = BTemp[_Height - 1 - y][x];
        }
    }
}

#if 0
//
// The following is taken from
// http://www.suchit-tiwari.org/antialias.html
//
void Bitmap::DrawLine(int X0, int Y0, int X1, int Y1)
{
    const UINT NumLevels = 256;
    const UINT IntensityBits = 8;
    const UINT BaseColor = 0;

    unsigned short IntensityShift, ErrorAdj, ErrorAcc;
    unsigned short ErrorAccTemp, Weighting, WeightingComplementMask;
    short DeltaX, DeltaY, Temp, XDir;

    X0 = Utility::Bound(X0, 0, int(_Width) - 1);
    X1 = Utility::Bound(X1, 0, int(_Width) - 1);

    Y0 = Utility::Bound(Y0, 0, int(_Height) - 1);
    Y1 = Utility::Bound(Y1, 0, int(_Height) - 1);

    /* Make sure the line runs top to bottom */
    if (Y0 > Y1)
    {
        Temp = Y0; Y0 = Y1; Y1 = Temp;
        Temp = X0; X0 = X1; X1 = Temp;
    }

    /* Draw the initial pixel, which is always exactly intersected by
      the line and so needs no weighting */

    (*this)[Y0][X0] = RGBColor::Black;

    if ((DeltaX = X1 - X0) >= 0)
    {
      XDir = 1;
    }
    else
    {
      XDir = -1;
      DeltaX = -DeltaX; /* make DeltaX positive */
    }
    /* Special-case horizontal, vertical, and diagonal lines, which
      require no weighting because they go right through the center of
      every pixel */

    if ((DeltaY = Y1 - Y0) == 0)
    {
        /* Horizontal line */
        while (DeltaX-- != 0)
        {
            X0 += XDir;
            (*this)[Y0][X0] = RGBColor::Black;
        }
        return;
    }
    if (DeltaX == 0) {
        /* Vertical line */
        do
        {
            Y0++;
            (*this)[Y0][X0] = RGBColor::Black;
        } while (--DeltaY != 0);
        return;
    }
    if (DeltaX == DeltaY) {
      /* Diagonal line */
      do
      {
         X0 += XDir;
         Y0++;
         (*this)[Y0][X0] = RGBColor::Black;
      } while (--DeltaY != 0);
      return;
    }
    /* Line is not horizontal, diagonal, or vertical */
    ErrorAcc = 0/* initialize the line error accumulator to 0 */
    /* # of bits by which to shift ErrorAcc to get intensity level */
    IntensityShift = 16 - IntensityBits;
    /* Mask used to flip all bits in an intensity weighting, producing the
      result (1 - intensity weighting) */

    WeightingComplementMask = NumLevels - 1;
    /* Is this an X-major or Y-major line? */
    if (DeltaY > DeltaX)
    {
      /* Y-major line; calculate 16-bit fixed-point fractional part of a
         pixel that X advances each time Y advances 1 pixel, truncating the
         result so that we won't overrun the endpoint along the X axis */

      ErrorAdj = unsigned short(((unsigned long) DeltaX << 16) / (unsigned long) DeltaY);
      /* Draw all pixels other than the first and last */
      while (--DeltaY) {
         ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
         ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
         if (ErrorAcc <= ErrorAccTemp) {
            /* The error accumulator turned over, so advance the X coord */
            X0 += XDir;
         }
         Y0++; /* Y-major, so always advance Y */
         /* The IntensityBits most significant bits of ErrorAcc give us the
            intensity weighting for this pixel, and the complement of the
            weighting for the paired pixel */

         Weighting = ErrorAcc >> IntensityShift;
        
         unsigned char LVal = (BaseColor + Weighting);
         (*this)[Y0][X0] = RGBColor(LVal, LVal, LVal);

         LVal = BaseColor + (Weighting ^ WeightingComplementMask);
         (*this)[Y0][X0 + XDir] = RGBColor(LVal, LVal, LVal);
      }
      /* Draw the final pixel, which is always exactly intersected by the line
         and so needs no weighting */

      (*this)[Y1][X1] = RGBColor::Black;
      return;
    }
    /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
      pixel that Y advances each time X advances 1 pixel, truncating the
      result to avoid overrunning the endpoint along the X axis */

    ErrorAdj = unsigned short(((unsigned long) DeltaY << 16) / (unsigned long) DeltaX);
    /* Draw all pixels other than the first and last */
    while (--DeltaX) {
      ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
      ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
      if (ErrorAcc <= ErrorAccTemp) {
         /* The error accumulator turned over, so advance the Y coord */
         Y0++;
      }
      X0 += XDir; /* X-major, so always advance X */
      /* The IntensityBits most significant bits of ErrorAcc give us the
         intensity weighting for this pixel, and the complement of the
         weighting for the paired pixel */

      Weighting = ErrorAcc >> IntensityShift;
     
      unsigned char LVal = (BaseColor + Weighting);
      (*this)[Y0][X0] = RGBColor(LVal, LVal, LVal);

      LVal = BaseColor + (Weighting ^ WeightingComplementMask);
      (*this)[Y0 + 1][X0] = RGBColor(LVal, LVal, LVal);
    }
    /* Draw the final pixel, which is always exactly intersected by the line
      and so needs no weighting */

    (*this)[Y1][X1] = RGBColor::Black;
}
#endif

#ifdef USE_D3D9
void Bitmap::LoadFromSurface(LPDIRECT3DSURFACE9 Surface)
{
    D3DSURFACE_DESC Desc;
    D3DLOCKED_RECT LockedRect;
    D3DAlwaysValidate(Surface->GetDesc(&Desc), "GetDesc");
    PersistentAssert(Desc.Format == D3DFMT_A8R8G8B8 ||
                     Desc.Format == D3DFMT_X8R8G8B8, "Invalid surface format");
    Allocate(Desc.Width, Desc.Height);
    D3DAlwaysValidate(Surface->LockRect(&LockedRect, NULL, 0), "LockRect");
    BYTE *_Data = (BYTE *)LockedRect.pBits;
    if(Desc.Format == D3DFMT_A8R8G8B8 || Desc.Format == D3DFMT_X8R8G8B8)
    {
        for(UINT y = 0; y < _Height; y++)
        {
            RGBColor *RowStart = (RGBColor *)(_Data + y * LockedRect.Pitch);
            memcpy((*this)[_Height - 1 - y], RowStart, sizeof(RGBColor) * _Width);
            //for(UINT x = 0; x < _Width; x++)
            //{
            //    (*this)[y][x] = RowStart[x];
            //}
        }
    }
    D3DAlwaysValidate(Surface->UnlockRect(), "UnlockRect");
}

UINT Bitmap::SaveDelta(const Bitmap &Bmp, const String &Filename) const
{
    const UINT Width = _Width;
    const UINT Height = _Height;
    const UINT PixelCount = Width * Height;
    const RGBColor *MyData = _Data;
    const RGBColor *PrevData = Bmp._Data;
    PersistentAssert(Width == Bmp.Width() && Height == Bmp.Height(), "Invalid dimensions");
   
    static BYTE *Storage = NULL;
    if(Storage == NULL)
    {
        Storage = new BYTE[PixelCount * sizeof(RGBColor) * 2];
    }
    BYTE *CurStorageByte = Storage;

    UINT PixelIndex = 0;
    while(PixelIndex < PixelCount)
    {
        const WORD MyColor16 = RGBColor16FromRGBColor32(*MyData);
        const WORD PrevColor16 = RGBColor16FromRGBColor32(*PrevData);
        if(MyColor16 == PrevColor16)
        {
            BYTE *Counter = CurStorageByte;
            CurStorageByte++;
            *Counter = 255;
            for(UINT MatchIndex = 0; MatchIndex < 128; MatchIndex++)
            {
                const WORD MatchMyColor16 = RGBColor16FromRGBColor32(*(MyData));
                const WORD MatchPrevColor16 = RGBColor16FromRGBColor32(*(PrevData));
                if(MatchMyColor16 == MatchPrevColor16)
                {
                    PrevData++;
                    MyData++;
                    PixelIndex++;
                    (*Counter)++;
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            BYTE *Counter = CurStorageByte;
            CurStorageByte++;
            *Counter = 127;
            //const UINT ChromaSubsamplingRate = 3;
            //UINT ChromaSubsamplingIndex = ChromaSubsamplingRate - 1;
            for(UINT MatchIndex = 0; MatchIndex < 128; MatchIndex++)
            {
                RGBColor MatchMyColor = *(MyData);
                const WORD MatchMyColor16 = RGBColor16FromRGBColor32(MatchMyColor);
                const WORD MatchPrevColor16 = RGBColor16FromRGBColor32(*(PrevData));
                if(MatchMyColor16 != MatchPrevColor16)
                {
                    //ChromaSubsamplingIndex++;
                    //if(ChromaSubsamplingIndex == ChromaSubsamplingRate)
                    {
                        //ChromaSubsamplingIndex = 0;
                        *((WORD*)CurStorageByte) = RGBColor16FromRGBColor32(MatchMyColor);
                        CurStorageByte += 2;
                    }
                    //else
                    //{
                    //    *CurStorageByte = (int(MatchMyColor.r) + int(MatchMyColor.g) + int(MatchMyColor.b)) / 3;
                    //    CurStorageByte++;
                    //}
                    PrevData++;
                    MyData++;
                    PixelIndex++;
                    (*Counter)++;
                }
                else
                {
                    break;
                }
            }
        }
    }
    FILE *File = Utility::CheckedFOpen(Filename.CString(), "wb");
    fwrite(Storage, CurStorageByte - Storage, 1, File);
    fclose(File);
    return (CurStorageByte - Storage);
}

void Bitmap::LoadDelta(const Bitmap &Bmp, const String &Filename)
{
    *this = Bmp;
    const UINT Width = _Width;
    const UINT Height = _Height;
    const UINT PixelCount = Width * Height;
    RGBColor *MyData = _Data;
    const RGBColor *PrevData = Bmp._Data;
   
    Vector<BYTE> Storage;
    Utility::GetFileData(Filename, Storage);
    BYTE *CurStorageByte = Storage.CArray();
    UINT PixelIndex = 0;
    while(PixelIndex < PixelCount)
    {
        BYTE Counter = *CurStorageByte;
        CurStorageByte++;
        if(Counter >= 0 && Counter < 128)
        {
            PixelIndex += Counter + 1;
        }
        else
        {
            Counter -= 128;
            //const UINT ChromaSubsamplingRate = 3;
            //UINT ChromaSubsamplingIndex = ChromaSubsamplingRate - 1;
            RGBColor PrevColor;
            for(UINT MatchIndex = 0; MatchIndex <= Counter; MatchIndex++)
            {
                //ChromaSubsamplingIndex++;
                //if(ChromaSubsamplingIndex == ChromaSubsamplingRate)
                {
                    //ChromaSubsamplingIndex = 0;
                    PrevColor = RGBColor32FromRGBColor16(*((WORD*)CurStorageByte));
                    CurStorageByte += 2;
                    MyData[PixelIndex] = PrevColor;
                }
                //else
                //{
                //    UINT L = (*CurStorageByte);
                //    CurStorageByte++;
                //    if(PrevColor.r == 0 && PrevColor.g == 0 && PrevColor.b == 0)
                //    {
                //        PrevColor = RGBColor(1, 1, 1);
                //    }
                //    Vec3f D = Vec3f::Normalize(Vec3f(PrevColor));
                //    D *= (3.0f / 255.0f) * L / (D.x + D.y + D.z);
                //    UINT B = (int(RGBColor(D).r) + int(RGBColor(D).g) + int(RGBColor(D).b)) / 3;
                //    MyData[PixelIndex] = RGBColor(D);
                //}
                PixelIndex++;
            }
        }
    }
}

#if 0
void Bitmap::SaveSurfaceToZLIB(LPDIRECT3DSURFACE9 Surface, const String &Filename, const BitmapSaveOptions &Options)
{
    D3DSURFACE_DESC Desc;
    D3DLOCKED_RECT LockedRect;
    D3DAlwaysValidate(Surface->GetDesc(&Desc), "GetDesc");
    PersistentAssert(Desc.Format == D3DFMT_A8R8G8B8 ||
                     Desc.Format == D3DFMT_X8R8G8B8, "Invalid surface format");
    D3DAlwaysValidate(Surface->LockRect(&LockedRect, NULL, 0), "LockRect");
    const UINT Width = Desc.Width;
    const UINT Height = Desc.Height;
    BYTE *_Data = (BYTE *)LockedRect.pBits;

    int Result;
    z_stream Stream;

    const UINT InputBytes = Width * Height * sizeof(RGBColor);
    BYTE *CompressedStream = new BYTE[InputBytes + 1024];

    Stream.zalloc = Z_NULL;
    Stream.zfree = Z_NULL;
    Stream.opaque = Z_NULL;
   
    Stream.avail_in = InputBytes;
    Stream.next_in = _Data;

    Stream.data_type = Z_BINARY;

    Stream.avail_out = InputBytes;
    Stream.next_out = CompressedStream;

    const int Level = 6;
    //Result = deflateInit(&Stream, Level);
    Result = deflateInit2(&Stream, Level, Z_DEFLATED, 8, 8, Z_HUFFMAN_ONLY);
    PersistentAssert(Result == Z_OK, "deflateInit failed");

    Result = deflate(&Stream, Z_FINISH);
    PersistentAssert(Result == Z_STREAM_END, "deflate failed");

    deflateEnd(&Stream);

    delete[] CompressedStream;

    D3DAlwaysValidate(Surface->UnlockRect(), "UnlockRect");
    //fclose(File);
}
#endif

#ifdef USE_PNG
void Bitmap::SaveSurfaceToPNG(LPDIRECT3DSURFACE9 Surface, const String &Filename, const BitmapSaveOptions &Options)
{
    D3DSURFACE_DESC Desc;
    D3DLOCKED_RECT LockedRect;
    D3DAlwaysValidate(Surface->GetDesc(&Desc), "GetDesc");
    PersistentAssert(Desc.Format == D3DFMT_A8R8G8B8 ||
                     Desc.Format == D3DFMT_X8R8G8B8, "Invalid surface format");
    D3DAlwaysValidate(Surface->LockRect(&LockedRect, NULL, 0), "LockRect");
    const UINT Width = Desc.Width;
    const UINT Height = Desc.Height;
    BYTE *_Data = (BYTE *)LockedRect.pBits;
    /*for(UINT y = 0; y < Height; y++)
    {
        RGBColor *RowStart = (RGBColor *)(_Data + y * LockedRect.Pitch);
        memcpy((*this)[Height - 1 - y], RowStart, sizeof(RGBColor) * Width);
        //for(UINT x = 0; x < _Width; x++)
        //{
        //    (*this)[y][x] = RowStart[x];
        //}
    }*/


    FILE *File;
    File = fopen(Filename.CString(), "wb");
    PersistentAssert(File != NULL, "File open for SavePNG failed");

    png_structp PngWrite = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    PersistentAssert(PngWrite != NULL, "png_create_write_struct failed");

    png_infop PngInfo = png_create_info_struct(PngWrite);
    PersistentAssert(PngInfo != NULL, "png_create_info_struct failed");

    png_init_io(PngWrite, File);

    png_set_IHDR(PngWrite, PngInfo, Width, Height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
   
    png_set_PLTE(PngWrite, PngInfo, NULL, 0);
    png_set_gAMA(PngWrite, PngInfo, 0.0);
   
    png_color_8 ColorInfo;

    ColorInfo.alpha = 0;
    UINT BytesPerPixel = 3;
    if(Options.SaveAlpha)
    {
        ColorInfo.alpha = 8;
        BytesPerPixel = 4;
    }
    ColorInfo.red = 8;
    ColorInfo.green = 8;
    ColorInfo.blue = 8;
    ColorInfo.gray = 0;
    png_set_sBIT(PngWrite, PngInfo, &ColorInfo);
   
   
    UINT ScratchSizeNeeded = sizeof(png_bytep) * Height;
    BYTE *ScratchSpace = Options.ScratchSpace;
    BYTE *ScratchSpaceStart = NULL;
    if(Options.ScratchSpaceSize < ScratchSizeNeeded)
    {
        ScratchSpace = new BYTE[ScratchSizeNeeded];
        ScratchSpaceStart = ScratchSpace;
    }

    png_bytep *RowPointers = (png_bytep *)ScratchSpace;
    for(UINT y = 0; y < Height; y++)
    {
        RowPointers[y] = (_Data + y * LockedRect.Pitch);
    }

    png_set_rows(PngWrite, PngInfo, RowPointers);
    png_write_png(PngWrite, PngInfo, NULL, NULL);

    png_destroy_write_struct(&PngWrite, &PngInfo);

    if(ScratchSpaceStart != NULL)
    {
        delete[] ScratchSpaceStart;
    }

    D3DAlwaysValidate(Surface->UnlockRect(), "UnlockRect");
    fclose(File);
}
#endif

#endif

Bitmap operator + (const Bitmap &L, const Bitmap &R)
{
    Assert(L.Width() == R.Width() && L.Height() == R.Height(), "Invalid dimensions in Bitmap operator +");
    Bitmap Result = L;
    for(UINT y = 0; y < Result.Height(); y++)
    {
        for(UINT x = 0; x < Result.Width(); x++)
        {
            Result[y][x] += R[y][x];
        }
    }
    return Result;
}

'MFC > Bitmap' 카테고리의 다른 글

Storing an Image  (0) 2013.08.16
Bitmap.h  (0) 2013.08.16
리소스에서 BMP 이미지 불러오기  (0) 2013.08.09
Writing a window image to a BMP file  (0) 2013.08.08
Converting DDB to DIB  (0) 2013.08.08
Posted by 곰돌이짱
,