跟上一篇解析并显示 bmp 类似,这次的对象是 24位(带rgba)的 png.
不同的是 bmp 格式比较简单,是自己写代码解析的.png 格式比较复杂,使用了libpng.
在此快速记录。
libpng 关键函数是 png_get_rows() ,能取得 png 的 二维图像数据,然后自己想办法搞到自己 new 出来的 内存里即可
纯试验,没处理泄露,没考虑代码结构
miaopng.cpp
Application.h
不同的是 bmp 格式比较简单,是自己写代码解析的.png 格式比较复杂,使用了libpng.
在此快速记录。
libpng 关键函数是 png_get_rows() ,能取得 png 的 二维图像数据,然后自己想办法搞到自己 new 出来的 内存里即可
纯试验,没处理泄露,没考虑代码结构
贴代码
miaopng.h
#ifndef __PNG_H__
#define __PNG_H__
#include <iostream>
namespace miaopng
{
void testpng(std::string filePath);
struct PngData{
int nWidth;
int nHeight;
unsigned char* imgData;
unsigned char getRAt(int x, int y)
{
unsigned char* addr = getAddrAt(x, y);
char col = addr[0];
return col;
}
unsigned char getGAt(int x, int y)
{
unsigned char* addr = getAddrAt(x, y);
char col = addr[1];
return col;
}
unsigned char getBAt(int x, int y)
{
unsigned char* addr = getAddrAt(x, y);
char col = addr[2];
return col;
}
unsigned char getAAt(int x, int y)
{
unsigned char* addr = getAddrAt(x, y);
char col = addr[3];
return col;
}
unsigned char* getAddrAt(int x, int y)
{
int sizePerLine = nWidth * 4;
long offset = sizePerLine * y + x * 4;
return this->imgData + offset;
}
};
size_t loadpng(const char* filePath, int* pnWidth, int* pnHeight, unsigned char** cbData);
}
#endif //__PNG_H__
miaopng.cpp
#include "miaopng.h"
#include <stdio.h>
#include <iostream>
#include <win32/png.h>
#include <assert.h>
namespace
{
typedef struct
{
const unsigned char* data;
size_t size;
int offset;
} tImageSource;
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);
if ((int)(isource->offset + length) <= isource->size)
{
memcpy(data, isource->data + isource->offset, length);
isource->offset += length;
}
else
{
png_error(png_ptr, "pngReaderCallback failed");
}
printf("123\n");
}
}
namespace miaopng
{
//void testpng(std::string filePath)
//{
// png_structp png_ptr = nullptr;
// png_infop info_ptr = nullptr;
//
// png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
// assert(png_ptr != nullptr);
// info_ptr = png_create_info_struct(png_ptr);
// assert(info_ptr != nullptr);
// FILE* fp = fopen(filePath.c_str(),"rb");
// fseek(fp, 0, SEEK_END);
// long len = ftell(fp);
// fseek(fp, 0, SEEK_SET);
// unsigned char* data = new unsigned char[len];
// fread(data, len, 1, fp);
// fclose(fp);
// tImageSource imageSource;
// imageSource.data = (unsigned char*)data;
// imageSource.size = len;
// imageSource.offset = 0;
// png_set_read_fn(png_ptr, &imageSource, pngReadCallback);
// // read png file info
// png_read_info(png_ptr, info_ptr);
// int _width = png_get_image_width(png_ptr, info_ptr);
// int _height = png_get_image_height(png_ptr, info_ptr);
// png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
// png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
// // force palette images to be expanded to 24-bit RGB
// // it may include alpha channel
// if (color_type == PNG_COLOR_TYPE_PALETTE)
// {
// png_set_palette_to_rgb(png_ptr);
// }
// // low-bit-depth grayscale images are to be expanded to 8 bits
// if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
// {
// bit_depth = 8;
// png_set_expand_gray_1_2_4_to_8(png_ptr);
// }
// // expand any tRNS chunk data into a full alpha channel
// if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
// {
// png_set_tRNS_to_alpha(png_ptr);
// }
// // reduce images with 16-bit samples to 8 bits
// if (bit_depth == 16)
// {
// png_set_strip_16(png_ptr);
// }
// // Expanded earlier for grayscale, now take care of palette and rgb
// if (bit_depth < 8) {
// png_set_packing(png_ptr);
// }
// // update info
// png_read_update_info(png_ptr, info_ptr);
// bit_depth = png_get_bit_depth(png_ptr, info_ptr);
// color_type = png_get_color_type(png_ptr, info_ptr);
// switch (color_type)
// {
// case PNG_COLOR_TYPE_GRAY:
// //_renderFormat = Texture2D::PixelFormat::I8;
// printf("123");
// break;
// case PNG_COLOR_TYPE_GRAY_ALPHA:
// //_renderFormat = Texture2D::PixelFormat::AI88;
// printf("123");
// break;
// case PNG_COLOR_TYPE_RGB:
// //_renderFormat = Texture2D::PixelFormat::RGB888;
// printf("123");
// break;
// case PNG_COLOR_TYPE_RGB_ALPHA:
// //_renderFormat = Texture2D::PixelFormat::RGBA8888;
// printf("123");
// break;
// default:
// break;
// }
//}
size_t loadpng(const char* filePath, int* pnWidth, int* pnHeight, unsigned char** cbData)
{
int file_size = 0;
FILE* fp = fopen(filePath,"rb");
assert(fp != nullptr);
png_infop info_ptr;
png_structp png_ptr;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr,fp); // png_ptr 与 文件绑定
png_read_png(png_ptr,info_ptr,PNG_TRANSFORM_EXPAND,0); // 读取 png 文件
// 基础信息
*pnWidth = png_get_image_width(png_ptr, info_ptr);
*pnHeight = png_get_image_height(png_ptr, info_ptr);
png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
assert(color_type == PNG_COLOR_TYPE_RGB_ALPHA); // 临时只支持一种 RGBA 的 png 格式
const int BLOCK_SIZE = 4; //(rgba 4 bytes)
file_size = (*pnWidth) * (*pnHeight) * BLOCK_SIZE;
*cbData = new unsigned char[file_size];
//*cbData = (unsigned char*)malloc(file_size);
// 开始读取数据 填充数据
png_bytep *row_point = NULL;
row_point = png_get_rows(png_ptr, info_ptr);
int pos = 0;
for (int y = 0; y < *pnHeight;y++)
{
for (int x = 0; x < *pnWidth * BLOCK_SIZE; x += BLOCK_SIZE)
{
(*cbData)[pos++] = row_point[y][x + 0]; // R
(*cbData)[pos++] = row_point[y][x + 1]; // G
(*cbData)[pos++] = row_point[y][x + 2]; // B
(*cbData)[pos++] = row_point[y][x + 3]; // A
}
}
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(fp);
return file_size;
}
}
main.cpp
#include <iostream>
#include "Application.h"
#include "mp3.h"
#include "miaopng.h"
#include <win32/png.h>
miaomp3::The24BitBmp bitmap;
miaopng::PngData pngData;
int main()
{
//miaomp3::testmp3("res/login.mp3");
miaomp3::testmp3("res/laoshi.bmp",bitmap);
//miaomp3::testmp3("res/linshiyin.bmp",bitmap);
//miaomp3::testmp3("res/ceshi.bmp", bitmap);
//miaopng::testpng("res/mingyuexin.png");
pngData.nWidth = 0;
pngData.nHeight = 0;
pngData.imgData = nullptr;
//miaopng::loadpng("res/mingyuexin.png",&pngData.nWidth,&pngData.nHeight,&pngData.imgData);
miaopng::loadpng("res/murongjiu.png",&pngData.nWidth,&pngData.nHeight,&pngData.imgData);
Application app;
//app.initialize(bitmap.width, bitmap.height);
app.initialize(pngData.nWidth, pngData.nHeight);
app.mainLoop();
return 0;
}
Application.h
#ifndef __APPLICATION_H__
#define __APPLICATION_H__
#include "SDL.h"
#include <stdio.h>
class Application
{
public:
Application();
virtual ~Application();
public:
bool initialize(int screenWidth,int screenHeight);
void mainLoop();
void cleanup();
private:
SDL_Window* _pWindow;
SDL_Surface* _pScreenSurface;
SDL_Renderer* _pRenderer;
int _screenWidth, _screenHeight;
private:
void startGame();
void showMainMenu();
void restartGame();
};
#endif //__APPLICATION_H__
Application.cpp
#include "Application.h"
#include <assert.h>
#include <stdlib.h>
#include "mp3.h"
#include "miaopng.h"
const int SCALE_FACTOR = 1;
//const int SCALE_FACTOR = 200;
Application::Application()
: _pWindow(nullptr)
, _pScreenSurface(nullptr)
, _pRenderer(nullptr)
, _screenWidth(100)
, _screenHeight(100)
{
}
Application::~Application()
{
cleanup();
}
bool Application::initialize(int screenWidth, int screenHeight)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("SDL can not initialized!SDL Error:%s\n", SDL_GetError());
return false;
}
_screenWidth = screenWidth * SCALE_FACTOR;
_screenHeight = screenHeight * SCALE_FACTOR;
_pWindow = SDL_CreateWindow("msdl1", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _screenWidth, _screenHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
//_pWindow = SDL_CreateWindow("msdl1", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _screenWidth, _screenHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN);
if (_pWindow == nullptr)
{
printf("SDL can't create window!SDL Error:%s\n", SDL_GetError());
return false;
}
int w, h;
SDL_GetWindowSize(_pWindow, &w, &h);
printf("Viewport Size: %d,%d\n",w,h);
_pScreenSurface = SDL_GetWindowSurface(_pWindow);
_pRenderer = SDL_CreateRenderer(_pWindow, -1, SDL_RENDERER_ACCELERATED);
if (_pRenderer == nullptr)
{
return false;
}
SDL_FillRect(_pScreenSurface, nullptr, SDL_MapRGBA(_pScreenSurface->format, 0, 0, 0, 0));
return true;
}
void Application::cleanup()
{
SDL_DestroyRenderer(_pRenderer);
SDL_DestroyWindow(_pWindow);
SDL_Quit();
}
void Application::mainLoop()
{
bool quit = false;
SDL_Event evt;
while (!quit)
{
while (SDL_PollEvent(&evt) != 0)
{
if (evt.type == SDL_QUIT){
quit = true;
}
}
{
SDL_SetRenderDrawColor(_pRenderer, (Uint8)255, (Uint8)255, (Uint8)255, 0xff);
SDL_RenderClear(_pRenderer);
SDL_SetRenderDrawColor(_pRenderer, 0x00, 0x00, 0x00, 0xff);
SDL_Rect fillRect = { 0, 0, _screenWidth, _screenHeight };
SDL_RenderFillRect(_pRenderer, &fillRect);
}
for (int row = 0; row < _screenHeight; row++)
{
for (int col = 0; col < _screenWidth; col++)
{
/*
随机颜色
*/
/*
int r = rand() / 100 % 256;
int g = rand() / 100 % 256;
int b = rand() / 100 % 256;
int a = 0xff;
*/
/*
bmp 格式图片
*/
/*
extern miaomp3::The24BitBmp bitmap;
int x = col/SCALE_FACTOR;
int y = row/SCALE_FACTOR;
y = bitmap.height - y - 1;
Uint8 r, g, b, a;
r = (Uint8)bitmap.getRAt(x, y);
g = (Uint8)bitmap.getGAt(x,y);
b = (Uint8)bitmap.getBAt(x,y);
a = 0xff;
//printf("(%d,%d):[%d,%d,%d]\n",x,y,r,g,b);
*/
/*
png 格式图片
*/
extern miaopng::PngData pngData;
int x = col / SCALE_FACTOR;
int y = row / SCALE_FACTOR;
Uint8 r, g, b, a;
r = (Uint8)pngData.getRAt(x, y);
g = (Uint8)pngData.getGAt(x, y);
b = (Uint8)pngData.getBAt(x, y);
a = (Uint8)pngData.getAAt(x, y);
SDL_SetRenderDrawColor(_pRenderer, r, g, b, a);
SDL_RenderDrawPoint(_pRenderer, col, row);
}
}
SDL_RenderPresent(_pRenderer);
}
}
void Application::startGame()
{
}
void Application::showMainMenu()
{
}
void Application::restartGame()
{
}
补充:
必须把 SDL 的 render 装填设置为 支持 blend ,否则 alpha 通道 显示不出来。上面代码漏了这句:
SDL_SetRenderDrawBlendMode(_pRenderer, SDL_BLENDMODE_BLEND);