libpng解析png图片,用SDL显示

本文介绍了一个使用libpng库解析24位PNG图像的示例程序。该程序能够读取PNG文件,并将其转换为可供进一步处理的二维图像数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跟上一篇解析并显示 bmp 类似,这次的对象是 24位(带rgba)的 png.
不同的是 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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值