mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-27 10:38:46 -04:00
b210446460
Checkpoint exact NCLIP and cull-scale code generation, overlay ABI 25, draw-area clipping, timeline and presented-image rings, mod counters, hotkey matching and savestate messages. Add canonical external ROM/disc verification with immutable plugin-scoped snapshots. Focused recompiler/runtime validation: 16 tests passed.
138 lines
4.8 KiB
C
138 lines
4.8 KiB
C
#include "present_image_ring.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
uint32_t frame;
|
|
uint16_t w;
|
|
uint8_t valid;
|
|
uint16_t *px; /* PRESENT_IMAGE_RING_MAX_W * PRESENT_IMAGE_RING_H, RGB565 */
|
|
} PresentImage;
|
|
|
|
static PresentImage s_ring[PRESENT_IMAGE_RING_CAP];
|
|
static uint16_t *s_pixels = NULL;
|
|
static uint32_t s_head = 0;
|
|
static int s_enabled = -1;
|
|
static int s_frozen = 0;
|
|
|
|
static int ring_ready(void) {
|
|
if (s_enabled < 0) {
|
|
const char *e = getenv("PSX_PRESENT_IMAGE_RING");
|
|
s_enabled = (!e || !*e || *e != '0') ? 1 : 0;
|
|
}
|
|
if (!s_enabled) return 0;
|
|
if (!s_pixels) {
|
|
const size_t per = (size_t)PRESENT_IMAGE_RING_MAX_W * PRESENT_IMAGE_RING_H;
|
|
s_pixels = (uint16_t *)calloc((size_t)PRESENT_IMAGE_RING_CAP * per, sizeof(uint16_t));
|
|
if (!s_pixels) { s_enabled = 0; return 0; }
|
|
for (int i = 0; i < PRESENT_IMAGE_RING_CAP; i++)
|
|
s_ring[i].px = s_pixels + (size_t)i * per;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int present_image_ring_thumb_w(int ww, int wh) {
|
|
if (ww <= 0 || wh <= 0) return 0;
|
|
long w = ((long)ww * PRESENT_IMAGE_RING_H + wh / 2) / wh;
|
|
if (w < 1) w = 1;
|
|
if (w > PRESENT_IMAGE_RING_MAX_W) w = PRESENT_IMAGE_RING_MAX_W;
|
|
return (int)w;
|
|
}
|
|
|
|
int present_image_ring_accepting(void) {
|
|
return !s_frozen && ring_ready();
|
|
}
|
|
|
|
static PresentImage *next_slot(uint32_t frame, int w) {
|
|
PresentImage *e = &s_ring[s_head];
|
|
s_head = (s_head + 1) % PRESENT_IMAGE_RING_CAP;
|
|
e->frame = frame;
|
|
e->w = (uint16_t)w;
|
|
e->valid = 1;
|
|
return e;
|
|
}
|
|
|
|
static uint16_t pack565(unsigned r, unsigned g, unsigned b) {
|
|
return (uint16_t)(((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3));
|
|
}
|
|
|
|
void present_image_ring_push(uint32_t frame, const uint8_t *rgba, int w,
|
|
int pitch, int bottom_up) {
|
|
if (!rgba || w <= 0 || w > PRESENT_IMAGE_RING_MAX_W) return;
|
|
if (!present_image_ring_accepting()) return;
|
|
PresentImage *e = next_slot(frame, w);
|
|
for (int y = 0; y < PRESENT_IMAGE_RING_H; y++) {
|
|
const int sy = bottom_up ? (PRESENT_IMAGE_RING_H - 1 - y) : y;
|
|
const uint8_t *src = rgba + (size_t)sy * pitch;
|
|
uint16_t *dst = e->px + (size_t)y * w;
|
|
for (int x = 0; x < w; x++)
|
|
dst[x] = pack565(src[4 * x], src[4 * x + 1], src[4 * x + 2]);
|
|
}
|
|
}
|
|
|
|
void present_image_ring_push_argb(uint32_t frame, const uint32_t *argb,
|
|
int sw, int sh, int src_pitch_px) {
|
|
if (!argb || sw <= 0 || sh <= 0) return;
|
|
if (!present_image_ring_accepting()) return;
|
|
const int w = present_image_ring_thumb_w(sw, sh);
|
|
PresentImage *e = next_slot(frame, w);
|
|
for (int y = 0; y < PRESENT_IMAGE_RING_H; y++) {
|
|
const int y0 = (int)((long)y * sh / PRESENT_IMAGE_RING_H);
|
|
int y1 = (int)((long)(y + 1) * sh / PRESENT_IMAGE_RING_H);
|
|
if (y1 <= y0) y1 = y0 + 1;
|
|
for (int x = 0; x < w; x++) {
|
|
const int x0 = (int)((long)x * sw / w);
|
|
int x1 = (int)((long)(x + 1) * sw / w);
|
|
if (x1 <= x0) x1 = x0 + 1;
|
|
unsigned r = 0, g = 0, b = 0, n = 0;
|
|
for (int yy = y0; yy < y1 && yy < sh; yy++)
|
|
for (int xx = x0; xx < x1 && xx < sw; xx++) {
|
|
const uint32_t p = argb[(size_t)yy * src_pitch_px + xx];
|
|
r += (p >> 16) & 0xFF; g += (p >> 8) & 0xFF; b += p & 0xFF; n++;
|
|
}
|
|
if (!n) n = 1;
|
|
e->px[(size_t)y * w + x] = pack565(r / n, g / n, b / n);
|
|
}
|
|
}
|
|
}
|
|
|
|
void present_image_ring_set_frozen(int frozen) { s_frozen = frozen ? 1 : 0; }
|
|
int present_image_ring_frozen(void) { return s_frozen; }
|
|
|
|
void present_image_ring_span(uint32_t *oldest, uint32_t *newest, int *count) {
|
|
uint32_t lo = UINT32_MAX, hi = 0;
|
|
int n = 0;
|
|
for (int i = 0; i < PRESENT_IMAGE_RING_CAP; i++) {
|
|
if (!s_ring[i].valid) continue;
|
|
n++;
|
|
if (s_ring[i].frame < lo) lo = s_ring[i].frame;
|
|
if (s_ring[i].frame > hi) hi = s_ring[i].frame;
|
|
}
|
|
if (oldest) *oldest = n ? lo : 0;
|
|
if (newest) *newest = hi;
|
|
if (count) *count = n;
|
|
}
|
|
|
|
int present_image_ring_get_rgb(uint32_t frame, uint8_t **rgb, int *w, int *h) {
|
|
if (!rgb || !s_pixels) return 0;
|
|
for (int i = 0; i < PRESENT_IMAGE_RING_CAP; i++) {
|
|
const PresentImage *e = &s_ring[i];
|
|
if (!e->valid || e->frame != frame) continue;
|
|
const size_t n = (size_t)e->w * PRESENT_IMAGE_RING_H;
|
|
uint8_t *out = (uint8_t *)malloc(n * 3);
|
|
if (!out) return 0;
|
|
for (size_t k = 0; k < n; k++) {
|
|
const uint16_t p = e->px[k];
|
|
out[3 * k] = (uint8_t)(((p >> 11) & 31) << 3);
|
|
out[3 * k + 1] = (uint8_t)(((p >> 5) & 63) << 2);
|
|
out[3 * k + 2] = (uint8_t)((p & 31) << 3);
|
|
}
|
|
*rgb = out;
|
|
if (w) *w = e->w;
|
|
if (h) *h = PRESENT_IMAGE_RING_H;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|