[Spice-devel] [vdagent-win PATCH v7 4/5] Support encoding PNG images
Frediano Ziglio
fziglio at redhat.com
Tue Jul 18 15:27:59 UTC 2017
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
vdagent/imagepng.cpp | 134 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 127 insertions(+), 7 deletions(-)
diff --git a/vdagent/imagepng.cpp b/vdagent/imagepng.cpp
index ede22f4..2d0f13e 100644
--- a/vdagent/imagepng.cpp
+++ b/vdagent/imagepng.cpp
@@ -19,6 +19,7 @@
#include <png.h>
#include <algorithm>
+#include <vector>
#include "imagepng.h"
@@ -36,17 +37,47 @@ private:
struct BufferIo {
uint8_t *buf;
uint32_t pos, size;
+ bool allocated;
+ BufferIo(uint8_t *_buf, uint32_t _size):
+ buf(_buf), pos(0), size(_size),
+ allocated(false)
+ {}
+ ~BufferIo() { if (allocated) free(buf); }
+ uint8_t *release() { allocated = false; return buf; }
};
static void read_from_bufio(png_structp png, png_bytep out, png_size_t size)
{
BufferIo& io(*(BufferIo*)png_get_io_ptr(png));
- if (io.pos + size >= io.size)
+ if (io.pos + size > io.size)
png_error(png, "read past end");
memcpy(out, io.buf+io.pos, size);
io.pos += size;
}
+static void write_to_bufio(png_structp png, png_bytep in, png_size_t size)
+{
+ BufferIo& io(*(BufferIo*)png_get_io_ptr(png));
+ if (io.pos + size > io.size) {
+ uint32_t new_size = io.size ? io.size * 2 : 4096;
+ while (io.pos + size >= new_size) {
+ new_size *= 2;
+ }
+ uint8_t *p = (uint8_t*) realloc(io.buf, new_size);
+ if (!p)
+ png_error(png, "out of memory");
+ io.allocated = true;
+ io.buf = p;
+ io.size = new_size;
+ }
+ memcpy(io.buf+io.pos, in, size);
+ io.pos += size;
+}
+
+static void flush_bufio(png_structp png)
+{
+}
+
size_t PngCoder::get_dib_size(const uint8_t *data, size_t size)
{
return convert_to_dib(NULL, data, size);
@@ -76,12 +107,21 @@ static void line_fixup_rgb2bgr(uint8_t *line, unsigned width)
}
}
+static void line_fixup_bgr2rgb(uint8_t *dst, const uint8_t *src, unsigned width,
+ unsigned src_pixel_len)
+{
+ for (; width; --width) {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst += 3;
+ src += src_pixel_len;
+ }
+}
+
size_t PngCoder::convert_to_dib(uint8_t *out_buf, const uint8_t *data, size_t size)
{
- BufferIo io;
- io.buf = (uint8_t *)data;
- io.pos = 0;
- io.size = size;
+ BufferIo io((uint8_t *)data, size);
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png)
@@ -230,9 +270,89 @@ void PngCoder::get_dib_data(uint8_t *dib, const uint8_t *data, size_t size)
convert_to_dib(dib, data, size);
}
-uint8_t *PngCoder::from_bitmap(const BITMAPINFO& info, const void *bits, long &size)
+uint8_t *PngCoder::from_bitmap(const BITMAPINFO& bmp_info, const void *bits, long &size)
{
- return NULL;
+ // this vector is here to avoid problems as libpng use setjmp/longjmp
+ std::vector<png_color> palette;
+ BufferIo io(NULL, 0);
+
+ png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png)
+ return 0;
+
+ png_infop info = png_create_info_struct(png);
+ if (!info) {
+ png_destroy_write_struct(&png, &info);
+ return 0;
+ }
+
+ if (setjmp(png_jmpbuf(png))) {
+ png_destroy_write_struct(&png, &info);
+ return 0;
+ }
+
+ png_set_write_fn(png, &io, write_to_bufio, flush_bufio);
+
+ const BITMAPINFOHEADER& head(bmp_info.bmiHeader);
+ int color_type;
+ int out_bits = head.biBitCount;
+ switch (out_bits) {
+ case 1:
+ case 4:
+ case 8:
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ break;
+ case 24:
+ case 32:
+ color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ default:
+ longjmp(png_jmpbuf(png), 1);
+ break;
+ }
+ // TODO detect gray
+ png_set_IHDR(png, info, head.biWidth, head.biHeight,
+ out_bits > 8 ? 8 : out_bits, color_type,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ // palette
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ palette.resize(head.biClrUsed);
+ const RGBQUAD *rgb = bmp_info.bmiColors;
+ for (unsigned color = 0; color < head.biClrUsed; ++color) {
+ palette[color].red = rgb->rgbRed;
+ palette[color].green = rgb->rgbGreen;
+ palette[color].blue = rgb->rgbBlue;
+ ++rgb;
+ }
+ png_set_PLTE(png, info, &palette[0], palette.size());
+ }
+
+ png_write_info(png, info);
+
+ const unsigned width = head.biWidth;
+ const unsigned height = head.biHeight;
+ const size_t stride = compute_dib_stride(width, out_bits);
+ const size_t image_size = stride * height;
+
+ // now do the actual conversion!
+ const uint8_t *src = (const uint8_t*)bits + image_size;
+ for (unsigned row = 0; row < height; ++row) {
+ src -= stride;
+ if (out_bits >= 24) {
+ uint8_t line[stride];
+ line_fixup_bgr2rgb(line, src, width, out_bits / 8u);
+ png_write_row(png, line);
+ } else {
+ png_write_row(png, src);
+ }
+ }
+ png_write_end(png, NULL);
+
+ png_destroy_write_struct(&png, &info);
+ size = io.pos;
+ return io.release();
}
ImageCoder *create_png_coder()
--
2.13.3
More information about the Spice-devel
mailing list