[Spice-devel] [vdagent-win PATCH v5 2/5] Initial rewrite of image conversion code

Frediano Ziglio fziglio at redhat.com
Mon Jul 17 09:37:08 UTC 2017


Remove CxImage linking.
Support Windows BMP format.

Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
 Makefile.am                 |   4 +-
 configure.ac                |   4 +-
 mingw-spice-vdagent.spec.in |   6 +-
 vdagent/image.cpp           | 170 ++++++++++++++++++++++++++++++++++----------
 vdagent/image.h             |  21 ++++++
 5 files changed, 157 insertions(+), 48 deletions(-)

Changes since v4:
- remove tiff dependency, was just for CxImage.

diff --git a/Makefile.am b/Makefile.am
index 868199e..b35dd57 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,8 +23,8 @@ LIBS = -lversion
 
 bin_PROGRAMS = vdagent vdservice
 
-vdagent_LDADD = -lwtsapi32 $(CXIMAGE_LIBS) vdagent_rc.$(OBJEXT)
-vdagent_CXXFLAGS = $(AM_CXXFLAGS) $(CXIMAGE_CFLAGS)
+vdagent_LDADD = -lwtsapi32 -lgdi32 vdagent_rc.$(OBJEXT)
+vdagent_CXXFLAGS = $(AM_CXXFLAGS)
 vdagent_LDFLAGS = $(AM_LDFLAGS) -Wl,--subsystem,windows
 vdagent_SOURCES =			\
 	common/vdcommon.cpp             \
diff --git a/configure.ac b/configure.ac
index ff489cc..4eac4b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_DEFINE_UNQUOTED([BUILD_YEAR], "$BUILD_YEAR", [Build year])
 # Check for programs
 AC_PROG_CC
 AC_PROG_CXX
+AX_CXX_COMPILE_STDCXX_11
 AM_PROG_CC_C_O
 AC_PROG_INSTALL
 AC_CHECK_TOOL(WINDRES, [windres])
@@ -100,9 +101,6 @@ dnl ---------------------------------------------------------------------------
 dnl - Check library dependencies
 dnl ---------------------------------------------------------------------------
 
-PKG_CHECK_MODULES(CXIMAGE, [cximage])
-CXIMAGE_LIBS=`$PKG_CONFIG --static --libs cximage`
-
 dnl ---------------------------------------------------------------------------
 dnl - Makefiles, etc.
 dnl ---------------------------------------------------------------------------
diff --git a/mingw-spice-vdagent.spec.in b/mingw-spice-vdagent.spec.in
index 563341d..cdc8dba 100644
--- a/mingw-spice-vdagent.spec.in
+++ b/mingw-spice-vdagent.spec.in
@@ -13,16 +13,14 @@ Source0:        vdagent-win-%{version}%{?_version_suffix}.tar.xz
 
 BuildRequires:  mingw32-filesystem >= 23
 BuildRequires:  mingw64-filesystem >= 23
-BuildRequires:  mingw32-cximage-static
-BuildRequires:  mingw64-cximage-static
 BuildRequires:  mingw32-jasper-static
 BuildRequires:  mingw64-jasper-static
 BuildRequires:  mingw32-libjpeg-turbo-static
 BuildRequires:  mingw64-libjpeg-turbo-static
 BuildRequires:  mingw32-libpng-static
 BuildRequires:  mingw64-libpng-static
-BuildRequires:  mingw32-libtiff-static
-BuildRequires:  mingw64-libtiff-static
+BuildRequires:  mingw32-gcc-c++
+BuildRequires:  mingw64-gcc-c++
 BuildRequires:  mingw32-zlib-static
 BuildRequires:  mingw64-zlib-static
 BuildRequires:  mingw32-winpthreads-static
diff --git a/vdagent/image.cpp b/vdagent/image.cpp
index 0b9c750..b32da6f 100644
--- a/vdagent/image.cpp
+++ b/vdagent/image.cpp
@@ -16,39 +16,48 @@
 */
 
 #include <spice/macros.h>
+#include <memory>
+#include <vector>
 
 #include "vdcommon.h"
 #include "image.h"
 
