📄 png_provider_generic.cpp
字号:
/* $Id: png_provider_generic.cpp,v 1.11 2003/08/21 21:14:47 mbn Exp $
**
** ClanLib Game SDK
** Copyright (C) 2003 The ClanLib Team
** For a total list of contributers see the file CREDITS.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Display/display_precomp.h"
#include "png_provider_generic.h"
#include "API/Core/System/error.h"
/*
Known Bugs:
- this provider might not work with 16bit PNG's
- this provider might not work with some grayscale PNG's (don't remember which one)
*/
// Workaround for a VC bug
#ifdef WIN32
# define for if(0);else for
#endif
/////////////////////////////////////////////////////////////////////////////
// CL_PNGProvider_Generic construction:
CL_PNGProvider_Generic::CL_PNGProvider_Generic(
const std::string &name,
CL_InputSourceProvider *_provider,
bool _transparent,
bool _ignore_alphachannel)
{
if (_provider == NULL)
{
provider = CL_InputSourceProvider::create_file_provider(".");
}
else
{
provider = _provider->clone();
}
ignore_alphachannel = _ignore_alphachannel;
transparent = _transparent;
indexed = true;
trans_redcol = 0;
trans_greencol = 0;
trans_bluecol = 0;
trans_col = -1;
m_uses_src_colorkey = false;
filename = name;
image = NULL;
}
CL_PNGProvider_Generic::~CL_PNGProvider_Generic()
{
perform_unlock();
delete provider;
}
/////////////////////////////////////////////////////////////////////////////
// CL_PNGProvider_Generic attributes:
bool CL_PNGProvider_Generic::is_indexed() const
{
return (format.get_depth()==8);
}
/////////////////////////////////////////////////////////////////////////////
// CL_PNGProvider_Generic operations:
void* CL_PNGProvider_Generic::get_data()
{
return image;
}
/*
Lock the surfaceprovider - which basically means open the file
and read the image into a temporary memory buffer - until
unlock() is called.
*/
void CL_PNGProvider_Generic::perform_lock()
{
//setting up PNGLIB stuff
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
throw CL_Error ("CL_PNGProvider_Generic: png_create_read_struct() failed");
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr,
(png_infopp)NULL, (png_infopp)NULL);
throw CL_Error ("CL_PNGProvider_Generic: png_create_info_struct() failed");
}
end_info = png_create_info_struct(png_ptr);
if (!end_info)
{
png_destroy_read_struct(&png_ptr, &info_ptr,
(png_infopp)NULL);
cl_assert(false);
}
if (setjmp(png_ptr->jmpbuf))
{
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
cl_assert(false);
}
cl_assert(provider != NULL);
input_source = provider->open_source(filename);
cl_assert(input_source!=NULL);
// tell libpng form whom it get the fileData
png_set_read_fn(png_ptr, this, &CL_PNGProvider_Generic::pngread_file);
// reading the header infos and actually read data ...
read_data();
// remove our data_provider from libpng
png_set_read_fn(png_ptr,NULL,NULL);
// free memory ...
png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
delete input_source;
// this could be integrated better, but I'm too tired, so I just hack CL_PixelBuffer
// support into it. -- mbn 21. feb 2002
CL_PixelBuffer_Generic::format.enable_colorkey(uses_src_colorkey());
CL_PixelBuffer_Generic::format.set_colorkey(get_src_colorkey());
CL_PixelBuffer_Generic::pitch = get_pitch();
CL_PixelBuffer_Generic::width = get_width();
CL_PixelBuffer_Generic::height = get_height();
}
void CL_PNGProvider_Generic::perform_unlock()
{
delete[] image;
image = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// CL_PNGProvider_Generic implementation:
void CL_PNGProvider_Generic::pngread_file(
png_structp png_ptr,
png_bytep data,
png_size_t length)
{
// since this method is static, we need to know who we are ...
CL_PNGProvider_Generic *instance = (CL_PNGProvider_Generic *) png_get_io_ptr(png_ptr);
// no error-checking here ....
unsigned int read_length = instance->get_input_source ()->read(data, length);
cl_assert (read_length == length);
}
void CL_PNGProvider_Generic::read_data()
{
// initial fileinfo
png_read_info(png_ptr, info_ptr);
// reduce 16bit/channel to 8Bit/channel
png_set_strip_16(png_ptr);
// reread infostruct to reflect the made settings
png_read_update_info(png_ptr, info_ptr);
width = png_get_image_width(png_ptr,info_ptr);
height = png_get_image_height(png_ptr,info_ptr);
color_type = png_get_color_type(png_ptr,info_ptr);
switch (color_type)
{
case PNG_COLOR_TYPE_GRAY:
read_data_grayscale ();
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
read_data_grayscale_alpha ();
break;
case PNG_COLOR_TYPE_PALETTE:
read_data_palette ();
break;
case PNG_COLOR_TYPE_RGB:
read_data_rgb ();
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
read_data_rgba ();
break;
default:
throw CL_Error ("CL_PNGProvider_Generic: Unsupported PNG format!");
break;
}
}
void CL_PNGProvider_Generic::read_data_rgb()
{
indexed = false;
format.set_red_mask(0xff0000);
format.set_green_mask(0x00ff00);
format.set_blue_mask(0x0000ff);
format.set_alpha_mask(0x000000);
format.set_depth(24);
pitch = png_get_rowbytes(png_ptr, info_ptr);
// This is deleted in the unlock () call
image = new unsigned char[pitch * height];
// setup pointers to each row in our target image
png_bytep* row_pointers = new png_bytep[height];
for (int y = 0; y < height; y++)
row_pointers[y] = image + (pitch * y);
png_read_image(png_ptr, row_pointers);
delete[] row_pointers;
// swap the colors in the right order
unsigned char tmp;
for (int i = 0; i < width * height; ++i)
{
tmp = image[3*i + 2];
image[3*i + 2] = image[3*i + 0];
image[3*i + 0] = tmp;
}
}
void CL_PNGProvider_Generic::read_data_rgba()
{
indexed = false;
format.set_red_mask(0xff000000);
format.set_green_mask(0x00ff0000);
format.set_blue_mask(0x0000ff00);
format.set_alpha_mask(0x000000ff);
format.set_depth(32);
pitch = png_get_rowbytes(png_ptr, info_ptr);
// This is deleted in the unlock () call
image = new unsigned char[pitch * height];
unsigned char* tmp_image = new unsigned char[pitch * height];
// setup pointers to each row in our target image
png_bytep* row_pointers = new png_bytep[height];
for (int y = 0; y < height; y++)
row_pointers[y] = tmp_image + (pitch * y);
png_read_image(png_ptr, row_pointers);
delete[] row_pointers;
if (!ignore_alphachannel)
{
for (int i = 0; i < pitch * height; i += 4)
{
image[i + 0] = tmp_image[i + 3];
image[i + 1] = tmp_image[i + 2];
image[i + 2] = tmp_image[i + 1];
image[i + 3] = tmp_image[i + 0];
}
}
else
{
for (int i = 0; i < pitch * height; i += 4)
{
image[i + 0] = 255;
image[i + 1] = tmp_image[i + 2];
image[i + 2] = tmp_image[i + 1];
image[i + 3] = tmp_image[i + 0];
}
}
delete[] tmp_image;
}
void CL_PNGProvider_Generic::read_data_grayscale()
{
format.set_red_mask(0xff0000);
format.set_green_mask(0x00ff00);
format.set_blue_mask(0x0000ff);
format.set_alpha_mask(0x000000);
format.set_depth(24);
indexed = false;
int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
pitch = rowbytes * 3;
// We expand the grayscale values if necessare, so we always
// get 8bits per pixel
if (bit_depth < 8) png_set_expand (png_ptr);
// Allocating the temporary buffer and fill it
unsigned char* tmp_image = new unsigned char[height * rowbytes];
png_bytep* row_pointers = new png_bytep[height];
for (int y = 0; y < height; y++)
row_pointers[y] = tmp_image + (rowbytes * y);
png_read_image(png_ptr, row_pointers);
delete[] row_pointers;
image = new unsigned char[height * pitch];
// Coverting the data in the tmp buffer to our final data
for (int i = 0; i < rowbytes * height; i++)
{
image[3*i + 0] = tmp_image[i];
image[3*i + 1] = tmp_image[i];
image[3*i + 2] = tmp_image[i];
}
delete[] tmp_image;
}
void CL_PNGProvider_Generic::read_data_grayscale_alpha()
{
format.set_red_mask(0xff000000);
format.set_green_mask(0x00ff0000);
format.set_blue_mask(0x0000ff00);
format.set_alpha_mask(0x000000ff);
format.set_depth(32);
pitch = width * 4;
indexed = false;
int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
// We expand the grayscale values if necessare, so we always
// get 8bits per pixel
if (bit_depth < 8) png_set_expand (png_ptr);
// Allocating the temporary buffer
unsigned char* tmp_image = new unsigned char[height * rowbytes];
png_bytep* row_pointers = new png_bytep[height];
for (int y = 0; y < height; y++)
row_pointers[y] = tmp_image + (rowbytes * y);
png_read_image(png_ptr, row_pointers);
delete[] row_pointers;
// Creating the final image out of tmp_image
image = new unsigned char[width * pitch];
if (!ignore_alphachannel)
{
for (int i = 0; i < rowbytes * height; i += 2)
{
image[2*i + 0] = tmp_image[i + 1];
image[2*i + 1] = tmp_image[i + 0];
image[2*i + 2] = tmp_image[i + 0];
image[2*i + 3] = tmp_image[i + 0];
}
}
else
{
for (int i = 0; i < rowbytes * height; i += 2)
{
image[2*i + 0] = 255;
image[2*i + 1] = tmp_image[i + 0];
image[2*i + 2] = tmp_image[i + 0];
image[2*i + 3] = tmp_image[i + 0];
}
}
delete[] tmp_image;
}
void CL_PNGProvider_Generic::read_data_palette()
{
format.set_red_mask(0x00000000);
format.set_green_mask(0x00000000);
format.set_blue_mask(0x00000000);
format.set_alpha_mask(0x00000000);
format.set_depth(8);
indexed = true;
int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
unsigned char* tmp_image = new unsigned char[height * rowbytes];
// Allocating the temporary buffer (will be deleted some
// screens below
png_bytep* row_pointers = new png_bytep[height];
for (int y = 0; y < height; y++)
row_pointers[y] = tmp_image + (rowbytes * y);
png_read_image(png_ptr, row_pointers);
if (bit_depth == 8)
{
// We don't need to convert the data, so we can use
// what we got
image = tmp_image;
pitch = rowbytes;
}
else
{
// We need to convert the data
pitch = width;
int y,x;
switch (bit_depth)
{
case 1:
image = new unsigned char[height * rowbytes * 8];
for (y = 0; y < height; y++)
{
for (x = 0; x < rowbytes; x++)
{
image[y*pitch + 8*x + 0] = row_pointers[y][x] >> 7;
image[y*pitch + 8*x + 1] = row_pointers[y][x] >> 6 & 0x1;
image[y*pitch + 8*x + 2] = row_pointers[y][x] >> 5 & 0x1;
image[y*pitch + 8*x + 3] = row_pointers[y][x] >> 4 & 0x1;
image[y*pitch + 8*x + 4] = row_pointers[y][x] >> 3 & 0x1;
image[y*pitch + 8*x + 5] = row_pointers[y][x] >> 2 & 0x1;
image[y*pitch + 8*x + 6] = row_pointers[y][x] >> 1 & 0x1;
image[y*pitch + 8*x + 7] = row_pointers[y][x] & 0x1;
}
}
break;
case 2:
image = new unsigned char[height * rowbytes * 4];
for (y = 0; y < height; y++)
{
for (x = 0; x < rowbytes; x++)
{
image[y*pitch + 4*x + 0] = row_pointers[y][x] >> 6;
image[y*pitch + 4*x + 1] = row_pointers[y][x] >> 4 & 0x3;
image[y*pitch + 4*x + 2] = row_pointers[y][x] >> 2 & 0x3;
image[y*pitch + 4*x + 3] = row_pointers[y][x] & 0x3;
}
}
break;
case 4:
image = new unsigned char[height * rowbytes * 2];
for (y = 0; y < height; y++)
{
for (x = 0; x < rowbytes; x++)
{
image[y*pitch + 2*x + 0] = row_pointers[y][x] >> 4;
image[y*pitch + 2*x + 1] = row_pointers[y][x] & 0x0f;
}
}
break;
default:
throw CL_Error ("CL_PNGProvider_Generic: Unhandled bit depth");
}
delete[] tmp_image;
}
delete[] row_pointers;
// Read the png palette and create the CL_Palette
int num_colors = 256;
png_colorp png_palette;
png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_colors);
if (num_colors > 256) num_colors = 256; // clanlib currently only support palette sizes of max 256.
for (int k = 0; k < num_colors; k++)
{
palette[k].set_color(png_palette[k].red, png_palette[k].green, png_palette[k].blue);
}
// Reading and setting up the transparent colors from the image
int num_trans = 0;
png_color_16p trans_values;
png_bytep trans;
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
// Setting up the transparent color
// FIXME: Can we only support a single transparent color?
if (num_trans == 1)
{
trans_col = trans[0];
m_uses_src_colorkey = true;
}
else if (num_trans > 1)
{
// Only one transpranent color is supported.
trans_col = trans[0];
// We collaps all transparent colors to a single one.
for (int j = 0; j < pitch * height; j++)
for (int i = 1; i < num_trans; i++)
{
if (trans[i] == image[j])
image[j] = trans_col;
}
m_uses_src_colorkey = true;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -