// ImageRender.cc 显示 PNG 图像的实现部分
// 需要 png_loader.cc 支持。
// author: panchao
#include "stdafx.h"
#include "./ImageRender.h"
#include <fstream>
#include <vector>
bool LoadPNG(
const unsigned
char *mem_source,
int mem_source_len, unsigned
char** png_data,
int* width,
int* height,
bool* is_alpha);
CImageRender::
CImageRender()
{
bitmap_ = NULL;
old_bitmap_ = NULL;
hdc_ = NULL;
is_alpha_ =
false;
}
CImageRender::
~CImageRender()
{
if (bitmap_) {
_ASSERT( old_bitmap_ && hdc_);
SelectObject( hdc_, old_bitmap_);
DeleteObject( bitmap_);
bitmap_ = NULL;
old_bitmap_ = NULL;
}
if (hdc_) {
DeleteDC( hdc_);
hdc_ = NULL;
}
}
void CImageRender::
LoadImage(
const unsigned
char *mem_source,
int mem_source_len)
{
if ( mem_source == NULL || mem_source_len == 0)
return;
HDC hdc = GetDC( NULL);
hdc_ = CreateCompatibleDC( hdc );
ReleaseDC( NULL, hdc);
if ( hdc_ == NULL )
return;
unsigned
char* png_data;
if ( ! LoadPNG( mem_source, mem_source_len, &png_data, &width_, &height_, &is_alpha_ ))
return;
_ASSERT( png_data != NULL );
width_ = width_;
height_ = height_;
is_alpha_ = is_alpha_;
int width_dword_align = (width_ + 3 ) / 4 * 4;
BITMAPINFO bmp_info;
::ZeroMemory(&bmp_info,
sizeof(bmp_info));
bmp_info.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
bmp_info.bmiHeader.biWidth = width_dword_align;
bmp_info.bmiHeader.biHeight = -height_;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 32;
bmp_info.bmiHeader.biCompression = BI_RGB;
_ASSERT( hdc_ != NULL && png_data!=NULL);
void *ppvBits = NULL;
bitmap_ = CreateDIBSection( hdc_, (LPBITMAPINFO)&bmp_info, DIB_RGB_COLORS, (
void**)&ppvBits, NULL, 0);
if ( bitmap_ != NULL )
{
memcpy ( ppvBits, png_data, height_ * width_ * 4);
_ASSERT(bitmap_ != NULL);
old_bitmap_ = (HBITMAP)SelectObject( hdc_, bitmap_);
}
else {
DeleteDC( hdc_);
hdc_ = NULL;
}
delete [] png_data;
}
void CImageRender::
LoadImage( wchar_t *file_path)
{
std::ifstream fin;
fin.open( file_path, std::ios::
in | std::ios::binary);
if ( ! fin.good())
return;
std::vector<
char> data;
fin.seekg(0, std::ios::end);
size_t size = fin.tellg();
data.resize(size);
fin.read( &data[0], size );
fin.close();
LoadImage( (
const unsigned
char*)&data[0], size);
}
inline
bool CImageRender::
good()
{
return bitmap_ != NULL;
}
void CImageRender::
DrawImage( HDC hdc,
int xDest,
int yDest,
int wDest,
int hDest,
int xSrc,
int ySrc,
int wSrc,
int hSrc )
{
if ( ! good() )
return ;
if ( wDest == -1) wDest = width_;
if ( hDest == -1) hDest = height_;
if ( wSrc == -1) wSrc = width_;
if ( hSrc == -1) hSrc = height_;
if ( is_alpha_ )
{
BLENDFUNCTION func = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
AlphaBlend( hdc, xDest, yDest, width_, height_, hdc_, xSrc, ySrc, width_, height_, func);
}
else {
if ( wDest==wSrc && hDest==hSrc)
BitBlt( hdc, xDest, yDest, wDest, hDest, hdc_, xSrc, ySrc, SRCCOPY);
else StretchBlt( hdc, xDest, yDest, wDest, hDest, hdc_, xSrc, ySrc, wSrc, hSrc, SRCCOPY);
}
}
// 提供读取 .png 文件的方法:
// 使用 libpng 库。
// author: panchao
#include "./thirdparty/libpng-1.5.0/png.h"
bool LoadPNG( const unsigned char *mem_source, int mem_source_len, unsigned char** png_data, int* width, int* height, bool* is_alpha);
//bool LoadPNG( const wchar_t *file_path, unsigned char** png_data, int* width, int* height, bool* is_alpha);
unsigned char* doConvertRGB8(png_structp png_ptr, png_infop info_ptr, int width, int height);
unsigned char* doConvertRGBA8(png_structp png_ptr, png_infop info_ptr, int width, int height);
unsigned char* doConvertGrey8(png_structp png_ptr, png_infop info_ptr, int width, int height);
unsigned char* doConvertGreyA8(png_structp png_ptr, png_infop info_ptr, int width, int height);
// PNG文件的数据:
// 这个类主要用于 PNG读取数据时的回调函数。
class CPNGFileSource
{
public:
const unsigned char* data;
png_size_t size;
png_size_t offset;
//从内存读取PNG图片的回调函数
static void
png_read_callback(png_structp png_ptr, png_bytep data, png_size_t length)
{
CPNGFileSource* isource = (CPNGFileSource*)png_get_io_ptr(png_ptr);
if(isource->offset + length <= isource->size)
{
memcpy(data, isource->data+isource->offset, length);
isource->offset += length;
}
else
png_error(png_ptr, "png_read_callback failed, out of memory.");
}
} ;
bool LoadPNG( const unsigned char *mem_source, int mem_source_len,
unsigned char** png_data, int* width, int* height, bool* is_alpha)
{
if ( png_data == NULL)
return false;
*png_data = NULL;
if ( mem_source == NULL)
return false;
//test if png, compare first 8 bytes
int is_png = !png_sig_cmp( mem_source, 0, 8);
if (!is_png) {
return false;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!png_ptr) {
return false;
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
return false;
}
//create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
return false;
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return false;
}
//init png reading
//png_init_io(png_ptr, fp);
CPNGFileSource imgsource;
imgsource.data = mem_source;
imgsource.size = mem_source_len;
imgsource.offset = 0;
png_set_read_fn( png_ptr, &imgsource, CPNGFileSource::png_read_callback);
//let libpng know you already read the first 8 bytes
//png_set_sig_bytes(png_ptr, 8);
////variables to pass to get info
int bit_depth, color_type;
png_uint_32 _width, _height;
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
_width = png_get_image_width(png_ptr, info_ptr);
_height = png_get_image_height(png_ptr, info_ptr);
png_byte *_png_data = NULL;
// rgb
if (color_type == PNG_COLOR_TYPE_RGB)
{
_png_data = doConvertRGB8(png_ptr, info_ptr, _width, _height);
}
// rgb with opacity
else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
_png_data = doConvertRGBA8(png_ptr, info_ptr, _width, _height);
}
// 256 grey values
else if (color_type == PNG_COLOR_TYPE_GRAY)
{
_png_data = doConvertGrey8(png_ptr, info_ptr, _width, _height);
}
// 256 grey values with opacity
else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
_png_data = doConvertGreyA8(png_ptr, info_ptr, _width, _height);
}
*png_data = _png_data;
*width = _width;
*height = _height;
*is_alpha = (color_type & PNG_COLOR_MASK_ALPHA) != 0;
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return _png_data != NULL;
}
//
//bool LoadPNG( const wchar_t *file_path, unsigned char** png_data, int* width, int* height, bool* is_alpha)
//{
// if ( png_data == NULL)
// return false;
//
// *png_data = NULL;
//
// if ( file_path == NULL)
// return false;
//
// //open file as binary
// FILE *fp = _wfopen( file_path, L"rb");
// if (!fp) {
// return false;
// }
//
// //read the header
// png_byte header[8];
// fread(header, 1, 8, fp);
//
// //test if png
// int is_png = !png_sig_cmp(header, 0, 8);
// if (!is_png) {
// fclose(fp);
// return false;
// }
//
// //create png struct
// png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
// NULL, NULL);
// if (!png_ptr) {
// fclose(fp);
// return false;
// }
//
// //create png info struct
// png_infop info_ptr = png_create_info_struct(png_ptr);
// if (!info_ptr) {
// png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
// fclose(fp);
// return false;
// }
//
// //create png info struct
// png_infop end_info = png_create_info_struct(png_ptr);
// if (!end_info) {
// png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
// fclose(fp);
// return false;
// }
//
// //png error stuff, not sure libpng man suggests this.
// if (setjmp(png_jmpbuf(png_ptr))) {
// png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
// fclose(fp);
// return false;
// }
//
// //init png reading
// png_init_io(png_ptr, fp);
//
// //let libpng know you already read the first 8 bytes
// png_set_sig_bytes(png_ptr, 8);
//
// ////variables to pass to get info
// int bit_depth, color_type;
// png_uint_32 _width, _height;
//
// png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
//
// bit_depth = png_get_bit_depth(png_ptr, info_ptr);
// color_type = png_get_color_type(png_ptr, info_ptr);
// _width = png_get_image_width(png_ptr, info_ptr);
// _height = png_get_image_height(png_ptr, info_ptr);
//
// png_byte *_png_data = NULL;
//
// // rgb
// if (color_type == PNG_COLOR_TYPE_RGB)
// {
// _png_data = doConvertRGB8(png_ptr, info_ptr, _width, _height);
// }
// // rgb with opacity
// else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
// {
// _png_data = doConvertRGBA8(png_ptr, info_ptr, _width, _height);
// }
//
// // 256 grey values
// else if (color_type == PNG_COLOR_TYPE_GRAY)
// {
// _png_data = doConvertGrey8(png_ptr, info_ptr, _width, _height);
// }
//
// // 256 grey values with opacity
// else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
// {
// _png_data = doConvertGreyA8(png_ptr, info_ptr, _width, _height);
// }
//
// *png_data = _png_data;
// *width = _width;
// *height = _height;
// *is_alpha = (color_type & PNG_COLOR_MASK_ALPHA) != 0;
//
// //clean up memory and close stuff
// png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
// fclose(fp);
//
// return _png_data != NULL;
//}
// -------------
// doConvertRGB8
// -------------
/**
* Gets the data from an 8-bit rgb image.
*
* @par Memory
* - The returned pointer is created by using the new[] operator.
* - You have to free the allocated memory yourself.
*
* @par Structure
* - The color-sequence is Blue-Green-Red-Alpha (8 bit each).
* - The first 4 values (RGBA) are located in the top-left corner of the image.
* - The last 4 values (RGBA) are located in the bottom-right corner of the image.
*/
unsigned char* doConvertRGB8(png_structp png_ptr, png_infop info_ptr, int width, int height)
{
// calculate needed memory
int size = height * width * 4;
// allocate memory
unsigned char* png_data, *rgba;
png_data = rgba = new unsigned char[size];
// get image rows
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
int pos = 0;
png_bytep row;
// get color values
for( int i = 0; i < height; ++ i )
{
row = row_pointers[i];
for(int j = 0; j < (3 * width); j += 3)
{
#ifdef LIBPNG_USING_OPENGL
*rgba ++ = row[j]; // red
*rgba ++ = row[j + 1]; // green
*rgba ++ = row[j + 2]; // blue
#else
*rgba ++ = row[j + 2]; // blue
*rgba ++ = row[j + 1]; // green
*rgba ++ = row[j]; // red
#endif
*rgba ++ = 0; // alpha
}
}
return png_data;
}
// --------------
// doConvertRGBA8
// --------------
/**
* Gets the data from an 8-bit rgb image with alpha values.
*
* @par Memory
* - The returned pointer is created by using the new[] operator.
* - You have to free the allocated memory yourself.
*
* @par Structure
* - The color-sequence is Blue-Green-Red-Alpha (8 bit each).
* - The first 4 values (RGBA) are located in the top-left corner of the image.
* - The last 4 values (RGBA) are located in the bottom-right corner of the image.
*/
unsigned char* doConvertRGBA8(png_structp png_ptr, png_infop info_ptr, int width, int height)
{
// calculate needed memory
int size = height * width * 4;
// allocate memory
unsigned char* png_data, *rgba;
png_data = rgba = new unsigned char[size];
// get image rows
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
int pos = 0;
png_bytep row;
unsigned char alpha;
// get color values
for( int i = 0; i < height; ++ i )
{
row = row_pointers[i];
for(int j = 0; j < (4 * width); j += 4)
{
alpha = row[j + 3];
#ifdef LIBPNG_USING_OPENGL
*rgba ++ = row[j]; // red
*rgba ++ = row[j + 1]; // green
*rgba ++ = row[j + 2]; // blue
#else
*rgba ++ = row[j + 2] * alpha / 0xff; // blue
*rgba ++ = row[j + 1] * alpha / 0xff; // green
*rgba ++ = row[j] * alpha / 0xff; // red
#endif
*rgba ++ = alpha; // alpha
}
}
return png_data;
}
// --------------
// doConvertGrey8
// --------------
/**
* Gets the data from an 8-bit monochrome image.
*
* @par Memory
* - The returned pointer is created by using the new[] operator.
* - You have to free the allocated memory yourself.
*
* @par Structure
* - The color-sequence is Blue-Green-Red-Alpha (8 bit each).
* - The first 4 values (RGBA) are located in the top-left corner of the image.
* - The last 4 values (RGBA) are located in the bottom-right corner of the image.
*/
unsigned char* doConvertGrey8(png_structp png_ptr, png_infop info_ptr, int width, int height)
{
// calculate needed memory
int size = height * width * 4;
// allocate memory
unsigned char* png_data, *rgba;
png_data = rgba = new unsigned char[size];
// get image rows
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
int pos = 0;
png_bytep row;
// get color values
for( int i = 0; i < height; ++ i )
{
row = row_pointers[i];
for(int j = 0; j < width; j++)
{
#ifdef LIBPNG_USING_OPENGL
*rgba ++ = row[j]; // red
*rgba ++ = row[j]; // green
*rgba ++ = row[j]; // blue
#else
*rgba ++ = row[j]; // blue
*rgba ++ = row[j]; // green
*rgba ++ = row[j]; // red
#endif
*rgba ++ = 0; // alpha
}
}
return png_data;
}
// ---------------
// doConvertGreyA8
// ---------------
/**
* Gets the data from an 8-bit monochrome image with alpha values.
*/
unsigned char* doConvertGreyA8(png_structp png_ptr, png_infop info_ptr, int width, int height)
{
// calculate needed memory
int size = height * width * 4;
// allocate memory
unsigned char* png_data, *rgba;
png_data = rgba = new unsigned char[size];
// get image rows
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
int pos = 0;
png_bytep row;
unsigned char alpha;
// get color values
for( int i = 0; i < height; ++ i )
{
row = row_pointers[i];
for(int j = 0; j < (2 * width); j += 2)
{
alpha = row_pointers[i][j + 1];
#ifdef LIBPNG_USING_OPENGL
*rgba ++ = row[j]; // red
*rgba ++ = row[j]; // green
*rgba ++ = row[j]; // blue
#else
*rgba ++ = row[j] * alpha / 0xff; // blue
*rgba ++ = row[j] * alpha / 0xff; // green
*rgba ++ = row[j] * alpha / 0xff; // red
#endif
*rgba ++ = alpha; // alpha
}
}
return png_data;
}