Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> --- Makefile.am | 6 +- configure.ac | 3 + vdagent/image.cpp | 8 +- vdagent/imagepng.cpp | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ vdagent/imagepng.h | 25 ++++++ 5 files changed, 277 insertions(+), 9 deletions(-) create mode 100644 vdagent/imagepng.cpp create mode 100644 vdagent/imagepng.h diff --git a/Makefile.am b/Makefile.am index 5626d0c..0f67cff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,8 +23,8 @@ LIBS = -lversion bin_PROGRAMS = vdagent vdservice -vdagent_LDADD = -lwtsapi32 -lgdi32 vdagent_rc.$(OBJEXT) -vdagent_CXXFLAGS = $(AM_CXXFLAGS) +vdagent_LDADD = $(LIBPNG_LIBS) $(ZLIB_LIBS) -lwtsapi32 -lgdi32 vdagent_rc.$(OBJEXT) +vdagent_CXXFLAGS = $(AM_CXXFLAGS) $(LIBPNG_CFLAGS) vdagent_LDFLAGS = $(AM_LDFLAGS) -Wl,--subsystem,windows vdagent_SOURCES = \ common/vdcommon.cpp \ @@ -44,6 +44,8 @@ vdagent_SOURCES = \ vdagent/as_user.h \ vdagent/image.cpp \ vdagent/image.h \ + vdagent/imagepng.cpp \ + vdagent/imagepng.h \ $(NULL) vdagent_rc.$(OBJEXT): vdagent/vdagent.rc diff --git a/configure.ac b/configure.ac index 49384f3..1645393 100644 --- a/configure.ac +++ b/configure.ac @@ -94,6 +94,9 @@ dnl --------------------------------------------------------------------------- dnl - Check library dependencies dnl --------------------------------------------------------------------------- +PKG_CHECK_MODULES(LIBPNG, [libpng]) +PKG_CHECK_MODULES(ZLIB, [zlib]) + dnl --------------------------------------------------------------------------- dnl - Makefiles, etc. dnl --------------------------------------------------------------------------- diff --git a/vdagent/image.cpp b/vdagent/image.cpp index 4f1dbb1..7050e40 100644 --- a/vdagent/image.cpp +++ b/vdagent/image.cpp @@ -21,9 +21,9 @@ #include "vdcommon.h" #include "image.h" +#include "imagepng.h" ImageCoder *create_bitmap_coder(); -ImageCoder *create_png_coder(); static ImageCoder *get_coder(uint32_t vdagent_type) { @@ -182,9 +182,3 @@ ImageCoder *create_bitmap_coder() { return new BitmapCoder(); } - -// TODO -ImageCoder *create_png_coder() -{ - return NULL; -} diff --git a/vdagent/imagepng.cpp b/vdagent/imagepng.cpp new file mode 100644 index 0000000..c5d3791 --- /dev/null +++ b/vdagent/imagepng.cpp @@ -0,0 +1,244 @@ +/* + Copyright (C) 2016 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "vdcommon.h" + +#include <png.h> +#include <algorithm> + +#include "imagepng.h" + +class PngCoder: public ImageCoder +{ +public: + PngCoder() {}; + size_t get_dib_size(const uint8_t *data, size_t size); + void get_dib_data(uint8_t *dib, const uint8_t *data, size_t size); + uint8_t *from_bitmap(const BITMAPINFO& info, const void *bits, long &size); +private: + size_t convert_to_dib(uint8_t *out_buf, const uint8_t *data, size_t size); +}; + +struct BufferIo { + uint8_t *buf; + uint32_t pos, size; +}; + +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) + png_error(png, "read past end"); + memcpy(out, io.buf+io.pos, size); + io.pos += size; +} + +size_t PngCoder::get_dib_size(const uint8_t *data, size_t size) +{ + return convert_to_dib(NULL, data, size); +} + +typedef void line_fixup_t(uint8_t *line, unsigned width); + +static void line_fixup_none(uint8_t *line, unsigned width) +{ +} + +static void line_fixup_2to4(uint8_t *line, unsigned width) +{ + width /= 4u; + while (width--) { + uint8_t from = line[width]; + line[width*2+1] = ((from & 0x03) << 0) | ((from & 0x0c) << 2); + line[width*2+0] = ((from & 0x30) >> 4) | ((from & 0xc0) >> 2); + } +} + +static void line_fixup_rgb2bgr(uint8_t *line, unsigned width) +{ + for (; width; --width) { + std::swap(line[0], line[2]); + line += 3; + } +} + +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; + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + return 0; + + png_infop info = png_create_info_struct(png); + if (!info) { + png_destroy_read_struct(&png, &info, NULL); + return 0; + } + + if (setjmp(png_jmpbuf(png))) { + png_destroy_read_struct(&png, &info, NULL); + return 0; + } + + png_set_read_fn(png, &io, read_from_bufio); + + png_read_info(png, info); + + // not so much precision is supported + unsigned bits = png_get_bit_depth(png, info); + if (bits == 16) + png_set_strip_16(png); + + unsigned out_bits; + bool is_gray = false; + line_fixup_t *line_fixup = line_fixup_none; + switch (png_get_color_type(png, info)) { + case PNG_COLOR_TYPE_GRAY: + is_gray = true; + out_bits = bits == 16 ? 8 : bits; + if (bits == 2) { +#if 0 + png_set_expand_gray_1_2_4_to_8(png); + out_bits = 8; +#else + line_fixup = line_fixup_2to4; + out_bits = 4; +#endif + } + break; + case PNG_COLOR_TYPE_PALETTE: + // should return 1, 2, 4 and 8, BMP does not support 2 + out_bits = bits; + if (bits == 2) { + line_fixup = line_fixup_2to4; + out_bits = 4; + } + break; + case PNG_COLOR_TYPE_RGB: + line_fixup = line_fixup_rgb2bgr; + out_bits = 24; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + line_fixup = line_fixup_rgb2bgr; + out_bits = 24; + png_set_strip_alpha(png); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + is_gray = true; + // gray with alpha should always be 8 bit but make it sure + // in case format change + png_set_expand_gray_1_2_4_to_8(png); + out_bits = 8; + png_set_strip_alpha(png); + break; + default: + longjmp(png_jmpbuf(png), 1); + break; + } + + const unsigned width = png_get_image_width(png, info); + const unsigned height = png_get_image_height(png, info); + const size_t stride = ((width * out_bits + 31u) & ~31u) / 8u; + const size_t image_size = stride * height; + const size_t palette_size = out_bits > 8 ? 0 : sizeof(RGBQUAD) * (1 << out_bits); + const size_t dib_size = sizeof(BITMAPINFOHEADER) + palette_size + image_size; + if (!out_buf) { + png_destroy_read_struct(&png, &info, NULL); + return dib_size; + } + + // TODO tests + // bits, 1, 2, 4, 8, 16 + // all color types + // alpha/not alpha + // indexed with not all colors + + // fill header + BITMAPINFOHEADER& head(*(BITMAPINFOHEADER *)out_buf); + memset(&head, 0, sizeof(head)); + head.biSize = sizeof(head); + head.biWidth = width; + head.biHeight = height; + head.biPlanes = 1; + head.biBitCount = out_bits; + head.biCompression = BI_RGB; + head.biSizeImage = image_size; + + // copy palette + RGBQUAD *rgb = (RGBQUAD *)(out_buf + sizeof(BITMAPINFOHEADER)); + if (is_gray) { + const unsigned colors = 1 << out_bits; + const unsigned mult = 255 / (colors - 1); + for (unsigned color = 0; color < colors; ++color) { + rgb->rgbBlue = rgb->rgbGreen = rgb->rgbRed = color * mult; + rgb->rgbReserved = 0; + ++rgb; + } + head.biClrUsed = colors; + } else if (out_bits <= 8) { + png_colorp palette = NULL; + int num_palette; + if (!png_get_PLTE(png, info, &palette, &num_palette)) { + png_error(png, "error getting palette"); + } + const unsigned colors = 1 << out_bits; + for (unsigned color = 0; color < colors; ++color) { + if (color < num_palette) { + rgb->rgbBlue = palette->blue; + rgb->rgbGreen = palette->green; + rgb->rgbRed = palette->red; + } else { + rgb->rgbBlue = rgb->rgbGreen = rgb->rgbRed = 0; + } + rgb->rgbReserved = 0; + ++rgb; + ++palette; + } + head.biClrUsed = colors; + } + + // now do the actual conversion! + uint8_t *dst = out_buf + sizeof(BITMAPINFOHEADER) + palette_size + image_size; + for (unsigned row = 0; row < height; ++row) { + ((uint32_t*)dst)[-1] = 0; // padding + dst -= stride; + png_read_row(png, dst, NULL); + line_fixup(dst, width); + } + + png_destroy_read_struct(&png, &info, NULL); + return dib_size; +} + +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) +{ + return NULL; +} + +ImageCoder *create_png_coder() +{ + return new PngCoder(); +} diff --git a/vdagent/imagepng.h b/vdagent/imagepng.h new file mode 100644 index 0000000..620c47d --- /dev/null +++ b/vdagent/imagepng.h @@ -0,0 +1,25 @@ +/* + Copyright (C) 2016 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef VDAGENT_IMAGEPNG_H_ +#define VDAGENT_IMAGEPNG_H_ + +#include "image.h" + +ImageCoder *create_png_coder(); + +#endif -- 2.7.4 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel