SOL9 2.0 Class: DIBSection

 SOL9 C++ Class Library  SOL9 Samples  SOL9 Tutorial  SOL9 FAQ  SOL9 ClassTree  SOL9 ClassList 

Source code

/******************************************************************************
 *
 * Copyright (c) 1999-2012 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *  DIBSection.h
 *
 *****************************************************************************/

// SOL++2000

// 2012/06/28 Added saveAs method.

#pragma once

#include <sol\GdiObject.h>
#include <sol\MemoryDC.h>
#include <sol\BinaryFile.h>
#include <sol\gdiplus\ImageTransformer.h>

namespace SOL {

class DIBSection :public GdiObject {

//private:
protected:
  
  MemoryDC  memDC;
  HBITMAP    hbitmap;
  HBITMAP    hprevBitmap;
  unsigned char*  buffer;

  BITMAPINFO*  bi;
  int      bitCount;
  int      bmiSize;

  int      sx;
  int      sy;
  int      ratio;    //Stretching ratio in % unit.
  DWORD    operation;  //Raster operation

  int      colorCount;

private:
  HBITMAP create(DC& dc) 
  {
    if (bi == NULL) {
      return NULL;
    }

    HDC hdc = dc.get();
    
    HDC cdc = ::CreateCompatibleDC(hdc);
    
    memDC.set(cdc);

    int width  = bi->bmiHeader.biWidth;
    int height = bi->bmiHeader.biHeight ;
  
    //Printf("DIBSection::create  bitCount %d\r\n", bitCount);
  
    buffer    = NULL;

    hbitmap = ::CreateDIBSection(cdc, bi,
          DIB_RGB_COLORS, (void**)&buffer, NULL, 0);

    if (hbitmap && buffer) {
      hprevBitmap = (HBITMAP)memDC.select(hbitmap);
    } else {
      destroy();
      MessageBox(NULL, _T("Failed to create DIBSection"), _T("SOL++ Fatal Error"),
      MB_OK);
    }

    return hbitmap;
  }

  unsigned char getColorIndex(COLORREF color)
  {
    unsigned char index = 0;

    if (bitCount <16) {

      if (bi && hbitmap ) {

        RGBQUAD* rgb = bi->bmiColors;

        for(int i = 0; i<colorCount; i++) {
          if (color == RGB(rgb->rgbRed, rgb->rgbGreen, rgb->rgbBlue)) {
            index = (unsigned char)i; //2000/11/18
            break;
          }
          rgb++;
        }
      }
    }
    return index;
  }


  RGBTRIPLE* getRGBTriple(int x, int y)
  {
    unsigned char* triple = NULL;

    int width  = getWidth();
    int height = getHeight();
  
    if (bitCount == 24 && width >0 && height > 0) {
      
      if (buffer && x >= 0 && x <width &&
        y >= 0 && y < height && hbitmap ) {

        unsigned char* tripleBuffer    = buffer;
      
        int dwLength = 0;
        int twidth = width * 3;
        if ( ((twidth) % 4) ==0) {
          dwLength = twidth;
        }
        else {
          dwLength = twidth + (4 - twidth % 4);
        }
      
        triple = (tripleBuffer + (height - y -1) * dwLength + x * 3);
        unsigned char* top = buffer + (height) * dwLength;

        if (triple >= top) {
          return NULL;
        }
      }
    }
    return (RGBTRIPLE*)triple;
  }


  WORD getColor(int x, int y)
  {
    WORD color = 0;
    int width  = getWidth();
    int height = getHeight();

    if (bitCount == 16 && width >0 && height > 0) {  
      if (buffer && x >= 0 && x <width &&
        y >= 0 && y <height && hbitmap ) {
      
        WORD* colorBuffer    = (WORD*)buffer;

        WORD* buff = colorBuffer + (height - y -1) * width + x;
        WORD* top  = colorBuffer + (height) * width;

        if (buff >= top) {
          return color;
        }
        color = *buff;
      }    
    }
    return color;
  }


