winamp/Src/Plugins/Library/ml_devices/imageCache.cpp
2024-09-24 14:54:57 +02:00

902 lines
18 KiB
C++

#include "main.h"
#include "./imageCache.h"
#include <wincodec.h>
#include <vector>
#include <algorithm>
struct DeviceColoredImage
{
size_t ref;
DeviceImage *base;
HBITMAP bitmap;
COLORREF color1;
COLORREF color2;
DeviceImageFilter filter;
void *filterParam;
};
typedef std::vector<DeviceColoredImage*> 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<DeviceImage*> 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;
}