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

Frediano Ziglio fziglio at redhat.com
Wed Nov 9 10:32:42 UTC 2016


Remove CxImage linking.
Support Windows BMP format.

Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
 Makefile.am       |   4 +-
 configure.ac      |   3 -
 vdagent/image.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++------------
 vdagent/image.h   |  13 ++++
 4 files changed, 158 insertions(+), 44 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 577a839..5626d0c 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 7f6511d..49384f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -94,9 +94,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/vdagent/image.cpp b/vdagent/image.cpp
index fb19dbc..4f1dbb1 100644
--- a/vdagent/image.cpp
+++ b/vdagent/image.cpp
@@ -16,71 +16,175 @@
 */
 
 #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;
 }
 
 uint8_t* get_raw_clipboard_image(const VDAgentClipboardRequest& clipboard_request,
                                  HANDLE clip_data, long& new_size)
 {
-    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 = ((head.biWidth * head.biBitCount + 31u) & ~31u) / 8u;
+    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 (*(const WORD*)data == 'B'+'M'*256u)
+        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);
+
+    size_t palette_size = 0;
+    DWORD max_colors = 0;
+    switch (head.biBitCount) {
+    case 1:
+        max_colors = 2;
+        break;
+    case 4:
+        max_colors = 16;
+        break;
+    case 8:
+        max_colors = 256;
+        break;
+    }
+    palette_size = sizeof(RGBQUAD) * std::min(head.biClrUsed, max_colors);
+
+    const size_t stride = ((head.biWidth * head.biBitCount + 31u) & ~31u) / 8u;
+    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 9ba6003..e7ed412 100644
--- a/vdagent/image.h
+++ b/vdagent/image.h
@@ -18,6 +18,19 @@
 #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);
+};
+
 /**
  * Returns image to put in the clipboard.
  * @param clipboard  data to write in the clipboard
-- 
2.7.4



More information about the Spice-devel mailing list