  RGBQUAD* getRGBQuad(int x, int y) 
  {
    RGBQUAD* quad = NULL;

    if (bi == NULL || buffer == NULL || hbitmap == NULL) {
      return quad;
    }

    int width  = getWidth();
    int height = getHeight();

    if (width <= 0 || height <= 0) {
      return quad;
    }

    if (x <0 || x >= width || y <0 || y >= height) {
      return quad;
    }

    if (bitCount==32) {
      RGBQUAD* quadBuffer    = (RGBQUAD*)buffer;
      quad = quadBuffer + (height - y -1)*width + x;
      RGBQUAD* top = quadBuffer + (height)*width ;

      if (quad >= top) {
        return NULL;
      }
      return quad;
    }

    // Sorry, not implemented for case of bitCount = 16;

    if (bitCount == 8) {
      quad = NULL;
      
      int nwidth = width;
      nwidth = (nwidth + 3)/4 *4;

      unsigned char* buff = (buffer + (height- y -1) * nwidth + x);
      unsigned char* top  = buffer + (height) * nwidth;
    
      if (buff < top) {
        unsigned char index = *buff;
        if (index >=0 && index <colorCount) {
          RGBQUAD* rgbQuad = bi->bmiColors;
          if (rgbQuad) {
            quad = &rgbQuad[index];
          }
        }
      }
      return quad;
    }


    if (bitCount == 4) {
      quad = NULL;

      int nwidth = (width +7)/8 * 8;
      unsigned char* buff = buffer + ((height- y -1) * nwidth + x)/2;
      unsigned char* top  = buffer + (height ) * nwidth;

      if (buff >= top) {
        return quad;
      }

      unsigned char index = *buff;
          
      if ((x % 2) == 0) {
        index = (unsigned char)(index >>4);

      } else {
        index = (unsigned char)(index & 0xf);
      }

      if (index >=0 && index <colorCount) {
        RGBQUAD* rgbQuad = bi->bmiColors;
        if (rgbQuad) {
          quad = &rgbQuad[index];
        }
      }

      return quad;
    }
  
    return quad;
  }


  int getColorCount(BITMAPINFOHEADER* bi)
  {
    if (bi == NULL) {
      return 0;
    }

    if (bi->biClrUsed != 0) {
      return (int)bi->biClrUsed;
    }

    return getColorCount(bi->biBitCount);
  }



  int getColorCount(int bits)
  {
    switch (bits){
    case 1:
      return 2;
    case 4:
      return 16;
    case 8:
      return 256;
        default:
      break;
    }
    return 0;
  }


//
public:
  DIBSection()
  :GdiObject(NULL), 
  memDC(),
  hbitmap(NULL),
  hprevBitmap(NULL),
  bi(NULL), 
  buffer(NULL)
  { 
    sx      = 0;
    sy      = 0;
    ratio    = 100;    
    operation  = SRCCOPY;
    bitCount  = 32;
    bmiSize     = 0;
    colorCount  = 0;
    bmiSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colorCount;
  }


public:
  DIBSection(DC& dc, int width, int height)
  :GdiObject(NULL), 
  memDC(dc), 
  hbitmap(NULL), 
  hprevBitmap(NULL),
  bi(NULL), 
  bmiSize(0),
  buffer(NULL)
{ 
    sx      = 0;
    sy      = 0;
    ratio    = 100;   
    operation  = SRCCOPY;
    bitCount    = 32;
    colorCount  = getColorCount(bitCount);

    bmiSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colorCount;

    bi = (BITMAPINFO*)new char[bmiSize];
    memset(bi, 0, bmiSize);

    if (width > 0 && height > 0) {
  
      bi->bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
      bi->bmiHeader.biWidth       = width;
      bi->bmiHeader.biHeight      = height;
      bi->bmiHeader.biPlanes      = 1;
      bi->bmiHeader.biBitCount    = (WORD)bitCount; //2000/11/18
      bi->bmiHeader.biCompression = BI_RGB;

      //Printf("DIBSection::DIBSection(DC& dc, int width, int height, int count=%d\r\n",
      //  bitCount);

      hbitmap = ::CreateDIBSection(memDC.get(), bi,  
        DIB_RGB_COLORS, (void**)&buffer, NULL, 0);

      if (hbitmap && buffer) {
        hprevBitmap = (HBITMAP)memDC.select(hbitmap);
      
      } else {
        destroy();
        MessageBox(NULL, _T("Failed to create DIBSection"), _T("SOL++ Fatal Error"),
          MB_OK);
      }
    }
  }

public:
  ~DIBSection()
  {
    destroy();
  }

public:
  void draw(DC& dc, int x, int y)
  {
    draw(dc.get(), x, y);
  }

public:
  void draw(HDC hdc, int x, int y)
  {
    if(hbitmap) {
      int w = getWidth();
      int h = getHeight();
      ::StretchBlt(hdc,   x, y, w*ratio/100, h*ratio/100,
            memDC.get(), sx, sy, w, h, operation);
    }
  }

public:
  void destroy()
  {
    if (hbitmap) {
      memDC.select(hprevBitmap);

      DIBSECTION ds;
      ::GetObject(hbitmap, sizeof(ds), &ds);
      ::CloseHandle(ds.dshSection);
      ::DeleteObject(hbitmap);
      hbitmap = NULL;
      hprevBitmap = NULL;
    }

    char* tmp = (char*)bi;
    delete [] tmp;
    bi = NULL;

    buffer    = NULL;
  }

public:
  int getWidth() 
  {
    int w = 0;
    if (bi) {
      w = bi->bmiHeader.biWidth;
    }
    return w;
  }

public:
  int getHeight() 
  {
    int h = 0;
    if (bi) {
      h = bi->bmiHeader.biHeight;
    }
    return h;
  }

public:
  void  clear()
  {
    for(int y = 0; y<getHeight(); y++) {
      for(int x = 0; x<getWidth(); x++) {
        setPixel(x, y, RGB(255, 255, 255));
      }
    }

    flush();
  }


public:
  HBITMAP create(DC& dc, int width, int height, int bits=32)
  {
    if (width <=0 || height <= 0) {
      return NULL;
    }

    destroy();

    bitCount = bits; //24; //32;
    colorCount = getColorCount(bitCount);

    int  size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colorCount;

    bi = (BITMAPINFO*)new char[size];
    memset(bi, 0, size);
    
  //Printf("DIBSection::create/4 bitCount=%d\r\n", bitCount);

    memset(&(bi->bmiHeader), 0, sizeof(bi->bmiHeader));

    bi->bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);

