mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 18:58:40 +00:00
233 lines
6.5 KiB
C
233 lines
6.5 KiB
C
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
|
#include "nanojpeg.h"
|
|
#include "libc/syscall.h"
|
|
#include "libc/libui.h"
|
|
#include "libc/stdlib.h"
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#define VIEWER_MAX_W 800
|
|
#define VIEWER_MAX_H 600
|
|
|
|
static uint32_t *viewer_pixels = NULL;
|
|
static int viewer_img_w = 0;
|
|
static int viewer_img_h = 0;
|
|
static char viewer_title[64] = "Viewer";
|
|
static bool viewer_has_image = false;
|
|
static char viewer_file_path[256];
|
|
|
|
static int win_w = 500;
|
|
static int win_h = 400;
|
|
|
|
static void viewer_strcpy(char *dst, const char *src) {
|
|
while (*src) *dst++ = *src++;
|
|
*dst = 0;
|
|
}
|
|
|
|
static int viewer_strlen(const char *s) {
|
|
int len = 0;
|
|
while (s[len]) len++;
|
|
return len;
|
|
}
|
|
|
|
static void viewer_scale_rgb_to_argb(const unsigned char *rgb, int src_w, int src_h,
|
|
uint32_t *dst, int dst_w, int dst_h) {
|
|
for (int y = 0; y < dst_h; y++) {
|
|
int src_y = y * src_h / dst_h;
|
|
if (src_y >= src_h) src_y = src_h - 1;
|
|
for (int x = 0; x < dst_w; x++) {
|
|
int src_x = x * src_w / dst_w;
|
|
if (src_x >= src_w) src_x = src_w - 1;
|
|
int idx = (src_y * src_w + src_x) * 3;
|
|
unsigned char r = rgb[idx];
|
|
unsigned char g = rgb[idx + 1];
|
|
unsigned char b = rgb[idx + 2];
|
|
dst[y * dst_w + x] = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void viewer_paint(ui_window_t win) {
|
|
int cx = 4;
|
|
int cy = 0;
|
|
int cw = win_w - 8;
|
|
int ch = win_h - 28;
|
|
|
|
ui_draw_rect(win, cx, cy, cw, ch, 0xFF1A1A1A);
|
|
|
|
if (!viewer_has_image) {
|
|
ui_draw_string(win, cx + 20, cy + ch / 2, "No image loaded", 0xFF888888);
|
|
return;
|
|
}
|
|
|
|
int disp_w = viewer_img_w;
|
|
int disp_h = viewer_img_h;
|
|
|
|
if (disp_w > cw - 8) {
|
|
disp_h = disp_h * (cw - 8) / disp_w;
|
|
disp_w = cw - 8;
|
|
}
|
|
if (disp_h > ch - 40) {
|
|
disp_w = disp_w * (ch - 40) / disp_h;
|
|
disp_h = ch - 40;
|
|
}
|
|
|
|
int ox = cx + (cw - disp_w) / 2;
|
|
int oy = cy + (ch - disp_h - 30) / 2;
|
|
|
|
uint32_t *temp_buf = malloc(disp_w * disp_h * sizeof(uint32_t));
|
|
if (temp_buf) {
|
|
for (int y = 0; y < disp_h; y++) {
|
|
int src_y = y * viewer_img_h / disp_h;
|
|
if (src_y >= viewer_img_h) src_y = viewer_img_h - 1;
|
|
for (int x = 0; x < disp_w; x++) {
|
|
int src_x = x * viewer_img_w / disp_w;
|
|
if (src_x >= viewer_img_w) src_x = viewer_img_w - 1;
|
|
temp_buf[y * disp_w + x] = viewer_pixels[src_y * viewer_img_w + src_x];
|
|
}
|
|
}
|
|
ui_draw_image(win, ox, oy, disp_w, disp_h, temp_buf);
|
|
free(temp_buf);
|
|
}
|
|
|
|
int btn_w = 160;
|
|
int btn_h = 22;
|
|
int btn_x = cx + (cw - btn_w) / 2;
|
|
int btn_y = (win_h - 20) - 30;
|
|
ui_draw_rounded_rect_filled(win, btn_x, btn_y, btn_w, btn_h, 6, 0xFF2D2D2D);
|
|
ui_draw_string(win, btn_x + 10, btn_y + 6, "Set as Wallpaper", 0xFFF0F0F0);
|
|
}
|
|
|
|
static void viewer_handle_click(ui_window_t win, int x, int y) {
|
|
if (!viewer_has_image) return;
|
|
|
|
int cx = 4;
|
|
int cw = win_w - 8;
|
|
int btn_w = 160;
|
|
int btn_x = cx + (cw - btn_w) / 2;
|
|
int btn_y = (win_h - 25) - 30; // Matches the hitboxes
|
|
|
|
if (x >= btn_x && x < btn_x + btn_w && y >= btn_y && y < btn_y + 22) {
|
|
// SYSTEM_CMD_SET_WALLPAPER_PATH is 31
|
|
sys_system(31, (uint64_t)viewer_file_path, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
void viewer_open_file(const char *path) {
|
|
int fd = sys_open(path, "r");
|
|
if (fd < 0) return;
|
|
|
|
// We can't use stat yet, so read chunks until EOF
|
|
// Alternatively, use a large buffer if sys_read handles large chunks.
|
|
int alloc_size = 2 * 1024 * 1024;
|
|
unsigned char *buf = malloc(alloc_size);
|
|
if (!buf) {
|
|
sys_close(fd);
|
|
return;
|
|
}
|
|
|
|
int total_read = 0;
|
|
while (total_read < alloc_size) {
|
|
int chunk = sys_read(fd, (char*)buf + total_read, alloc_size - total_read);
|
|
if (chunk <= 0) break;
|
|
total_read += chunk;
|
|
}
|
|
sys_close(fd);
|
|
|
|
if (total_read <= 0) {
|
|
free(buf);
|
|
return;
|
|
}
|
|
|
|
njInit();
|
|
nj_result_t result = njDecode(buf, total_read);
|
|
if (result != NJ_OK) {
|
|
njDone();
|
|
free(buf);
|
|
return;
|
|
}
|
|
|
|
int img_w = njGetWidth();
|
|
int img_h = njGetHeight();
|
|
unsigned char *rgb = njGetImage();
|
|
|
|
if (!rgb || img_w <= 0 || img_h <= 0) {
|
|
njDone();
|
|
free(buf);
|
|
return;
|
|
}
|
|
|
|
int fit_w = img_w;
|
|
int fit_h = img_h;
|
|
if (fit_w > VIEWER_MAX_W) {
|
|
fit_h = fit_h * VIEWER_MAX_W / fit_w;
|
|
fit_w = VIEWER_MAX_W;
|
|
}
|
|
if (fit_h > VIEWER_MAX_H) {
|
|
fit_w = fit_w * VIEWER_MAX_H / fit_h;
|
|
fit_h = VIEWER_MAX_H;
|
|
}
|
|
|
|
viewer_pixels = malloc(fit_w * fit_h * sizeof(uint32_t));
|
|
if (viewer_pixels) {
|
|
viewer_scale_rgb_to_argb(rgb, img_w, img_h, viewer_pixels, fit_w, fit_h);
|
|
viewer_img_w = fit_w;
|
|
viewer_img_h = fit_h;
|
|
viewer_has_image = true;
|
|
}
|
|
|
|
njDone();
|
|
free(buf);
|
|
|
|
viewer_strcpy(viewer_file_path, path);
|
|
|
|
const char *fname = path;
|
|
int plen = viewer_strlen(path);
|
|
for (int i = plen - 1; i >= 0; i--) {
|
|
if (path[i] == '/') {
|
|
fname = &path[i + 1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
viewer_title[0] = 'V'; viewer_title[1] = 'i'; viewer_title[2] = 'e';
|
|
viewer_title[3] = 'w'; viewer_title[4] = 'e'; viewer_title[5] = 'r';
|
|
viewer_title[6] = ' '; viewer_title[7] = '-'; viewer_title[8] = ' ';
|
|
int ti = 9;
|
|
for (int i = 0; fname[i] && ti < 60; i++) {
|
|
viewer_title[ti++] = fname[i];
|
|
}
|
|
viewer_title[ti] = 0;
|
|
|
|
win_w = fit_w + 16;
|
|
if (win_w < 200) win_w = 200;
|
|
win_h = fit_h + 64;
|
|
if (win_h < 100) win_h = 100;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc > 1) {
|
|
viewer_open_file(argv[1]);
|
|
}
|
|
|
|
ui_window_t win = ui_window_create(viewer_title, 100, 50, win_w, win_h);
|
|
if (!win) return 1;
|
|
|
|
gui_event_t ev;
|
|
while (1) {
|
|
if (ui_get_event(win, &ev)) {
|
|
if (ev.type == GUI_EVENT_PAINT) {
|
|
viewer_paint(win);
|
|
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
|
|
} else if (ev.type == GUI_EVENT_CLICK) {
|
|
viewer_handle_click(win, ev.arg1, ev.arg2);
|
|
} else if (ev.type == GUI_EVENT_CLOSE) {
|
|
sys_exit(0);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|