歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

使用libpng讀取PNG圖片像素數據

libpng 是一套免費的、公開源代碼的程序庫,支持對 PNG 圖形文件的創建、讀寫等操作。可以下載源代碼。libpng 使用 zlib 程序庫作為壓縮引擎,zlib 也是著名的 gzip (GNU zip) 所采用的壓縮引擎。zlib 是通用的壓縮庫,提供了一套 in-memory 壓縮和解壓函數,並能檢測解壓出來的數據的完整性(integrity)。zlib 也支持讀寫 gzip (.gz) 格式的文件這些都是免費和公開的哦。在下載了pbglib,並且要下載zlib,如何才能加到代碼中呢?以VC6.為例來說明。其他CBuild等編譯器相信也一樣的。比如我們從網上下載了一個lpng1210.zip和zlib-1.2.3.tar.tar。首先把他們解壓。在lpng的目錄中尋找 projects/visualc6/libpng.dsw。並在zlib中尋找zlib-1.2.3/projects/visualc6 /zlib.dsw。用vc打開libpng工程文件,把zlib的工程文件也添加進去。設置好在setting中設置好的zlib.lib和pbg.lib的輸出路徑,以及include路徑(這個簡單方法相信大家都明白吧)。運行後,就生成了兩個靜態庫了。lib做好以後,就可以將lib加到我們要做的工程中去。在link中設置好,並將png.h和zlib.h都加進去。這樣就可以正式的使用libpng了!

libpng 的詳細介紹:請點這裡
libpng 的下載地址:請點這裡

下面到重點內容了。如何用pnglib實現對文件的讀寫? 現在讓我們來試著寫一段代碼來讀取PNG像素數據(from File or Stream),並把讀取的數據存儲到一個數組rgba中,用來生成OpenGl紋理

typedef struct {
    u8* pixelData;
    int imageWidth;
    int imageHeight;
}ImageInfo; 

typedef struct {
    u8* data;
    int size;
    int offset;
}ImageSource;

 

//----------------------------------------------------------------------------------------------------------------
static void pngReaderCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
    ImageSource* isource = (ImageSource*)png_get_io_ptr(png_ptr);

    if(isource->offset + length <= isource->size)
    {
      memcpy(data, isource->data + isource->offset, length);
      isource->offset += length;
    }
    else
    {
        png_error(png_ptr,"pngReaderCallback failed");
    }
}

//----------------------------------------------------------------------------------------------------------------

ImageInfo*  decodePNGFromStream(const u8* pixelData, const u32& dataSize)
{
    png_structp png_ptr;
    png_infop info_ptr;
    int width, height, rowBytes;
    png_byte color_type;  //可以是PNG_COLOR_TYPE_RGB,PNG_COLOR_TYPE_PALETTE.......等
    png_byte bit_depth;
    png_colorp palette;

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
    if (!png_ptr)
    {
        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
        TFC_DEBUG("ReadPngFile: Failed to create png_ptr");
    }
    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr)
    {
        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
        TFC_DEBUG("ReadPngFile: Failed to create info_ptr");
    }
    if (setjmp(png_jmpbuf(png_ptr)))
    {
        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
        TFC_DEBUG("ReadPngFile: Failed to read the PNG file");
    }


    ImageSource imgsource;
    imgsource.data = (u8*)pixelData;
    imgsource.size = dataSize;
    imgsource.offset = 0;
    //define our own callback function for I/O instead of reading from a file
    png_set_read_fn(png_ptr,&imgsource, pngReaderCallback );


    /* **************************************************
    * The low-level read interface in libpng (http://www.libpng.org/pub/png/libpng-1.2.5-manual.html)
    * **************************************************
    */
    png_read_info(png_ptr, info_ptr);
    width = info_ptr->width;
    height = info_ptr->height;
    color_type = info_ptr->color_type;
    bit_depth = info_ptr->bit_depth;
    rowBytes = info_ptr->rowbytes;
    palette= info_ptr->palette;

    // Convert stuff to appropriate formats!
    if(color_type==PNG_COLOR_TYPE_PALETTE)
    {
        png_set_packing(png_ptr);
        png_set_palette_to_rgb(png_ptr); //Expand data to 24-bit RGB or 32-bit RGBA if alpha available.
    }
    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
        png_set_gray_1_2_4_to_8(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        png_set_gray_to_rgb(png_ptr);
    if (bit_depth == 16)
        png_set_strip_16(png_ptr);


    //Expand paletted or RGB images with transparency to full alpha channels so the data will be available as RGBA quartets.
    if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
    {
        png_set_tRNS_to_alpha(png_ptr);
    }

    //png_read_update_info(png_ptr, info_ptr);
    u8* rgba = new u8[width * height * 4];  //each pixel(RGBA) has 4 bytes
    png_bytep * row_pointers;
    row_pointers = (png_bytep*)png_malloc(sizeof(png_bytep) * height);
    for (int y = 0; y < height; y++)
    {
        row_pointers[y] = (png_bytep)png_malloc(width<<2); //each pixel(RGBA) has 4 bytes
    }
    png_read_image(png_ptr, row_pointers);

    //unlike store the pixel data from top-left corner, store them from bottom-left corner for OGLES Texture drawing...
    int pos = (width * height * 4) - (4 * width);
    for(int row = 0; row < height; row++)
    {
      for(int col = 0; col < (4 * width); col += 4)
      {
          rgba[pos++] = row_pointers[row][col];        // red
          rgba[pos++] = row_pointers[row][col + 1];    // green
          rgba[pos++] = row_pointers[row][col + 2];    // blue
          rgba[pos++] = row_pointers[row][col + 3];    // alpha
      }
      pos=(pos - (width * 4)*2); //move the pointer back two rows
    }

    ImageInfo* imageInfo = (ImageInfo*)kdMalloc(sizeof(ImageInfo));
    imageInfo->pixelData = rgba;
    imageInfo->imageHeight = height;
    imageInfo->imageWidth = width;

    //clean up after the read, and free any memory allocated
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    return imageInfo;
}

