From a858300f257959793df085cd80569f466635a336 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 19:24:36 -0400 Subject: [PATCH] Filled in image.cpp. Also switched on WIC since we'll need it for uiArea, though uiTable will still need classic GDI. Now let's integrate this into uiTable. --- windows/CMakeLists.txt | 2 +- windows/image.cpp | 137 +++++++++++++++++++++++++++++++++++-- windows/init.cpp | 5 ++ windows/table.cpp | 2 + windows/uipriv_windows.hpp | 7 ++ windows/winapi.hpp | 1 + 6 files changed, 148 insertions(+), 6 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b871b529..31ba2c93 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -78,7 +78,7 @@ set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) # TODO prune this list set(_LIBUI_LIBS - user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid + user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid windowscodecs PARENT_SCOPE) if(NOT MSVC) diff --git a/windows/image.cpp b/windows/image.cpp index e3b78475..a2fb2853 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -1,12 +1,23 @@ #include "uipriv_windows.hpp" -// stubbed out windows image list implementation. -// Required for uiTable control, but windows implemenation -// doesn't currently have image support. + +IWICImagingFactory *uiprivWICFactory = NULL; + +HRESULT uiprivInitImage(void) +{ + return CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, (void **) (&uiprivWICFactory)); +} + +void uiprivUninitImage(void) +{ + uiprivWICFactory->Release(); + uiprivWICFactory = NULL; +} struct uiImage { double width; double height; - // HIMAGELIST images; + std::vector *bitmaps; }; uiImage *uiNewImage(double width, double height) @@ -16,16 +27,132 @@ uiImage *uiNewImage(double width, double height) i = uiprivNew(uiImage); i->width = width; i->height = height; + i->bitmaps = new std::vector; return i; } void uiFreeImage(uiImage *i) { + for (IWICBitmap *b : *(i->bitmaps)) + b->Release(); + delete i->bitmaps; uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) { - // not implemented + IWICBitmap *b; + HRESULT hr; + + hr = uiprivWICFactory->CreateBitmapFromMemory(pixelWidth, pixelHeight, + GUID_WICPixelFormat32bppRGBA, pixelStride, + pixelStride * pixelHeight, (BYTE *) pixels, + &b); + if (hr != S_OK) + logHRESULT(L"error calling CreateBitmapFromMemory() in uiImageAppend()", hr); + i->bitmaps->push_back(b); } +struct matcher { + IWICBitmap *best; + int distX; + int distY; + int targetX; + int targetY; + bool foundLarger; +}; + +// TODO is this the right algorithm? +static void match(IWICBitmap *b, struct matcher *m) +{ + UINT ux, uy; + int x, y; + int x2, y2; + HRESULT hr; + + hr = b->GetSize(&ux, &uy); + if (hr != S_OK) + logHRESULT(L"error calling GetSize() in match()", hr); + x = ux; + y = uy; + if (m->best == NULL) + goto writeMatch; + + if (x < m->targetX && y < m->targetY) + if (m->foundLarger) + // always prefer larger ones + return; + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + // we set foundLarger below + goto writeMatch; + +// TODO +#define abs(x) ((x) < 0 ? -(x) : (x)) + x2 = abs(m->targetX - x); + y2 = abs(m->targetY - y); + if (x2 < m->distX && y2 < m->distY) + goto writeMatch; + + // TODO weight one dimension? threshhold? + return; + +writeMatch: + // must set this here too; otherwise the first image will never have ths set + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + m->foundLarger = true; + m->best = b; + m->distX = abs(m->targetX - x); + m->distY = abs(m->targetY - y); +} + +IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc) +{ + struct matcher m; + + m.best = NULL; + m.distX = INT_MAX; + m.distY = INT_MAX; + m.targetX = i->width * GetDeviceCaps(dc, LOGPIXELSX); + m.targetY = i->height * GetDeviceCaps(dc, LOGPIXELSY); + m.foundLarger = false; + for (IWICBitmap *b : *(i->bitmaps)) + match(b, &m); + return m.best; +} + +// TODO see if we can really pass NULL to CreateDIBSection()'s HDC parameter, and if so, use HBITMAPs before WIC maybe? +HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc) +{ + BITMAPINFO bmi; + UINT width, height; + HBITMAP hb; + VOID *bits; + BITMAP bmp; + HRESULT hr; + + ZeroMemory(&bmi, sizeof (BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + hr = b->GetSize(&width, &height); + if (hr != S_OK) + logHRESULT(L"error calling GetSize() in uiprivWICToGDI()", hr); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -((int) height); + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + hb = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, + &bits, NULL, 0); + if (hb == NULL) + logLastError(L"error calling CreateDIBSection() in uiprivWICToGDI()"); + + // now we need to figure out the stride of the image data GDI gave us + // TODO find out if CreateDIBSection fills that in bmi for us + if (GetObject(hb, sizeof (BITMAP), &bmp) == 0) + logLastError(L"error calling GetObject() in uiprivWICToGDI()"); + hr = b->CopyPixels(NULL, bmp.bmWidthBytes, + bmp.bmWidthBytes * bmp.bmHeight, (BYTE *) bits); + if (hr != S_OK) + logHRESULT(L"error calling CopyPixels() in uiprivWICToGDI()", hr); + + return hb; +} diff --git a/windows/init.cpp b/windows/init.cpp index 24831143..2fb52c0f 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -131,11 +131,16 @@ const char *uiInit(uiInitOptions *o) if (registerD2DScratchClass(hDefaultIcon, hDefaultCursor) == 0) return ieLastErr("initializing D2D scratch window class"); + hr = uiprivInitImage(); + if (hr != S_OK) + return ieHRESULT("initializing WIC", hr); + return NULL; } void uiUninit(void) { + uiprivUninitImage(); uninitMenus(); unregisterD2DScratchClass(); unregisterMessageFilter(); diff --git a/windows/table.cpp b/windows/table.cpp index 37b5b700..e2fa606c 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -416,6 +416,8 @@ uiTable *uiNewTable(uiTableModel *model) t->backgroundColumn = -1; // TODO update these when the DPI changes + // TODO handle errors + // TODO try adding a real transparent image t->dummyLarge = ImageList_Create( GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR32, diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 77982322..aab50aa7 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -163,3 +163,10 @@ extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); // draw.cpp extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); + +// image.cpp +extern IWICImagingFactory *uiprivWICFactory; +extern HRESULT uiprivInitImage(void); +extern void uiprivUninitImage(void); +extern IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc); +extern HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc); diff --git a/windows/winapi.hpp b/windows/winapi.hpp index ef595bba..f1306db7 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include