agg/src/platform/win32/agg_win32_bmp.cpp

634 lines
19 KiB
C++

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
// Contact: mcseemagg@yahoo.com
//----------------------------------------------------------------------------
//
// class pixel_map
//
//----------------------------------------------------------------------------
#include <cstring>
#include <cstdio>
#include "platform/win32/agg_win32_bmp.h"
#include "agg_basics.h"
namespace agg
{
//------------------------------------------------------------------------
pixel_map::~pixel_map()
{
destroy();
}
//------------------------------------------------------------------------
pixel_map::pixel_map() :
m_bmp(0),
m_buf(0),
m_bpp(0),
m_is_internal(false),
m_img_size(0),
m_full_size(0)
{
}
//------------------------------------------------------------------------
void pixel_map::destroy()
{
if(m_bmp && m_is_internal) delete [] (unsigned char*)m_bmp;
m_bmp = 0;
m_is_internal = false;
m_buf = 0;
}
//------------------------------------------------------------------------
void pixel_map::create(unsigned width,
unsigned height,
org_e org,
unsigned clear_val)
{
destroy();
if(width == 0) width = 1;
if(height == 0) height = 1;
m_bpp = org;
create_from_bmp(create_bitmap_info(width, height, m_bpp));
create_gray_scale_palette(m_bmp);
m_is_internal = true;
if(clear_val <= 255)
{
std::memset(m_buf, clear_val, m_img_size);
}
}
//------------------------------------------------------------------------
HBITMAP pixel_map::create_dib_section(HDC h_dc,
unsigned width,
unsigned height,
org_e org,
unsigned clear_val)
{
destroy();
if(width == 0) width = 1;
if(height == 0) height = 1;
m_bpp = org;
HBITMAP h_bitmap = create_dib_section_from_args(h_dc, width, height, m_bpp);
create_gray_scale_palette(m_bmp);
m_is_internal = true;
if(clear_val <= 255)
{
std::memset(m_buf, clear_val, m_img_size);
}
return h_bitmap;
}
//------------------------------------------------------------------------
void pixel_map::clear(unsigned clear_val)
{
if(m_buf) std::memset(m_buf, clear_val, m_img_size);
}
//------------------------------------------------------------------------
void pixel_map::attach_to_bmp(BITMAPINFO *bmp)
{
if(bmp)
{
destroy();
create_from_bmp(bmp);
m_is_internal = false;
}
}
//static
//------------------------------------------------------------------------
unsigned pixel_map::calc_full_size(BITMAPINFO *bmp)
{
if(bmp == 0) return 0;
return sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * calc_palette_size(bmp) +
bmp->bmiHeader.biSizeImage;
}
//static
//------------------------------------------------------------------------
unsigned pixel_map::calc_header_size(BITMAPINFO *bmp)
{
if(bmp == 0) return 0;
return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * calc_palette_size(bmp);
}
//static
//------------------------------------------------------------------------
unsigned pixel_map::calc_palette_size(unsigned clr_used, unsigned bits_per_pixel)
{
int palette_size = 0;
if(bits_per_pixel <= 8)
{
palette_size = clr_used;
if(palette_size == 0)
{
palette_size = 1 << bits_per_pixel;
}
}
return palette_size;
}
//static
//------------------------------------------------------------------------
unsigned pixel_map::calc_palette_size(BITMAPINFO *bmp)
{
if(bmp == 0) return 0;
return calc_palette_size(bmp->bmiHeader.biClrUsed, bmp->bmiHeader.biBitCount);
}
//static
//------------------------------------------------------------------------
unsigned char * pixel_map::calc_img_ptr(BITMAPINFO *bmp)
{
if(bmp == 0) return 0;
return ((unsigned char*)bmp) + calc_header_size(bmp);
}
//static
//------------------------------------------------------------------------
BITMAPINFO* pixel_map::create_bitmap_info(unsigned width,
unsigned height,
unsigned bits_per_pixel)
{
unsigned line_len = calc_row_len(width, bits_per_pixel);
unsigned img_size = line_len * height;
unsigned rgb_size = calc_palette_size(0, bits_per_pixel) * sizeof(RGBQUAD);
unsigned full_size = sizeof(BITMAPINFOHEADER) + rgb_size + img_size;
BITMAPINFO *bmp = (BITMAPINFO *) new unsigned char[full_size];
bmp->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmp->bmiHeader.biWidth = width;
bmp->bmiHeader.biHeight = height;
bmp->bmiHeader.biPlanes = 1;
bmp->bmiHeader.biBitCount = (unsigned short)bits_per_pixel;
bmp->bmiHeader.biCompression = 0;
bmp->bmiHeader.biSizeImage = img_size;
bmp->bmiHeader.biXPelsPerMeter = 0;
bmp->bmiHeader.biYPelsPerMeter = 0;
bmp->bmiHeader.biClrUsed = 0;
bmp->bmiHeader.biClrImportant = 0;
return bmp;
}
//static
//------------------------------------------------------------------------
void pixel_map::create_gray_scale_palette(BITMAPINFO *bmp)
{
if(bmp == 0) return;
unsigned rgb_size = calc_palette_size(bmp);
RGBQUAD *rgb = (RGBQUAD*)(((unsigned char*)bmp) + sizeof(BITMAPINFOHEADER));
unsigned brightness;
unsigned i;
for(i = 0; i < rgb_size; i++)
{
brightness = (255 * i) / (rgb_size - 1);
rgb->rgbBlue =
rgb->rgbGreen =
rgb->rgbRed = (unsigned char)brightness;
rgb->rgbReserved = 0;
rgb++;
}
}
//static
//------------------------------------------------------------------------
unsigned pixel_map::calc_row_len(unsigned width, unsigned bits_per_pixel)
{
unsigned n = width;
unsigned k;
switch(bits_per_pixel)
{
case 1: k = n;
n = n >> 3;
if(k & 7) n++;
break;
case 4: k = n;
n = n >> 1;
if(k & 3) n++;
break;
case 8:
break;
case 16: n *= 2;
break;
case 24: n *= 3;
break;
case 32: n *= 4;
break;
case 48: n *= 6;
break;
case 64: n *= 8;
break;
case 96: n *= 12;
break;
case 128: n *= 16;
break;
default: n = 0;
break;
}
return ((n + 3) >> 2) << 2;
}
//------------------------------------------------------------------------
void pixel_map::draw(HDC h_dc, const RECT *device_rect, const RECT *bmp_rect) const
{
if(m_bmp == 0 || m_buf == 0) return;
unsigned bmp_x = 0;
unsigned bmp_y = 0;
unsigned bmp_width = m_bmp->bmiHeader.biWidth;
unsigned bmp_height = m_bmp->bmiHeader.biHeight;
unsigned dvc_x = 0;
unsigned dvc_y = 0;
unsigned dvc_width = m_bmp->bmiHeader.biWidth;
unsigned dvc_height = m_bmp->bmiHeader.biHeight;
if(bmp_rect)
{
bmp_x = bmp_rect->left;
bmp_y = bmp_rect->top;
bmp_width = bmp_rect->right - bmp_rect->left;
bmp_height = bmp_rect->bottom - bmp_rect->top;
}
dvc_x = bmp_x;
dvc_y = bmp_y;
dvc_width = bmp_width;
dvc_height = bmp_height;
if(device_rect)
{
dvc_x = device_rect->left;
dvc_y = device_rect->top;
dvc_width = device_rect->right - device_rect->left;
dvc_height = device_rect->bottom - device_rect->top;
}
if(dvc_width != bmp_width || dvc_height != bmp_height)
{
::SetStretchBltMode(h_dc, COLORONCOLOR);
::StretchDIBits(
h_dc, // handle of device context
dvc_x, // x-coordinate of upper-left corner of source rect.
dvc_y, // y-coordinate of upper-left corner of source rect.
dvc_width, // width of source rectangle
dvc_height, // height of source rectangle
bmp_x,
bmp_y, // x, y -coordinates of upper-left corner of dest. rect.
bmp_width, // width of destination rectangle
bmp_height, // height of destination rectangle
m_buf, // address of bitmap bits
m_bmp, // address of bitmap data
DIB_RGB_COLORS, // usage
SRCCOPY // raster operation code
);
}
else
{
::SetDIBitsToDevice(
h_dc, // handle to device context
dvc_x, // x-coordinate of upper-left corner of
dvc_y, // y-coordinate of upper-left corner of
dvc_width, // source rectangle width
dvc_height, // source rectangle height
bmp_x, // x-coordinate of lower-left corner of
bmp_y, // y-coordinate of lower-left corner of
0, // first scan line in array
bmp_height, // number of scan lines
m_buf, // address of array with DIB bits
m_bmp, // address of structure with bitmap info.
DIB_RGB_COLORS // RGB or palette indexes
);
}
}
//------------------------------------------------------------------------
void pixel_map::draw(HDC h_dc, int x, int y, double scale) const
{
if(m_bmp == 0 || m_buf == 0) return;
unsigned width = unsigned(m_bmp->bmiHeader.biWidth * scale);
unsigned height = unsigned(m_bmp->bmiHeader.biHeight * scale);
RECT rect;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
draw(h_dc, &rect);
}
//------------------------------------------------------------------------
void pixel_map::blend(HDC h_dc, const RECT *device_rect, const RECT *bmp_rect) const
{
#if !defined(AGG_BMP_ALPHA_BLEND)
draw(h_dc, device_rect, bmp_rect);
return;
#else
if(m_bpp != 32)
{
draw(h_dc, device_rect, bmp_rect);
return;
}
if(m_bmp == 0 || m_buf == 0) return;
unsigned bmp_x = 0;
unsigned bmp_y = 0;
unsigned bmp_width = m_bmp->bmiHeader.biWidth;
unsigned bmp_height = m_bmp->bmiHeader.biHeight;
unsigned dvc_x = 0;
unsigned dvc_y = 0;
unsigned dvc_width = m_bmp->bmiHeader.biWidth;
unsigned dvc_height = m_bmp->bmiHeader.biHeight;
if(bmp_rect)
{
bmp_x = bmp_rect->left;
bmp_y = bmp_rect->top;
bmp_width = bmp_rect->right - bmp_rect->left;
bmp_height = bmp_rect->bottom - bmp_rect->top;
}
dvc_x = bmp_x;
dvc_y = bmp_y;
dvc_width = bmp_width;
dvc_height = bmp_height;
if(device_rect)
{
dvc_x = device_rect->left;
dvc_y = device_rect->top;
dvc_width = device_rect->right - device_rect->left;
dvc_height = device_rect->bottom - device_rect->top;
}
HDC mem_dc = ::CreateCompatibleDC(h_dc);
void* buf = 0;
HBITMAP bmp = ::CreateDIBSection(
mem_dc,
m_bmp,
DIB_RGB_COLORS,
&buf,
0,
0
);
memcpy(buf, m_buf, m_bmp->bmiHeader.biSizeImage);
HBITMAP temp = (HBITMAP)::SelectObject(mem_dc, bmp);
BLENDFUNCTION blend;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
#if defined(AC_SRC_ALPHA)
blend.AlphaFormat = AC_SRC_ALPHA;
//#elif defined(AC_SRC_NO_PREMULT_ALPHA)
// blend.AlphaFormat = AC_SRC_NO_PREMULT_ALPHA;
#else
#error "No appropriate constant for alpha format. Check version of wingdi.h, There must be AC_SRC_ALPHA or AC_SRC_NO_PREMULT_ALPHA"
#endif
blend.SourceConstantAlpha = 255;
::AlphaBlend(
h_dc,
dvc_x,
dvc_y,
dvc_width,
dvc_height,
mem_dc,
bmp_x,
bmp_y,
bmp_width,
bmp_height,
blend
);
::SelectObject(mem_dc, temp);
::DeleteObject(bmp);
::DeleteObject(mem_dc);
#endif //defined(AGG_BMP_ALPHA_BLEND)
}
//------------------------------------------------------------------------
void pixel_map::blend(HDC h_dc, int x, int y, double scale) const
{
if(m_bmp == 0 || m_buf == 0) return;
unsigned width = unsigned(m_bmp->bmiHeader.biWidth * scale);
unsigned height = unsigned(m_bmp->bmiHeader.biHeight * scale);
RECT rect;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
blend(h_dc, &rect);
}
//------------------------------------------------------------------------
bool pixel_map::load_from_bmp(FILE *fd)
{
BITMAPFILEHEADER bmf;
BITMAPINFO *bmi = 0;
unsigned bmp_size;
if (std::fread(&bmf, sizeof(bmf), 1, fd) != 1) goto bmperr;
if(bmf.bfType != 0x4D42) goto bmperr;
bmp_size = bmf.bfSize - sizeof(BITMAPFILEHEADER);
bmi = (BITMAPINFO*) new unsigned char [bmp_size];
if(std::fread(bmi, 1, bmp_size, fd) != bmp_size) goto bmperr;
destroy();
m_bpp = bmi->bmiHeader.biBitCount;
create_from_bmp(bmi);
m_is_internal = 1;
return true;
bmperr:
if(bmi) delete [] (unsigned char*) bmi;
return false;
}
//------------------------------------------------------------------------
bool pixel_map::load_from_bmp(const char *filename)
{
FILE *fd = std::fopen(filename, "rb");
bool ret = false;
if(fd)
{
ret = load_from_bmp(fd);
std::fclose(fd);
}
return ret;
}
//------------------------------------------------------------------------
bool pixel_map::save_as_bmp(FILE *fd) const
{
if(m_bmp == 0) return 0;
BITMAPFILEHEADER bmf;
bmf.bfType = 0x4D42;
bmf.bfOffBits = calc_header_size(m_bmp) + sizeof(bmf);
bmf.bfSize = bmf.bfOffBits + m_img_size;
bmf.bfReserved1 = 0;
bmf.bfReserved2 = 0;
std::fwrite(&bmf, sizeof(bmf), 1, fd);
std::fwrite(m_bmp, m_full_size, 1, fd);
return true;
}
//------------------------------------------------------------------------
bool pixel_map::save_as_bmp(const char *filename) const
{
FILE *fd = std::fopen(filename, "wb");
bool ret = false;
if(fd)
{
ret = save_as_bmp(fd);
std::fclose(fd);
}
return ret;
}
//------------------------------------------------------------------------
unsigned char* pixel_map::buf()
{
return m_buf;
}
//------------------------------------------------------------------------
unsigned pixel_map::width() const
{
return m_bmp->bmiHeader.biWidth;
}
//------------------------------------------------------------------------
unsigned pixel_map::height() const
{
return m_bmp->bmiHeader.biHeight;
}
//------------------------------------------------------------------------
int pixel_map::stride() const
{
return calc_row_len(m_bmp->bmiHeader.biWidth,
m_bmp->bmiHeader.biBitCount);
}
//private
//------------------------------------------------------------------------
void pixel_map::create_from_bmp(BITMAPINFO *bmp)
{
if(bmp)
{
m_img_size = calc_row_len(bmp->bmiHeader.biWidth,
bmp->bmiHeader.biBitCount) *
bmp->bmiHeader.biHeight;
m_full_size = calc_full_size(bmp);
m_bmp = bmp;
m_buf = calc_img_ptr(bmp);
}
}
//private
//------------------------------------------------------------------------
HBITMAP pixel_map::create_dib_section_from_args(HDC h_dc,
unsigned width,
unsigned height,
unsigned bits_per_pixel)
{
unsigned line_len = calc_row_len(width, bits_per_pixel);
unsigned img_size = line_len * height;
unsigned rgb_size = calc_palette_size(0, bits_per_pixel) * sizeof(RGBQUAD);
unsigned full_size = sizeof(BITMAPINFOHEADER) + rgb_size;
BITMAPINFO *bmp = (BITMAPINFO *) new unsigned char[full_size];
bmp->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmp->bmiHeader.biWidth = width;
bmp->bmiHeader.biHeight = height;
bmp->bmiHeader.biPlanes = 1;
bmp->bmiHeader.biBitCount = (unsigned short)bits_per_pixel;
bmp->bmiHeader.biCompression = 0;
bmp->bmiHeader.biSizeImage = img_size;
bmp->bmiHeader.biXPelsPerMeter = 0;
bmp->bmiHeader.biYPelsPerMeter = 0;
bmp->bmiHeader.biClrUsed = 0;
bmp->bmiHeader.biClrImportant = 0;
void* img_ptr = 0;
HBITMAP h_bitmap = ::CreateDIBSection(h_dc, bmp, DIB_RGB_COLORS, &img_ptr, NULL, 0);
if(img_ptr)
{
m_img_size = calc_row_len(width, bits_per_pixel) * height;
m_full_size = 0;
m_bmp = bmp;
m_buf = (unsigned char *) img_ptr;
}
return h_bitmap;
}
}