Создание 16-битного изображения с помощью libpng

Я пытаюсь написать 16-битное RGB-изображение, используя libpng, где каждый цвет точки происходит из входного "массива" экземпляра. Следующий код работает, но выдает 8-битный.

template<typename T> void savePNG(Array2<Vec3<T> > *arrayImg, const std::string filename){

    /* create file */
    FILE *fp = fopen(filename.c_str(), "wb");
    if (!fp){
        std::runtime_error("[write_png_file] File could not be opened for writing");
        return;
    }


    /* initialize stuff */
    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if (!png_ptr){
        std::runtime_error("[write_png_file] png_create_write_struct failed");
        return;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr){
        std::runtime_error("[write_png_file] png_create_info_struct failed");
        return;
    }

    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during init_io");
        return;
    }

    png_init_io(png_ptr, fp);


    /* write header */
    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during writing header");
        return;
    }

    png_set_IHDR(png_ptr, info_ptr,
            (png_uint_32) arrayImg->dimension[0], (png_uint_32) arrayImg->dimension[1],
            (png_byte) 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
            PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_write_info(png_ptr, info_ptr);


    /* write bytes */
    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during writing bytes");
        return;
    }

    /* Initialize rows of PNG. */
    png_bytepp row_pointers = (png_bytepp) malloc (sizeof(png_bytep)*arrayImg->dimension[1]);
    for (size_t y = 0; y < arrayImg->dimension[1]; ++y) {
        row_pointers[y] = (png_bytep) malloc (png_get_rowbytes(png_ptr,info_ptr));
        for (size_t x = 0; x < arrayImg->dimension[0]; ++x) {
            for (size_t k = 0; k < 3; k++) {
                row_pointers[y][x*3+k] = (uint8_t) (clamp((*arrayImg)[y][x][k],T(0.0),T(1.0))*T(255));
            }
        }
    }

    /* Writes PNG. */
    png_write_image(png_ptr, (png_bytepp) row_pointers);


    /* end write */
    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during end of write");
        return;
    }

    png_write_end(png_ptr, NULL);

    /* cleanup heap allocation */
    for (size_t y=0; y<arrayImg->dimension[1]; y++){
        free(row_pointers[y]);
    }
    free(row_pointers);

    fclose(fp);
}

Следующий код должен создать 16-битную версию. Он создает изображение с правильными размерами, но все данные смещены влево:

template<typename T> void savePNG(Array2<Vec3<T> > *arrayImg, const std::string filename){

    /* create file */
    FILE *fp = fopen(filename.c_str(), "wb");
    if (!fp){
        std::runtime_error("[write_png_file] File could not be opened for writing");
        return;
    }


    /* initialize stuff */
    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if (!png_ptr){
        std::runtime_error("[write_png_file] png_create_write_struct failed");
        return;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr){
        std::runtime_error("[write_png_file] png_create_info_struct failed");
        return;
    }

    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during init_io");
        return;
    }

    png_init_io(png_ptr, fp);


    /* write header */
    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during writing header");
        return;
    }

    png_set_IHDR(png_ptr, info_ptr,
            (png_uint_32) arrayImg->dimension[0], (png_uint_32) arrayImg->dimension[1],
            (png_byte) 16, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
            PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_write_info(png_ptr, info_ptr);


    /* write bytes */
    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during writing bytes");
        return;
    }

    /* Initialize rows of PNG. */
    png_bytepp row_pointers = (png_bytepp) malloc (sizeof(png_bytep)*arrayImg->dimension[1]);
    for (size_t y = 0; y < arrayImg->dimension[1]; ++y) {
        row_pointers[y] = (png_bytep) malloc (png_get_rowbytes(png_ptr,info_ptr));
        for (size_t x = 0; x < arrayImg->dimension[0]; ++x) {
            for (size_t k = 0; k < 3; k++) {
                row_pointers[y][x*3+k] = (uint16_t) (clamp((*arrayImg)[y][x][k],T(0.0),T(1.0))*T(65535));
            }
        }
    }

    /* Writes PNG. */
    png_write_image(png_ptr, (png_bytepp) row_pointers);


    /* end write */
    if (setjmp(png_jmpbuf(png_ptr))){
        std::runtime_error("[write_png_file] Error during end of write");
        return;
    }

    png_write_end(png_ptr, NULL);

    /* cleanup heap allocation */
    for (size_t y=0; y<arrayImg->dimension[1]; y++){
        free(row_pointers[y]);
    }
    free(row_pointers);

    fclose(fp);
}

Насколько я понимаю, проблема заключается в том, что в настоящее время данные усекаются из-за типов png_byte, png_bytep и / или png_bytepp. Как я могу записать 16-битные данные в два 8-битных?

2 ответа

Решение

Если кто-нибудь когда-либо сталкивается с такой проблемой. Вот несколько вариантов:

  • Попробуйте прочитать API...
  • Используйте функцию void png_save_uint_16 (png_bytep buf, unsigned int i);

Ваша "k" считает байты, и у вас есть 6 байтов на пиксель, а не 3.

Петля

for (size_t k = 0; k < 3; k++) {
    row_pointers[y][x*3+k] = (uint16_t)clamp(...);
}

должно быть что-то вроде

for (size_t k = 0; k < 6; k+=2) {
    png_save_uint_16(row_pointers[y][x*6+k],(unsigned int)clamp(...);
}

Это будет помещать два байта за раз (результат вашей операции "зажим ()") в буфер в row_pointers[y].

Другие вопросы по тегам