首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Lib Png: Png24到Png32,反之亦然

Lib Png: Png24到Png32,反之亦然
EN

Stack Overflow用户
提问于 2012-12-24 04:26:17
回答 1查看 1.2K关注 0票数 1

我最近在上一篇文章中解决了位图问题。现在我又带着另一个图像问题回来了。这一次是PNG。

我正在使用LibPng来读写PNG文件。我有一个结构,它包含每个像素的BGRA信息。我知道像素是以RGBA格式存储的。所以我指定了B和R的交换,这很好用。我想我以某种方式颠倒了图像,但我不是很确定。

当我尝试将24位PNG转换为32位PNG时,我的问题出现了,反之亦然。目前,我可以做24到24和32到32。

你们能检查一下我的代码并告诉我在尝试从24转换到32时我做错了什么吗?

下面的代码用于加载相同的PNG并将其写回磁盘。我确保将所有内容都包含在这个文件中,这样你们就可以编译它,看看是否有必要。

代码语言:javascript
运行
复制
#include <iostream>
#include <vector>
#include <fstream>
#include <stdexcept>
#include "Libraries/LibPng/Include/png.h"

typedef union RGB
{
    uint32_t Color;
    struct
    {
        unsigned char B, G, R, A;
    } RGBA;
} *PRGB;


std::vector<RGB> Pixels;
uint32_t BitsPerPixel, width, height;
int bitdepth, colortype, interlacetype, channels;


void ReadFromStream(png_structp PngPointer, png_bytep Data, png_size_t Length) //For reading using ifstream rather than FILE*
{
    std::ifstream *Stream = (std::ifstream*)png_get_io_ptr(PngPointer);
    Stream->read((char*)Data, Length);
}

void WriteToStream(png_structp PngPointer, png_bytep Data, png_size_t Length)  //For writing using ofstream rather than FILE*
{
    std::ofstream *Stream = (std::ofstream*)png_get_io_ptr(PngPointer);
    Stream->write((char*)Data, Length);
}


void Load(const char* FilePath)
{
    std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
    if (!hFile.is_open()){throw std::invalid_argument("File Not Found.");}

    unsigned char Header[8] = {0};
    hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));

    if (png_sig_cmp(Header, 0, 8))
    {
        hFile.close();
        throw std::invalid_argument("Error: Invalid File Format. Required: Png.");
    }

    png_structp PngPointer = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    if (!PngPointer)
    {
        hFile.close();
        throw std::runtime_error("Error: Cannot Create Read Structure.");
    }

    png_infop InfoPointer = png_create_info_struct(PngPointer);
    if (!InfoPointer)
    {
        hFile.close();
        png_destroy_read_struct(&PngPointer, nullptr, nullptr);
        throw std::runtime_error("Error: Cannot Create InfoPointer Structure.");
    }

    png_infop EndInfo = png_create_info_struct(PngPointer);
    if (!EndInfo)
    {
        hFile.close();
        png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
        throw std::runtime_error("Error: Cannot Create EndInfo Structure.");
    }

    if (setjmp(png_jmpbuf(PngPointer)))
    {
        hFile.close();
        png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
        throw std::runtime_error("Error: Cannot Set Jump Pointer.");
    }

    png_set_sig_bytes(PngPointer, sizeof(Header));
    png_set_read_fn(PngPointer, reinterpret_cast<void*>(&hFile), ReadFromStream);
    png_read_info(PngPointer, InfoPointer);


    //This is where I start getting the info and storing it..

    channels = png_get_channels(PngPointer, InfoPointer);
    png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);
    png_set_strip_16(PngPointer);
    png_set_packing(PngPointer);

    switch (colortype)
    {
        case PNG_COLOR_TYPE_GRAY:
        {
            png_set_expand(PngPointer);
            break;
        }

        case PNG_COLOR_TYPE_GRAY_ALPHA:
        {
            png_set_gray_to_rgb(PngPointer);
            break;
        }

        case PNG_COLOR_TYPE_RGB:
        {
            png_set_bgr(PngPointer);
            BitsPerPixel = 24;
            break;
        }

        case PNG_COLOR_TYPE_RGBA:
        {
            png_set_bgr(PngPointer);
            BitsPerPixel = 32;
            break;
        }

        default: png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr); throw std::runtime_error("Error: Png Type not supported."); break;
    }

    //Store the new data.

    png_read_update_info(PngPointer, InfoPointer);
    channels = png_get_channels(PngPointer, InfoPointer);
    png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);

    Pixels.resize(width * height);
    std::vector<unsigned char*> RowPointers(height);
    unsigned char* BuffPos = reinterpret_cast<unsigned char*>(Pixels.data());

    //Set the row pointers to my Pixels vector. This way, the image is stored upright in my vector<BGRA> Pixels.
    //I think this is flipping it for some reason :S

    for (size_t I = 0; I < height; ++I)
    {
        RowPointers[I] = BuffPos + (I * width * ((BitsPerPixel > 24) ? 4 : 3));
    }

    png_read_image(PngPointer, RowPointers.data());                 //Get the pixels as BGRA and store it in my struct vector.
    png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
    hFile.close();

    std::cout<<"Loading Parameters..."<<std::endl;
    std::cout<<"Bits:  "<<BitsPerPixel<<std::endl;
    std::cout<<"Depth: "<<bitdepth<<std::endl;
    std::cout<<"CType: "<<colortype<<std::endl;
}

