Подтвердить что ты не робот

Как закодировать PNG для буфера с помощью libpng?

В настоящее время я использую следующее для записи PNG в файл:

#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/* Pixels in this bitmap structure are stored as BGR. */
typedef struct _RGBPixel {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
} RGBPixel;

/* Structure for containing decompressed bitmaps. */
typedef struct _RGBBitmap {
    RGBPixel *pixels;
    size_t width;
    size_t height;
    size_t bytewidth;
    uint8_t bytes_per_pixel;
} RGBBitmap;

/* Returns pixel of bitmap at given point. */
#define RGBPixelAtPoint(image, x, y) \
    *(((image)->pixels) + (((image)->bytewidth * (y)) \
                        + ((x) * (image)->bytes_per_pixel)))

/* Attempts to save PNG to file; returns 0 on success, non-zero on error. */
int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
    FILE *fp = fopen(path, "wb");
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    size_t x, y;
    png_uint_32 bytes_per_row;
    png_byte **row_pointers = NULL;

    if (fp == NULL) return -1;

    /* Initialize the write struct. */
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {
        fclose(fp);
        return -1;
    }

    /* Initialize the info struct. */
    info_ptr = png_create_info_struct(png_ptr);
    if (info_ptr == NULL) {
        png_destroy_write_struct(&png_ptr, NULL);
        fclose(fp);
        return -1;
    }

    /* Set up error handling. */
    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fclose(fp);
        return -1;
    }

    /* Set image attributes. */
    png_set_IHDR(png_ptr,
                 info_ptr,
                 bitmap->width,
                 bitmap->height,
                 8,
                 PNG_COLOR_TYPE_RGB,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);

    /* Initialize rows of PNG. */
    bytes_per_row = bitmap->width * bitmap->bytes_per_pixel;
    row_pointers = png_malloc(png_ptr, bitmap->height * sizeof(png_byte *));
    for (y = 0; y < bitmap->height; ++y) {
        uint8_t *row = png_malloc(png_ptr, sizeof(uint8_t) * bitmap->bytes_per_pixel);
        row_pointers[y] = (png_byte *)row;
        for (x = 0; x < bitmap->width; ++x) {
            RGBPixel color = RGBPixelAtPoint(bitmap, x, y);
            *row++ = color.red;
            *row++ = color.green;
            *row++ = color.blue;
        }
    }

    /* Actually write the image data. */
    png_init_io(png_ptr, fp);
    png_set_rows(png_ptr, info_ptr, row_pointers);
    png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

    /* Cleanup. */
    for (y = 0; y < bitmap->height; y++) {
        png_free(png_ptr, row_pointers[y]);
    }
    png_free(png_ptr, row_pointers);

    /* Finish writing. */
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return 0;
}

Как написать аналогичную функцию (в C) для кодирования PNG в буфере памяти?

Прототип выглядел бы примерно так:

uint8_t *encode_png_to_buffer(RGBBitmap *source);

И кажется, что мне, вероятно, нужно будет использовать png_set_write_fn().

Но кроме этого я не уверен, как подойти к этому. Есть ли примеры этого? Конечно, я не первый, кто нуждается в этой функции.

4b9b3361

Ответ 1

Да, используя png_set_write_fn что-то вроде этого - untested:

Обновлено с изменениями из комментариев

/* structure to store PNG image bytes */
struct mem_encode
{
  char *buffer;
  size_t size;
}


void
my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
  /* with libpng15 next line causes pointer deference error; use libpng12 */
  struct mem_encode* p=(struct mem_encode*)png_get_io_ptr(png_ptr); /* was png_ptr->io_ptr */
  size_t nsize = p->size + length;

  /* allocate or grow buffer */
  if(p->buffer)
    p->buffer = realloc(p->buffer, nsize);
  else
    p->buffer = malloc(nsize);

  if(!p->buffer)
    png_error(png_ptr, "Write Error");

  /* copy new bytes to end of buffer */
  memcpy(p->buffer + p->size, data, length);
  p->size += length;
}

/* This is optional but included to show how png_set_write_fn() is called */
void
my_png_flush(png_structp png_ptr)
{
}



