Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> --- vdagent/imagepng.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 6 deletions(-) diff --git a/vdagent/imagepng.cpp b/vdagent/imagepng.cpp index c5d3791..a4a27db 100644 --- a/vdagent/imagepng.cpp +++ b/vdagent/imagepng.cpp @@ -19,6 +19,7 @@ #include <png.h> #include <algorithm> +#include <vector> #include "imagepng.h" @@ -36,6 +37,13 @@ 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) @@ -47,6 +55,26 @@ static void read_from_bufio(png_structp png, png_bytep out, png_size_t 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) { + io.allocated = true; + uint32_t new_size = io.size ? io.size * 2 : 4096; + uint8_t *p = (uint8_t*) realloc(io.buf, new_size); + if (!p) + png_error(png, "out of memory"); + 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 +104,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) @@ -233,9 +270,93 @@ 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: + color_type = PNG_COLOR_TYPE_PALETTE; + break; + case 4: + color_type = PNG_COLOR_TYPE_PALETTE; + break; + 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 = ((width * out_bits + 31u) & ~31u) / 8u; + 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.7.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel