Compare commits
2 Commits
d2fbe62f7a
...
38544ed3f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 38544ed3f1 | |||
| ab0e577c6f |
@ -774,11 +774,11 @@ impl Gfx {
|
||||
let fetch_x = if tile.entry.flip_x() { size - 1 - local_x } else { local_x };
|
||||
let fetch_y = if tile.entry.flip_y() { size - 1 - local_y } else { local_y };
|
||||
let px_index = tile.bank.get_pixel_index(tile.entry.glyph_id, fetch_x, fetch_y);
|
||||
if px_index == 0 {
|
||||
let color = tile.bank.resolve_color(tile.entry.palette_id, px_index);
|
||||
if color.alpha() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -823,17 +823,12 @@ impl Gfx {
|
||||
let fetch_x = if sprite.flip_x { size - 1 - local_x } else { local_x };
|
||||
let fetch_y = if sprite.flip_y { size - 1 - local_y } else { local_y };
|
||||
|
||||
// 1. Get index
|
||||
let px_index = bank.get_pixel_index(sprite.glyph.glyph_id, fetch_x, fetch_y);
|
||||
|
||||
// 2. Transparency
|
||||
if px_index == 0 {
|
||||
let color = bank.resolve_color(sprite.glyph.palette_id, px_index);
|
||||
if color.alpha() == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@ -999,16 +994,23 @@ mod tests {
|
||||
use prometeu_hal::tile::Tile;
|
||||
use prometeu_hal::tilemap::TileMap;
|
||||
|
||||
fn make_glyph_bank(tile_size: TileSize, palette_colors: &[(u8, Color)]) -> GlyphBank {
|
||||
fn make_glyph_bank(tile_size: TileSize, palette_colors: &[(u8, u8, Color)]) -> GlyphBank {
|
||||
let size = tile_size as usize;
|
||||
let mut bank = GlyphBank::new(tile_size, size, size);
|
||||
for (palette_id, color) in palette_colors {
|
||||
bank.palettes[*palette_id as usize][1] = *color;
|
||||
for (palette_id, color_index, color) in palette_colors {
|
||||
bank.palettes[*palette_id as usize][*color_index as usize] = *color;
|
||||
}
|
||||
for y in 0..size {
|
||||
for x in 0..size {
|
||||
bank.pixel_indices[y * bank.width + x] = 1;
|
||||
bank
|
||||
}
|
||||
|
||||
fn make_filled_glyph_bank(
|
||||
tile_size: TileSize,
|
||||
pixel_index: u8,
|
||||
palette_colors: &[(u8, u8, Color)],
|
||||
) -> GlyphBank {
|
||||
let mut bank = make_glyph_bank(tile_size, palette_colors);
|
||||
for index in 0..bank.pixel_indices.len() {
|
||||
bank.pixel_indices[index] = pixel_index;
|
||||
}
|
||||
bank
|
||||
}
|
||||
@ -1094,6 +1096,90 @@ mod tests {
|
||||
assert_eq!(gfx.back[9 * 10 + 9], Color::WHITE.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sprite_draws_opaque_color_index_zero() {
|
||||
let bank = make_filled_glyph_bank(TileSize::Size8, 0, &[(0, 0, Color::GREEN)]);
|
||||
let mut back = vec![Color::BLACK.raw(); 8 * 8];
|
||||
let sprite = Sprite {
|
||||
glyph: Glyph { glyph_id: 0, palette_id: 0 },
|
||||
x: 0,
|
||||
y: 0,
|
||||
layer: 0,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
bank_id: 0,
|
||||
active: true,
|
||||
priority: 0,
|
||||
};
|
||||
|
||||
Gfx::draw_sprite_pixel_by_pixel(&mut back, 8, 8, &sprite, &bank);
|
||||
|
||||
assert_eq!(back[0], Color::GREEN.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sprite_skips_alpha_zero_color_index_zero() {
|
||||
let bank = make_filled_glyph_bank(TileSize::Size8, 0, &[(0, 0, Color::TRANSPARENT)]);
|
||||
let mut back = vec![Color::RED.raw(); 8 * 8];
|
||||
let sprite = Sprite {
|
||||
glyph: Glyph { glyph_id: 0, palette_id: 0 },
|
||||
x: 0,
|
||||
y: 0,
|
||||
layer: 0,
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
bank_id: 0,
|
||||
active: true,
|
||||
priority: 0,
|
||||
};
|
||||
|
||||
Gfx::draw_sprite_pixel_by_pixel(&mut back, 8, 8, &sprite, &bank);
|
||||
|
||||
assert_eq!(back[0], Color::RED.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cached_tile_draws_opaque_color_index_zero() {
|
||||
let bank = make_filled_glyph_bank(TileSize::Size8, 0, &[(0, 0, Color::GREEN)]);
|
||||
let mut back = vec![Color::BLACK.raw(); 8 * 8];
|
||||
let mut target = RenderTarget { back: &mut back, screen_w: 8, screen_h: 8 };
|
||||
let entry = CachedTileEntry {
|
||||
active: true,
|
||||
glyph_id: 0,
|
||||
palette_id: 0,
|
||||
flags: 0,
|
||||
glyph_asset_id: 0,
|
||||
};
|
||||
|
||||
Gfx::draw_cached_tile_pixels(
|
||||
&mut target,
|
||||
CachedTileDraw { x: 0, y: 0, entry, bank: &bank, tile_size: TileSize::Size8 },
|
||||
);
|
||||
|
||||
assert_eq!(target.back[0], Color::GREEN.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cached_tile_skips_alpha_zero_color_index_zero() {
|
||||
let bank = make_filled_glyph_bank(TileSize::Size8, 0, &[(0, 0, Color::TRANSPARENT)]);
|
||||
let mut back = vec![Color::RED.raw(); 8 * 8];
|
||||
let mut target = RenderTarget { back: &mut back, screen_w: 8, screen_h: 8 };
|
||||
let entry = CachedTileEntry {
|
||||
active: true,
|
||||
glyph_id: 0,
|
||||
palette_id: 0,
|
||||
flags: 0,
|
||||
glyph_asset_id: 0,
|
||||
};
|
||||
|
||||
Gfx::draw_cached_tile_pixels(
|
||||
&mut target,
|
||||
CachedTileDraw { x: 0, y: 0, entry, bank: &bank, tile_size: TileSize::Size8 },
|
||||
);
|
||||
|
||||
assert_eq!(target.back[0], Color::RED.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_draw_rect() {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
@ -1127,7 +1213,11 @@ mod tests {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
banks.install_glyph_bank(
|
||||
0,
|
||||
Arc::new(make_glyph_bank(TileSize::Size8, &[(0, Color::RED), (1, Color::GREEN)])),
|
||||
Arc::new(make_filled_glyph_bank(
|
||||
TileSize::Size8,
|
||||
1,
|
||||
&[(0, 1, Color::RED), (1, 1, Color::GREEN)],
|
||||
)),
|
||||
);
|
||||
|
||||
let mut scene = make_scene([0, 0, 0, 0]);
|
||||
@ -1152,9 +1242,15 @@ mod tests {
|
||||
let banks = Arc::new(MemoryBanks::new());
|
||||
banks.install_glyph_bank(
|
||||
0,
|
||||
Arc::new(make_glyph_bank(
|
||||
Arc::new(make_filled_glyph_bank(
|
||||
TileSize::Size8,
|
||||
&[(0, Color::RED), (1, Color::GREEN), (2, Color::BLUE), (4, Color::WHITE)],
|
||||
1,
|
||||
&[
|
||||
(0, 1, Color::RED),
|
||||
(1, 1, Color::GREEN),
|
||||
(2, 1, Color::BLUE),
|
||||
(4, 1, Color::WHITE),
|
||||
],
|
||||
)),
|
||||
);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use prometeu_bytecode::Value;
|
||||
use prometeu_hal::HostContext;
|
||||
use prometeu_hal::{HostContext, color::Color};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct BuiltinTypeKey {
|
||||
@ -56,6 +56,7 @@ pub enum BuiltinScalarType {
|
||||
Int,
|
||||
Float,
|
||||
Bool,
|
||||
Str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -362,12 +363,63 @@ const VEC2_ZERO_LAYOUT: [AbiType; 2] =
|
||||
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
|
||||
const VEC2_ZERO_SLOTS: [BuiltinConstSlotValue; 2] =
|
||||
[BuiltinConstSlotValue::Float(0.0), BuiltinConstSlotValue::Float(0.0)];
|
||||
const BUILTIN_CONSTS: [BuiltinConstMeta; 1] = [BuiltinConstMeta {
|
||||
const COLOR_BLACK_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::BLACK.raw() as i64)];
|
||||
const COLOR_WHITE_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::WHITE.raw() as i64)];
|
||||
const COLOR_RED_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::RED.raw() as i64)];
|
||||
const COLOR_GREEN_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::GREEN.raw() as i64)];
|
||||
const COLOR_BLUE_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::BLUE.raw() as i64)];
|
||||
const COLOR_YELLOW_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::YELLOW.raw() as i64)];
|
||||
const COLOR_ORANGE_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::ORANGE.raw() as i64)];
|
||||
const COLOR_INDIGO_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::INDIGO.raw() as i64)];
|
||||
const COLOR_GRAY_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::GRAY.raw() as i64)];
|
||||
const COLOR_CYAN_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::CYAN.raw() as i64)];
|
||||
const COLOR_MAGENTA_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::MAGENTA.raw() as i64)];
|
||||
const COLOR_TRANSPARENT_SLOTS: [BuiltinConstSlotValue; 1] =
|
||||
[BuiltinConstSlotValue::Int64(Color::TRANSPARENT.raw() as i64)];
|
||||
|
||||
const BUILTIN_CONSTS: [BuiltinConstMeta; 13] = [
|
||||
BuiltinConstMeta {
|
||||
key: BuiltinConstKey::new("vec2", "zero", 1),
|
||||
flat_slot_layout: &VEC2_ZERO_LAYOUT,
|
||||
flat_slot_width: 2,
|
||||
materializer: BuiltinConstMaterializer::Direct(&VEC2_ZERO_SLOTS),
|
||||
}];
|
||||
},
|
||||
color_const_meta("black", &COLOR_BLACK_SLOTS),
|
||||
color_const_meta("white", &COLOR_WHITE_SLOTS),
|
||||
color_const_meta("red", &COLOR_RED_SLOTS),
|
||||
color_const_meta("green", &COLOR_GREEN_SLOTS),
|
||||
color_const_meta("blue", &COLOR_BLUE_SLOTS),
|
||||
color_const_meta("yellow", &COLOR_YELLOW_SLOTS),
|
||||
color_const_meta("orange", &COLOR_ORANGE_SLOTS),
|
||||
color_const_meta("indigo", &COLOR_INDIGO_SLOTS),
|
||||
color_const_meta("gray", &COLOR_GRAY_SLOTS),
|
||||
color_const_meta("cyan", &COLOR_CYAN_SLOTS),
|
||||
color_const_meta("magenta", &COLOR_MAGENTA_SLOTS),
|
||||
color_const_meta("transparent", &COLOR_TRANSPARENT_SLOTS),
|
||||
];
|
||||
|
||||
const fn color_const_meta(
|
||||
name: &'static str,
|
||||
slots: &'static [BuiltinConstSlotValue],
|
||||
) -> BuiltinConstMeta {
|
||||
BuiltinConstMeta {
|
||||
key: BuiltinConstKey::new("color", name, 1),
|
||||
flat_slot_layout: &COLOR_LAYOUT,
|
||||
flat_slot_width: 1,
|
||||
materializer: BuiltinConstMaterializer::Direct(slots),
|
||||
}
|
||||
}
|
||||
|
||||
const VEC2_DOT_ARGS: [AbiType; 4] = [
|
||||
AbiType::Scalar(BuiltinScalarType::Float),
|
||||
@ -379,6 +431,20 @@ const SINGLE_FLOAT_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Float
|
||||
const VEC2_LENGTH_ARGS: [AbiType; 2] =
|
||||
[AbiType::Scalar(BuiltinScalarType::Float), AbiType::Scalar(BuiltinScalarType::Float)];
|
||||
const NO_ARGS: [AbiType; 0] = [];
|
||||
const COLOR_ARGS: [AbiType; 1] = [AbiType::Builtin(COLOR)];
|
||||
const COLOR_RET: [AbiType; 1] = [AbiType::Builtin(COLOR)];
|
||||
const COLOR_RGB_ARGS: [AbiType; 3] = [
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
];
|
||||
const COLOR_RGBA_ARGS: [AbiType; 4] = [
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
AbiType::Scalar(BuiltinScalarType::Int),
|
||||
];
|
||||
const COLOR_HTML_RGBA_ARGS: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Str)];
|
||||
const PAD_RET: [AbiType; 1] = [AbiType::Builtin(INPUT_PAD)];
|
||||
const TOUCH_RET: [AbiType; 1] = [AbiType::Builtin(INPUT_TOUCH)];
|
||||
const BUTTON_RET: [AbiType; 1] = [AbiType::Builtin(INPUT_BUTTON)];
|
||||
@ -388,7 +454,7 @@ const BUTTON_ARGS: [AbiType; 1] = [AbiType::Builtin(INPUT_BUTTON)];
|
||||
const BOOL_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Bool)];
|
||||
const INT_RET: [AbiType; 1] = [AbiType::Scalar(BuiltinScalarType::Int)];
|
||||
|
||||
const INTRINSICS: [IntrinsicMeta; 23] = [
|
||||
const INTRINSICS: [IntrinsicMeta; 42] = [
|
||||
IntrinsicMeta {
|
||||
id: 0x1000,
|
||||
owner: "vec2",
|
||||
@ -411,6 +477,215 @@ const INTRINSICS: [IntrinsicMeta; 23] = [
|
||||
may_allocate: false,
|
||||
implementation: vec2_length,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1100,
|
||||
owner: "color",
|
||||
name: "from_raw",
|
||||
version: 1,
|
||||
arg_layout: &INT_RET,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_from_raw,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1101,
|
||||
owner: "color",
|
||||
name: "rgb",
|
||||
version: 1,
|
||||
arg_layout: &COLOR_RGB_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_rgb,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1102,
|
||||
owner: "color",
|
||||
name: "rgba",
|
||||
version: 1,
|
||||
arg_layout: &COLOR_RGBA_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_rgba,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1103,
|
||||
owner: "color",
|
||||
name: "html_rgba",
|
||||
version: 1,
|
||||
arg_layout: &COLOR_HTML_RGBA_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_html_rgba,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1104,
|
||||
owner: "color",
|
||||
name: "gray_scale",
|
||||
version: 1,
|
||||
arg_layout: &INT_RET,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_gray_scale,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1105,
|
||||
owner: "color",
|
||||
name: "hex",
|
||||
version: 1,
|
||||
arg_layout: &COLOR_ARGS,
|
||||
ret_layout: &INT_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_hex,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1106,
|
||||
owner: "color",
|
||||
name: "alpha",
|
||||
version: 1,
|
||||
arg_layout: &COLOR_ARGS,
|
||||
ret_layout: &INT_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_alpha,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1120,
|
||||
owner: "color",
|
||||
name: "black",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_black,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1121,
|
||||
owner: "color",
|
||||
name: "white",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_white,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1122,
|
||||
owner: "color",
|
||||
name: "red",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_red,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1123,
|
||||
owner: "color",
|
||||
name: "green",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_green,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1124,
|
||||
owner: "color",
|
||||
name: "blue",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_blue,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1125,
|
||||
owner: "color",
|
||||
name: "yellow",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_yellow,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1126,
|
||||
owner: "color",
|
||||
name: "orange",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_orange,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1127,
|
||||
owner: "color",
|
||||
name: "indigo",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_indigo,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1128,
|
||||
owner: "color",
|
||||
name: "gray",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_gray,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x1129,
|
||||
owner: "color",
|
||||
name: "cyan",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_cyan,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x112A,
|
||||
owner: "color",
|
||||
name: "magenta",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_magenta,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x112B,
|
||||
owner: "color",
|
||||
name: "transparent",
|
||||
version: 1,
|
||||
arg_layout: &NO_ARGS,
|
||||
ret_layout: &COLOR_RET,
|
||||
deterministic: true,
|
||||
may_allocate: false,
|
||||
implementation: color_transparent,
|
||||
},
|
||||
IntrinsicMeta {
|
||||
id: 0x2000,
|
||||
owner: "input",
|
||||
@ -698,11 +973,35 @@ fn expect_float_arg(args: &[Value], index: usize) -> Result<f64, IntrinsicExecut
|
||||
})
|
||||
}
|
||||
|
||||
fn expect_int_arg(args: &[Value], index: usize) -> Result<i64, IntrinsicExecutionError> {
|
||||
let value = args
|
||||
.get(index)
|
||||
.ok_or(IntrinsicExecutionError::ArityMismatch { expected: index + 1, got: args.len() })?;
|
||||
value.as_integer().ok_or(IntrinsicExecutionError::TypeMismatch {
|
||||
index,
|
||||
expected: AbiType::Scalar(BuiltinScalarType::Int),
|
||||
})
|
||||
}
|
||||
|
||||
fn expect_string_arg(args: &[Value], index: usize) -> Result<&str, IntrinsicExecutionError> {
|
||||
let value = args
|
||||
.get(index)
|
||||
.ok_or(IntrinsicExecutionError::ArityMismatch { expected: index + 1, got: args.len() })?;
|
||||
match value {
|
||||
Value::String(value) => Ok(value.as_ref()),
|
||||
_ => Err(IntrinsicExecutionError::TypeMismatch {
|
||||
index,
|
||||
expected: AbiType::Scalar(BuiltinScalarType::Str),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn value_matches_abi_type(value: &Value, expected: AbiType) -> bool {
|
||||
match expected {
|
||||
AbiType::Scalar(BuiltinScalarType::Int) => value.as_integer().is_some(),
|
||||
AbiType::Scalar(BuiltinScalarType::Float) => value.as_float().is_some(),
|
||||
AbiType::Scalar(BuiltinScalarType::Bool) => matches!(value, Value::Boolean(_)),
|
||||
AbiType::Scalar(BuiltinScalarType::Str) => matches!(value, Value::String(_)),
|
||||
AbiType::Builtin(_) => value.as_integer().is_some(),
|
||||
}
|
||||
}
|
||||
@ -755,6 +1054,179 @@ fn vec2_length(
|
||||
Ok(vec![Value::Float((x * x + y * y).sqrt())])
|
||||
}
|
||||
|
||||
fn color_value(color: Color) -> Vec<Value> {
|
||||
vec![Value::Int64(color.raw() as i64)]
|
||||
}
|
||||
|
||||
fn expect_color_arg(args: &[Value], index: usize) -> Result<Color, IntrinsicExecutionError> {
|
||||
let raw = u32::try_from(expect_int_arg(args, index)?).map_err(|_| {
|
||||
IntrinsicExecutionError::TypeMismatch { index, expected: AbiType::Builtin(COLOR) }
|
||||
})?;
|
||||
Ok(Color::from_raw(raw))
|
||||
}
|
||||
|
||||
fn expect_channel(args: &[Value], index: usize) -> Result<u8, IntrinsicExecutionError> {
|
||||
let value = expect_int_arg(args, index)?;
|
||||
u8::try_from(value).map_err(|_| IntrinsicExecutionError::TypeMismatch {
|
||||
index,
|
||||
expected: AbiType::Scalar(BuiltinScalarType::Int),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_html_rgba(value: &str) -> Option<Color> {
|
||||
let hex = value.strip_prefix('#').unwrap_or(value);
|
||||
if hex.len() != 8 || !hex.as_bytes().iter().all(u8::is_ascii_hexdigit) {
|
||||
return None;
|
||||
}
|
||||
Some(Color::from_raw(u32::from_str_radix(hex, 16).ok()?))
|
||||
}
|
||||
|
||||
fn color_from_raw(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
let raw = u32::try_from(expect_int_arg(args, 0)?).map_err(|_| {
|
||||
IntrinsicExecutionError::TypeMismatch {
|
||||
index: 0,
|
||||
expected: AbiType::Scalar(BuiltinScalarType::Int),
|
||||
}
|
||||
})?;
|
||||
Ok(color_value(Color::from_raw(raw)))
|
||||
}
|
||||
|
||||
fn color_rgb(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
Ok(color_value(Color::rgb(
|
||||
expect_channel(args, 0)?,
|
||||
expect_channel(args, 1)?,
|
||||
expect_channel(args, 2)?,
|
||||
)))
|
||||
}
|
||||
|
||||
fn color_rgba(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
Ok(color_value(Color::rgba(
|
||||
expect_channel(args, 0)?,
|
||||
expect_channel(args, 1)?,
|
||||
expect_channel(args, 2)?,
|
||||
expect_channel(args, 3)?,
|
||||
)))
|
||||
}
|
||||
|
||||
fn color_html_rgba(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
let color = parse_html_rgba(expect_string_arg(args, 0)?).ok_or({
|
||||
IntrinsicExecutionError::TypeMismatch {
|
||||
index: 0,
|
||||
expected: AbiType::Scalar(BuiltinScalarType::Str),
|
||||
}
|
||||
})?;
|
||||
Ok(color_value(color))
|
||||
}
|
||||
|
||||
fn color_gray_scale(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
Ok(color_value(Color::gray_scale(expect_channel(args, 0)?)))
|
||||
}
|
||||
|
||||
fn color_hex(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
Ok(vec![Value::Int64(expect_color_arg(args, 0)?.hex() as i64)])
|
||||
}
|
||||
|
||||
fn color_alpha(
|
||||
args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
Ok(vec![Value::Int64(expect_color_arg(args, 0)?.alpha() as i64)])
|
||||
}
|
||||
|
||||
fn color_const(color: Color) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
Ok(color_value(color))
|
||||
}
|
||||
|
||||
fn color_black(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::BLACK)
|
||||
}
|
||||
|
||||
fn color_white(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::WHITE)
|
||||
}
|
||||
|
||||
fn color_red(_: &[Value], _: &mut HostContext<'_>) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::RED)
|
||||
}
|
||||
|
||||
fn color_green(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::GREEN)
|
||||
}
|
||||
|
||||
fn color_blue(_: &[Value], _: &mut HostContext<'_>) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::BLUE)
|
||||
}
|
||||
|
||||
fn color_yellow(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::YELLOW)
|
||||
}
|
||||
|
||||
fn color_orange(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::ORANGE)
|
||||
}
|
||||
|
||||
fn color_indigo(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::INDIGO)
|
||||
}
|
||||
|
||||
fn color_gray(_: &[Value], _: &mut HostContext<'_>) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::GRAY)
|
||||
}
|
||||
|
||||
fn color_cyan(_: &[Value], _: &mut HostContext<'_>) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::CYAN)
|
||||
}
|
||||
|
||||
fn color_magenta(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::MAGENTA)
|
||||
}
|
||||
|
||||
fn color_transparent(
|
||||
_: &[Value],
|
||||
_: &mut HostContext<'_>,
|
||||
) -> Result<Vec<Value>, IntrinsicExecutionError> {
|
||||
color_const(Color::TRANSPARENT)
|
||||
}
|
||||
|
||||
fn input_pad(
|
||||
_args: &[Value],
|
||||
_ctx: &mut HostContext<'_>,
|
||||
@ -1067,6 +1539,19 @@ mod tests {
|
||||
assert_eq!(looked_up, materialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_constant_materializes_color_values() {
|
||||
let red = materialize_builtin_constant("color", "red", 1)
|
||||
.expect("materialization lookup must succeed")
|
||||
.expect("constant must exist");
|
||||
assert_eq!(red, vec![Value::Int64(Color::RED.raw() as i64)]);
|
||||
|
||||
let transparent = materialize_builtin_constant("color", "transparent", 1)
|
||||
.expect("materialization lookup must succeed")
|
||||
.expect("constant must exist");
|
||||
assert_eq!(transparent, vec![Value::Int64(Color::TRANSPARENT.raw() as i64)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intrinsic_metadata_exposes_arg_and_return_layouts() {
|
||||
let dot = lookup_intrinsic("vec2", "dot", 1).expect("vec2.dot must exist");
|
||||
@ -1105,6 +1590,29 @@ mod tests {
|
||||
assert_eq!(result, vec![Value::Float(5.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_intrinsics_construct_and_project_rgba8888() {
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
let rgba = lookup_intrinsic("color", "rgba", 1).expect("color.rgba must exist");
|
||||
let result = (rgba.implementation)(
|
||||
&[Value::Int64(0x11), Value::Int64(0x22), Value::Int64(0x33), Value::Int64(0x44)],
|
||||
&mut ctx,
|
||||
)
|
||||
.expect("rgba implementation must execute");
|
||||
assert_eq!(result, vec![Value::Int64(0x11223344)]);
|
||||
|
||||
let html = lookup_intrinsic("color", "html_rgba", 1).expect("color.html_rgba must exist");
|
||||
let result = (html.implementation)(&[Value::String("#AABBCCDD".into())], &mut ctx)
|
||||
.expect("html_rgba implementation must execute");
|
||||
assert_eq!(result, vec![Value::Int64(0xAABBCCDD)]);
|
||||
|
||||
let alpha = lookup_intrinsic("color", "alpha", 1).expect("color.alpha must exist");
|
||||
let result = (alpha.implementation)(&[Value::Int64(0xAABBCCDD)], &mut ctx)
|
||||
.expect("alpha implementation must execute");
|
||||
assert_eq!(result, vec![Value::Int64(0xDD)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_intrinsic_requires_hardware_context() {
|
||||
let mut ctx = HostContext::new(None);
|
||||
|
||||
@ -1,6 +1,25 @@
|
||||
final_id_hex,final_id_dec,canonical_name,canonical_version,owner,name,arg_slots,ret_slots,arg_layout,ret_layout,deterministic,may_allocate
|
||||
0x1000,4096,vec2.dot,1,vec2,dot,4,1,float|float|float|float,float,true,false
|
||||
0x1001,4097,vec2.length,1,vec2,length,2,1,float|float,float,true,false
|
||||
0x1100,4352,color.from_raw,1,color,from_raw,1,1,int,builtin:color,true,false
|
||||
0x1101,4353,color.rgb,1,color,rgb,3,1,int|int|int,builtin:color,true,false
|
||||
0x1102,4354,color.rgba,1,color,rgba,4,1,int|int|int|int,builtin:color,true,false
|
||||
0x1103,4355,color.html_rgba,1,color,html_rgba,1,1,str,builtin:color,true,false
|
||||
0x1104,4356,color.gray_scale,1,color,gray_scale,1,1,int,builtin:color,true,false
|
||||
0x1105,4357,color.hex,1,color,hex,1,1,builtin:color,int,true,false
|
||||
0x1106,4358,color.alpha,1,color,alpha,1,1,builtin:color,int,true,false
|
||||
0x1120,4384,color.black,1,color,black,0,1,,builtin:color,true,false
|
||||
0x1121,4385,color.white,1,color,white,0,1,,builtin:color,true,false
|
||||
0x1122,4386,color.red,1,color,red,0,1,,builtin:color,true,false
|
||||
0x1123,4387,color.green,1,color,green,0,1,,builtin:color,true,false
|
||||
0x1124,4388,color.blue,1,color,blue,0,1,,builtin:color,true,false
|
||||
0x1125,4389,color.yellow,1,color,yellow,0,1,,builtin:color,true,false
|
||||
0x1126,4390,color.orange,1,color,orange,0,1,,builtin:color,true,false
|
||||
0x1127,4391,color.indigo,1,color,indigo,0,1,,builtin:color,true,false
|
||||
0x1128,4392,color.gray,1,color,gray,0,1,,builtin:color,true,false
|
||||
0x1129,4393,color.cyan,1,color,cyan,0,1,,builtin:color,true,false
|
||||
0x112A,4394,color.magenta,1,color,magenta,0,1,,builtin:color,true,false
|
||||
0x112B,4395,color.transparent,1,color,transparent,0,1,,builtin:color,true,false
|
||||
0x2000,8192,input.pad,1,input,pad,0,1,,builtin:input.pad,true,false
|
||||
0x2001,8193,input.touch,1,input,touch,0,1,,builtin:input.touch,true,false
|
||||
0x2010,8208,input.pad.up,1,input.pad,up,1,1,builtin:input.pad,builtin:input.button,true,false
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user