版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sz76211822/article/details/84339215
宽度不为4的倍数的png自动转为4的倍数的png,即扩展bmp的像素了。支持3通道(RGB)的png图片以及4通道(RGBA)的png图片解析。
下载地址:https://download.csdn.net/download/sz76211822/10800747
#ifndef __PARSE_PNG__
#define __PARSE_PNG__
class ParsePng
{
public:
ParsePng();
~ParsePng();
bool Parse(const char *pstrFilePath, unsigned char* pBitmapBuffer, long nBufferSize, long& nWidth, long& nHeight);
private:
void write_bmp_header(long nWidth, long nHeight, long nDepth);
private:
long m_nLenHeader;
unsigned char* m_pImage;
unsigned char* m_pCache;
};
#endif
源文件:
#include "ParsePng.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C"{
#include "include/png.h"
}
#define MAX_LINE_SIZE 2 * 1024 * 1024
#define MAX_IMAGE_SIZE 20 * 1024 * 1024
#pragma pack(2)
typedef struct BITMAPFILEHEADER {
u_int16_t bfType;
u_int32_t bfSize;
u_int16_t bfReserved1;
u_int16_t bfReserved2;
u_int32_t bfOffBits;
} BITMAPFILEHEADER;
typedef struct BITMAPINFOHEADER {
u_int32_t biSize;
u_int32_t biWidth;
u_int32_t biHeight;
u_int16_t biPlanes;
u_int16_t biBitCount;
u_int32_t biCompression;
u_int32_t biSizeImage;
u_int32_t biXPelsPerMeter;
u_int32_t biYPelsPerMeter;
u_int32_t biClrUsed;
u_int32_t biClrImportant;
} BITMAPINFODEADER;
ParsePng::ParsePng()
{
m_nLenHeader = 0;
m_pImage = NULL;
while(!m_pImage){
try{
m_pImage = new unsigned char[MAX_IMAGE_SIZE];
}
catch(...){}
}
m_pCache = NULL;
while(!m_pCache){
try{
m_pCache = new unsigned char[MAX_LINE_SIZE];
}
catch(...){}
}
}
ParsePng::~ParsePng()
{
if(m_pImage){
delete[] m_pImage;
m_pImage = NULL;
}
if(m_pCache){
delete[] m_pCache;
m_pCache = NULL;
}
}
void ParsePng::write_bmp_header(long nWidth, long nHeight, long nDepth)
{
struct BITMAPFILEHEADER bfh;
struct BITMAPINFOHEADER bih;
unsigned long headersize = 0;
unsigned long filesize = 0;
if(nWidth % 4 != 0){//4字节对齐
nWidth += 4 - nWidth % 4;
}
if (nDepth == 1) {
headersize = 14 + 40 + 256 * 4;
filesize = headersize + nWidth * nHeight;
}
if (nDepth == 3) {
headersize = 14 + 40;
filesize = headersize + nWidth * nHeight * nDepth;
}
memset(&bfh, 0, sizeof(struct BITMAPFILEHEADER));
memset(&bih, 0, sizeof(struct BITMAPINFOHEADER));
//写入比较关键的几个bmp头参数
bfh.bfType = 0x4D42;
bfh.bfSize = filesize;
bfh.bfOffBits = headersize;
bih.biSize = 40;
bih.biWidth = nWidth;
bih.biHeight = nHeight;
bih.biPlanes = 1;
bih.biBitCount = (unsigned short)nDepth * 8;
bih.biSizeImage = nWidth * nHeight * nDepth;
memcpy(m_pImage, &bfh, sizeof(struct BITMAPFILEHEADER));
memcpy(m_pImage + sizeof(struct BITMAPFILEHEADER), &bih, sizeof(struct BITMAPINFOHEADER));
if (nDepth == 1) {//灰度图像要添加调色板
unsigned char *platte = NULL;
while(!platte){
try{
platte = new unsigned char[256*4];
}
catch(...){}
}
unsigned char j = 0;
for (int i = 0; i < 1024; i += 4) {
platte[i] = j;
platte[i+1] = j;
platte[i+2] = j;
platte[i+3] = 0;
j++;
}
memcpy(m_pImage + sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER), platte, sizeof(unsigned char) * 1024);
m_nLenHeader = sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER) + sizeof(unsigned char) * 1024;
delete[] platte;
}
else{
m_nLenHeader = sizeof(struct BITMAPFILEHEADER) + sizeof(struct BITMAPINFOHEADER);
}
}
bool ParsePng::Parse(const char *pstrFilePath, unsigned char* pBitmapBuffer, long nBufferSize, long& nWidth, long& nHeight)
{
m_nLenHeader = 0;
memset(m_pImage, 0, MAX_IMAGE_SIZE);
memset(m_pCache, 0, MAX_LINE_SIZE);
FILE *pFile = fopen(pstrFilePath, "rb+");
if(pFile){//文件打开失败
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL){
fclose(pFile);
return false;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL){
fclose(pFile);
png_destroy_write_struct(&png_ptr, NULL);
return false;
}
if (setjmp(png_jmpbuf(png_ptr))){//如果程序跑到这里了,那么写入文件时出现了问题
fclose(pFile);
png_destroy_write_struct(&png_ptr, &info_ptr);
return false;
}
rewind(pFile);
png_init_io(png_ptr, pFile);//开始读文件
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);//读文件
/*获取宽度,高度,位深,颜色类型*/
long nChannel = png_get_channels(png_ptr, info_ptr); //获取通道数
long nDepth = png_get_bit_depth(png_ptr, info_ptr); //获取位深
int color_type = png_get_color_type(png_ptr, info_ptr); //颜色类型
/* row_pointers里边就是rgba数据 */
png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);
nWidth = png_get_image_width(png_ptr, info_ptr);
nHeight = png_get_image_height(png_ptr, info_ptr);
long nRemain = 0;
if(nWidth % 4 != 0){
nRemain = 4 - nWidth % 4;
}
write_bmp_header(nWidth, nHeight, 3);
if(nChannel == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA){/*如果是RGB+alpha通道,或者RGB+其它字节*/
for(int i = nHeight - 1; i >= 0; i--){
long nOffset = 0;
for(int j = 0; j < nWidth * nChannel; j += nChannel){/* 一个字节一个字节的赋值 */
m_pCache[nOffset++] = row_pointers[i][j + 2]; // red
m_pCache[nOffset++] = row_pointers[i][j + 1]; // green
m_pCache[nOffset++] = row_pointers[i][j + 0]; // blue
//m_pCache[j * 4 + 3] = row_pointers[i][j * 4 + 3]; // alpha
}
for(int i = 0; i < nRemain * 3; i++){//4字节对齐,比如图片宽度是157,那么补到160个像素。bmp每个像素占3个字节,故(160 - 157) * 3 = 9。我这里是补字节数
m_pCache[nOffset++] = 0x00;
}
memcpy(m_pImage + m_nLenHeader, m_pCache, nOffset);
m_nLenHeader += sizeof(unsigned char) * nOffset;
}
fclose(pFile);
png_destroy_read_struct(&png_ptr, &info_ptr, 0);//撤销数据占用的内存
if(pBitmapBuffer){
if(nBufferSize >= m_nLenHeader){
memcpy(pBitmapBuffer, m_pImage, m_nLenHeader);
}
}
return true;
}
else if(nChannel == 3 || color_type == PNG_COLOR_TYPE_RGB){/* 如果是RGB通道 */
for(int i = nHeight - 1; i >= 0; i--){
long nOffset = 0;
for(int j = 0; j < nChannel * nWidth; j += nChannel){/* 一个字节一个字节的赋值 */
m_pCache[nOffset++] = row_pointers[i][j + 2]; // red
m_pCache[nOffset++] = row_pointers[i][j + 1]; // green
m_pCache[nOffset++] = row_pointers[i][j + 0]; // blue
}
for(int i = 0; i < nRemain * 3; i++){//4字节对齐,比如图片宽度是157,那么补到160个像素。bmp每个像素占3个字节,故(160 - 157) * 3 = 9。我这里是补字节数
m_pCache[nOffset++] = 0x00;
}
memcpy(m_pImage + m_nLenHeader, m_pCache, nOffset);
m_nLenHeader += sizeof(unsigned char) * nOffset;
}
fclose(pFile);
png_destroy_read_struct(&png_ptr, &info_ptr, 0);//撤销数据占用的内存
if(pBitmapBuffer){
if(nBufferSize >= m_nLenHeader){
memcpy(pBitmapBuffer, m_pImage, m_nLenHeader);
}
}
return true;
}
}
return false;
}