int save_png_to_file(RGBBitmap *bitmap, const char *path)
{
...
/* static */
struct mem_encode state;

/* initialise - put this before png_write_png() call */
state.buffer = NULL;
state.size = 0;

/* if my_png_flush() is not needed, change the arg to NULL */
png_set_write_fn(png_ptr, &state, my_png_write_data, my_png_flush);

... call png_write_png() ...

/* now state.buffer contains the PNG image of size s.size bytes */

/* cleanup */
if(state.buffer)
  free(state.buffer);

Ответ 2

#include <png.h>
#include <vector>
#include <iostream>
#include <stdlib.h>

//encode and write PNG to memory (std::vector) with libpng on C++

typedef unsigned char ui8;
#define ASSERT_EX(cond, error_message) do { if (!(cond)) { std::cerr << error_message; exit(1);} } while(0)

static void PngWriteCallback(png_structp  png_ptr, png_bytep data, png_size_t length) {
    std::vector<ui8> *p = (std::vector<ui8>*)png_get_io_ptr(png_ptr);
    p->insert(p->end(), data, data + length);
}

struct TPngDestructor {
    png_struct *p;
    TPngDestructor(png_struct *p) : p(p)  {}
    ~TPngDestructor() { if (p) {  png_destroy_write_struct(&p, NULL); } }
};

void WritePngToMemory(size_t w, size_t h, const ui8 *dataRGBA, std::vector<ui8> *out) {
    out->clear();
    png_structp p = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    ASSERT_EX(p, "png_create_write_struct() failed");
    TPngDestructor destroyPng(p);
    png_infop info_ptr = png_create_info_struct(p);
    ASSERT_EX(info_ptr, "png_create_info_struct() failed");
    ASSERT_EX(0 == setjmp(png_jmpbuf(p)), "setjmp(png_jmpbuf(p) failed");
    png_set_IHDR(p, info_ptr, w, h, 8,
            PNG_COLOR_TYPE_RGBA,
            PNG_INTERLACE_NONE,
            PNG_COMPRESSION_TYPE_DEFAULT,
            PNG_FILTER_TYPE_DEFAULT);
    //png_set_compression_level(p, 1);
    std::vector<ui8*> rows(h);
    for (size_t y = 0; y < h; ++y)
        rows[y] = (ui8*)dataRGBA + y * w * 4;
    png_set_rows(p, info_ptr, &rows[0]);
    png_set_write_fn(p, out, PngWriteCallback, NULL);
    png_write_png(p, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
}

Ответ 3

Я нашел более раннюю версию этого кода и взломал его для создания и сохранения 16-битного PNG файла в градациях серого. Он запускается и генерирует этот PNG: Пример оттенков серого от около 0 до около 64k в формате 400x400 png

Вот полный источник и команда GCC для создания исполняемого файла. Он работает в моей системе OpenSuse 42/64 с GCC → gcc версии 4.8.5 (SUSE Linux) и libpng 1.6.23, но может растопить ваш конденсатор потока, если кто-нибудь будет настолько неосмотрительным или наглым, чтобы попытаться его запустить.;)

#include <png.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "string.h"

/* 8 QBit RGB/24 to 16 QBit Grayscale hack
 * based on code found at
 * http://www.lemoda.net/c/write-png/  and png.h libpng version 1.6.23
 */
/*
 gcc -L/usr/local/static -I/usr/local/static/include -lpng16 /home/photog/bin/png.test.gray16.c  -lm -o  /home/photog/bin/png.tg16

 */
// =============================================================================
typedef struct {
    uint8_t red;  uint8_t green;  uint8_t blue;  // A colored pixel
} pixel_t;

typedef struct {
    uint16_t gray;  // A GRAY pixel
} pixel_gray_16_t;

typedef struct  {  // A picture
    pixel_gray_16_t *pixels;
    size_t width;
    size_t height;
} bitmap_t;

// =============================================================================
// Write "bitmap" to a PNG file specified by "path"; returns 0 on
//   success, non-zero on error
static int save_png_to_file (bitmap_t *bitmap, const char *path)  {
    FILE * fp;
    png_structp png_ptr = NULL;
    png_infop  info_ptr = NULL;
    size_t x, y;
    int pidx=0;  // Pixel_Index
    png_byte **row_pointers = NULL;  // KLUDGE!!

    /* "status" contains the return value of this function. At first
       it is set to a value which means 'failure'. When the routine
       has finished its work, it is set to a value which means
       'success'. */
    int status = -1;
    /* The following number is set by trial and error only. I cannot
       see where it it is documented in the libpng manual  */
    int pixel_size = 2;  // 3 for RGB/24;
    int depth = 16;      // 8 for RGB/24;

    fp = fopen (path, "wb");  if (! fp) {  goto fopen_failed;  }

    png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr == NULL) {  goto png_create_write_struct_failed;  }

    info_ptr = png_create_info_struct (png_ptr);
    if (info_ptr == NULL) {  goto png_create_info_struct_failed;  }

    /* Set up error handling. */
    if (setjmp (png_jmpbuf (png_ptr))) {  goto png_failure;  }

    // Set image attributes;  # de fine PNG_COLOR_TYPE_GRAY 0
    png_set_IHDR (png_ptr, info_ptr, bitmap->width, bitmap->height, depth,
        PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
        PNG_FILTER_TYPE_DEFAULT);

    /* Initialize rows of PNG. */
    row_pointers=png_malloc(png_ptr, bitmap->height * sizeof(png_uint_16 *));

    // Copy system Callocated user data to PNG owned space  
    for (y=0, pidx=0; y < bitmap->height; ++y) {
        png_byte *row = 
            png_malloc(png_ptr, sizeof(uint8_t) * bitmap->width * pixel_size);
        row_pointers[y] = row;
        memcpy((void *)row, bitmap->pixels+pidx, bitmap->width * 2);
        pidx += bitmap->width;  // Move to next row
    }
    /* Write the image data to "fp". */
    png_init_io (png_ptr, fp);
    png_set_rows (png_ptr, info_ptr, row_pointers);
    png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN,NULL); 
    //png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);

    // The routine has successfully written the file, so we set "status" to a 
    // value which indicates success
    status = 0;
    for(y=0; y < bitmap->height; y++)  png_free (png_ptr, row_pointers[y]);  
    png_free (png_ptr, row_pointers);

 png_failure:
 png_create_info_struct_failed:
    png_destroy_write_struct (&png_ptr, &info_ptr);
 png_create_write_struct_failed:
    fclose (fp);
 fopen_failed:
    return status;
}

