Использование libpng 1.2 для записи буфера изображения RGB в буфер PNG в памяти, вызывающий ошибку сегментации - PullRequest
0 голосов
/ 10 ноября 2018

Мой проект должен получать содержимое файла PNG через HTTP-запрос, что-то делать с изображением и отправлять сгенерированный PNG обратно в ответ HTTP. Весь код должен быть выполнен на C / C ++.

Я новичок в libpng. Поэтому я пытаюсь записать прототип, считав файл PNG в буфер unsigned char, вывести значения RGB (игнорировать альфа), не выполнять операции, создать новый буфер unsigned char с содержимым файла PNG, записать новый файл в На диск и валидацию я «генерирую» одно и то же изображение. Я сослался на этот вопрос в StackOverflow

Мой код (при удалении некоторой несвязанной функции полный исполняемый код здесь ):

#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;
}

Используемый мной make-файл: здесь .

После компиляции и запуска моего кода я вижу вывод (я подтвердил, что c.png я использовал файл PNG от Irfanview):

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*> в качестве строк. Пока не повезло. Любая идея будет оценена.

Моя среда, если имеет значение:

  • Ubuntu 16.04
  • libpng 1.2
    • libpng12-0 / xenial-updates, xenial-security, теперь 1.2.54-1ubuntu1.1 amd64 [установлено]
    • libpng12-dev / xenial-updates, xenial-security, теперь 1.2.54-1ubuntu1.1 amd64 [установлено, автоматически]
  • g ++ (Ubuntu 5.4.0-6ubuntu1 ~ 16.04.10) 5.4.0 20160609

1 Ответ

0 голосов
/ 11 ноября 2018

Я нашел решение: используя 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);
...