    bi->bmiHeader.biWidth       = width;
    bi->bmiHeader.biHeight      = height;
    bi->bmiHeader.biPlanes      = 1;
    bi->bmiHeader.biBitCount    = (WORD)bitCount; //2000/11/18
    bi->bmiHeader.biCompression = BI_RGB;
  
    bi->bmiColors->rgbBlue     = 0;
    bi->bmiColors->rgbGreen    = 0;
    bi->bmiColors->rgbRed      = 0;
    bi->bmiColors->rgbReserved = 0;

    HDC hdc = dc.get();
    
    HDC cdc = ::CreateCompatibleDC(hdc);
    
    memDC.set(cdc);
    buffer     = NULL;

    hbitmap = ::CreateDIBSection(cdc, bi,
          DIB_RGB_COLORS, (void**)&buffer, NULL, 0);
    
    if (hbitmap && buffer) {
      hprevBitmap = (HBITMAP)memDC.select(hbitmap);
    } else {
      destroy();
      MessageBox(NULL, _T("Failed to create DIBSection"), _T("SOL++ Fatal Error"),
        MB_OK);
    }
    return hbitmap;
  }


public:
  DC&    getDC() { 
    return memDC; 
  }

  HBITMAP  getBitmap() { 
      return hbitmap; 
  }


public:
  COLORREF getPixel(int x, int y) 
  {  
    COLORREF color = 0;
    if (bitCount == 24) {
      RGBTRIPLE* triple = getRGBTriple(x, y);
      if (triple) {
        color = RGB(triple->rgbtRed, triple->rgbtGreen, triple->rgbtBlue);
      }
      return color;
    }

    // bitCount == 32, 8, 4
    RGBQUAD*  quad = getRGBQuad(x, y);
    color = 0;  
    if (quad) {
      color = RGB(quad->rgbRed, quad->rgbGreen, quad->rgbBlue);
    }
    return color;
  }

public:
  BOOL getPixel(int x, int y, int& r, int& g, int& b) 
  {
    BOOL  rc = FALSE;

    if (bitCount == 24) {
      RGBTRIPLE* triple = getRGBTriple(x, y);
      if (triple) {
        r  = triple->rgbtRed;
        g  = triple->rgbtGreen;
        b  = triple->rgbtBlue;
        rc = TRUE;
      } 
      return rc;    
    }

    RGBQUAD*  quad = getRGBQuad(x, y);
    if (quad) {
      r  = quad->rgbRed;
      g  = quad->rgbGreen;
      b  = quad->rgbBlue;
      rc = TRUE;
    }
    return rc;
  }

public:
  void DIBSection::setPixel(int x, int y, COLORREF color) 
  {
    if (bi == NULL || buffer == NULL || hbitmap == NULL) {
      return;
    }

    int width  = getWidth();
    int height = getHeight();

    if (width <= 0 || height <= 0) {
      return;  
    }

    if (x < 0 || x >=width || y <0 || y >= height) {
      return;
    }

    if (bitCount == 32) {
      RGBQUAD*  quad = getRGBQuad(x, y);
      if (quad) {
        quad->rgbRed   = GetRValue(color);
        quad->rgbGreen = GetGValue(color);
        quad->rgbBlue  = GetBValue(color);
      }
    }

    if (bitCount == 24) {
      RGBTRIPLE*  quad = getRGBTriple(x, y);
      if (quad) {
        quad->rgbtRed   = GetRValue(color);
        quad->rgbtGreen = GetGValue(color);
        quad->rgbtBlue  = GetBValue(color);
      }
    }
    // Sorry, not implemented for the cases of lower bitCount.
  }

