mostly qoi image support
refactor image loader for multiple filetypes port some broken math stuff make logger more readable
This commit is contained in:
parent
ab8522fbe1
commit
6a8d92d2c2
|
@ -36,7 +36,19 @@ impl log::Log for Logger {
|
||||||
Level::Debug => "25",
|
Level::Debug => "25",
|
||||||
Level::Trace => "103",
|
Level::Trace => "103",
|
||||||
};
|
};
|
||||||
let module = record.module_path().unwrap_or_default();
|
let module = record
|
||||||
|
.module_path()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.rsplit_once(':')
|
||||||
|
.unwrap_or_default()
|
||||||
|
.1;
|
||||||
|
if module == "" {
|
||||||
|
crate::arch::log(format_args!(
|
||||||
|
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m: {}\r\n",
|
||||||
|
record.args(),
|
||||||
|
))
|
||||||
|
.expect("write to serial console");
|
||||||
|
} else {
|
||||||
let line = record.line().unwrap_or_default();
|
let line = record.line().unwrap_or_default();
|
||||||
crate::arch::log(format_args!(
|
crate::arch::log(format_args!(
|
||||||
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n",
|
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n",
|
||||||
|
@ -44,6 +56,7 @@ impl log::Log for Logger {
|
||||||
))
|
))
|
||||||
.expect("write to serial console");
|
.expect("write to serial console");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn flush(&self) {}
|
fn flush(&self) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,6 @@ impl Package {
|
||||||
&path,
|
&path,
|
||||||
Options {
|
Options {
|
||||||
fmt: true,
|
fmt: true,
|
||||||
// optimize: true,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&mut bytes,
|
&mut bytes,
|
||||||
|
@ -83,7 +82,6 @@ impl Package {
|
||||||
hblang::run_compiler(
|
hblang::run_compiler(
|
||||||
&path,
|
&path,
|
||||||
Options {
|
Options {
|
||||||
// optimize: true,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&mut bytes,
|
&mut bytes,
|
||||||
|
@ -100,7 +98,6 @@ impl Package {
|
||||||
&path,
|
&path,
|
||||||
Options {
|
Options {
|
||||||
dump_asm: true,
|
dump_asm: true,
|
||||||
// optimize: true,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
&mut bytes,
|
&mut bytes,
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
.{Color, Surface, new_surface} := @use("./lib.hb");
|
|
||||||
.{log, memory} := @use("../../stn/src/lib.hb")
|
|
||||||
|
|
||||||
BitmapFileHeader := packed struct {
|
|
||||||
img_type: u16,
|
|
||||||
size: u32,
|
|
||||||
reserved_1: u16,
|
|
||||||
reserved_2: u16,
|
|
||||||
offset: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
BitmapInfoHeader := packed struct {
|
|
||||||
size: u32,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
planes: u16,
|
|
||||||
bits: u16,
|
|
||||||
compression: u32,
|
|
||||||
image_size: u32,
|
|
||||||
x_resolution: i32,
|
|
||||||
y_resolution: i32,
|
|
||||||
n_colours: u32,
|
|
||||||
important_colours: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
BitmapColorHeader := packed struct {
|
|
||||||
red_mask: u32,
|
|
||||||
green_mask: u32,
|
|
||||||
blue_mask: u32,
|
|
||||||
alpha_mask: u32,
|
|
||||||
color_space_type: u32,
|
|
||||||
unused: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
surface_from_bmp := fn(bmp: ^u8): ?Surface {
|
|
||||||
file_header := @as(^BitmapFileHeader, @bitcast(bmp))
|
|
||||||
if file_header.img_type != 0x4D42 {
|
|
||||||
log.error("failed to load bmp image: not a bmp image, idiot\0")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader)))
|
|
||||||
bmp += file_header.offset
|
|
||||||
|
|
||||||
px := info_header.width * info_header.height
|
|
||||||
ptr := @as(^Color, @bitcast(bmp))
|
|
||||||
tmp := @as(Color, idk)
|
|
||||||
row := @as(i32, 0)
|
|
||||||
|
|
||||||
loop if row == info_header.height / 2 break else {
|
|
||||||
col := @as(i32, 0)
|
|
||||||
loop if col == info_header.width break else {
|
|
||||||
top_index := row * info_header.width + col
|
|
||||||
bottom_index := (info_header.height - 1 - row) * info_header.width + col
|
|
||||||
|
|
||||||
tmp = *(ptr + top_index);
|
|
||||||
*(ptr + top_index) = *(ptr + bottom_index);
|
|
||||||
*(ptr + bottom_index) = tmp
|
|
||||||
|
|
||||||
col += 1
|
|
||||||
}
|
|
||||||
row += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return .(@bitcast(bmp), info_header.width, info_header.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
new_surface_from_bmp := fn(bmp: ^u8): ?Surface {
|
|
||||||
file_header := @as(^BitmapFileHeader, @bitcast(bmp))
|
|
||||||
if file_header.img_type != 0x4D42 {
|
|
||||||
log.error("failed to load bmp image: not a bmp image, idiot\0")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader)))
|
|
||||||
bmp += file_header.offset
|
|
||||||
|
|
||||||
width := @as(uint, @intcast(info_header.width))
|
|
||||||
height := @as(uint, @intcast(info_header.height))
|
|
||||||
|
|
||||||
surface := new_surface(width, height)
|
|
||||||
top_start_idx := surface.buf
|
|
||||||
bottom_start_idx := surface.buf + width * (height - 1)
|
|
||||||
rows_to_copy := height
|
|
||||||
top_cursor := @as(^Color, @bitcast(bmp))
|
|
||||||
bottom_cursor := top_cursor + width * (height - 1)
|
|
||||||
|
|
||||||
loop if rows_to_copy <= 1 break else {
|
|
||||||
@inline(memory.copy, Color, top_cursor, bottom_start_idx, @bitcast(width))
|
|
||||||
@inline(memory.copy, Color, bottom_cursor, top_start_idx, @bitcast(width))
|
|
||||||
|
|
||||||
top_start_idx += surface.width
|
|
||||||
bottom_start_idx -= surface.width
|
|
||||||
top_cursor += width
|
|
||||||
bottom_cursor -= width
|
|
||||||
rows_to_copy -= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows_to_copy == 1 {
|
|
||||||
@inline(memory.copy, Color, top_cursor, top_start_idx, @bitcast(width))
|
|
||||||
}
|
|
||||||
|
|
||||||
return surface
|
|
||||||
}
|
|
52
sysdata/libraries/render/src/image/bmp.hb
Normal file
52
sysdata/libraries/render/src/image/bmp.hb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
.{Color, Surface, new_surface, put_surface} := @use("../lib.hb");
|
||||||
|
.{log, memory} := @use("../../../stn/src/lib.hb")
|
||||||
|
|
||||||
|
BitmapFileHeader := packed struct {
|
||||||
|
magic: u16,
|
||||||
|
size: u32,
|
||||||
|
reserved_1: u16,
|
||||||
|
reserved_2: u16,
|
||||||
|
offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapInfoHeader := packed struct {
|
||||||
|
size: u32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
planes: u16,
|
||||||
|
bits: u16,
|
||||||
|
compression: u32,
|
||||||
|
image_size: u32,
|
||||||
|
x_resolution: i32,
|
||||||
|
y_resolution: i32,
|
||||||
|
n_colours: u32,
|
||||||
|
important_colours: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapColorHeader := packed struct {
|
||||||
|
red_mask: u32,
|
||||||
|
green_mask: u32,
|
||||||
|
blue_mask: u32,
|
||||||
|
alpha_mask: u32,
|
||||||
|
color_space_type: u32,
|
||||||
|
unused: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
from := fn(bmp: ^u8): ?Surface {
|
||||||
|
file_header := @as(^BitmapFileHeader, @bitcast(bmp))
|
||||||
|
info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader)))
|
||||||
|
bmp += file_header.offset
|
||||||
|
|
||||||
|
if file_header.magic != 0x4D42 | info_header.width == 0 | info_header.height == 0 {
|
||||||
|
log.error("Invalid BMP image.\0")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
width := @as(uint, info_header.width)
|
||||||
|
height := @as(uint, info_header.height)
|
||||||
|
lhs := Surface.(@bitcast(bmp), width, height)
|
||||||
|
rhs := new_surface(width, height)
|
||||||
|
put_surface(rhs, lhs, .(0, 0), true)
|
||||||
|
|
||||||
|
return rhs
|
||||||
|
}
|
34
sysdata/libraries/render/src/image/lib.hb
Normal file
34
sysdata/libraries/render/src/image/lib.hb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
.{log} := @use("../../../stn/src/lib.hb");
|
||||||
|
.{Surface} := @use("../lib.hb")
|
||||||
|
bmp := @use("bmp.hb")
|
||||||
|
qoi := @use("qoi.hb")
|
||||||
|
|
||||||
|
BMP := 0x4D42
|
||||||
|
QOI := 0x66696F71
|
||||||
|
// stand-in for null until bugfix
|
||||||
|
DOES_NOT_EXIST := 1 << 32
|
||||||
|
|
||||||
|
get_format := fn(file: ^u8): uint {
|
||||||
|
if *@as(^u16, @bitcast(file)) == BMP {
|
||||||
|
return BMP
|
||||||
|
} else if *@as(^u32, @bitcast(file)) == QOI {
|
||||||
|
return QOI
|
||||||
|
} else {
|
||||||
|
return DOES_NOT_EXIST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from := fn(file: ^u8): ?Surface {
|
||||||
|
format := get_format(file)
|
||||||
|
|
||||||
|
if format == DOES_NOT_EXIST {
|
||||||
|
log.error("Could not detect image format.\0")
|
||||||
|
return null
|
||||||
|
} else if format == BMP {
|
||||||
|
return bmp.from(file)
|
||||||
|
} else if format == QOI {
|
||||||
|
return qoi.from(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
101
sysdata/libraries/render/src/image/qoi.hb
Normal file
101
sysdata/libraries/render/src/image/qoi.hb
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
.{Color, Surface, new_surface} := @use("../lib.hb");
|
||||||
|
.{log, memory} := @use("../../../stn/src/lib.hb")
|
||||||
|
|
||||||
|
/* source:
|
||||||
|
https://github.com/phoboslab/qoi/blob/master/qoi.h */
|
||||||
|
|
||||||
|
QOI_SRGB := 0
|
||||||
|
QOI_LINEAR := 1
|
||||||
|
QOI_OP_INDEX := 0x0
|
||||||
|
QOI_OP_DIFF := 0x40
|
||||||
|
QOI_OP_LUMA := 0x80
|
||||||
|
QOI_OP_RUN := 0xC0
|
||||||
|
QOI_OP_RGB := 0xFE
|
||||||
|
QOI_OP_RGBA := 0xFF
|
||||||
|
QOI_MASK_2 := 0xC0
|
||||||
|
QOI_COLOR_HASH := fn(c: Color): u8 {
|
||||||
|
return (c.r * 3 + c.g * 5 + c.b * 7 + c.a * 11) % 64
|
||||||
|
}
|
||||||
|
QOI_MAGIC := 0x716F6966
|
||||||
|
QOI_PIXELS_MAX := 400000000
|
||||||
|
|
||||||
|
QuiteOkayHeader := packed struct {
|
||||||
|
magic: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
channels: u8,
|
||||||
|
colorspace: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
be_to_le := fn(big: u32): u32 {
|
||||||
|
return (big & 0xFF000000) >> 24 | (big & 0xFF0000) >> 8 | (big & 0xFF00) << 8 | (big & 0xFF) << 24
|
||||||
|
}
|
||||||
|
|
||||||
|
from := fn(qoi: ^u8): ?Surface {
|
||||||
|
header := @as(^QuiteOkayHeader, @bitcast(qoi))
|
||||||
|
|
||||||
|
qoi += @sizeof(QuiteOkayHeader)
|
||||||
|
|
||||||
|
width := be_to_le(header.width)
|
||||||
|
height := be_to_le(header.height)
|
||||||
|
|
||||||
|
if be_to_le(header.magic) != QOI_MAGIC | width == 0 | height == 0 | header.channels < 3 | header.channels > 4 {
|
||||||
|
log.error("Invalid QOI image.\0")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
surface := new_surface(width, height)
|
||||||
|
index := @as([Color; 64], idk)
|
||||||
|
|
||||||
|
run := 0
|
||||||
|
px := Color.(0, 0, 0, 255)
|
||||||
|
px_pos := 0
|
||||||
|
|
||||||
|
total_pixels := width * height
|
||||||
|
|
||||||
|
loop if px_pos >= total_pixels break else {
|
||||||
|
if run > 0 {
|
||||||
|
run -= 1
|
||||||
|
} else {
|
||||||
|
b1 := *qoi
|
||||||
|
qoi += 1
|
||||||
|
|
||||||
|
if b1 == QOI_OP_RGB {
|
||||||
|
px.r = *qoi
|
||||||
|
px.g = *(qoi + 1)
|
||||||
|
px.b = *(qoi + 2)
|
||||||
|
qoi += 3
|
||||||
|
} else if b1 == QOI_OP_RGBA {
|
||||||
|
px.r = *qoi
|
||||||
|
px.g = *(qoi + 1)
|
||||||
|
px.b = *(qoi + 2)
|
||||||
|
px.a = *(qoi + 3)
|
||||||
|
qoi += 4
|
||||||
|
} else if (b1 & QOI_MASK_2) == QOI_OP_INDEX {
|
||||||
|
px = index[b1 & 0x3F]
|
||||||
|
} else if (b1 & QOI_MASK_2) == QOI_OP_DIFF {
|
||||||
|
px.r = px.r + (b1 >> 4 & 0x3) - 2 & 0xFF
|
||||||
|
px.g = px.g + (b1 >> 2 & 0x3) - 2 & 0xFF
|
||||||
|
px.b = px.b + (b1 & 0x3) - 2 & 0xFF
|
||||||
|
} else if (b1 & QOI_MASK_2) == QOI_OP_LUMA {
|
||||||
|
b2 := *qoi
|
||||||
|
vg := (b1 & 0x3F) - 32
|
||||||
|
|
||||||
|
px.r = px.r + vg - 8 + (b2 >> 4 & 0xF) & 0xFF
|
||||||
|
px.g = px.g + vg & 0xFF
|
||||||
|
px.b = px.b + vg - 8 + (b2 & 0xF) & 0xFF
|
||||||
|
qoi += 1
|
||||||
|
} else if (b1 & QOI_MASK_2) == QOI_OP_RUN {
|
||||||
|
run = b1 & 0x3F
|
||||||
|
}
|
||||||
|
|
||||||
|
index[@inline(QOI_COLOR_HASH, px)] = px
|
||||||
|
};
|
||||||
|
|
||||||
|
*(surface.buf + px_pos) = px
|
||||||
|
|
||||||
|
px_pos += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return surface
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
software := @use("software.hb")
|
software := @use("software.hb")
|
||||||
image := @use("image.hb")
|
image := @use("image/lib.hb")
|
||||||
text := @use("text.hb")
|
text := @use("text.hb")
|
||||||
|
|
||||||
// default mode
|
// default mode
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -68,3 +68,17 @@ reverse := fn(s: ^u8): void {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
equals := fn(lhs: ^u8, rhs: ^u8): bool {
|
||||||
|
if lhs == rhs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
loop if *(lhs + i) != *(rhs + i) {
|
||||||
|
return false
|
||||||
|
} else if *lhs == 0 {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
BIN
sysdata/programs/render_example/src/examples/assets/mini.qoi
Normal file
BIN
sysdata/programs/render_example/src/examples/assets/mini.qoi
Normal file
Binary file not shown.
|
@ -1,38 +1,29 @@
|
||||||
.{Vec2} := @use("../../../../libraries/stn/src/lib.hb").math
|
.{log} := @use("../../../../libraries/stn/src/lib.hb")
|
||||||
render := @use("../../../../libraries/render/src/lib.hb")
|
render := @use("../../../../libraries/render/src/lib.hb")
|
||||||
|
|
||||||
/* expected result:
|
/* expected result:
|
||||||
a cute image bounces around the screen */
|
a cute qoi image and a cute bmp image */
|
||||||
|
|
||||||
bmp_1 := @embed("./assets/able.bmp")
|
qoi := @embed("./assets/mini.qoi")
|
||||||
bmp_2 := @embed("./assets/mini.bmp")
|
bmp := @embed("./assets/mini.bmp")
|
||||||
|
|
||||||
example := fn(): void {
|
example := fn(): void {
|
||||||
// strictly we should be null checking here but i am lazy
|
|
||||||
images := [render.Surface].(
|
|
||||||
@unwrap(render.image.surface_from_bmp(@bitcast(&bmp_1))),
|
|
||||||
@unwrap(render.image.surface_from_bmp(@bitcast(&bmp_2))),
|
|
||||||
)
|
|
||||||
screen := render.init(true)
|
screen := render.init(true)
|
||||||
vel := Vec2(int).(1, 1)
|
image_qoi := render.image.from(@bitcast(&qoi))
|
||||||
pos := Vec2(uint).(100, 100)
|
image_bmp := render.image.from(@bitcast(&bmp))
|
||||||
n := 0
|
|
||||||
loop {
|
if image_qoi == null {
|
||||||
image := images[n]
|
log.error("failed to load qoi image for whatever reason\0")
|
||||||
render.put_surface(screen, image, pos, false)
|
return
|
||||||
render.sync(screen)
|
}
|
||||||
|
if image_bmp == null {
|
||||||
|
log.error("failed to load bmp image for whatever reason\0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
render.clear(screen, render.black)
|
render.clear(screen, render.black)
|
||||||
|
render.put_surface(screen, image_bmp, .((screen.width - image_bmp.width * 3) / 2, (screen.height - image_bmp.height) / 2), false)
|
||||||
if pos.x == 0 | pos.x == screen.width - image.width {
|
render.put_surface(screen, image_qoi, .((screen.width + image_qoi.width) / 2, (screen.height - image_qoi.height) / 2), false)
|
||||||
vel.x = -vel.x
|
render.sync(screen)
|
||||||
n = 1 - n
|
|
||||||
}
|
|
||||||
if pos.y == 0 | pos.y == screen.height - image.height {
|
|
||||||
vel.y = -vel.y
|
|
||||||
n = 1 - n
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += @bitcast(vel)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
.{example: main} := @use("./examples/text.hb")
|
.{example: main} := @use("./examples/image.hb")
|
Loading…
Reference in a new issue