//----------------------------------------------------------------------------------------------------------------

ImageInfo*  decodePNGFromFile(char* fileName)
{
    char png_header[8];
    png_structp png_ptr;
    png_infop info_ptr;
    int width, height, rowBytes;
    png_byte color_type;
    png_byte bit_depth;
    png_colorp palette;

    /* open file and test for it being a png */
    FILE *file = fopen(fileName, "rb");
    fread(png_header, 1, 8, file);
    if(png_sig_cmp((png_bytep)png_header, 0, 8))
    {
        TFC_DEBUG("Not a PNG file...");
        fclose(file);
    }
    /* initialise structures for reading a png file */
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    info_ptr = png_create_info_struct(png_ptr);
    if (setjmp(png_jmpbuf(png_ptr)))
    {
        png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
        TFC_DEBUG("ReadPngFile: Failed to read the PNG file");
        fclose(file);
    }
    //I/O initialisation methods
    png_init_io(png_ptr, file);
    png_set_sig_bytes(png_ptr, 8);  //Required!!!


    /* **************************************************
    * The high-level read interface in libpng (http://www.libpng.org/pub/png/libpng-1.2.5-manual.html)
    * **************************************************
    */
      png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
      width = info_ptr->width;
      height = info_ptr->height;
      unsigned char* rgba = new unsigned char[width * height * 4];  //each pixel(RGBA) has 4 bytes
      png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);

      //Original PNG pixel data stored from top-left corner, BUT OGLES Texture drawing is from bottom-left corner
      //int pos = 0;
      //for(int row = 0; row < height; row++)
      //{
          //for(int col = 0; col < (4 * width); col += 4)
          //{
              //rgba[pos++] = row_pointers[row][col];    // red
              //rgba[pos++] = row_pointers[row][col + 1]; // green
              //rgba[pos++] = row_pointers[row][col + 2]; // blue
              //rgba[pos++] = row_pointers[row][col + 3]; // alpha
          //}
      //}


      //unlike store the pixel data from top-left corner, store them from bottom-left corner for OGLES Texture drawing...
      int pos = (width * height * 4) - (4 * width);
      for(int row = 0; row < height; row++)
      {
          for(int col = 0; col < (4 * width); col += 4)
          {
              rgba[pos++] = row_pointers[row][col];        // red
              rgba[pos++] = row_pointers[row][col + 1]; // green
              rgba[pos++] = row_pointers[row][col + 2]; // blue
              rgba[pos++] = row_pointers[row][col + 3]; // alpha
          }
          pos=(pos - (width * 4)*2); //move the pointer back two rows
      }

    ImageInfo* imageInfo = (ImageInfo*)kdMalloc(sizeof(ImageInfo));
    imageInfo->pixelData = rgba;
    imageInfo->imageHeight = height;
    imageInfo->imageWidth = width;

    //clean up after the read, and free any memory allocated
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    fclose(file);
    return imageInfo;
}

//----------------------------------------------------------------------------------------------------------------

 

int createPNGTextureFromStream(const u8* pixelData, const u32& dataSize)
{
        GLuint textureID;
        glEnable(GL_TEXTURE_2D);
        glGenTextures(1,&textureID);
        glBindTexture(GL_TEXTURE_2D,textureID);
        ImageInfo* imageInfo = decodePNGFromStream(pixelData, dataSize);

        glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,imageInfo->imageWidth,imageInfo->imageHeight,0,

                                GL_RGBA,GL_UNSIGNED_BYTE,imageInfo->pixelData);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

        delete[] imageInfo->pixelData;

        delete imageInfo;
        return textureID;

}


//----------------------------------------------------------------------------------------------------------------

 

void main()

{

      //Testcase1: decoding png data from a raw png bufferstream and create the corresponding Texture

      //假設我們從某個地方可以拿到一個unsigned char* 的PNG數據源pixelData.

      int texId = createPNGTextureFromStream(pixelData, dataSize);

 

      //Testcase2: decoding png data from a given png file and and create the corresponding Texture

      char* fileName = "example.png";

      int texId = createPNGTextureFromFile(fileName);

 

      //現在我們就可以用創建出來的textureID來繪制紋理了。。。。

}

接下來請看第2頁精彩內容: http://www.linuxidc.com/Linux/2013-08/88651p2.htm

Copyright © Linux教程網 All Rights Reserved