Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Holds types and code necessary for converting images to ASCII and rendering as images. Can
//! handle different image types.

pub mod converters;
pub mod image_data;
pub mod image_writer;
pub mod render_char_to_png;
pub(crate) mod converters;
pub(crate) mod image_data;
pub(crate) mod image_writer;
pub(crate) mod render_char_to_png;
6 changes: 3 additions & 3 deletions src/conversion/converters.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Holds converters for different image types.

pub mod generic_converter;
pub mod gif_converter;
pub mod png_converter;
pub(crate) mod generic_converter;
pub(crate) mod gif_converter;
pub(crate) mod png_converter;
75 changes: 57 additions & 18 deletions src/conversion/converters/generic_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,54 @@ use regex::Regex;
const FONT_FILE: &str = "../../../fonts/UbuntuMono.ttf";
const FONT_BYTES: &[u8] = include_bytes!("../../../fonts/UbuntuMono.ttf");

/// Simple struct for holding a 2d image with its width and height.
#[derive(Clone, Debug)]
pub(crate) struct Imgii2dImage {
pub(crate) image_2d: Vec<ImageData>,
pub(crate) width: usize,
pub(crate) height: usize,
}

/// Generic function for parsing and rendering ASCII into an image.
///
/// * `imgii_options`: The imgii options for rendering ASCII.
/// * `ascii_text`: The ASCII text to render.
pub fn render_ascii_generic(
///
/// # Returns
/// `Ok` containing a 2d `Vec` if `ImageData`, holding each character image, otherwise an `Err`.
pub(crate) fn render_ascii_generic(
imgii_options: &ImgiiOptions,
ascii_text: String,
) -> Result<Vec<Vec<ImageData>>, ImgiiError> {
) -> Result<Imgii2dImage, ImgiiError> {
// set up font for rendering
let font = FontRef::try_from_slice(FONT_BYTES)
// there's nothing useful in this error, convert it!
.map_err(|_| FontError::new(String::from(FONT_FILE)))?;

// contains lines of images
// starting at 0 is the top, first line of the vector
// inside an inner vec, 0 starts at the leftmost character of the line
let mut image_2d_vec = vec![];
// 2d Vec of images for each character
let mut image_2d_vec = Vec::new();

// read every line in the file
for line in ascii_text.lines() {
let mut char_images = vec![];
// create this once since it will always be the same
let transparent_png = str_to_transparent_png(imgii_options);

// width and height, in characters
// NOTE: we can know height beforehand but we have to wait until we have parsed a whole line of
// text to know the width
let (mut width, height) = (0, ascii_text.lines().count());

// read every line in the file
for (i, line) in ascii_text.lines().enumerate() {
// we need to find each character that we are going to write
// we assume that there's only one character for each color
let control_char = '\u{1b}'; // represents the ansi escape character `\033`
let mut pattern_string = String::from(control_char);
let pattern = r"\[38;2;([0-9]+);([0-9]+);([0-9]+)m(.)";
pattern_string += pattern;
// NOTE: \u{1b} represents the \033 character
let pattern_str = concat!('\u{1b}', r"\[38;2;([0-9]+);([0-9]+);([0-9]+)m(.)");

// TODO: if multiple threads are using this same regex object, maybe we could make it a
// static global or compile it early so we can reuse it? Maybe as a "parser" object?
let re = Regex::new(&pattern_string)?;
let re = Regex::new(pattern_str)?;

// current line's width
let mut line_width = 0;

// create the image for this character
for (_full_str, [r, g, b, the_str]) in re.captures_iter(line).map(|c| c.extract()) {
Expand All @@ -62,7 +78,7 @@ pub fn render_ascii_generic(
let generated_png = {
if the_str.trim().is_empty() {
// create a transparent png for a space
str_to_transparent_png(imgii_options)
transparent_png.clone()
} else {
// render the actual text if it's not empty
let colored = ColoredStr {
Expand All @@ -76,11 +92,34 @@ pub fn render_ascii_generic(
}
};

char_images.push(generated_png);
line_width += 1;
image_2d_vec.push(generated_png);
}

image_2d_vec.push(char_images);
// check that this width is always the same now that we have the width
if i != 0 {
assert_eq!(
width, line_width,
"width {} is not equal to the current line width {}",
width, line_width
);
} else {
width = line_width;
// now we can reserve the rest of the space for our vec
image_2d_vec.reserve(width * height);
}
}

Ok(image_2d_vec)
assert!(
width * height == image_2d_vec.len(),
"expected length of the 2d vector was {} but got {}",
width * height,
image_2d_vec.len()
);

Ok(Imgii2dImage {
image_2d: image_2d_vec,
width,
height,
})
}
38 changes: 21 additions & 17 deletions src/conversion/converters/gif_converter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{fs::File, io::BufReader};

use crate::{
conversion::{converters::generic_converter::render_ascii_generic, image_data::ImageData},
conversion::converters::generic_converter::{Imgii2dImage, render_ascii_generic},
error::{BoxedDynErr, ImgiiError},
options::{ImgiiOptions, RasciiOptions},
};
Expand All @@ -12,7 +12,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};

/// Holds the metadata for a frame that has been deconstructed.
#[derive(Debug, Clone)]
pub struct FrameMetadata {
pub(crate) struct FrameMetadata {
/// The left value for this frame.
left: u32,
/// The top value for this frame.
Expand All @@ -24,7 +24,7 @@ pub struct FrameMetadata {
/// Holds the deconstructed frame data for a single frame, before it is converted to image data.
/// Holds the raw ASCII string and frame metadata.
#[derive(Debug, Clone)]
pub struct NonRenderedFramePart {
pub(crate) struct NonRenderedFramePart {
/// The ASCII representation of the frame.
image_ascii: String,
/// The frame metadata for this frame.
Expand All @@ -33,9 +33,9 @@ pub struct NonRenderedFramePart {

/// Holds the deconstructed frame data for a single frame that has been rendered to a 2D vector.
#[derive(Debug, Clone)]
pub struct RenderedFramePart {
pub(crate) struct RenderedFramePart {
/// The image data with the rendered image data for this frame as a 2D vector.
image_data: Vec<Vec<ImageData>>,
image_data: Imgii2dImage,
/// The frame metadata for this frame.
frame_metadata: FrameMetadata,
}
Expand All @@ -47,25 +47,25 @@ pub struct RenderedFramePart {
impl FrameMetadata {
/// Creates a new [`FrameMetadata`].
#[must_use]
pub fn new(left: u32, top: u32, delay: Delay) -> Self {
pub(crate) fn new(left: u32, top: u32, delay: Delay) -> Self {
Self { left, top, delay }
}

/// Gets the x offset for this frame.
#[must_use]
pub fn left(&self) -> u32 {
pub(crate) fn left(&self) -> u32 {
self.left
}

/// Gets the y offset for this frame.
#[must_use]
pub fn top(&self) -> u32 {
pub(crate) fn top(&self) -> u32 {
self.top
}

/// Gets the delay of this frame.
#[must_use]
pub fn delay(&self) -> Delay {
pub(crate) fn delay(&self) -> Delay {
self.delay
}
}
Expand All @@ -77,7 +77,8 @@ impl RenderedFramePart {
/// * `image_data`: The image data.
/// * `frame_metadata`: The frame metadata.
#[must_use]
pub fn new(image_data: Vec<Vec<ImageData>>, frame_metadata: FrameMetadata) -> Self {
#[allow(dead_code)]
pub(crate) fn new(image_data: Imgii2dImage, frame_metadata: FrameMetadata) -> Self {
Self {
image_data,
frame_metadata,
Expand All @@ -86,20 +87,23 @@ impl RenderedFramePart {

/// Gets the image data for this frame.
#[must_use]
pub fn image_data(&self) -> &Vec<Vec<ImageData>> {
#[allow(dead_code)]
pub(crate) fn image_data(&self) -> &Imgii2dImage {
&self.image_data
}

/// Gets the metadata for this frame.
#[must_use]
pub fn frame_metadata(&self) -> &FrameMetadata {
#[allow(dead_code)]
pub(crate) fn frame_metadata(&self) -> &FrameMetadata {
&self.frame_metadata
}

/// Moves out of this RenderedFramePart, returning a tuple containing the image data followed
/// by metadata.
#[must_use]
pub fn into_frame_data(self) -> (Vec<Vec<ImageData>>, FrameMetadata) {
#[allow(dead_code)]
pub(crate) fn into_frame_data(self) -> (Imgii2dImage, FrameMetadata) {
(self.image_data, self.frame_metadata)
}
}
Expand All @@ -111,7 +115,7 @@ impl NonRenderedFramePart {
/// * `image_ascii`: The ASCII string representation of an image.
/// * `frame_metadata`: The frame metadata.
#[must_use]
pub fn new(image_ascii: String, frame_metadata: FrameMetadata) -> Self {
pub(crate) fn new(image_ascii: String, frame_metadata: FrameMetadata) -> Self {
Self {
image_ascii,
frame_metadata,
Expand All @@ -129,7 +133,7 @@ impl NonRenderedFramePart {
///
/// * `input_file_name`: The input file name.
/// * `rascii_options`: The RASCII options for converting to ASCII.
pub fn read_gif_as_deconstructed_ascii(
pub(crate) fn read_gif_as_deconstructed_ascii(
input_file_name: &str,
rascii_options: &RasciiOptions,
) -> Result<Vec<Option<NonRenderedFramePart>>, ImgiiError> {
Expand Down Expand Up @@ -159,7 +163,7 @@ pub fn read_gif_as_deconstructed_ascii(
///
/// * `input_file_name`: the input file name.
/// * `imgii_options`: the imgii options for rendering ascii.
pub fn read_as_deconstructed_rendered_gif_vec(
pub(crate) fn read_as_deconstructed_rendered_gif_vec(
input_file_name: &str,
imgii_options: &ImgiiOptions,
) -> Result<Vec<Option<RenderedFramePart>>, ImgiiError> {
Expand Down Expand Up @@ -196,7 +200,7 @@ pub fn read_as_deconstructed_rendered_gif_vec(
///
/// # Returns
/// `Err()` upon error reading the GIF, `Ok()` otherwise.
pub fn read_deconstructed_gif(
pub(crate) fn read_deconstructed_gif(
input_file_name: &str,
) -> Result<Vec<(DynamicImage, FrameMetadata)>, ImgiiError> {
let file_in = BufReader::new(File::open(input_file_name)?);
Expand Down
8 changes: 4 additions & 4 deletions src/conversion/converters/png_converter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::generic_converter::render_ascii_generic;
use crate::{
conversion::image_data::ImageData,
conversion::converters::generic_converter::Imgii2dImage,
error::{BoxedDynErr, ImgiiError},
options::{ImgiiOptions, RasciiOptions},
};
Expand All @@ -17,10 +17,10 @@ use rascii_art_img::render_image_to;
/// # Returns
/// * `Vec<Vec<ImageData>>`: A 2d `Vec` of images, containing each rendered character from the
/// image.
pub fn parse_ascii_to_2d_png_vec(
pub(crate) fn parse_ascii_to_2d_png_vec(
input_file_name: &str,
imgii_options: &ImgiiOptions,
) -> Result<Vec<Vec<ImageData>>, ImgiiError> {
) -> Result<Imgii2dImage, ImgiiError> {
let ascii_text = read_png_as_ascii(input_file_name, imgii_options.rascii_options())?;
render_ascii_generic(imgii_options, ascii_text)
}
Expand All @@ -33,7 +33,7 @@ pub fn parse_ascii_to_2d_png_vec(
///
/// # Returns
/// * `String` containing the colored image data as ASCII, colored using terminal escape sequences.
pub fn read_png_as_ascii(
pub(crate) fn read_png_as_ascii(
input_file_name: &str,
rascii_options: &RasciiOptions,
) -> Result<String, ImgiiError> {
Expand Down
8 changes: 4 additions & 4 deletions src/conversion/image_data.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use image::ImageBuffer;

// easier to read
pub type InternalImage = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
pub(crate) type InternalImage = ImageBuffer<image::Rgba<u8>, Vec<u8>>;

/// Represents the image data to work with.
/// Holds an `ImageBuffer` with the image data.
#[derive(Debug, Clone)]
pub struct ImageData(InternalImage);
pub(crate) struct ImageData(InternalImage);

impl ImageData {
/// Create a new ImageData struct as this image buffer.
pub fn new(image_buffer: InternalImage) -> Self {
pub(crate) fn new(image_buffer: InternalImage) -> Self {
Self(image_buffer)
}

/// Gets a reference to the internal buffer for this image data.
pub fn as_buffer(&self) -> &InternalImage {
pub(crate) fn as_buffer(&self) -> &InternalImage {
&self.0
}
}
Expand Down
Loading