mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 07:12:55 -05:00
Initial work on font map generation
This commit is contained in:
parent
8675b252f3
commit
45d6c1cbf1
13 changed files with 185 additions and 128 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -9,9 +9,13 @@ Cargo.lock
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
# Ignore CZ image files
|
# Ignore text files
|
||||||
|
*.txt
|
||||||
|
|
||||||
|
# Ignore testing files
|
||||||
*.cz*
|
*.cz*
|
||||||
*.CZ*
|
*.CZ*
|
||||||
*.png
|
*.png
|
||||||
|
*.ttf
|
||||||
|
|
||||||
test_files/*
|
test_files/*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"cz",
|
"cz", "font",
|
||||||
"utils",
|
"utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -10,4 +10,4 @@ A encoder/decoder for CZ# image files used in
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
thiserror = "1.0.59"
|
thiserror = "1.0.59"
|
||||||
image = "0.25.1"
|
image = {version = "0.25.1", default-features = false}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use image::Rgba;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -371,25 +372,25 @@ impl Default for ExtendedHeader {
|
||||||
pub fn get_palette<T: Seek + ReadBytesExt + Read>(
|
pub fn get_palette<T: Seek + ReadBytesExt + Read>(
|
||||||
input: &mut T,
|
input: &mut T,
|
||||||
num_colors: usize,
|
num_colors: usize,
|
||||||
) -> Result<Vec<[u8; 4]>, CzError> {
|
) -> Result<Vec<Rgba<u8>>, CzError> {
|
||||||
let mut colormap = Vec::with_capacity(num_colors);
|
let mut colormap = Vec::with_capacity(num_colors);
|
||||||
let mut rgba_buf = [0u8; 4];
|
let mut rgba_buf = [0u8; 4];
|
||||||
|
|
||||||
for _ in 0..num_colors {
|
for _ in 0..num_colors {
|
||||||
input.read_exact(&mut rgba_buf)?;
|
input.read_exact(&mut rgba_buf)?;
|
||||||
colormap.push(rgba_buf);
|
colormap.push(rgba_buf.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(colormap)
|
Ok(colormap)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_palette(input: &[u8], palette: &[[u8; 4]]) -> Result<Vec<u8>, CzError> {
|
pub fn apply_palette(input: &[u8], palette: &[Rgba<u8>]) -> Result<Vec<u8>, CzError> {
|
||||||
let mut output_map = Vec::new();
|
let mut output_map = Vec::new();
|
||||||
|
|
||||||
for byte in input.iter() {
|
for byte in input.iter() {
|
||||||
let color = palette.get(*byte as usize);
|
let color = palette.get(*byte as usize);
|
||||||
if let Some(color) = color {
|
if let Some(color) = color {
|
||||||
output_map.extend_from_slice(color);
|
output_map.extend_from_slice(&color.0);
|
||||||
} else {
|
} else {
|
||||||
return Err(CzError::PaletteError);
|
return Err(CzError::PaletteError);
|
||||||
}
|
}
|
||||||
|
@ -398,7 +399,7 @@ pub fn apply_palette(input: &[u8], palette: &[[u8; 4]]) -> Result<Vec<u8>, CzErr
|
||||||
Ok(output_map)
|
Ok(output_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rgba_to_indexed(input: &[u8], palette: &[[u8; 4]]) -> Result<Vec<u8>, CzError> {
|
pub fn rgba_to_indexed(input: &[u8], palette: &[Rgba<u8>]) -> Result<Vec<u8>, CzError> {
|
||||||
let mut output_map = Vec::new();
|
let mut output_map = Vec::new();
|
||||||
let mut cache = HashMap::new();
|
let mut cache = HashMap::new();
|
||||||
|
|
||||||
|
@ -406,7 +407,7 @@ pub fn rgba_to_indexed(input: &[u8], palette: &[[u8; 4]]) -> Result<Vec<u8>, CzE
|
||||||
let value = match cache.get(rgba) {
|
let value = match cache.get(rgba) {
|
||||||
Some(val) => *val,
|
Some(val) => *val,
|
||||||
None => {
|
None => {
|
||||||
let value = palette.iter().position(|e| e == rgba).unwrap_or_default() as u8;
|
let value = palette.iter().position(|e| e.0 == rgba).unwrap_or_default() as u8;
|
||||||
cache.insert(rgba, value);
|
cache.insert(rgba, value);
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::binio::BitIO;
|
use crate::binio::BitIO;
|
||||||
use crate::common::{CzError, CzHeader};
|
use crate::common::CzError;
|
||||||
|
|
||||||
/// The size of compressed data in each chunk
|
/// The size of compressed data in each chunk
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -227,82 +227,6 @@ fn copy_one(input: &[u8], src: usize) -> u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
|
||||||
let width = header.width() as u32;
|
|
||||||
let height = header.height() as u32;
|
|
||||||
let mut output_buf = data.to_vec();
|
|
||||||
|
|
||||||
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize;
|
|
||||||
let pixel_byte_count = header.depth() >> 3;
|
|
||||||
let line_byte_count = (width * pixel_byte_count as u32) as usize;
|
|
||||||
|
|
||||||
let mut curr_line: Vec<u8>;
|
|
||||||
let mut prev_line: Vec<u8> = Vec::with_capacity(line_byte_count);
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
for y in 0..height {
|
|
||||||
curr_line = data[i..i + line_byte_count].to_vec();
|
|
||||||
|
|
||||||
if y % block_height as u32 != 0 {
|
|
||||||
for x in 0..line_byte_count {
|
|
||||||
curr_line[x] = u8::wrapping_add(curr_line[x], prev_line[x])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_line.clone_from(&curr_line);
|
|
||||||
if pixel_byte_count == 4 {
|
|
||||||
output_buf[i..i + line_byte_count].copy_from_slice(&curr_line);
|
|
||||||
} else if pixel_byte_count == 3 {
|
|
||||||
for x in 0..line_byte_count {
|
|
||||||
let loc = ((y * width) as usize + x) * 4;
|
|
||||||
|
|
||||||
output_buf[loc..loc + 4].copy_from_slice(&[
|
|
||||||
curr_line[x],
|
|
||||||
curr_line[x + 1],
|
|
||||||
curr_line[x + 2],
|
|
||||||
0xFF,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i += line_byte_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
output_buf
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diff_line<T: CzHeader>(header: &T, input: &[u8]) -> Vec<u8> {
|
|
||||||
let width = header.width() as u32;
|
|
||||||
let height = header.height() as u32;
|
|
||||||
|
|
||||||
let mut data = Vec::with_capacity(input.len());
|
|
||||||
|
|
||||||
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize;
|
|
||||||
let pixel_byte_count = header.depth() >> 3;
|
|
||||||
let line_byte_count = (width * pixel_byte_count as u32) as usize;
|
|
||||||
|
|
||||||
let mut curr_line;
|
|
||||||
let mut prev_line: Vec<u8> = Vec::with_capacity(line_byte_count);
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
for y in 0..height {
|
|
||||||
curr_line = input[i..i + line_byte_count].to_vec();
|
|
||||||
if y % block_height as u32 != 0 {
|
|
||||||
for x in 0..line_byte_count {
|
|
||||||
curr_line[x] = curr_line[x].wrapping_sub(prev_line[x]);
|
|
||||||
prev_line[x] = prev_line[x].wrapping_add(curr_line[x]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
prev_line.clone_from(&curr_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.extend_from_slice(&curr_line);
|
|
||||||
i += line_byte_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
let mut size = size;
|
let mut size = size;
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
@ -354,22 +278,21 @@ pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
|
|
||||||
fn compress_lzw(data: &[u8], size: usize, last: Vec<u8>) -> (usize, Vec<u16>, Vec<u8>) {
|
fn compress_lzw(data: &[u8], size: usize, last: Vec<u8>) -> (usize, Vec<u16>, Vec<u8>) {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut dictionary = HashMap::new();
|
let mut dictionary = HashMap::with_capacity(size);
|
||||||
for i in 0..=255 {
|
for i in 0..=255 {
|
||||||
dictionary.insert(vec![i], i as u16);
|
dictionary.insert(vec![i], i as u16);
|
||||||
}
|
}
|
||||||
let mut dictionary_count = (dictionary.len() + 1) as u16;
|
let mut dictionary_count = (dictionary.len() + 1) as u16;
|
||||||
|
|
||||||
let mut element = Vec::new();
|
let mut element = Vec::with_capacity(512);
|
||||||
if !last.is_empty() {
|
if !last.is_empty() {
|
||||||
element = last
|
element = last
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut compressed = Vec::with_capacity(size);
|
let mut compressed = Vec::new();
|
||||||
for c in data {
|
for c in data {
|
||||||
let mut entry = element.clone();
|
let mut entry = element.clone();
|
||||||
entry.push(*c);
|
entry.push(*c);
|
||||||
|
|
||||||
if dictionary.contains_key(&entry) {
|
if dictionary.contains_key(&entry) {
|
||||||
element = entry
|
element = entry
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
|
use image::Rgba;
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
|
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
|
||||||
|
@ -16,7 +17,7 @@ use crate::{
|
||||||
pub struct DynamicCz {
|
pub struct DynamicCz {
|
||||||
header_common: CommonHeader,
|
header_common: CommonHeader,
|
||||||
header_extended: Option<ExtendedHeader>,
|
header_extended: Option<ExtendedHeader>,
|
||||||
palette: Option<Vec<[u8; 4]>>,
|
palette: Option<Vec<Rgba<u8>>>,
|
||||||
bitmap: Vec<u8>,
|
bitmap: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +82,9 @@ impl DynamicCz {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicCz {
|
impl DynamicCz {
|
||||||
pub fn decode<T: Seek + ReadBytesExt + Read>(input: &mut T) -> Result<Self, CzError> {
|
pub fn decode<T: Seek + ReadBytesExt + Read>(
|
||||||
|
input: &mut T
|
||||||
|
) -> Result<Self, CzError> {
|
||||||
// Get the header common to all CZ images
|
// Get the header common to all CZ images
|
||||||
let header_common = CommonHeader::from_bytes(input)?;
|
let header_common = CommonHeader::from_bytes(input)?;
|
||||||
let mut header_extended = None;
|
let mut header_extended = None;
|
||||||
|
@ -144,7 +147,7 @@ impl DynamicCz {
|
||||||
output_bitmap = rgba_to_indexed(self.bitmap(), pal)?;
|
output_bitmap = rgba_to_indexed(self.bitmap(), pal)?;
|
||||||
|
|
||||||
for rgba in pal {
|
for rgba in pal {
|
||||||
out_file.write_all(rgba)?;
|
out_file.write_all(&rgba.0)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => output_bitmap = self.bitmap().clone(),
|
_ => output_bitmap = self.bitmap().clone(),
|
||||||
|
|
|
@ -9,8 +9,6 @@ pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, C
|
||||||
let block_info = get_chunk_info(bytes)?;
|
let block_info = get_chunk_info(bytes)?;
|
||||||
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
|
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
|
||||||
|
|
||||||
dbg!(&block_info);
|
|
||||||
|
|
||||||
// Get the bitmap
|
// Get the bitmap
|
||||||
let bitmap = decompress(bytes, &block_info).unwrap();
|
let bitmap = decompress(bytes, &block_info).unwrap();
|
||||||
|
|
||||||
|
@ -20,8 +18,6 @@ pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, C
|
||||||
pub fn encode<T: WriteBytesExt + Write>(output: &mut T, bitmap: &[u8]) -> Result<(), CzError> {
|
pub fn encode<T: WriteBytesExt + Write>(output: &mut T, bitmap: &[u8]) -> Result<(), CzError> {
|
||||||
let (compressed_data, compressed_info) = compress(bitmap, 0xFEFD);
|
let (compressed_data, compressed_info) = compress(bitmap, 0xFEFD);
|
||||||
|
|
||||||
dbg!(&compressed_info);
|
|
||||||
|
|
||||||
compressed_info.write_into(output)?;
|
compressed_info.write_into(output)?;
|
||||||
|
|
||||||
output.write_all(&compressed_data)?;
|
output.write_all(&compressed_data)?;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
use crate::common::{CommonHeader, CzError, CzHeader};
|
use crate::common::{CommonHeader, CzError, CzHeader};
|
||||||
use crate::compression::{compress, decompress, diff_line, get_chunk_info, line_diff};
|
use crate::compression::{compress, decompress, get_chunk_info};
|
||||||
|
|
||||||
pub fn decode<T: Seek + ReadBytesExt + Read>(
|
pub fn decode<T: Seek + ReadBytesExt + Read>(
|
||||||
bytes: &mut T,
|
bytes: &mut T,
|
||||||
|
@ -32,3 +32,86 @@ pub fn encode<T: WriteBytesExt + Write, H: CzHeader>(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Function to extract the data from a CZ3 file after compression
|
||||||
|
///
|
||||||
|
/// Uses the previous line to determine the characterisitcs of the
|
||||||
|
/// following lines
|
||||||
|
fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
|
||||||
|
let width = header.width() as u32;
|
||||||
|
let height = header.height() as u32;
|
||||||
|
let mut output_buf = data.to_vec();
|
||||||
|
|
||||||
|
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize;
|
||||||
|
let pixel_byte_count = header.depth() >> 3;
|
||||||
|
let line_byte_count = (width * pixel_byte_count as u32) as usize;
|
||||||
|
|
||||||
|
let mut curr_line: Vec<u8>;
|
||||||
|
let mut prev_line: Vec<u8> = Vec::with_capacity(line_byte_count);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
for y in 0..height {
|
||||||
|
curr_line = data[i..i + line_byte_count].to_vec();
|
||||||
|
|
||||||
|
if y % block_height as u32 != 0 {
|
||||||
|
for x in 0..line_byte_count {
|
||||||
|
curr_line[x] = u8::wrapping_add(curr_line[x], prev_line[x])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_line.clone_from(&curr_line);
|
||||||
|
if pixel_byte_count == 4 {
|
||||||
|
output_buf[i..i + line_byte_count].copy_from_slice(&curr_line);
|
||||||
|
} else if pixel_byte_count == 3 {
|
||||||
|
for x in 0..line_byte_count {
|
||||||
|
let loc = ((y * width) as usize + x) * 4;
|
||||||
|
|
||||||
|
output_buf[loc..loc + 4].copy_from_slice(&[
|
||||||
|
curr_line[x],
|
||||||
|
curr_line[x + 1],
|
||||||
|
curr_line[x + 2],
|
||||||
|
0xFF,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += line_byte_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Function to encode data into the CZ3 format before compression
|
||||||
|
///
|
||||||
|
/// Read more in [`line_diff`]
|
||||||
|
fn diff_line<T: CzHeader>(header: &T, input: &[u8]) -> Vec<u8> {
|
||||||
|
let width = header.width() as u32;
|
||||||
|
let height = header.height() as u32;
|
||||||
|
|
||||||
|
let mut data = Vec::with_capacity(input.len());
|
||||||
|
|
||||||
|
let block_height = (f32::ceil(height as f32 / 3.0) as u16) as usize;
|
||||||
|
let pixel_byte_count = header.depth() >> 3;
|
||||||
|
let line_byte_count = (width * pixel_byte_count as u32) as usize;
|
||||||
|
|
||||||
|
let mut curr_line;
|
||||||
|
let mut prev_line: Vec<u8> = Vec::with_capacity(line_byte_count);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
for y in 0..height {
|
||||||
|
curr_line = input[i..i + line_byte_count].to_vec();
|
||||||
|
if y % block_height as u32 != 0 {
|
||||||
|
for x in 0..line_byte_count {
|
||||||
|
curr_line[x] = curr_line[x].wrapping_sub(prev_line[x]);
|
||||||
|
prev_line[x] = prev_line[x].wrapping_add(curr_line[x]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prev_line.clone_from(&curr_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.extend_from_slice(&curr_line);
|
||||||
|
i += line_byte_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
10
font/Cargo.toml
Normal file
10
font/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "font"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
0
font/src/lib.rs
Normal file
0
font/src/lib.rs
Normal file
|
@ -7,5 +7,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cz = { path = "../cz" }
|
cz = { path = "../cz" }
|
||||||
|
fontdue = { version = "0.8.0", features = ["parallel"] }
|
||||||
image = "0.25.1"
|
image = "0.25.1"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
|
22
utils/src/font_generation.rs
Normal file
22
utils/src/font_generation.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Error,
|
||||||
|
path::Path
|
||||||
|
};
|
||||||
|
use fontdue::{Font, FontSettings};
|
||||||
|
|
||||||
|
pub fn load_font<P: ?Sized + AsRef<Path>>(
|
||||||
|
path: &P
|
||||||
|
) -> Result<Font, Error> {
|
||||||
|
let font_file: Vec<u8> = fs::read(path)?;
|
||||||
|
|
||||||
|
let font = Font::from_bytes(
|
||||||
|
font_file,
|
||||||
|
FontSettings {
|
||||||
|
scale: 72.0,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
Ok(font)
|
||||||
|
}
|
|
@ -1,38 +1,52 @@
|
||||||
use std::time::Instant;
|
mod font_generation;
|
||||||
|
|
||||||
use cz::{
|
use font_generation::load_font;
|
||||||
common::{CzVersion, ExtendedHeader},
|
use image::{ColorType, DynamicImage, GenericImage, GenericImageView};
|
||||||
dynamic::DynamicCz,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let timer = Instant::now();
|
let font = load_font("NotoSans-Regular.ttf").unwrap();
|
||||||
let mio = image::open("mio_inverted.png").unwrap().to_rgba8();
|
|
||||||
println!("Opening PNG took {:?}", timer.elapsed());
|
|
||||||
|
|
||||||
let timer = Instant::now();
|
let mut characters = vec![];
|
||||||
let cz_mio = DynamicCz::from_raw(
|
for ascii_char in 32..2048 {
|
||||||
CzVersion::CZ3,
|
characters.push(char::from_u32(ascii_char).unwrap())
|
||||||
mio.width() as u16,
|
}
|
||||||
mio.height() as u16,
|
|
||||||
mio.into_raw(),
|
|
||||||
)
|
|
||||||
.with_extended_header(
|
|
||||||
ExtendedHeader::new()
|
|
||||||
.with_crop(1280, 960)
|
|
||||||
.with_bounds(1280, 960),
|
|
||||||
);
|
|
||||||
println!("Constructing CZ3 took {:?}", timer.elapsed());
|
|
||||||
|
|
||||||
let timer = Instant::now();
|
const FONT_SIZE: f32 = 24.0;
|
||||||
cz_mio.save_as_png("test_save.png").unwrap();
|
const FONT_BOX: f32 = 25.0;
|
||||||
println!("Saving CZ3 as PNG took {:?}", timer.elapsed());
|
|
||||||
|
|
||||||
let timer = Instant::now();
|
let mut font_grid = DynamicImage::new(2560, 1800, ColorType::L8);
|
||||||
cz_mio.save_as_cz("test1.cz3").unwrap();
|
|
||||||
println!("Saving CZ3 as CZ3 took {:?}", timer.elapsed());
|
|
||||||
|
|
||||||
let timer = Instant::now();
|
let mut x_offset = 0.0;
|
||||||
let img = DynamicCz::open("test1.cz3").unwrap();
|
let mut y_offset = 0.0;
|
||||||
println!("Opening saved CZ3 took {:?}", timer.elapsed());
|
for (_i, character) in characters.iter().enumerate() {
|
||||||
|
let (metrics, char_bitmap) = font.rasterize(*character, FONT_SIZE);
|
||||||
|
|
||||||
|
let mut char_image: DynamicImage = image::GrayImage::from_raw(
|
||||||
|
metrics.width as u32,
|
||||||
|
metrics.height as u32,
|
||||||
|
char_bitmap
|
||||||
|
).unwrap().into();
|
||||||
|
let char_image = char_image.crop(0, 0, FONT_BOX as u32, FONT_BOX as u32);
|
||||||
|
|
||||||
|
let char_x_offset = (((FONT_BOX / 2.0) - metrics.advance_width) + metrics.bounds.xmin).ceil() as u32;
|
||||||
|
let char_y_offset = (((FONT_BOX / 1.5) - metrics.bounds.height) - metrics.bounds.ymin).ceil() as u32;
|
||||||
|
|
||||||
|
for y in 0..char_image.height() {
|
||||||
|
for x in 0..char_image.width() {
|
||||||
|
font_grid.put_pixel(
|
||||||
|
x + char_x_offset + x_offset as u32,
|
||||||
|
y + char_y_offset + y_offset as u32,
|
||||||
|
char_image.get_pixel(x, y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x_offset += FONT_BOX;
|
||||||
|
if x_offset + FONT_BOX >= font_grid.width() as f32 {
|
||||||
|
x_offset = 0.0;
|
||||||
|
y_offset += FONT_BOX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
font_grid.save("grid.png").unwrap();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue