implements PLN-0068
This commit is contained in:
parent
1256d33a72
commit
668a061964
@ -658,7 +658,7 @@ impl AssetManager {
|
||||
let offset = (p * 16 + c) * 2;
|
||||
let color_raw =
|
||||
u16::from_le_bytes([palette_data[offset], palette_data[offset + 1]]);
|
||||
*slot = Color(color_raw);
|
||||
*slot = Color::from_rgb565_lossy(color_raw);
|
||||
}
|
||||
}
|
||||
|
||||
@ -690,7 +690,7 @@ impl AssetManager {
|
||||
let offset = (p * 16 + c) * 2;
|
||||
let color_raw =
|
||||
u16::from_le_bytes([palette_data[offset], palette_data[offset + 1]]);
|
||||
*slot = Color(color_raw);
|
||||
*slot = Color::from_rgb565_lossy(color_raw);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1313,7 +1313,7 @@ mod tests {
|
||||
AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode");
|
||||
|
||||
assert_eq!(bank.pixel_indices, vec![1, 0, 2, 3]);
|
||||
assert_eq!(bank.palettes[0][0], Color(0x1234));
|
||||
assert_eq!(bank.palettes[0][0], Color::from_rgb565_lossy(0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -381,7 +381,7 @@ impl Gfx {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, color: Color) {
|
||||
self.back.fill(color.0);
|
||||
self.back.fill(color.to_rgb565_lossy());
|
||||
}
|
||||
|
||||
/// Rectangle with blend mode.
|
||||
@ -406,7 +406,7 @@ impl Gfx {
|
||||
let x1 = (x + w).clamp(0, fw);
|
||||
let y1 = (y + h).clamp(0, fh);
|
||||
|
||||
let src = color.0;
|
||||
let src = color.to_rgb565_lossy();
|
||||
|
||||
for yy in y0..y1 {
|
||||
let row = (yy as usize) * self.w;
|
||||
@ -429,7 +429,7 @@ impl Gfx {
|
||||
return;
|
||||
}
|
||||
if x >= 0 && x < self.w as i32 && y >= 0 && y < self.h as i32 {
|
||||
self.back[y as usize * self.w + x as usize] = color.0;
|
||||
self.back[y as usize * self.w + x as usize] = color.to_rgb565_lossy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -582,7 +582,7 @@ impl Gfx {
|
||||
return;
|
||||
}
|
||||
for x in start..=end {
|
||||
self.back[y as usize * self.w + x as usize] = color.0;
|
||||
self.back[y as usize * self.w + x as usize] = color.to_rgb565_lossy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -600,7 +600,7 @@ impl Gfx {
|
||||
return;
|
||||
}
|
||||
for y in start..=end {
|
||||
self.back[y as usize * self.w + x as usize] = color.0;
|
||||
self.back[y as usize * self.w + x as usize] = color.to_rgb565_lossy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,7 +657,7 @@ impl Gfx {
|
||||
update: &ResolverUpdate,
|
||||
resolved_glyph_slots: &[usize; 4],
|
||||
) {
|
||||
self.back.fill(Color::BLACK.raw());
|
||||
self.back.fill(Color::BLACK.to_rgb565_lossy());
|
||||
self.populate_layer_buckets();
|
||||
|
||||
for (layer_index, glyph_slot) in
|
||||
@ -779,7 +779,7 @@ impl Gfx {
|
||||
}
|
||||
|
||||
let color = tile.bank.resolve_color(tile.entry.palette_id, px_index);
|
||||
target.back[world_y as usize * target.screen_w + world_x as usize] = color.raw();
|
||||
target.back[world_y as usize * target.screen_w + world_x as usize] = color.to_rgb565_lossy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -834,7 +834,7 @@ impl Gfx {
|
||||
// 3. Resolve color via palette (from the tile inside the sprite)
|
||||
let color = bank.resolve_color(sprite.glyph.palette_id, px_index);
|
||||
|
||||
back[world_y as usize * screen_w + world_x as usize] = color.raw();
|
||||
back[world_y as usize * screen_w + world_x as usize] = color.to_rgb565_lossy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -848,17 +848,17 @@ impl Gfx {
|
||||
|
||||
let weight = level as u16;
|
||||
let inv_weight = 31 - weight;
|
||||
let (fr, fg, fb) = Color::unpack_to_native(fade_color.0);
|
||||
let (fr, fg, fb) = unpack_rgb565_native(fade_color.to_rgb565_lossy());
|
||||
|
||||
for px in back.iter_mut() {
|
||||
let (sr, sg, sb) = Color::unpack_to_native(*px);
|
||||
let (sr, sg, sb) = unpack_rgb565_native(*px);
|
||||
|
||||
// Formula: (src * weight + fade * inv_weight) / 31
|
||||
let r = ((sr as u16 * weight + fr as u16 * inv_weight) / 31) as u8;
|
||||
let g = ((sg as u16 * weight + fg as u16 * inv_weight) / 31) as u8;
|
||||
let b = ((sb as u16 * weight + fb as u16 * inv_weight) / 31) as u8;
|
||||
|
||||
*px = Color::pack_from_native(r, g, b);
|
||||
*px = pack_rgb565_native(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
@ -900,7 +900,7 @@ impl Gfx {
|
||||
}
|
||||
|
||||
let glyph = glyph_for_char(c);
|
||||
let raw = color.0;
|
||||
let raw = color.to_rgb565_lossy();
|
||||
|
||||
let row_start = (-y).clamp(0, glyph_h) as usize;
|
||||
let row_end = (screen_h - y).clamp(0, glyph_h) as usize;
|
||||
@ -932,49 +932,60 @@ fn blend_rgb565(dst: u16, src: u16, mode: BlendMode) -> u16 {
|
||||
BlendMode::None => src,
|
||||
|
||||
BlendMode::Half => {
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
let (dr, dg, db) = unpack_rgb565_native(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565_native(src);
|
||||
let r = ((dr as u16 + sr as u16) >> 1) as u8;
|
||||
let g = ((dg as u16 + sg as u16) >> 1) as u8;
|
||||
let b = ((db as u16 + sb as u16) >> 1) as u8;
|
||||
Color::pack_from_native(r, g, b)
|
||||
pack_rgb565_native(r, g, b)
|
||||
}
|
||||
|
||||
BlendMode::HalfPlus => {
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
let (dr, dg, db) = unpack_rgb565_native(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565_native(src);
|
||||
|
||||
let r = (dr as u16 + ((sr as u16) >> 1)).min(31) as u8;
|
||||
let g = (dg as u16 + ((sg as u16) >> 1)).min(63) as u8;
|
||||
let b = (db as u16 + ((sb as u16) >> 1)).min(31) as u8;
|
||||
|
||||
Color::pack_from_native(r, g, b)
|
||||
pack_rgb565_native(r, g, b)
|
||||
}
|
||||
|
||||
BlendMode::HalfMinus => {
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
let (dr, dg, db) = unpack_rgb565_native(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565_native(src);
|
||||
|
||||
let r = (dr as i16 - ((sr as i16) >> 1)).max(0) as u8;
|
||||
let g = (dg as i16 - ((sg as i16) >> 1)).max(0) as u8;
|
||||
let b = (db as i16 - ((sb as i16) >> 1)).max(0) as u8;
|
||||
|
||||
Color::pack_from_native(r, g, b)
|
||||
pack_rgb565_native(r, g, b)
|
||||
}
|
||||
|
||||
BlendMode::Full => {
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
let (dr, dg, db) = unpack_rgb565_native(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565_native(src);
|
||||
|
||||
let r = (dr as u16 + sr as u16).min(31) as u8;
|
||||
let g = (dg as u16 + sg as u16).min(63) as u8;
|
||||
let b = (db as u16 + sb as u16).min(31) as u8;
|
||||
|
||||
Color::pack_from_native(r, g, b)
|
||||
pack_rgb565_native(r, g, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_rgb565_native(px: u16) -> (u8, u8, u8) {
|
||||
let r = ((px >> 11) & 0x1F) as u8;
|
||||
let g = ((px >> 5) & 0x3F) as u8;
|
||||
let b = (px & 0x1F) as u8;
|
||||
(r, g, b)
|
||||
}
|
||||
|
||||
fn pack_rgb565_native(r: u8, g: u8, b: u8) -> u16 {
|
||||
((r as u16 & 0x1F) << 11) | ((g as u16 & 0x3F) << 5) | (b as u16 & 0x1F)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -1133,7 +1144,7 @@ mod tests {
|
||||
gfx.hud_fade_level = 31;
|
||||
gfx.render_scene_from_cache(&cache, &update, &[0, 0, 0, 0]);
|
||||
|
||||
assert_eq!(gfx.back[0], Color::RED.raw());
|
||||
assert_eq!(gfx.back[0], Color::RED.to_rgb565_lossy());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1184,7 +1195,7 @@ mod tests {
|
||||
|
||||
gfx.render_scene_from_cache(&cache, &update, &[0, 0, 0, 0]);
|
||||
|
||||
assert_eq!(gfx.back[0], Color::BLUE.raw());
|
||||
assert_eq!(gfx.back[0], Color::BLUE.to_rgb565_lossy());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,75 +1,87 @@
|
||||
/// Represents a 16-bit color in the RGB565 format.
|
||||
///
|
||||
/// The RGB565 format is a common high-color representation for embedded systems:
|
||||
/// - **Red**: 5 bits (0..31)
|
||||
/// - **Green**: 6 bits (0..63)
|
||||
/// - **Blue**: 5 bits (0..31)
|
||||
///
|
||||
/// Prometeu does not have a hardware alpha channel. Transparency is achieved
|
||||
/// by using a specific color key (Magenta / 0xF81F) which the GFX engine
|
||||
/// skips during rendering.
|
||||
/// Represents a canonical RGBA8888 color in RGBA channel order.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Color(pub u16);
|
||||
pub struct Color(pub u32);
|
||||
|
||||
impl Color {
|
||||
pub const BLACK: Self = Self::rgb(0, 0, 0); // 0x0000
|
||||
pub const WHITE: Self = Self::rgb(255, 255, 255); // 0xFFFF
|
||||
pub const RED: Self = Self::rgb(255, 0, 0); // 0xF800
|
||||
pub const GREEN: Self = Self::rgb(0, 255, 0); // 0x07E0
|
||||
pub const BLUE: Self = Self::rgb(0, 0, 255); // 0x001F
|
||||
pub const YELLOW: Self = Self::rgb(255, 255, 0); // 0xFFE0
|
||||
pub const BLACK: Self = Self::rgb(0, 0, 0);
|
||||
pub const WHITE: Self = Self::rgb(255, 255, 255);
|
||||
pub const RED: Self = Self::rgb(255, 0, 0);
|
||||
pub const GREEN: Self = Self::rgb(0, 255, 0);
|
||||
pub const BLUE: Self = Self::rgb(0, 0, 255);
|
||||
pub const YELLOW: Self = Self::rgb(255, 255, 0);
|
||||
pub const ORANGE: Self = Self::rgb(255, 128, 0);
|
||||
pub const INDIGO: Self = Self::rgb(75, 0, 130);
|
||||
pub const GRAY: Self = Self::rgb(128, 128, 128);
|
||||
pub const CYAN: Self = Self::rgb(0, 255, 255); // 0x07FF
|
||||
pub const MAGENTA: Self = Self::rgb(255, 0, 255); // 0xF81F
|
||||
pub const COLOR_KEY: Self = Self::MAGENTA;
|
||||
pub const TRANSPARENT: Self = Self::MAGENTA;
|
||||
pub const CYAN: Self = Self::rgb(0, 255, 255);
|
||||
pub const MAGENTA: Self = Self::rgb(255, 0, 255);
|
||||
pub const TRANSPARENT: Self = Self::rgba(0, 0, 0, 0);
|
||||
#[deprecated(note = "Use RGBA alpha transparency; COLOR_KEY is temporary migration residue.")]
|
||||
pub const COLOR_KEY: Self = Self::TRANSPARENT;
|
||||
|
||||
/// Extracts channels in the native RGB565 range:
|
||||
/// R: 0..31, G: 0..63, B: 0..31
|
||||
/// Extracts RGBA8888 channels from canonical RGBA raw order.
|
||||
#[inline(always)]
|
||||
pub const fn unpack_to_native(px: u16) -> (u8, u8, u8) {
|
||||
let r = ((px >> 11) & 0x1F) as u8;
|
||||
let g = ((px >> 5) & 0x3F) as u8;
|
||||
let b = (px & 0x1F) as u8;
|
||||
(r, g, b)
|
||||
pub const fn unpack_to_native(px: u32) -> (u8, u8, u8, u8) {
|
||||
let r = ((px >> 24) & 0xFF) as u8;
|
||||
let g = ((px >> 16) & 0xFF) as u8;
|
||||
let b = ((px >> 8) & 0xFF) as u8;
|
||||
let a = (px & 0xFF) as u8;
|
||||
(r, g, b, a)
|
||||
}
|
||||
|
||||
/// Packs channels from the native RGB565 range into a pixel:
|
||||
/// R: 0..31, G: 0..63, B: 0..31
|
||||
/// Packs RGBA8888 channels into canonical RGBA raw order.
|
||||
#[inline(always)]
|
||||
pub const fn pack_from_native(r: u8, g: u8, b: u8) -> u16 {
|
||||
((r as u16 & 0x1F) << 11) | ((g as u16 & 0x3F) << 5) | (b as u16 & 0x1F)
|
||||
pub const fn pack_from_native(r: u8, g: u8, b: u8, a: u8) -> u32 {
|
||||
((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | a as u32
|
||||
}
|
||||
|
||||
/// Creates an RGB565 color from 8-bit components (0..255).
|
||||
/// Creates an opaque RGBA8888 color from 8-bit RGB components.
|
||||
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
let r5 = (r as u16 >> 3) & 0x1F;
|
||||
let g6 = (g as u16 >> 2) & 0x3F;
|
||||
let b5 = (b as u16 >> 3) & 0x1F;
|
||||
Self::rgba(r, g, b, 255)
|
||||
}
|
||||
|
||||
Self((r5 << 11) | (g6 << 5) | b5)
|
||||
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Self(Self::pack_from_native(r, g, b, a))
|
||||
}
|
||||
|
||||
pub const fn gray_scale(c: u8) -> Self {
|
||||
Self::rgb(c, c, c)
|
||||
}
|
||||
|
||||
pub const fn from_raw(raw: u16) -> Self {
|
||||
pub const fn from_raw(raw: u32) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
pub const fn raw(self) -> u16 {
|
||||
pub const fn raw(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub const fn hex(self) -> i32 {
|
||||
let (r5, g6, b5) = Self::unpack_to_native(self.0);
|
||||
let r8 = ((r5 as u32) << 3) | ((r5 as u32) >> 2);
|
||||
let g8 = ((g6 as u32) << 2) | ((g6 as u32) >> 4);
|
||||
let b8 = ((b5 as u32) << 3) | ((b5 as u32) >> 2);
|
||||
let hex = r8 << 16 | g8 << 8 | b8;
|
||||
let (r, g, b, _) = Self::unpack_to_native(self.0);
|
||||
let hex = (r as u32) << 16 | (g as u32) << 8 | b as u32;
|
||||
hex as i32
|
||||
}
|
||||
|
||||
pub const fn alpha(self) -> u8 {
|
||||
(self.0 & 0xFF) as u8
|
||||
}
|
||||
|
||||
#[deprecated(note = "Temporary bridge until the renderer buffer migrates to RGBA8888.")]
|
||||
pub const fn to_rgb565_lossy(self) -> u16 {
|
||||
let (r, g, b, _) = Self::unpack_to_native(self.0);
|
||||
let r5 = (r as u16 >> 3) & 0x1F;
|
||||
let g6 = (g as u16 >> 2) & 0x3F;
|
||||
let b5 = (b as u16 >> 3) & 0x1F;
|
||||
(r5 << 11) | (g6 << 5) | b5
|
||||
}
|
||||
|
||||
#[deprecated(note = "Temporary bridge until asset palettes migrate to RGBA8888.")]
|
||||
pub const fn from_rgb565_lossy(raw: u16) -> Self {
|
||||
let r5 = ((raw >> 11) & 0x1F) as u32;
|
||||
let g6 = ((raw >> 5) & 0x3F) as u32;
|
||||
let b5 = (raw & 0x1F) as u32;
|
||||
let r = ((r5 << 3) | (r5 >> 2)) as u8;
|
||||
let g = ((g6 << 2) | (g6 >> 4)) as u8;
|
||||
let b = ((b5 << 3) | (b5 >> 2)) as u8;
|
||||
Self::rgba(r, g, b, 255)
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,9 +32,10 @@ pub struct GlyphBank {
|
||||
|
||||
/// Decoded pixel data stored as one palette index per pixel.
|
||||
/// Serialized payloads are packed; runtime memory is expanded.
|
||||
/// Index 0 is always reserved for transparency.
|
||||
/// Palette indices are ordinary indices; transparency is resolved through
|
||||
/// the RGBA alpha channel of the palette entry.
|
||||
pub pixel_indices: Vec<u8>,
|
||||
/// Runtime-facing v1 palette table: 64 palettes of 16 RGB565 colors each.
|
||||
/// Runtime-facing v1 palette table: 64 palettes of 16 RGBA8888 colors each.
|
||||
pub palettes: [[Color; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1],
|
||||
}
|
||||
|
||||
@ -45,7 +46,7 @@ impl GlyphBank {
|
||||
tile_size,
|
||||
width,
|
||||
height,
|
||||
pixel_indices: vec![0; width * height], // Index 0 = Transparent
|
||||
pixel_indices: vec![0; width * height],
|
||||
palettes: [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1],
|
||||
}
|
||||
}
|
||||
@ -65,22 +66,16 @@ impl GlyphBank {
|
||||
if pixel_x < self.width && pixel_y < self.height {
|
||||
self.pixel_indices[pixel_y * self.width + pixel_x]
|
||||
} else {
|
||||
0 // Default to transparent if out of bounds
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a 4-bit index to a real RGB565 Color using the specified palette.
|
||||
/// Maps a 4-bit index to a real RGBA8888 Color using the specified palette.
|
||||
pub fn resolve_color(&self, palette_id: u8, pixel_index: u8) -> Color {
|
||||
// Hardware Rule: Index 0 is always transparent.
|
||||
// We use Magenta as the 'transparent' signal color during composition.
|
||||
if pixel_index == 0 {
|
||||
return Color::COLOR_KEY;
|
||||
}
|
||||
|
||||
self.palettes
|
||||
.get(palette_id as usize)
|
||||
.and_then(|palette| palette.get(pixel_index as usize))
|
||||
.copied()
|
||||
.unwrap_or(Color::COLOR_KEY)
|
||||
.unwrap_or(Color::TRANSPARENT)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,6 @@ pub enum Syscall {
|
||||
GfxDrawDisc = 0x1005,
|
||||
GfxDrawSquare = 0x1006,
|
||||
GfxDrawText = 0x1008,
|
||||
GfxClear565 = 0x1010,
|
||||
ComposerBindScene = 0x1101,
|
||||
ComposerUnbindScene = 0x1102,
|
||||
ComposerSetCamera = 0x1103,
|
||||
|
||||
@ -29,8 +29,4 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
|
||||
.args(4)
|
||||
.caps(caps::GFX)
|
||||
.cost(20),
|
||||
SyscallRegistryEntry::builder(Syscall::GfxClear565, "gfx", "clear_565")
|
||||
.args(1)
|
||||
.caps(caps::GFX)
|
||||
.cost(20),
|
||||
];
|
||||
|
||||
@ -21,7 +21,6 @@ impl Syscall {
|
||||
0x1005 => Some(Self::GfxDrawDisc),
|
||||
0x1006 => Some(Self::GfxDrawSquare),
|
||||
0x1008 => Some(Self::GfxDrawText),
|
||||
0x1010 => Some(Self::GfxClear565),
|
||||
0x1101 => Some(Self::ComposerBindScene),
|
||||
0x1102 => Some(Self::ComposerUnbindScene),
|
||||
0x1103 => Some(Self::ComposerSetCamera),
|
||||
@ -71,7 +70,6 @@ impl Syscall {
|
||||
Self::GfxDrawDisc => "GfxDrawDisc",
|
||||
Self::GfxDrawSquare => "GfxDrawSquare",
|
||||
Self::GfxDrawText => "GfxDrawText",
|
||||
Self::GfxClear565 => "GfxClear565",
|
||||
Self::ComposerBindScene => "ComposerBindScene",
|
||||
Self::ComposerUnbindScene => "ComposerUnbindScene",
|
||||
Self::ComposerSetCamera => "ComposerSetCamera",
|
||||
|
||||
@ -210,10 +210,6 @@ fn status_first_syscall_signatures_are_pinned() {
|
||||
assert_eq!(draw_text.arg_slots, 4);
|
||||
assert_eq!(draw_text.ret_slots, 0);
|
||||
|
||||
let clear_565 = meta_for(Syscall::GfxClear565);
|
||||
assert_eq!(clear_565.arg_slots, 1);
|
||||
assert_eq!(clear_565.ret_slots, 0);
|
||||
|
||||
let bind_scene = meta_for(Syscall::ComposerBindScene);
|
||||
assert_eq!(bind_scene.arg_slots, 1);
|
||||
assert_eq!(bind_scene.ret_slots, 1);
|
||||
|
||||
@ -95,8 +95,10 @@ impl VmRuntimeHost<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_color(&self, value: i64) -> Color {
|
||||
Color::from_raw(value as u16)
|
||||
pub(crate) fn get_color(&self, value: i64) -> Result<Color, VmFault> {
|
||||
let raw = u32::try_from(value)
|
||||
.map_err(|_| VmFault::Trap(TRAP_OOB, "Color value out of bounds".into()))?;
|
||||
Ok(Color::from_raw(raw))
|
||||
}
|
||||
|
||||
fn int_arg_to_usize_status(value: i64) -> Result<usize, ComposerOpStatus> {
|
||||
@ -126,7 +128,6 @@ impl VmRuntimeHost<'_> {
|
||||
| Syscall::GfxDrawDisc
|
||||
| Syscall::GfxDrawSquare
|
||||
| Syscall::GfxDrawText
|
||||
| Syscall::GfxClear565
|
||||
| Syscall::ComposerBindScene
|
||||
| Syscall::ComposerUnbindScene
|
||||
| Syscall::ComposerSetCamera
|
||||
@ -183,7 +184,7 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
Syscall::SystemHasCart => unreachable!(),
|
||||
Syscall::SystemRunCart => unreachable!(),
|
||||
Syscall::GfxClear => {
|
||||
let color = self.get_color(expect_int(args, 0)?);
|
||||
let color = self.get_color(expect_int(args, 0)?)?;
|
||||
hw.gfx_mut().clear(color);
|
||||
Ok(())
|
||||
}
|
||||
@ -192,7 +193,7 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
let y = expect_int(args, 1)? as i32;
|
||||
let w = expect_int(args, 2)? as i32;
|
||||
let h = expect_int(args, 3)? as i32;
|
||||
let color = self.get_color(expect_int(args, 4)?);
|
||||
let color = self.get_color(expect_int(args, 4)?)?;
|
||||
hw.gfx_mut().fill_rect(x, y, w, h, color);
|
||||
Ok(())
|
||||
}
|
||||
@ -201,7 +202,7 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
let y1 = expect_int(args, 1)? as i32;
|
||||
let x2 = expect_int(args, 2)? as i32;
|
||||
let y2 = expect_int(args, 3)? as i32;
|
||||
let color = self.get_color(expect_int(args, 4)?);
|
||||
let color = self.get_color(expect_int(args, 4)?)?;
|
||||
hw.gfx_mut().draw_line(x1, y1, x2, y2, color);
|
||||
Ok(())
|
||||
}
|
||||
@ -209,7 +210,7 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
let x = expect_int(args, 0)? as i32;
|
||||
let y = expect_int(args, 1)? as i32;
|
||||
let r = expect_int(args, 2)? as i32;
|
||||
let color = self.get_color(expect_int(args, 3)?);
|
||||
let color = self.get_color(expect_int(args, 3)?)?;
|
||||
hw.gfx_mut().draw_circle(x, y, r, color);
|
||||
Ok(())
|
||||
}
|
||||
@ -217,8 +218,8 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
let x = expect_int(args, 0)? as i32;
|
||||
let y = expect_int(args, 1)? as i32;
|
||||
let r = expect_int(args, 2)? as i32;
|
||||
let border_color = self.get_color(expect_int(args, 3)?);
|
||||
let fill_color = self.get_color(expect_int(args, 4)?);
|
||||
let border_color = self.get_color(expect_int(args, 3)?)?;
|
||||
let fill_color = self.get_color(expect_int(args, 4)?)?;
|
||||
hw.gfx_mut().draw_disc(x, y, r, border_color, fill_color);
|
||||
Ok(())
|
||||
}
|
||||
@ -227,8 +228,8 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
let y = expect_int(args, 1)? as i32;
|
||||
let w = expect_int(args, 2)? as i32;
|
||||
let h = expect_int(args, 3)? as i32;
|
||||
let border_color = self.get_color(expect_int(args, 4)?);
|
||||
let fill_color = self.get_color(expect_int(args, 5)?);
|
||||
let border_color = self.get_color(expect_int(args, 4)?)?;
|
||||
let fill_color = self.get_color(expect_int(args, 5)?)?;
|
||||
hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color);
|
||||
Ok(())
|
||||
}
|
||||
@ -236,18 +237,10 @@ impl NativeInterface for VmRuntimeHost<'_> {
|
||||
let x = expect_int(args, 0)? as i32;
|
||||
let y = expect_int(args, 1)? as i32;
|
||||
let msg = expect_string(args, 2, "message")?;
|
||||
let color = self.get_color(expect_int(args, 3)?);
|
||||
let color = self.get_color(expect_int(args, 3)?)?;
|
||||
hw.gfx_mut().draw_text(x, y, &msg, color);
|
||||
Ok(())
|
||||
}
|
||||
Syscall::GfxClear565 => {
|
||||
let color_val = expect_int(args, 0)? as u32;
|
||||
if color_val > 0xFFFF {
|
||||
return Err(VmFault::Trap(TRAP_OOB, "Color value out of bounds".into()));
|
||||
}
|
||||
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
||||
Ok(())
|
||||
}
|
||||
Syscall::ComposerBindScene => {
|
||||
let scene_bank_id = match Self::int_arg_to_usize_status(expect_int(args, 0)?) {
|
||||
Ok(id) => id,
|
||||
|
||||
@ -2545,12 +2545,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_syscall_results_count_mismatch_panic() {
|
||||
// GfxClear565 (0x1010) expects 0 results
|
||||
// GfxClear (0x1001) expects 0 results
|
||||
let rom = vec![
|
||||
0x17, 0x00, // PushI32
|
||||
0x00, 0x00, 0x00, 0x00, // value 0
|
||||
0x70, 0x00, // Syscall + Reserved
|
||||
0x10, 0x10, 0x00, 0x00, // Syscall ID 0x1010
|
||||
0x01, 0x10, 0x00, 0x00, // Syscall ID 0x1001
|
||||
];
|
||||
|
||||
struct BadNative;
|
||||
@ -2562,7 +2562,7 @@ mod tests {
|
||||
ret: &mut HostReturn,
|
||||
_ctx: &mut HostContext,
|
||||
) -> Result<(), VmFault> {
|
||||
// Wrong: GfxClear565 is void but we push something
|
||||
// Wrong: GfxClear is void but we push something
|
||||
ret.push_int(42);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -35,4 +35,4 @@
|
||||
{"type":"discussion","id":"DSC-0032","status":"done","ticket":"system-os-lifecycle-process-task-contract","title":"Agenda - SystemOS Lifecycle, Process and Task Contract","created_at":"2026-05-14","updated_at":"2026-05-15","tags":["runtime","os","lifecycle","process","task","shell","firmware"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0041","file":"discussion/lessons/DSC-0032-system-os-lifecycle-process-task-contract/LSN-0041-systemos-lifecycle-authority.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
|
||||
{"type":"discussion","id":"DSC-0033","status":"done","ticket":"system-os-service-ownership-and-module-layout","title":"Agenda - SystemOS Service Ownership and Module Layout","created_at":"2026-05-14","updated_at":"2026-05-15","tags":["runtime","os","services","module-layout","vm","window-manager","logging"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0033-system-os-service-ownership-and-module-layout/LSN-0042-systemos-service-ownership-boundary.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
|
||||
{"type":"discussion","id":"DSC-0036","status":"done","ticket":"prometeu-hub-ui-direction","title":"Agenda - Prometeu Hub UI Direction","created_at":"2026-05-15","updated_at":"2026-05-22","tags":["hub","ui","shell","system-apps","lifecycle","design-system"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0036-prometeu-hub-ui-direction/LSN-0045-hub-ui-slices-should-prove-os-boundaries.md","status":"done","created_at":"2026-05-22","updated_at":"2026-05-22"}]}
|
||||
{"type":"discussion","id":"DSC-0037","status":"in_progress","ticket":"rgba8888-framebuffer-and-pixel-format-direction","title":"Agenda - RGBA8888 Framebuffer and Pixel Format Direction","created_at":"2026-05-22","updated_at":"2026-05-23","tags":["gfx","framebuffer","rgb565","rgba8888","renderer","assets","host","backend"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-rgba8888-framebuffer-and-pixel-format-direction.md","status":"accepted","created_at":"2026-05-22","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0029","file":"DEC-0029-rgba8888-runtime-pixel-format-contract.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0067","file":"PLN-0067-rgba8888-published-contracts-and-specs.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0068","file":"PLN-0068-rgba8888-color-type-hal-and-abi-surface.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0069","file":"PLN-0069-rgba8888-cpu-renderer-and-composition.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0070","file":"PLN-0070-rgba8888-asset-palettes-tooling-and-fixtures.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0071","file":"PLN-0071-rgba8888-host-presentation-path.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0072","file":"PLN-0072-rgba8888-residue-removal-and-end-to-end-validation.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0037","status":"in_progress","ticket":"rgba8888-framebuffer-and-pixel-format-direction","title":"Agenda - RGBA8888 Framebuffer and Pixel Format Direction","created_at":"2026-05-22","updated_at":"2026-05-23","tags":["gfx","framebuffer","rgb565","rgba8888","renderer","assets","host","backend"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-rgba8888-framebuffer-and-pixel-format-direction.md","status":"accepted","created_at":"2026-05-22","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0029","file":"DEC-0029-rgba8888-runtime-pixel-format-contract.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0067","file":"PLN-0067-rgba8888-published-contracts-and-specs.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0068","file":"PLN-0068-rgba8888-color-type-hal-and-abi-surface.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0069","file":"PLN-0069-rgba8888-cpu-renderer-and-composition.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0070","file":"PLN-0070-rgba8888-asset-palettes-tooling-and-fixtures.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0071","file":"PLN-0071-rgba8888-host-presentation-path.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0072","file":"PLN-0072-rgba8888-residue-removal-and-end-to-end-validation.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]}],"lessons":[]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0068
|
||||
ticket: rgba8888-framebuffer-and-pixel-format-direction
|
||||
title: RGBA8888 Color Type HAL and ABI Surface
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-05-23
|
||||
ref_decisions: [DEC-0029]
|
||||
tags: [gfx, framebuffer, rgb565, rgba8888, renderer, assets, host, backend]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user