#![forbid(unsafe_code)]
use crate::c_types::{GifByteType, SavedImage};
use core::ffi::c_int;
pub const GIF_FONT_WIDTH: usize = 8;
pub const GIF_FONT_HEIGHT: usize = 8;
pub(crate) static GIF_ASCII_TABLE_8X8: [[u8; GIF_FONT_WIDTH]; 128] = [
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], [0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00], [0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00], [0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00], [0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00], [0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00], [0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00], [0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00], [0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff], [0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00], [0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff], [0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00], [0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18], [0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00], [0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f], [0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00], [0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00], [0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00], [0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18], [0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00], [0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00], [0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e], [0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00], [0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff], [0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00], [0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00], [0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00], [0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00], [0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00], [0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00], [0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00], [0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00], [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], [0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00], [0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], [0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00], [0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00], [0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00], [0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00], [0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00], [0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00], [0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00], [0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00], [0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00], [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70], [0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00], [0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00], [0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00], [0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00], [0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00], [0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00], [0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00], [0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00], [0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00], [0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00], [0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00], [0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00], [0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00], [0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00], [0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00], [0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00], [0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00], [0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00], [0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00], [0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00], [0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00], [0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00], [0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00], [0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00], [0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00], [0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00], [0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00], [0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00], [0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00], [0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00], [0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00], [0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00], [0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00], [0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00], [0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00], [0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06], [0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00], [0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00], [0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00], [0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00], [0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00], [0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00], [0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00], [0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00], [0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00], [0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00], [0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00], [0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00], [0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff], [0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00], [0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00], [0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00], [0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00], [0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00], [0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00], [0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00], [0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c], [0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00], [0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00], [0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0], [0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00], [0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00], [0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00], [0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00], [0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00], [0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0], [0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06], [0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00], [0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00], [0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00], [0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00], [0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00], [0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00], [0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00], [0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c], [0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00], [0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00], [0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00], [0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00], [0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00], [0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00], ];
pub(crate) fn gif_draw_text8x8(
image: &mut SavedImage,
x: c_int,
y: c_int,
legend: &[u8],
color: c_int,
) {
if x < 0 || y < 0 {
return;
}
let width = image.ImageDesc.Width as usize;
let raster = image.raster_bits_mut();
for row in 0..GIF_FONT_HEIGHT {
let Some(base_offset) =
width.checked_mul(y as usize + row).and_then(|v| v.checked_add(x as usize))
else {
return;
};
let mut col = 0usize;
for &ch_byte in legend {
let ch = (ch_byte & 0x7f) as usize;
for j in 0..GIF_FONT_WIDTH {
if ((GIF_ASCII_TABLE_8X8[ch][row] as u16) & (1u16 << (GIF_FONT_WIDTH - j))) != 0 {
if let Some(idx) = base_offset.checked_add(col) {
if idx < raster.len() {
raster[idx] = color as GifByteType;
}
}
}
col += 1;
}
}
}
}
pub(crate) fn gif_draw_box(
image: &mut SavedImage,
x: c_int,
y: c_int,
w: c_int,
d: c_int,
color: c_int,
) {
if x < 0 || y < 0 || w <= 0 || d <= 0 {
return;
}
let width = image.ImageDesc.Width as usize;
let raster = image.raster_bits_mut();
let Some(base) = width.checked_mul(y as usize).and_then(|v| v.checked_add(x as usize)) else {
return;
};
for j in 0..w as usize {
if let Some(top) = base.checked_add(j) {
if top < raster.len() {
raster[top] = color as GifByteType;
}
}
if let Some(bottom) = (d as usize)
.checked_mul(width)
.and_then(|v| base.checked_add(v))
.and_then(|v| v.checked_add(j))
{
if bottom < raster.len() {
raster[bottom] = color as GifByteType;
}
}
}
for j in 0..d as usize {
if let Some(left) = j.checked_mul(width).and_then(|v| base.checked_add(v)) {
if left < raster.len() {
raster[left] = color as GifByteType;
}
}
if let Some(right) = j
.checked_mul(width)
.and_then(|v| base.checked_add(v))
.and_then(|v| v.checked_add(w as usize))
{
if right < raster.len() {
raster[right] = color as GifByteType;
}
}
}
}
pub(crate) fn gif_draw_rectangle(
image: &mut SavedImage,
x: c_int,
y: c_int,
w: c_int,
d: c_int,
color: c_int,
) {
if x < 0 || y < 0 || w <= 0 || d <= 0 {
return;
}
let width = image.ImageDesc.Width as usize;
let raster = image.raster_bits_mut();
let Some(bp) = width.checked_mul(y as usize).and_then(|v| v.checked_add(x as usize)) else {
return;
};
for i in 0..d as usize {
let Some(start) = i.checked_mul(width).and_then(|v| bp.checked_add(v)) else {
return;
};
let Some(end) = start.checked_add(w as usize) else {
return;
};
if end <= raster.len() {
raster[start..end].fill(color as u8);
}
}
}
pub(crate) fn gif_draw_boxed_text8x8(
image: &mut SavedImage,
x: c_int,
y: c_int,
legend: &[u8],
border: c_int,
bg: c_int,
fg: c_int,
) {
let mut j = 0i32;
let mut line_count = 0i32;
let mut text_width = 0i32;
for &ch in legend {
if ch == b'\r' {
if j > text_width {
text_width = j;
}
j = 0;
line_count += 1;
} else if ch != b'\t' {
j += 1;
}
}
line_count += 1;
if j > text_width {
text_width = j;
}
gif_draw_rectangle(
image,
x + 1,
y + 1,
border + text_width * GIF_FONT_WIDTH as c_int + border - 1,
border + line_count * GIF_FONT_HEIGHT as c_int + border - 1,
bg,
);
let mut line_idx = 0i32;
for line in legend.split(|&b| b == b'\r') {
let (text, leadspace) = if line.first() == Some(&b'\t') {
let trimmed = &line[1..];
let lead = (text_width - trimmed.len() as i32) / 2;
(trimmed, lead)
} else {
(line, 0)
};
gif_draw_text8x8(
image,
x + border + leadspace * GIF_FONT_WIDTH as c_int,
y + border + GIF_FONT_HEIGHT as c_int * line_idx,
text,
fg,
);
line_idx += 1;
}
gif_draw_box(
image,
x,
y,
border + text_width * GIF_FONT_WIDTH as c_int + border,
border + line_count * GIF_FONT_HEIGHT as c_int + border,
fg,
);
}