1. 程式人生 > >centos7 C++ 呼叫libpng png轉bmp

centos7 C++ 呼叫libpng png轉bmp

    寬度不為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;
}