我的项目需要通过HTTP请求接收PNG文件内容,对图像进行处理,然后在HTTP响应中将生成的PNG发送回去。所有代码都需要在C / C ++中完成。
我是libpng
的新手。因此,我尝试编写一个原型,将一个PNG文件读入unsigned char
缓冲区,取出RGB值(忽略alpha),不进行操作,使用PNG文件内容创建一个新的unsigned char
缓冲区,编写新文件到磁盘并验证我“生成”相同的映像。我引用了this question in StackOverflow
我的代码(删除了一些不相关的功能,完整的可运行代码为here):
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <png.h>
using namespace std;
typedef struct
{
png_bytep data;
int size;
} ImageTarget;
int read_png(string file_path, unsigned char** buffer)
{
// ... ...
}
void write_png(string file_path, unsigned char* buffer, int length)
{
// ... ...
}
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
// ... ...
}
void pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
cout << "- length ----------- " << length << endl;
ImageTarget * itarget = (ImageTarget*)png_get_io_ptr(png_ptr);
size_t nsize = itarget->size + length;
cout << "- nsize ----------- " << nsize << endl;
cout << "- data ------ " << (size_t) itarget->data << endl;
if(itarget->data != nullptr)
itarget->data = (unsigned char*)realloc(itarget->data, nsize);
else
itarget->data = (unsigned char*)malloc(nsize);
memcpy(itarget->data + itarget->size, data, length);
itarget->size += length;
}
int main()
{
const string Input_PNG = "pic/c.png";
const string Output_PNG = "output/output.png";
unsigned char* buffer = nullptr;
int length = read_png(Input_PNG, &buffer);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
png_infop info_ptr = png_create_info_struct(png_ptr);
ImageSource imgsource;
imgsource.data = buffer;
imgsource.size = length;
imgsource.offset = 0;
png_set_read_fn(png_ptr, &imgsource, pngReadCallback);
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_ALPHA, 0);
int w = png_get_image_width( png_ptr, info_ptr );
int h = png_get_image_height( png_ptr, info_ptr );
cout << "Image width (from PNG file): " << w << endl;
cout << "Image height (from PNG file): " << h << endl;
png_bytep* row_pointers = png_get_rows( png_ptr, info_ptr );
png_bytep raw_rgb = (png_bytep)malloc(w * h * 3);
int i = 0;
for(int y=0; y<h; ++y ) {
for(int x=0; x<w*3; ) {
raw_rgb[i++] = row_pointers[y][x++]; // red
raw_rgb[i++] = row_pointers[y][x++]; // green
raw_rgb[i++] = row_pointers[y][x++]; // blue
}
}
// Do Something
png_destroy_read_struct( &png_ptr, &info_ptr, 0);
// ---------------------------------
png_structp wpng_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop winfo_ptr = png_create_info_struct(wpng_ptr);
setjmp(png_jmpbuf(wpng_ptr));
png_set_IHDR(wpng_ptr, winfo_ptr, 720, 720, 8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_set_rows(wpng_ptr, winfo_ptr, &raw_rgb);
ImageTarget itarget;
itarget.data = nullptr;
itarget.size = 0;
png_set_write_fn(wpng_ptr, &itarget, pngWriteCallback, NULL);
png_write_png(wpng_ptr, winfo_ptr, PNG_TRANSFORM_IDENTITY, NULL);
cout << "Output file name: " << Output_PNG << endl;
write_png(Output_PNG, itarget.data, length);
return 0;
}
我使用的Makefile:here。
编译并运行我的代码后,我看到输出(我已验证我使用的c.png
是Irfanview的PNG文件)
Image width (from PNG file): 720
Image height (from PNG file): 720
- length ----------- 8
- nsize ----------- 8
- data ------ 0
- length ----------- 8
- nsize ----------- 16
- data ------ 22161152
- length ----------- 13
- nsize ----------- 29
- data ------ 22161152
- length ----------- 4
- nsize ----------- 33
- data ------ 22161152
[1] 6675 segmentation fault (core dumped) ./png_from_buffer
用core
检查gdb
文件,这是输出:
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:35
#1 0x00007f3c41b3caf0 in png_write_row () from /lib/x86_64-linux-gnu/libpng12.so.0
#2 0x00007f3c41b3cd78 in png_write_image () from /lib/x86_64-linux-gnu/libpng12.so.0
#3 0x00007f3c41b3d61b in png_write_png () from /lib/x86_64-linux-gnu/libpng12.so.0
#4 0x000000000040269c in main () at main.cpp:181
我尝试使用其他缓冲区,将其转换为vector<unsigned char*>
作为行。到目前为止没有运气。任何想法将不胜感激。
我的环境,如果需要的话:
答案 0 :(得分:0)
我想出了一个解决方案:使用libpng v1.6
可以很容易地解决此问题,如下所示:
// READING....
png_image image;
memset(&image, 0, (sizeof image));
image.version = PNG_IMAGE_VERSION;
if (png_image_begin_read_from_memory(&image, file_buffer, length) == 0)
{
return -1;
}
png_bytep buffer;
image.format = PNG_FORMAT_BGR;
size_t input_data_length = PNG_IMAGE_SIZE(image);
buffer = (png_bytep)malloc(input_data_length);
memset(buffer, 0, input_data_length);
if (png_image_finish_read(&image, NULL, buffer, 0, NULL) == 0)
{
return -1;
}
写到内存缓冲区也很容易:
// WRITING......
png_image wimage;
memset(&wimage, 0, (sizeof wimage));
wimage.version = PNG_IMAGE_VERSION;
wimage.format = PNG_FORMAT_BGR;
wimage.height = 720;
wimage.width = 720;
// Get memory size
bool wresult = png_image_write_to_memory(&wimage, nullptr, &wlength, 0, buffer, 0, nullptr);
if (!wresult)
{
cout << "Error: " << image.message << endl;
}
// Real write to memory
unsigned char* wbuffer = (unsigned char*)malloc(wlength);
wresult = png_image_write_to_memory(&wimage, wbuffer, &wlength, 0, buffer, 0, nullptr);
write_png(Output_PNG, wbuffer, wlength);