-#include "ximage.h"
+ImageCoder *create_bitmap_coder();
+ImageCoder *create_png_coder();
 
-typedef struct ImageType {
-    uint32_t type;
-    DWORD cximage_format;
-} ImageType;
-
-static const ImageType image_types[] = {
-    {VD_AGENT_CLIPBOARD_IMAGE_PNG, CXIMAGE_FORMAT_PNG},
-    {VD_AGENT_CLIPBOARD_IMAGE_BMP, CXIMAGE_FORMAT_BMP},
-};
-
-static DWORD get_cximage_format(uint32_t type)
+static ImageCoder *get_coder(uint32_t vdagent_type)
 {
-    for (unsigned int i = 0; i < SPICE_N_ELEMENTS(image_types); i++) {
-        if (image_types[i].type == type) {
-            return image_types[i].cximage_format;
-        }
+    switch (vdagent_type) {
+    case VD_AGENT_CLIPBOARD_IMAGE_BMP:
+        return create_bitmap_coder();
+    case VD_AGENT_CLIPBOARD_IMAGE_PNG:
+        return create_png_coder();
     }
-    return 0;
+    return NULL;
 }
 
 HANDLE get_image_handle(const VDAgentClipboard& clipboard, uint32_t size, UINT &format)
 {
-    HANDLE clip_data;
-    DWORD cximage_format = get_cximage_format(clipboard.type);
-    ASSERT(cximage_format);
-    CxImage image((BYTE*)clipboard.data, size, cximage_format);
-    clip_data = image.CopyToHandle();
+    std::unique_ptr<ImageCoder> coder(get_coder(clipboard.type));
+    if (!coder) {
+        return NULL;
+    }
+
+    format = CF_DIB;
+    size_t dib_size = coder->get_dib_size(clipboard.data, size);
+    if (!dib_size) {
+        return NULL;
+    }
+    HANDLE clip_data = GlobalAlloc(GMEM_MOVEABLE, dib_size);
+    if (clip_data) {
+        uint8_t* dst = (uint8_t*)GlobalLock(clip_data);
+        if (!dst) {
+            GlobalFree(clip_data);
+            return NULL;
+        }
+        coder->get_dib_data(dst, clipboard.data, size);
+        GlobalUnlock(clip_data);
+    }
     return clip_data;
 }
 
@@ -57,32 +66,115 @@ uint8_t* get_raw_clipboard_image(const VDAgentClipboardRequest& clipboard_reques
 {
     new_size = 0;
 
-    CxImage image;
-    uint8_t *new_data = NULL;
-    DWORD cximage_format = get_cximage_format(clipboard_request.type);
-    HPALETTE pal = 0;
+    if (GetObjectType(clip_data) != OBJ_BITMAP) {
+        return NULL;
+    }
+
+    std::unique_ptr<ImageCoder> coder(get_coder(clipboard_request.type));
+    if (!coder) {
+        return NULL;
+    }
 
-    ASSERT(cximage_format);
+    HPALETTE pal = 0;
     if (IsClipboardFormatAvailable(CF_PALETTE)) {
         pal = (HPALETTE)GetClipboardData(CF_PALETTE);
     }
-    if (!image.CreateFromHBITMAP((HBITMAP)clip_data, pal)) {
-        vd_printf("Image create from handle failed");
-        return NULL;
+
+    // extract DIB
+    BITMAP bitmap;
+    GetObject(clip_data, sizeof(bitmap), &bitmap);
+
+    struct {
+        BITMAPINFOHEADER head;
+        RGBQUAD colors[256];
+    } info;
+
+    BITMAPINFOHEADER& head(info.head);
+    memset(&head, 0, sizeof(head));
+    head.biSize = sizeof(head);
+    head.biWidth = bitmap.bmWidth;
+    head.biHeight = bitmap.bmHeight;
+    head.biPlanes = bitmap.bmPlanes;
+    head.biBitCount = bitmap.bmBitsPixel >= 16 ? 24 : bitmap.bmBitsPixel;
+    head.biCompression = BI_RGB;
+
+    HDC dc = GetDC(NULL);
+    HPALETTE old_pal = NULL;
+    if (pal) {
+        old_pal = (HPALETTE)SelectObject(dc, pal);
+        RealizePalette(dc);
+    }
+    size_t stride = compute_dib_stride(head.biWidth, head.biBitCount);
+    std::vector<uint8_t> bits(stride * head.biHeight);
+    int res = GetDIBits(dc, (HBITMAP) clip_data, 0, head.biHeight,
+                        &bits[0], (LPBITMAPINFO)&info, DIB_RGB_COLORS);
+    if (pal) {
+        SelectObject(dc, old_pal);
     }
-    if (!image.Encode(new_data, new_size, cximage_format)) {
-        vd_printf("Image encode to type %u failed", clipboard_request.type);
+    ReleaseDC(NULL, dc);
+    if (!res) {
         return NULL;
     }
-    vd_printf("Image encoded to %lu bytes", new_size);
-    return new_data;
+
+    // convert DIB to desired format
+    return coder->from_bitmap(*(LPBITMAPINFO)&info, &bits[0], new_size);
 }
 
 void free_raw_clipboard_image(uint8_t *data)
 {
-    // this is really just a free however is better to make
-    // the free from CxImage code as on Windows the free
-    // can be different between libraries
-    CxImage image;
-    image.FreeMemory(data);
+    free(data);
+}
+
+class BitmapCoder: public ImageCoder
+{
+public:
+    BitmapCoder() {};
+    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);
+};
+
+size_t BitmapCoder::get_dib_size(const uint8_t *data, size_t size)
+{
+    if (memcmp(data, "BM", 2) == 0)
+        return size > 14 ? size - 14 : 0;
+    return size;
+}
+
+void BitmapCoder::get_dib_data(uint8_t *dib, const uint8_t *data, size_t size)
+{
+    // just strip the file header if present, images can be either BMP or DIB
+    size_t new_size = get_dib_size(data, size);
+    memcpy(dib, data + (size - new_size), new_size);
+}
+
+uint8_t *BitmapCoder::from_bitmap(const BITMAPINFO& info, const void *bits, long &size)
+{
+    const BITMAPINFOHEADER& head(info.bmiHeader);
+
+    const DWORD max_palette_colors = head.biBitCount <= 8 ? 1 << head.biBitCount : 0;
+    size_t palette_size = sizeof(RGBQUAD) * std::min(head.biClrUsed, max_palette_colors);
+
+    const size_t stride = compute_dib_stride(head.biWidth, head.biBitCount);
+    const size_t image_size = stride * head.biHeight;
+    size = sizeof(head) + palette_size + image_size;
+
+    uint8_t *data = (uint8_t *) malloc(size);
+    if (!data) {
+        return NULL;
+    }
+    memcpy(data, &info, sizeof(head) + palette_size);
+    memcpy(data + sizeof(head) + palette_size, bits, image_size);
+    return data;
+}
+
+ImageCoder *create_bitmap_coder()
+{
+    return new BitmapCoder();
+}
+
+// TODO
+ImageCoder *create_png_coder()
+{
+    return NULL;
 }
diff --git a/vdagent/image.h b/vdagent/image.h
index b70f53a..773999c 100644
--- a/vdagent/image.h
+++ b/vdagent/image.h
@@ -18,6 +18,27 @@
 #ifndef VDAGENT_IMAGE_H_
 #define VDAGENT_IMAGE_H_
 
+class ImageCoder
+{
+public:
+    ImageCoder() {};
+    virtual ~ImageCoder() {}
+    virtual size_t get_dib_size(const uint8_t *data, size_t size)=0;
+    virtual void get_dib_data(uint8_t *dib, const uint8_t *data, size_t size)=0;
+    virtual uint8_t *from_bitmap(const BITMAPINFO& info, const void *bits, long &size)=0;
+private:
+    ImageCoder(const ImageCoder& rhs);
+    void operator=(const ImageCoder &rhs);
+};
+
+/**
+ * Compute stride in bytes of a DIB
+ */
+static inline size_t compute_dib_stride(unsigned width, unsigned bit_count)
+{
+    return ((width * bit_count + 31u) & ~31u) / 8u;
+}
+
 /**
  * Returns image to put in the clipboard.
  *
-- 
2.13.3



More information about the Spice-devel mailing list