summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrediano Ziglio <fziglio@redhat.com>2016-11-08 13:26:28 +0000
committerFrediano Ziglio <fziglio@redhat.com>2017-07-26 10:51:06 +0100
commit3dedcaa44e5a5b6450c8642e4721270a5ed08e54 (patch)
treea7445123dd74144c9acabe116edcd12ca0368fec
parent95f603bdb0f66c696acf2bcb26ca838265725c45 (diff)
Write code to decode PNG format
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> Acked-by: Christophe Fergeau <cfergeau@redhat.com>
-rw-r--r--Makefile.am6
-rw-r--r--configure.ac3
-rw-r--r--vdagent/image.cpp8
-rw-r--r--vdagent/imagepng.cpp245
-rw-r--r--vdagent/imagepng.h25
5 files changed, 278 insertions, 9 deletions
diff --git a/Makefile.am b/Makefile.am
index b35dd57..175d8f7 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 4eac4b4..bb33075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,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 faec18f..82cfb0e 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)
{
@@ -172,9 +172,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..a89b7f7
--- /dev/null
+++ b/vdagent/imagepng.cpp
@@ -0,0 +1,245 @@
+/*
+ Copyright (C) 2017 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 ReadBufferIo {
+ const uint8_t *buf;
+ uint32_t pos, size;
+ ReadBufferIo(const uint8_t *_buf, uint32_t _size):
+ buf(_buf), pos(0), size(_size)
+ {}
+};
+
+static void read_from_bufio(png_structp png, png_bytep out, png_size_t size)
+{
+ ReadBufferIo& io(*(ReadBufferIo*)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 int width);
+
+static void line_fixup_none(uint8_t *line, unsigned int width)
+{
+}
+
+static void line_fixup_2bpp_to_4bpp(uint8_t *line, unsigned int width)
+{
+ width = (width + 3) / 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);
+ }
+}
+
+size_t PngCoder::convert_to_dib(uint8_t *out_buf, const uint8_t *data, size_t size)
+{
+ ReadBufferIo io(data, 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 int bits = png_get_bit_depth(png, info);
+ if (bits == 16)
+ png_set_strip_16(png);
+
+ unsigned int 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;
+ if (bits == 16) {
+ out_bits = 8;
+ } else if (bits == 2) {
+ line_fixup = line_fixup_2bpp_to_4bpp;
+ out_bits = 4;
+ } else {
+ out_bits = bits;
+ }
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ // should return 1, 4 and 8, BMP does not support 2
+ out_bits = bits;
+ if (bits == 2) {
+ line_fixup = line_fixup_2bpp_to_4bpp;
+ out_bits = 4;
+ }
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ png_set_bgr(png);
+ out_bits = 24;
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ png_set_bgr(png);
+ 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:
+ png_error(png, "PNG color type not supported");
+ break;
+ }
+
+ const unsigned int width = png_get_image_width(png, info);
+ const unsigned int height = png_get_image_height(png, info);
+ const size_t stride = compute_dib_stride(width, out_bits);
+ const size_t image_size = stride * height;
+ int palette_colors;
+ // no palette
+ if (out_bits > 8) {
+ palette_colors = 0;
+ // 2 bit PNG converted to 4 bit BMP
+ } else if (bits == 2) {
+ palette_colors = 4;
+ } else {
+ palette_colors = 1 << out_bits;
+ }
+ const size_t palette_size = palette_colors * sizeof(RGBQUAD);
+ const size_t dib_size = sizeof(BITMAPINFOHEADER) + palette_size + image_size;
+
+ // just called to get the size, return the information
+ 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 int mult = 255 / (palette_colors - 1);
+ for (int color = 0; color < palette_colors; ++color) {
+ rgb->rgbBlue = rgb->rgbGreen = rgb->rgbRed = color * mult;
+ rgb->rgbReserved = 0;
+ ++rgb;
+ }
+ head.biClrUsed = palette_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");
+ }
+ for (int color = 0; color < palette_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 = palette_colors;
+ }
+
+ // now do the actual conversion!
+ uint8_t *dst = out_buf + sizeof(BITMAPINFOHEADER) + palette_size + image_size;
+ for (unsigned int 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)
+{
+ // TODO not implemented
+ 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..1d1f654
--- /dev/null
+++ b/vdagent/imagepng.h
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2017 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