void Save(const char* FilePath)
{
    std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
    if (!hFile.is_open()) {throw std::invalid_argument("Cannot open file for writing.");}

    png_structp PngPointer = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    if (!PngPointer)
    {
        hFile.close();
        throw std::runtime_error("Error: Cannot Create Write Structure.");
    }

    png_infop InfoPointer = png_create_info_struct(PngPointer);
    if (!InfoPointer)
    {
        hFile.close();
        png_destroy_write_struct(&PngPointer, nullptr);
        throw std::runtime_error("Error: Cannot Create InfoPointer Structure.");
    }

    if (setjmp(png_jmpbuf(PngPointer)))
    {
        hFile.close();
        png_destroy_write_struct(&PngPointer, &InfoPointer);
        throw std::runtime_error("Error: Cannot Set Jump Pointer.");
    }

    std::cout<<"\nSaving Parameters..."<<std::endl;
    std::cout<<"Bits:  "<<BitsPerPixel<<std::endl;
    std::cout<<"Depth: "<<bitdepth<<std::endl;
    std::cout<<"CType: "<<colortype<<std::endl;

    //This is where I set all the Information..

    png_set_IHDR (PngPointer, InfoPointer, width, height, bitdepth, BitsPerPixel == 24 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    std::vector<unsigned char*> RowPointers(height);
    unsigned char* BuffPos = reinterpret_cast<unsigned char*>(Pixels.data());

    //Set the Row pointers to my vector<BGRA> Pixels. It should have been stored upright already.
    //I think this flips it upside down :S?

    for (size_t I = 0; I < height; ++I)
    {
        RowPointers[I] = BuffPos + (I * width * ((BitsPerPixel > 24) ? 4 : 3));
    }

    png_set_bgr(PngPointer);        //My struct vector holds BGRA and PNG requires RGBA so swap them..
    png_set_write_fn(PngPointer, reinterpret_cast<void*>(&hFile), WriteToStream, nullptr);
    png_set_rows(PngPointer, InfoPointer, RowPointers.data());
    png_write_png(PngPointer, InfoPointer, PNG_TRANSFORM_IDENTITY, NULL);
    png_destroy_write_struct(&PngPointer, &InfoPointer);
    hFile.close();
}

void SetBitsPerPixel(uint32_t BPP)
{
    BitsPerPixel = BPP;
    bitdepth = (BPP > 24 ? 8 : 6);
    channels = (BPP > 24 ? 4 : 3);
    colortype = (BPP > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
}


int main()
{
    Load("C:/Images/Png24.png");
    SetBitsPerPixel(32);
    Save("C:/Images/Output/Png32.png");
}

I加载(用MS-Paint制作的24位PNG ):

当我将其保存为24时,它保存得完美无缺。当我尝试将其保存回32时,它看起来如下所示:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-12-24 09:48:02

尝试对源代码进行以下修改。未测试

在您的Load函数中,更改以下内容:

代码语言:javascript
运行
复制
    case PNG_COLOR_TYPE_RGB:
    {
        png_set_bgr(PngPointer);
        BitsPerPixel = 24;
        break;
    }

要这样做:

代码语言:javascript
运行
复制
    case PNG_COLOR_TYPE_RGB:
    {
        png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);
        png_set_bgr(PngPointer);
        BitsPerPixel = 32;
        break;
    }

在您的LoadSave函数中更改此设置:

代码语言:javascript
运行
复制
for (size_t I = 0; I < height; ++I)
{
    RowPointers[I] = BuffPos + (I * width * ((BitsPerPixel > 24) ? 4 : 3));
}

要这样做:

代码语言:javascript
运行
复制
size_t BytesPerLine = width << 2;
unsigned char *ptr = BuffPos;
for (size_t I = 0; I < height; ++I, ptr += BytesPerLine)
    RowPointers[I] = ptr;

在您的保存功能中,更改以下设置:

代码语言:javascript
运行
复制
png_write_png(PngPointer, InfoPointer, PNG_TRANSFORM_IDENTITY, NULL);

要这样做:

代码语言:javascript
运行
复制
png_write_png(PngPointer, InfoPointer, BitsPerPixel == 24 ? PNG_TRANSFORM_STRIP_FILLER : PNG_TRANSFORM_IDENTITY, NULL);

并在您的SetBitsPerPixel函数中更改下面这一行:

代码语言:javascript
运行
复制
bitdepth = (BPP > 24 ? 8 : 6);

要这样做:

代码语言:javascript
运行
复制
bitdepth = (BPP >= 24 ? 8 : 6);

实际上,我不确定你为什么要这样做,因为据我所知,24和32bpp图像应该有8位的位深度。

注释:此修改适用于24和32bpp图像,因此如果您想要使用索引或灰度图像,则可能需要进行一些额外的修改。关键是你应该总是把像素保存在BGRA缓冲区中(不管你加载的是索引图像、灰度图像还是没有alpha通道的RGB图像)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14014454

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档