  void  setOperation(DWORD op) { 
    operation = op; 
  }

  void  setSource(int x, int y) { 
    sx = x; sy = y; 
  }

  void   setStretchingRatio(int r) { 
    ratio = r; 
  }

public:
  BOOL load(DC& dc, const TCHAR* filename)
  {
    BinaryFile bmpFile;
    BOOL rc = FALSE;

    const int MAX_SIZE =  16000;
  
    if (bmpFile.openReadOnly(filename)) {
      char* fileHeader = new char[MAX_SIZE];
      bmpFile.read(0, fileHeader, MAX_SIZE);
    
      BITMAPFILEHEADER* fh = (BITMAPFILEHEADER*)fileHeader;
      DWORD offset = fh->bfOffBits;

      if(memcmp(fileHeader,"BM",2) != 0){
        return rc;
      }

      //Destroy previously loaded a bitmap image.
      destroy();

      int  n = sizeof(BITMAPFILEHEADER);

      BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)&fileHeader[n];
      colorCount = getColorCount(bih);

      bmiSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * colorCount;

      char* bmi = new char[bmiSize];
      memcpy(bmi, &fileHeader[n], bmiSize);

      bi = (BITMAPINFO*)bmi;  
    
      bitCount = bi->bmiHeader.biBitCount;
      long filesize = bmpFile.seek(0, FILE_END);
    
      long imageSize = filesize - fh->bfOffBits;
  
      //Printf("imageFile=[%s] filesize=[%d] bitCount=[%d]\r\n", filename, 
        //filesize, bitCount);
      //Printf("ImageSize = [%d]\r\n", filesize - fh->bfOffBits);
      //Printf("width %d height = %d size = %d sizeImage = %d\r\n",
      //  bi->bmiHeader.biWidth, bi->bmiHeader.biHeight,
      //  bi->bmiHeader.biSize,
      //  bi->bmiHeader.biSizeImage);

      if (create(dc) == NULL){
        delete [] fileHeader;
        return rc;
      }

      long readSize = 0;
      long restSize = imageSize;

      char* ptr = (char*)buffer;
      int    size = 32000;

      long  totalReadSize = 0;
    
      while(restSize> size) {    
        //Printf("ptr %d  offset = %d  RequestSize=[%d]  restSize=[%d]\r\n",
        //  ptr, offset, size, restSize);
        readSize  = bmpFile.read(offset, ptr,  size);
        offset   += readSize;
        ptr      += readSize;
        restSize -= readSize;

        if (readSize == 0) {
          break;
        }
        totalReadSize += readSize;
      }

      if (restSize > 0) {
        //Printf("Out ptr %d pos = %d  RequestSize=[%d]  rest=[%d]\r\n",
        //  ptr, offset, size, restSize);
        for(int i = 0; i<restSize; i++) {
          totalReadSize += bmpFile.read(offset++, ptr++, 1);
        }      
        //Printf("Out totalReadSize=[%d] rest=[%d]\r\n",
        //  totalReadSize, restSize);
      }

      delete [] fileHeader;

      rc = TRUE;
    
      flush();
    } 
    return rc;
  }

  int    getBitCount() { 
    return bitCount; 
  }

  int    getBmiSize() {
    return bmiSize; 
  }

  BITMAPINFO* getBitmapInfo() { 
    return bi; 
  }

  uchar* getBuffer()
  {
    return buffer;
  }
  
public:
  //2012/06/28
  bool saveAs(const TCHAR* filename)
  {
    bool rc = false;
    Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromHBITMAP(hbitmap, NULL);
    if (bitmap) {
      if (bitmap-> GetLastStatus() == Gdiplus::Ok) {

        ImageTransformer transformer;
        rc = transformer.saveAs(bitmap, _bstr_t(filename));
      }
      delete bitmap;
    }
    return rc;
  }

};

}


Last modified: 5 May 2019

Copyright (c) 2009-2019 Antillia.com ALL RIGHTS RESERVED.