169 lines
4.4 KiB
C
169 lines
4.4 KiB
C
// widgetImage.c — Image widget (displays bitmap, responds to clicks)
|
|
|
|
#include "widgetInternal.h"
|
|
#include "../thirdparty/stb_image.h"
|
|
|
|
|
|
// ============================================================
|
|
// wgtImage
|
|
// ============================================================
|
|
//
|
|
// Create an image widget from raw pixel data already in display format.
|
|
// Takes ownership of the data buffer (freed on destroy).
|
|
|
|
WidgetT *wgtImage(WidgetT *parent, uint8_t *data, int32_t w, int32_t h, int32_t pitch) {
|
|
WidgetT *wgt = widgetAlloc(parent, WidgetImageE);
|
|
|
|
if (wgt) {
|
|
wgt->as.image.data = data;
|
|
wgt->as.image.imgW = w;
|
|
wgt->as.image.imgH = h;
|
|
wgt->as.image.imgPitch = pitch;
|
|
wgt->as.image.pressed = false;
|
|
}
|
|
|
|
return wgt;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// wgtImageFromFile
|
|
// ============================================================
|
|
//
|
|
// Load an image from a file (BMP, PNG, JPEG, GIF), convert to
|
|
// display pixel format, and create an image widget.
|
|
|
|
WidgetT *wgtImageFromFile(WidgetT *parent, const char *path) {
|
|
if (!parent || !path) {
|
|
return NULL;
|
|
}
|
|
|
|
// Find the AppContextT to get display format
|
|
WidgetT *root = parent;
|
|
|
|
while (root->parent) {
|
|
root = root->parent;
|
|
}
|
|
|
|
AppContextT *ctx = (AppContextT *)root->userData;
|
|
|
|
if (!ctx) {
|
|
return NULL;
|
|
}
|
|
|
|
const DisplayT *d = &ctx->display;
|
|
|
|
// Load image via stb_image (force RGB)
|
|
int imgW;
|
|
int imgH;
|
|
int channels;
|
|
uint8_t *rgb = stbi_load(path, &imgW, &imgH, &channels, 3);
|
|
|
|
if (!rgb) {
|
|
return NULL;
|
|
}
|
|
|
|
// Convert RGB to display pixel format
|
|
int32_t bpp = d->format.bytesPerPixel;
|
|
int32_t pitch = imgW * bpp;
|
|
uint8_t *buf = (uint8_t *)malloc(pitch * imgH);
|
|
|
|
if (!buf) {
|
|
stbi_image_free(rgb);
|
|
return NULL;
|
|
}
|
|
|
|
for (int32_t y = 0; y < imgH; y++) {
|
|
for (int32_t x = 0; x < imgW; x++) {
|
|
const uint8_t *src = rgb + (y * imgW + x) * 3;
|
|
uint32_t color = packColor(d, src[0], src[1], src[2]);
|
|
uint8_t *dst = buf + y * pitch + x * bpp;
|
|
|
|
if (bpp == 1) {
|
|
*dst = (uint8_t)color;
|
|
} else if (bpp == 2) {
|
|
*(uint16_t *)dst = (uint16_t)color;
|
|
} else {
|
|
*(uint32_t *)dst = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
stbi_image_free(rgb);
|
|
|
|
return wgtImage(parent, buf, imgW, imgH, pitch);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// wgtImageSetData
|
|
// ============================================================
|
|
|
|
void wgtImageSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int32_t pitch) {
|
|
if (!w || w->type != WidgetImageE) {
|
|
return;
|
|
}
|
|
|
|
free(w->as.image.data);
|
|
w->as.image.data = data;
|
|
w->as.image.imgW = imgW;
|
|
w->as.image.imgH = imgH;
|
|
w->as.image.imgPitch = pitch;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetImageCalcMinSize
|
|
// ============================================================
|
|
|
|
void widgetImageCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|
(void)font;
|
|
w->calcMinW = w->as.image.imgW;
|
|
w->calcMinH = w->as.image.imgH;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetImageOnMouse
|
|
// ============================================================
|
|
|
|
void widgetImageOnMouse(WidgetT *hit) {
|
|
hit->as.image.pressed = true;
|
|
wgtInvalidate(hit);
|
|
|
|
if (hit->onClick) {
|
|
hit->onClick(hit);
|
|
}
|
|
|
|
hit->as.image.pressed = false;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetImagePaint
|
|
// ============================================================
|
|
|
|
void widgetImagePaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
|
(void)font;
|
|
(void)colors;
|
|
|
|
if (!w->as.image.data) {
|
|
return;
|
|
}
|
|
|
|
// Center the image within the widget bounds
|
|
int32_t imgW = w->as.image.imgW;
|
|
int32_t imgH = w->as.image.imgH;
|
|
int32_t dx = w->x + (w->w - imgW) / 2;
|
|
int32_t dy = w->y + (w->h - imgH) / 2;
|
|
|
|
// Offset by 1px when pressed (button-press effect)
|
|
if (w->as.image.pressed) {
|
|
dx++;
|
|
dy++;
|
|
}
|
|
|
|
rectCopy(d, ops, dx, dy,
|
|
w->as.image.data, w->as.image.imgPitch,
|
|
0, 0, imgW, imgH);
|
|
}
|