From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/Plugins/Library/ml_devices/imageCache.cpp | 902 ++++++++++++++++++++++++++ 1 file changed, 902 insertions(+) create mode 100644 Src/Plugins/Library/ml_devices/imageCache.cpp (limited to 'Src/Plugins/Library/ml_devices/imageCache.cpp') diff --git a/Src/Plugins/Library/ml_devices/imageCache.cpp b/Src/Plugins/Library/ml_devices/imageCache.cpp new file mode 100644 index 00000000..52e745b7 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/imageCache.cpp @@ -0,0 +1,902 @@ +#include "main.h" +#include "./imageCache.h" +#include +#include +#include + + +struct DeviceColoredImage +{ + size_t ref; + DeviceImage *base; + HBITMAP bitmap; + COLORREF color1; + COLORREF color2; + DeviceImageFilter filter; + void *filterParam; +}; + +typedef std::vector DeviceColoredImageList; + +struct DeviceImage +{ + size_t ref; + DeviceImageCache *cache; + wchar_t *source; + HBITMAP bitmap; + HBITMAP exactBitmap; + int width; + int height; + DeviceImageLoader loader; + void *loaderParam; + DeviceColoredImageList list; +}; +typedef std::vector DeviceImageList; + +struct DeviceImageCache +{ + DeviceImageList list; + IWICImagingFactory *wicFactory; +}; + +typedef struct DeviceImageSeachParam +{ + const wchar_t *path; + int width; + int height; +} DeviceImageSeachParam; + +typedef struct DeviceColoredImageSeachParam +{ + COLORREF color1; + COLORREF color2; +} DeviceColoredImageSeachParam; + + +DeviceImageCache * +DeviceImageCache_Create() +{ + DeviceImageCache *self; + + self = new DeviceImageCache(); + if (NULL == self) + return NULL; + + self->wicFactory = NULL; + + return self; +} + + +void +DeviceImageCache_Free(DeviceImageCache *self) +{ + if (NULL == self) + return; + + size_t index = self->list.size(); + while(index--) + { + DeviceImage *image = self->list[index]; + image->cache = NULL; + } + + if (NULL != self->wicFactory) + self->wicFactory->Release(); + + delete(self); +} + + +static DeviceImage * +DeviceImage_Create(DeviceImageCache *cache, const wchar_t *path, int width, int height, + DeviceImageLoader loader, void *loaderParam) +{ + DeviceImage *self; + + if (NULL == loader) + return NULL; + + self = new DeviceImage(); + if (NULL == self) + return NULL; + + self->ref = 1; + self->cache = cache; + self->source = ResourceString_Duplicate(path); + self->loader = loader; + self->loaderParam = loaderParam; + self->bitmap = NULL; + self->exactBitmap = NULL; + self->width = width; + self->height = height; + + return self; +} + +static void +DeviceImage_Free(DeviceImage *self) +{ + if (NULL == self) + return; + + if (NULL != self->source) + ResourceString_Free(self->source); + + if (NULL != self->bitmap) + DeleteObject(self->bitmap); + + if (NULL != self->exactBitmap && + self->exactBitmap != self->bitmap) + { + DeleteObject(self->exactBitmap); + } + + delete(self); +} + +static int +DeviceImageCache_SearchCb(const void *key, const void *element) +{ + DeviceImageSeachParam *search; + DeviceImage *image; + int result; + + search = (DeviceImageSeachParam*)key; + image = (DeviceImage*)element; + + result = search->height - image->height; + if (0 != result) + return result; + + result = search->width - image->width; + if (0 != result) + return result; + + if (FALSE != IS_INTRESOURCE(search->path) || + FALSE != IS_INTRESOURCE(image->source)) + { + return (int)(INT_PTR)(search->path - image->source); + } + + return CompareString(CSTR_INVARIANT, 0, search->path, -1, image->source, -1) - 2; +} + +static int +DeviceImageCache_SortCb(const void *element1, const void *element2) +{ + DeviceImage *image1; + DeviceImage *image2; + int result; + + image1 = (DeviceImage*)element1; + image2 = (DeviceImage*)element2; + + result = image1->height - image2->height; + if (0 != result) + return result; + + result = image1->width - image2->width; + if (0 != result) + return result; + + if (FALSE != IS_INTRESOURCE(image1->source) || + FALSE != IS_INTRESOURCE(image2->source)) + { + return (int)(INT_PTR)(image1->source - image2->source); + } + + return CompareString(CSTR_INVARIANT, 0, image1->source, -1, image2->source, -1) - 2; +} +static int +DeviceImageCache_SortCb_V2(const void* element1, const void* element2) +{ + return DeviceImageCache_SortCb(element1, element2) < 0; +} + + +static HBITMAP +DeviceImage_DefaultImageLoader(const wchar_t *path, int width, int height, void *param) +{ + HBITMAP bitmap; + unsigned int flags; + + flags = ISF_PREMULTIPLY; + if (FALSE == IS_INTRESOURCE(path)) + flags |= ISF_LOADFROMFILE; + + bitmap = Image_Load(path, SRC_TYPE_PNG, flags, 0, 0); + + return bitmap; +} + +DeviceImage * +DeviceImageCache_GetImage(DeviceImageCache *self, const wchar_t *path, int width, int height, DeviceImageLoader loader, void *user) +{ + DeviceImage *image, *image_ptr = 0; + DeviceImageSeachParam searchParam; + + if (width < 1) + width = 0; + + if (height < 1) + height = 0; + + if (NULL == self) + return NULL; + + if (NULL == path || + (FALSE == IS_INTRESOURCE(path) && L'\0' == *path)) + { + return NULL; + } + + searchParam.height = height; + searchParam.width = width; + searchParam.path = path; + + //image_ptr = (DeviceImage**)bsearch(&searchParam, &self->list[0], self->list.size(), + // sizeof(DeviceImage**), + // DeviceImageCache_SearchCb); + auto it = std::find_if(self->list.begin(), self->list.end(), + [&](DeviceImage* upT) -> bool + { + return DeviceImageCache_SearchCb(&searchParam, upT) == 0; + } + ); + if (it != self->list.end()) + { + image_ptr = *it; + } + + + if (NULL != image_ptr) + { + image = image_ptr; + DeviceImage_AddRef(image); + return image; + } + + if (NULL == loader) + loader = DeviceImage_DefaultImageLoader; + + image = DeviceImage_Create(self, path, width, height, loader, user); + if (NULL != image) + { + self->list.push_back(image); + //qsort(&self->list[0], self->list.size(), sizeof(DeviceImage**), DeviceImageCache_SortCb); + std::sort(self->list.begin(), self->list.end(), DeviceImageCache_SortCb_V2); + } + + return image; +} + +static BOOL +DeviceImageCache_RemoveImage(DeviceImageCache *self, DeviceImage *image) +{ + size_t index; + + if (NULL == self || NULL == image) + return FALSE; + + index = self->list.size(); + while(index--) + { + if (self->list[index] == image) + { + self->list.erase(self->list.begin() + index); + return TRUE; + } + } + + return FALSE; +} +size_t +DeviceImage_AddRef(DeviceImage *self) +{ + if (NULL == self) + return 0; + return InterlockedIncrement((LONG*)&self->ref); +} + +size_t +DeviceImage_Release(DeviceImage *self) +{ + size_t r; + if (NULL == self || 0 == self->ref) + return 0; + + r = InterlockedDecrement((LONG*)&self->ref); + if (0 == r) + { + if (NULL != self->cache) + DeviceImageCache_RemoveImage(self->cache, self); + DeviceImage_Free(self); + return 0; + } + + return r; +} + +BOOL +DeviceImage_GetSize(DeviceImage *self, int *width, int *height) +{ + if (NULL == self) + return FALSE; + + if (NULL != width) + *width = self->width; + + if (NULL != height) + *height = self->height; + + return TRUE; +} + +static IWICImagingFactory* +DeviceImage_GetWicFactory(DeviceImageCache *cache) +{ + IWICImagingFactory *wicFactory; + HRESULT hr; + + if (NULL != cache && + NULL != cache->wicFactory) + { + cache->wicFactory->AddRef(); + return cache->wicFactory; + } + + hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&wicFactory)); + + if (FAILED(hr)) + return NULL; + + if (NULL != cache) + { + wicFactory->AddRef(); + cache->wicFactory = wicFactory; + } + + return wicFactory; +} + + +static HBITMAP +DeviceImage_HBitmapFromWicSource(IWICBitmapSource *wicSource, unsigned int targetWidth, + unsigned int targetHeight, DeviceImageFlags flags) +{ + HRESULT hr; + HBITMAP bitmap; + BITMAPINFO bitmapInfo; + unsigned int width, height, bitmapWidth, bitmapHeight; + void *pixelData = NULL; + WICPixelFormatGUID pixelFormat; + HDC windowDC; + unsigned int strideSize, imageSize; + + if (NULL == wicSource) + return NULL; + + hr = wicSource->GetPixelFormat(&pixelFormat); + if (FAILED(hr) || + (GUID_WICPixelFormat32bppPBGRA != pixelFormat && + GUID_WICPixelFormat32bppBGR != pixelFormat && + GUID_WICPixelFormat32bppBGRA != pixelFormat && + GUID_WICPixelFormat32bppRGBA != pixelFormat && + GUID_WICPixelFormat32bppPRGBA != pixelFormat)) + { + return NULL; + } + + hr = wicSource->GetSize(&width, &height); + if (FAILED(hr)) + return NULL; + + if (0 != (DeviceImage_ExactSize & flags)) + { + bitmapWidth = (targetWidth > width) ? targetWidth : width; + bitmapHeight = (targetHeight > height) ? targetHeight : height; + } + else + { + bitmapWidth = width; + bitmapHeight = height; + } + + ZeroMemory(&bitmapInfo, sizeof(bitmapInfo)); + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = bitmapWidth; + bitmapInfo.bmiHeader.biHeight = -(LONG)bitmapHeight; + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + windowDC = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE); + + bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, &pixelData, NULL, 0); + + if (NULL != windowDC) + ReleaseDC(NULL, windowDC); + + if (NULL == bitmap) + return NULL; + + hr = UIntMult(bitmapWidth, sizeof(DWORD), &strideSize); + if (SUCCEEDED(hr)) + { + if (0 != (DeviceImage_ExactSize & flags) && + (bitmapWidth > width || bitmapHeight > height)) + { + if (SUCCEEDED(UIntMult(strideSize, bitmapHeight, &imageSize))) + ZeroMemory(pixelData, imageSize); + } + + hr = UIntMult(strideSize, height, &imageSize); + if (SUCCEEDED(hr)) + { + unsigned int offset, delta; + + offset = 0; + + if (0 != (DeviceImage_AlignVCenter & flags)) + { + delta = bitmapHeight - height; + delta = delta/2 + delta%2; + } + else if (0 != (DeviceImage_AlignBottom & flags)) + delta = bitmapHeight - height; + else + delta = 0; + + if (0 != delta && SUCCEEDED(UIntMult(delta, strideSize, &delta))) + offset += delta; + + if (0 != (DeviceImage_AlignHCenter & flags)) + { + delta = bitmapWidth - width; + delta = delta/2; + } + else if (0 != (DeviceImage_AlignRight & flags)) + delta = bitmapWidth - width; + else + delta = 0; + + if (0 != delta && SUCCEEDED(UIntMult(delta, sizeof(DWORD), &delta))) + offset += delta; + + hr = wicSource->CopyPixels(NULL, strideSize, imageSize, ((BYTE*)pixelData) + offset); + } + } + + if (FAILED(hr)) + { + DeleteObject(bitmap); + bitmap = NULL; + } + + return bitmap; +} + +static BOOL +DeviceImage_ScaleBitmap(HBITMAP sourceBitmap, int width, int height, DeviceImageCache *cache, + DeviceImageFlags flags, HBITMAP *scalledBitmap) +{ + HBITMAP resultBitmap; + BITMAP sourceInfo; + IWICImagingFactory *wicFactory; + unsigned int requestedWidth, requestedHeight; + double scale, scaleH; + int t; + + if (NULL == sourceBitmap || NULL == scalledBitmap) + return FALSE; + + if (sizeof(sourceInfo) != GetObject(sourceBitmap, sizeof(sourceInfo), &sourceInfo)) + return FALSE; + + if (sourceInfo.bmHeight < 0) + sourceInfo.bmHeight = -sourceInfo.bmHeight; + + scale = (double)width / sourceInfo.bmWidth; + scaleH = (double)height / sourceInfo.bmHeight; + + if (scale > scaleH) + scale = scaleH; + + if (1.0 == scale) + { + *scalledBitmap = NULL; + return TRUE; + } + + requestedWidth = width; + requestedHeight = height; + + t = (int)((sourceInfo.bmWidth * scale) + 0.5); + if (t < width) + width = t; + + t = (int)((sourceInfo.bmHeight * scale) + 0.5); + if (t < height) + height = t; + + + resultBitmap = NULL; + + wicFactory = DeviceImage_GetWicFactory(cache); + if (NULL != wicFactory) + { + HRESULT hr; + IWICBitmap *wicBitmap; + + hr = wicFactory->CreateBitmapFromHBITMAP(sourceBitmap, NULL, + WICBitmapUsePremultipliedAlpha, &wicBitmap); + if (SUCCEEDED(hr)) + { + IWICBitmapScaler *wicScaler; + hr = wicFactory->CreateBitmapScaler(&wicScaler); + if (SUCCEEDED(hr)) + { + hr = wicScaler->Initialize(wicBitmap, width, height, WICBitmapInterpolationModeFant); + if (SUCCEEDED(hr)) + { + resultBitmap = DeviceImage_HBitmapFromWicSource(wicScaler, + requestedWidth, requestedHeight, flags); + } + wicScaler->Release(); + + } + wicBitmap->Release(); + } + + wicFactory->Release(); + } + + *scalledBitmap = resultBitmap; + return (NULL != resultBitmap); +} + +static HBITMAP +DeviceImage_GetExactBitmap(DeviceImage *self, DeviceImageFlags flags) +{ + if (NULL == self) + return NULL; + + if (NULL == self->exactBitmap) + { + if (NULL != self->bitmap) + { + BITMAP bi; + if (sizeof(bi) == GetObject(self->bitmap, sizeof(bi), &bi) && + bi.bmWidth == self->width && bi.bmHeight == self->height) + { + self->exactBitmap = self->bitmap; + return self->exactBitmap; + } + } + + if (NULL != self->loader) + { + HBITMAP bitmap; + bitmap = self->loader(self->source, self->width, self->height, self->loaderParam); + if (NULL != bitmap) + { + if (FALSE != DeviceImage_ScaleBitmap(bitmap, self->width, self->height, + self->cache, flags, &self->exactBitmap)) + { + + if (NULL == self->exactBitmap) + { + self->exactBitmap = bitmap; + bitmap = NULL; + } + } + + if (NULL != bitmap) + DeleteObject(bitmap); + } + } + } + + return self->exactBitmap; +} + + +HBITMAP +DeviceImage_GetBitmap(DeviceImage *self, DeviceImageFlags flags) +{ + if (NULL == self) + return NULL; + + if (0 != (DeviceImage_ExactSize & flags)) + return DeviceImage_GetExactBitmap(self, flags); + + if (NULL == self->bitmap) + { + if (NULL != self->loader) + { + HBITMAP bitmap; + bitmap = self->loader(self->source, self->width, self->height, self->loaderParam); + if (NULL != bitmap) + { + if (FALSE == DeviceImage_ScaleBitmap(bitmap, self->width, self->height, + self->cache, flags, &self->bitmap)) + { + self->bitmap = NULL; + } + + if (NULL != self->bitmap) + DeleteObject(bitmap); + else + self->bitmap = bitmap; + } + } + } + + return self->bitmap; +} + + +static DeviceColoredImage * +DeveiceColoredImage_Create(DeviceImage *base, COLORREF color1, COLORREF color2, + DeviceImageFilter filter, void *user) +{ + DeviceColoredImage *coloredImage; + + if (NULL == base) + return NULL; + + coloredImage = (DeviceColoredImage*)malloc(sizeof(DeviceColoredImage)); + if (NULL == coloredImage) + return NULL; + + ZeroMemory(coloredImage, sizeof(DeviceColoredImage)); + + coloredImage->ref = 1; + coloredImage->base = base; + coloredImage->color1 = color1; + coloredImage->color2 = color2; + coloredImage->filter = filter; + coloredImage->filterParam = user; + + DeviceImage_AddRef(base); + + return coloredImage; +} + +static void +DeviceColoredImage_Free(DeviceColoredImage *self) +{ + if (NULL == self) + return; + + if (NULL != self->bitmap) + DeleteObject(self->bitmap); + + free(self); +} + +static int +DeviceColoredImage_SearchCb(const void *key, const void *element) +{ + DeviceColoredImageSeachParam *search; + DeviceColoredImage *image; + int result; + + search = (DeviceColoredImageSeachParam*)key; + image = (DeviceColoredImage*)element; + + result = search->color1 - image->color1; + if (0 != result) + return result; + + return search->color2- image->color2; + +} + +static int +DeviceColoredImage_SortCb(const void *element1, const void *element2) +{ + DeviceColoredImage *image1; + DeviceColoredImage *image2; + int result; + + image1 = (DeviceColoredImage*)element1; + image2 = (DeviceColoredImage*)element2; + + result = image1->color1 - image2->color1; + if (0 != result) + return result; + + return image1->color2- image2->color2; +} + +static int +DeviceColoredImage_SortCb_V2(const void* element1, const void* element2) +{ + return DeviceColoredImage_SortCb(element1, element2) < 0; +} + +DeviceColoredImage * +DeviceImage_GetColoredImage(DeviceImage *self, COLORREF color1, COLORREF color2, + DeviceImageFilter filter, void *user) +{ + size_t listSize; + DeviceColoredImage *image; + DeviceColoredImageSeachParam searchParam; + + searchParam.color1 = color1; + searchParam.color2 = color2; + + listSize = self->list.size(); + if (listSize > 0) + { + DeviceColoredImage* image_ptr = NULL; + //DeviceColoredImage **image_ptr = (DeviceColoredImage**)bsearch(&searchParam, &self->list[0], listSize, + // sizeof(DeviceColoredImage**), + // DeviceColoredImage_SearchCb); + + auto it = std::find_if(self->list.begin(), self->list.end(), + [&](DeviceColoredImage* upT) -> bool + { + return DeviceColoredImage_SearchCb(&searchParam, upT) == 0; + } + ); + if (it != self->list.end()) + { + image_ptr = *it; + } + + if (NULL != image_ptr) + { + image = image_ptr; + DeviceColoredImage_AddRef(image); + return image; + } + } + + image = DeveiceColoredImage_Create(self, color1, color2, filter, user); + if (NULL == image) + return NULL; + + self->list.push_back(image); + listSize++; + + if (listSize > 1) + { + //qsort(&self->list[0], self->list.size(), sizeof(DeviceColoredImage**), + // DeviceColoredImage_SortCb); + std::sort(self->list.begin(), self->list.end(), DeviceColoredImage_SortCb_V2); + } + + return image; +} + +static void +DeviceImage_RemoveColored(DeviceImage *self, DeviceColoredImage *coloredImage) +{ + size_t index; + + if (NULL == self || NULL == coloredImage) + return; + + index = self->list.size(); + while(index--) + { + if (coloredImage == self->list[index]) + { + self->list.erase(self->list.begin() + index); + } + } +} + +size_t +DeviceColoredImage_AddRef(DeviceColoredImage *self) +{ + if (NULL == self) + return 0; + return InterlockedIncrement((LONG*)&self->ref); +} + +size_t +DeviceColoredImage_Release(DeviceColoredImage *self) +{ + size_t r; + if (NULL == self || 0 == self->ref) + return 0; + + r = InterlockedDecrement((LONG*)&self->ref); + if (0 == r) + { + if (NULL != self->base) + { + DeviceImage_RemoveColored(self->base, self); + DeviceImage_Release(self->base); + } + DeviceColoredImage_Free(self); + return 0; + } + + return r; +} + +static BOOL +DeviceColoredImage_DefaultFilter(HBITMAP bitmap, COLORREF color1, COLORREF color2, void *user) +{ + DIBSECTION bitmapDib; + BITMAP *bitmapInfo; + MLIMAGEFILTERAPPLYEX filter; + + if (sizeof(bitmapDib) != GetObjectW(bitmap, sizeof(bitmapDib), &bitmapDib)) + return FALSE; + + bitmapInfo = &bitmapDib.dsBm; + + filter.cbSize = sizeof(filter); + filter.pData = (BYTE*)bitmapInfo->bmBits; + filter.cx = bitmapInfo->bmWidth; + filter.cy = bitmapInfo->bmHeight; + filter.bpp = bitmapInfo->bmBitsPixel; + filter.imageTag = NULL; + + filter.filterUID = MLIF_GRAYSCALE_UID; + MLImageFilter_ApplyEx(Plugin_GetLibraryWindow(), &filter); + + filter.rgbBk = color1; + filter.rgbFg = color2; + + if (32 == bitmapInfo->bmBitsPixel) + { + filter.filterUID = MLIF_FILTER1_PRESERVE_ALPHA_UID; + MLImageFilter_ApplyEx(Plugin_GetLibraryWindow(), &filter); + } + else + { + filter.filterUID = MLIF_FILTER1_UID; + MLImageFilter_ApplyEx(Plugin_GetLibraryWindow(), &filter); + } + + return TRUE; +} + +HBITMAP +DeviceColoredImage_GetBitmap(DeviceColoredImage *self, DeviceImageFlags flags) +{ + if (NULL == self) + return NULL; + + if (NULL == self->bitmap) + { + HBITMAP bitmap; + bitmap = DeviceImage_GetBitmap(self->base, flags); + if (NULL != bitmap) + { + self->bitmap = Image_DuplicateDib(bitmap); + if (NULL != self->bitmap) + { + DeviceImageFilter filter; + if (NULL == self->filter) + filter = DeviceColoredImage_DefaultFilter; + else + filter = self->filter; + + Image_Demultiply(self->bitmap, NULL); + filter(self->bitmap, self->color1, self->color2, self->filterParam); + Image_Premultiply(self->bitmap, NULL); + } + } + } + + return self->bitmap; +} + +DeviceImage* +DeviceColoredImage_GetBaseImage(DeviceColoredImage *self) +{ + if (NULL == self || NULL == self->base) + return NULL; + + DeviceImage_AddRef(self->base); + return self->base; +} \ No newline at end of file -- cgit