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,13 +36,26 @@ impl log::Log for Logger {
|
|||
Level::Debug => "25",
|
||||
Level::Trace => "103",
|
||||
};
|
||||
let module = record.module_path().unwrap_or_default();
|
||||
let line = record.line().unwrap_or_default();
|
||||
crate::arch::log(format_args!(
|
||||
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n",
|
||||
record.args(),
|
||||
))
|
||||
.expect("write to serial console");
|
||||
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();
|
||||
crate::arch::log(format_args!(
|
||||
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n",
|
||||
record.args(),
|
||||
))
|
||||
.expect("write to serial console");
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
|
|
|
@ -74,7 +74,6 @@ impl Package {
|
|||
&path,
|
||||
Options {
|
||||
fmt: true,
|
||||
// optimize: true,
|
||||
..Default::default()
|
||||
},
|
||||
&mut bytes,
|
||||
|
@ -83,7 +82,6 @@ impl Package {
|
|||
hblang::run_compiler(
|
||||
&path,
|
||||
Options {
|
||||
// optimize: true,
|
||||
..Default::default()
|
||||
},
|
||||
&mut bytes,
|
||||
|
@ -100,7 +98,6 @@ impl Package {
|
|||
&path,
|
||||
Options {
|
||||
dump_asm: true,
|
||||
// optimize: true,
|
||||
..Default::default()
|
||||
},
|
||||
&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")
|
||||
image := @use("image.hb")
|
||||
image := @use("image/lib.hb")
|
||||
text := @use("text.hb")
|
||||
|
||||
// default mode
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -67,4 +67,18 @@ reverse := fn(s: ^u8): void {
|
|||
j -= 1
|
||||
}
|
||||
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")
|
||||
|
||||
/* expected result:
|
||||
a cute image bounces around the screen */
|
||||
a cute qoi image and a cute bmp image */
|
||||
|
||||
bmp_1 := @embed("./assets/able.bmp")
|
||||
bmp_2 := @embed("./assets/mini.bmp")
|
||||
qoi := @embed("./assets/mini.qoi")
|
||||
bmp := @embed("./assets/mini.bmp")
|
||||
|
||||
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)
|
||||
vel := Vec2(int).(1, 1)
|
||||
pos := Vec2(uint).(100, 100)
|
||||
n := 0
|
||||
loop {
|
||||
image := images[n]
|
||||
render.put_surface(screen, image, pos, false)
|
||||
render.sync(screen)
|
||||
render.clear(screen, render.black)
|
||||
image_qoi := render.image.from(@bitcast(&qoi))
|
||||
image_bmp := render.image.from(@bitcast(&bmp))
|
||||
|
||||
if pos.x == 0 | pos.x == screen.width - image.width {
|
||||
vel.x = -vel.x
|
||||
n = 1 - n
|
||||
}
|
||||
if pos.y == 0 | pos.y == screen.height - image.height {
|
||||
vel.y = -vel.y
|
||||
n = 1 - n
|
||||
}
|
||||
|
||||
pos += @bitcast(vel)
|
||||
if image_qoi == null {
|
||||
log.error("failed to load qoi image for whatever reason\0")
|
||||
return
|
||||
}
|
||||
if image_bmp == null {
|
||||
log.error("failed to load bmp image for whatever reason\0")
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
render.put_surface(screen, image_qoi, .((screen.width + image_qoi.width) / 2, (screen.height - image_qoi.height) / 2), false)
|
||||
render.sync(screen)
|
||||
return
|
||||
}
|
|
@ -1 +1 @@
|
|||
.{example: main} := @use("./examples/text.hb")
|
||||
.{example: main} := @use("./examples/image.hb")
|
Loading…
Reference in a new issue