Недавно я решил проблему с растровым изображением в своем последнем посте. Теперь я вернулся с другой проблемой с изображением. На этот раз это PNG.
Я использую LibPng для чтения и записи файлов PNG. У меня есть структура, которая содержит информацию BGRA каждого пикселя. Я знаю, что пиксели хранятся правильно и в формате RGBA. Таким образом, я указал обмен B и R. Это отлично работает. Я думаю, что я как-то переворачиваю изображение, но я не совсем уверен.
Моя проблема возникает, когда я пытаюсь преобразовать 24-битный PNG в 32-битный PNG и наоборот. В настоящее время я могу сделать 24 до 24 и 32 до 32 просто отлично.
Ребята, вы можете просмотреть мой код и сказать, что я делаю неправильно, когда пытаюсь конвертировать из 24 в 32?
Приведенный ниже код работает для загрузки и записи того же PNG-файла обратно на диск. Я позаботился о том, чтобы включить все в этот файл, чтобы вы, ребята, могли скомпилировать его и посмотреть, если это необходимо.
#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");
}
Я загружаю (24-битный PNG с MS-Paint):
Когда я сохраняю это как 24, это сохраняет безупречно. Когда я пытаюсь сохранить его как 32, он выглядит так:
Попробуйте внести следующие изменения в ваш исходный код. Не испытано!
В вашем нагрузка функция изменить это:
case PNG_COLOR_TYPE_RGB:
{
png_set_bgr(PngPointer);
BitsPerPixel = 24;
break;
}
к этому:
case PNG_COLOR_TYPE_RGB:
{
png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);
png_set_bgr(PngPointer);
BitsPerPixel = 32;
break;
}
В вашем нагрузка а также Сохранить функции меняют это:
for (size_t I = 0; I < height; ++I)
{
RowPointers[I] = BuffPos + (I * width * ((BitsPerPixel > 24) ? 4 : 3));
}
к этому:
size_t BytesPerLine = width << 2;
unsigned char *ptr = BuffPos;
for (size_t I = 0; I < height; ++I, ptr += BytesPerLine)
RowPointers[I] = ptr;
В вашем Сохранить функция изменить это:
png_write_png(PngPointer, InfoPointer, PNG_TRANSFORM_IDENTITY, NULL);
к этому:
png_write_png(PngPointer, InfoPointer, BitsPerPixel == 24 ? PNG_TRANSFORM_STRIP_FILLER : PNG_TRANSFORM_IDENTITY, NULL);
и в вашем SetBitsPerPixel Функция изменить эту строку:
bitdepth = (BPP > 24 ? 8 : 6);
к этому:
bitdepth = (BPP >= 24 ? 8 : 6);
На самом деле, я не уверен, почему вы это делаете, поскольку, насколько я знаю, изображения 24 и 32 bpp должны иметь глубина цвета 8 бит.
НОТА: Эти модификации предназначены для изображений 24 и 32 bpp, поэтому, если вы хотите использовать индексированный или же оттенки серого изображения, возможно, потребуется внести некоторые дополнительные изменения. Дело в том, что вы всегда должны сохранять пиксели в буфере BGRA (независимо от того, загружаете ли вы индексированные, полутоновые или RGB-изображения без альфа-канала).
Других решений пока нет …