Files
2026-10-13 20:04:31 -04:00

129 lines
6.8 KiB
C

/* solana_img.h: the launcher's art (included once by solana.c).
*
* The PNGs in assets/png are built into the exe as RCDATA resources (solana.rc).
* They are decoded with WIC (part of Windows), scaled with its high-quality
* (Fant) scaler to the size they are drawn at for the current DPI, optionally
* tinted (the white icons), and drawn with AlphaBlend. Scaled copies are kept
* until the DPI changes. */
#define COBJMACROS
#include <wincodec.h>
/* resource ids (solana.rc) */
enum {
IMG_BANNER = 100, IMG_WORDMARK, IMG_WORDMARK_TEXT, IMG_EMPTY, IMG_BGTILE,
IMG_I_ADD = 120, IMG_I_BUILD, IMG_I_CANCEL, IMG_I_FOLDER, IMG_I_LOG, IMG_I_PLAY, IMG_I_REBUILD, IMG_I_REMOVE, IMG_I_SCAN, IMG_I_SETTINGS,
IMG_PS_CROSS = 140, IMG_PS_CIRCLE, IMG_PS_SQUARE, IMG_PS_TRIANGLE, IMG_PS_L1, IMG_PS_R1, IMG_PS_L2, IMG_PS_R2, IMG_PS_START, IMG_PS_SELECT,
IMG_XB_A = 160, IMG_XB_B, IMG_XB_X, IMG_XB_Y, IMG_XB_LB, IMG_XB_RB, IMG_XB_LT, IMG_XB_RT, IMG_XB_MENU, IMG_XB_VIEW,
};
static IWICImagingFactory *wic;
typedef struct { int id, w, h, tinted; COLORREF tint; HBITMAP bmp; } img_t;
#define NIMG 160
static img_t imgs[NIMG];
static int nimgs;
static HBRUSH tile_br; static int tile_sz;
static void img_flush(void) /* (the DPI changed) */
{
if (tile_br) { DeleteObject(tile_br); tile_br = 0; }
for (int i = 0; i < nimgs; i++) if (imgs[i].bmp) DeleteObject(imgs[i].bmp);
nimgs = 0;
}
/* the resource's natural size */
static IWICBitmapFrameDecode *img_frame(int id)
{
if (!wic && FAILED(CoCreateInstance(&CLSID_WICImagingFactory, 0, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void **)&wic))) return 0;
HRSRC r = FindResourceW(0, MAKEINTRESOURCEW(id), (LPCWSTR)RT_RCDATA);
if (!r) return 0;
HGLOBAL g = LoadResource(0, r);
void *p = g ? LockResource(g) : 0;
DWORD n = SizeofResource(0, r);
if (!p || !n) return 0;
IWICStream *st = 0; IWICBitmapDecoder *dec = 0; IWICBitmapFrameDecode *fr = 0;
if (SUCCEEDED(IWICImagingFactory_CreateStream(wic, &st)) && SUCCEEDED(IWICStream_InitializeFromMemory(st, (BYTE *)p, n)) &&
SUCCEEDED(IWICImagingFactory_CreateDecoderFromStream(wic, (IStream *)st, 0, WICDecodeMetadataCacheOnDemand, &dec)))
IWICBitmapDecoder_GetFrame(dec, 0, &fr);
if (dec) IWICBitmapDecoder_Release(dec);
if (st) IWICStream_Release(st);
return fr;
}
static int img_size(int id, int *w, int *h)
{
static int cid[64], cw[64], ch[64], nc;
for (int i = 0; i < nc; i++) if (cid[i] == id) { *w = cw[i]; *h = ch[i]; return 1; }
IWICBitmapFrameDecode *fr = img_frame(id);
if (!fr) return 0;
UINT a, b; IWICBitmapFrameDecode_GetSize(fr, &a, &b); IWICBitmapFrameDecode_Release(fr);
if (nc < 64) { cid[nc] = id; cw[nc] = (int)a; ch[nc] = (int)b; nc++; }
*w = (int)a; *h = (int)b;
return 1;
}
/* a premultiplied 32-bit DIB of the image at w x h (tinted: its alpha in that colour) */
static HBITMAP img_get(int id, int w, int h, int tinted, COLORREF tint)
{
if (w < 1 || h < 1 || w > 8192 || h > 8192) return 0;
for (int i = 0; i < nimgs; i++)
if (imgs[i].id == id && imgs[i].w == w && imgs[i].h == h && imgs[i].tinted == tinted && (!tinted || imgs[i].tint == tint)) return imgs[i].bmp;
IWICBitmapFrameDecode *fr = img_frame(id);
if (!fr) return 0;
IWICBitmapScaler *sc = 0; IWICFormatConverter *cv = 0; HBITMAP bmp = 0;
if (SUCCEEDED(IWICImagingFactory_CreateBitmapScaler(wic, &sc)) &&
SUCCEEDED(IWICBitmapScaler_Initialize(sc, (IWICBitmapSource *)fr, (UINT)w, (UINT)h, WICBitmapInterpolationModeFant)) &&
SUCCEEDED(IWICImagingFactory_CreateFormatConverter(wic, &cv)) &&
SUCCEEDED(IWICFormatConverter_Initialize(cv, (IWICBitmapSource *)sc, &GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, 0, 0, WICBitmapPaletteTypeCustom))) {
BITMAPINFO bi; memset(&bi, 0, sizeof bi);
bi.bmiHeader.biSize = sizeof bi.bmiHeader; bi.bmiHeader.biWidth = w; bi.bmiHeader.biHeight = -h;
bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB;
void *bits = 0;
bmp = CreateDIBSection(0, &bi, DIB_RGB_COLORS, &bits, 0, 0);
if (bmp && SUCCEEDED(IWICFormatConverter_CopyPixels(cv, 0, (UINT)w * 4, (UINT)(w * h * 4), (BYTE *)bits))) {
if (tinted) {
unsigned *px = bits, r = GetRValue(tint), g = GetGValue(tint), b = GetBValue(tint);
for (int i = 0; i < w * h; i++) { unsigned a = px[i] >> 24; px[i] = (a << 24) | ((r * a / 255) << 16) | ((g * a / 255) << 8) | (b * a / 255); }
}
} else if (bmp) { DeleteObject(bmp); bmp = 0; }
}
if (cv) IWICFormatConverter_Release(cv);
if (sc) IWICBitmapScaler_Release(sc);
IWICBitmapFrameDecode_Release(fr);
if (!bmp) return 0;
if (nimgs == NIMG) img_flush();
imgs[nimgs].id = id; imgs[nimgs].w = w; imgs[nimgs].h = h; imgs[nimgs].tinted = tinted; imgs[nimgs].tint = tint; imgs[nimgs].bmp = bmp; nimgs++;
return bmp;
}
/* part (sx, sy, sw, sh) of the image scaled to w x h, at (x, y) */
static int img_blit(HDC dc, int id, int w, int h, int sx, int sy, int sw, int sh, int x, int y, int tinted, COLORREF tint, int alpha)
{
HBITMAP b = img_get(id, w, h, tinted, tint);
if (!b) return 0;
HDC m = CreateCompatibleDC(dc);
HGDIOBJ old = SelectObject(m, b);
BLENDFUNCTION bf = { AC_SRC_OVER, 0, (BYTE)alpha, AC_SRC_ALPHA };
AlphaBlend(dc, x, y, sw, sh, m, sx, sy, sw, sh, bf);
SelectObject(m, old); DeleteDC(m);
return 1;
}
static int img_draw(HDC dc, int id, int x, int y, int w, int h) { return img_blit(dc, id, w, h, 0, 0, w, h, x, y, 0, 0, 255); }
static int img_icon(HDC dc, int id, int x, int y, int sz, COLORREF c) { return img_blit(dc, id, sz, sz, 0, 0, sz, sz, x, y, 1, c, 255); }
/* the image filling r (cropped, never stretched): anchored right (ax = 1) or centred (0.5), centred vertically */
static int img_cover(HDC dc, int id, RECT r, double ax)
{
int iw, ih, w = r.right - r.left, h = r.bottom - r.top;
if (w <= 0 || h <= 0 || !img_size(id, &iw, &ih)) return 0;
double s = (double)w / iw; if ((double)h / ih > s) s = (double)h / ih;
int sw = (int)(iw * s + 0.999), sh = (int)(ih * s + 0.999);
{ /* the banner is resized with the window: keep only the latest size of it */
for (int i = 0; i < nimgs; i++) if (imgs[i].id == id && !imgs[i].tinted && (imgs[i].w != sw || imgs[i].h != sh)) { DeleteObject(imgs[i].bmp); imgs[i] = imgs[--nimgs]; i--; }
}
return img_blit(dc, id, sw, sh, (int)((sw - w) * ax), (sh - h) / 2, w, h, r.left, r.top, 0, 0, 255);
}
/* a pattern brush of the tile at the current DPI (0 if there is none) */
static HBRUSH img_tile_brush(int id, int size)
{
if (tile_br && tile_sz == size) return tile_br;
if (tile_br) { DeleteObject(tile_br); tile_br = 0; }
HBITMAP b = img_get(id, size, size, 0, 0);
if (b) { tile_br = CreatePatternBrush(b); tile_sz = size; }
return tile_br;
}