// =============================================================================
// =============================================================================


int main ()  {
    bitmap_t fruit;
    const char ofn[]={ "fruit.g16.png" };  // Output FileName
    int x, y, pidx=0;
    uint16_t gray_u16;
    float graysf;  // Gray Scale factor. 0->0, last_pix -> QMax(16)

    fruit.width  = 400;  // Size the image
    fruit.height = 400;
    graysf=(65535.0f/fruit.width)/fruit.height;  // Last pix => 65535
    fruit.pixels=calloc(sizeof(pixel_gray_16_t), fruit.width * fruit.height);

    // Create linear black -> white gradient
    for(y=0; y < fruit.height; y++)  {
        for(x=0; x < fruit.width; x++)  {
            gray_u16=(uint16_t)lrintf((y*fruit.width+x)*graysf);
            fruit.pixels[pidx++].gray = gray_u16;
        }
    }

    // Write the image to a file
    save_png_to_file (&fruit, ofn);
    printf("Wrote gray/16 PNG file %s\n", ofn);
    return 0;
}  this line may  not compile
// =============================================================================

groch, я нашел выше, урезанный код полезен в получении 16 QBit PNG-кода. Этот непроверенный и потенциально опасный взлом был добавлен для справочных целей только с отказом от ответственности за присущие ему риски его запуска. Была добавлена ​​строка ошибки компилятора, поэтому кто-то должен был ее отредактировать для компиляции, сделав ее собственной, настроенной версией. Сообщение могло добавить iota полезной информации к уже хорошей теме. Чисто философские недоумения добавляют шум, но нет света... Возможно, вы могли бы добавить функцию в код?

ПРИМЕЧАНИЕ. Первый запуск получил странный результат для Intel Skylake CPU:

Вы были Endianed!

Неверный Endian! PNG сохраняются в нормальном, сетевом порядке байтов. Это то, что байт-скремблированный дискурс действительно выглядит!

Пришлось изменить эту строку:

png_write_png(png_ptr,info_ptr, PNG_TRANSFORM_SWAP_ENDIAN,NULL);