centos7 C++ 呼叫libpng png轉bmp
阿新 • • 發佈:2019-01-11
寬度不為4的倍數的png自動轉為4的倍數的png,即擴充套件bmp的畫素了。支援3通道(RGB)的png圖片以及4通道(RGBA)的png圖片解析。
#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; }