tapciify/
background_string.rs1#[cfg(feature = "rayon")]
4use rayon::prelude::*;
5
6use crate::threshold_utils::{ThresholdPixel, DEFAULT_THRESHOLD};
7use crate::{AsciiArt, AsciiArtPixel, SizeError};
8
9pub trait BackgroundStringArtConverter {
11 fn background_string_art(&self, string: &str, colored: bool) -> Result<AsciiArt, SizeError>;
35}
36
37impl BackgroundStringArtConverter for image::DynamicImage {
38 fn background_string_art(&self, string: &str, colored: bool) -> Result<AsciiArt, SizeError> {
39 self.clone()
40 .into_rgba8()
41 .background_string_art(string, colored)
42 }
43}
44
45impl BackgroundStringArtConverter for image::RgbImage {
46 fn background_string_art(&self, string: &str, colored: bool) -> Result<AsciiArt, SizeError> {
47 if self.width() == 0 || self.height() == 0 {
48 return Err(SizeError);
49 }
50
51 #[cfg(feature = "rayon")]
52 let iter = self.par_pixels();
53 #[cfg(not(feature = "rayon"))]
54 let iter = self.pixels();
55
56 let characters = iter
57 .enumerate()
58 .map(|(index, pixel)| AsciiArtPixel {
59 character: match pixel.threshold_pixel(DEFAULT_THRESHOLD) {
60 true => string.chars().nth(index % string.chars().count()).unwrap(),
61 false => ' ',
62 },
63 r: pixel.0[0],
64 g: pixel.0[1],
65 b: pixel.0[2],
66 a: 255,
67 })
68 .collect::<Vec<AsciiArtPixel>>();
69
70 Ok(AsciiArt::new(
71 characters,
72 self.width(),
73 self.height(),
74 colored,
75 ))
76 }
77}
78
79impl BackgroundStringArtConverter for image::RgbaImage {
80 fn background_string_art(&self, string: &str, colored: bool) -> Result<AsciiArt, SizeError> {
81 if self.width() == 0 || self.height() == 0 {
82 return Err(SizeError);
83 }
84
85 #[cfg(feature = "rayon")]
86 let iter = self.par_pixels();
87 #[cfg(not(feature = "rayon"))]
88 let iter = self.pixels();
89
90 let characters = iter
91 .enumerate()
92 .map(|(index, pixel)| AsciiArtPixel {
93 character: match pixel.threshold_pixel(DEFAULT_THRESHOLD) {
94 true => string.chars().nth(index % string.chars().count()).unwrap(),
95 false => ' ',
96 },
97 r: pixel.0[0],
98 g: pixel.0[1],
99 b: pixel.0[2],
100 a: pixel.0[3],
101 })
102 .collect::<Vec<AsciiArtPixel>>();
103
104 Ok(AsciiArt::new(
105 characters,
106 self.width(),
107 self.height(),
108 colored,
109 ))
110 }
111}
112
113impl BackgroundStringArtConverter for image::GrayImage {
114 fn background_string_art(&self, string: &str, colored: bool) -> Result<AsciiArt, SizeError> {
115 if self.width() == 0 || self.height() == 0 {
116 return Err(SizeError);
117 }
118
119 #[cfg(feature = "rayon")]
120 let iter = self.par_pixels();
121 #[cfg(not(feature = "rayon"))]
122 let iter = self.pixels();
123
124 let characters = iter
125 .enumerate()
126 .map(|(index, pixel)| AsciiArtPixel {
127 character: match pixel.threshold_pixel(DEFAULT_THRESHOLD) {
128 true => string.chars().nth(index % string.chars().count()).unwrap(),
129 false => ' ',
130 },
131 r: pixel.0[0],
132 g: pixel.0[0],
133 b: pixel.0[0],
134 a: 255,
135 })
136 .collect::<Vec<AsciiArtPixel>>();
137
138 Ok(AsciiArt::new(
139 characters,
140 self.width(),
141 self.height(),
142 colored,
143 ))
144 }
145}
146
147impl BackgroundStringArtConverter for image::GrayAlphaImage {
148 fn background_string_art(&self, string: &str, colored: bool) -> Result<AsciiArt, SizeError> {
149 if self.width() == 0 || self.height() == 0 {
150 return Err(SizeError);
151 }
152
153 #[cfg(feature = "rayon")]
154 let iter = self.par_pixels();
155 #[cfg(not(feature = "rayon"))]
156 let iter = self.pixels();
157
158 let characters = iter
159 .enumerate()
160 .map(|(index, pixel)| AsciiArtPixel {
161 character: match pixel.threshold_pixel(DEFAULT_THRESHOLD) {
162 true => string.chars().nth(index % string.chars().count()).unwrap(),
163 false => ' ',
164 },
165 r: pixel.0[0],
166 g: pixel.0[0],
167 b: pixel.0[0],
168 a: pixel.0[1],
169 })
170 .collect::<Vec<AsciiArtPixel>>();
171
172 Ok(AsciiArt::new(
173 characters,
174 self.width(),
175 self.height(),
176 colored,
177 ))
178 }
179}