1extern crate core;
19
20pub mod clipping;
21pub mod drawable;
22pub mod drawing;
23pub mod image;
24#[cfg(feature = "image_loading")]
25pub mod image_loading;
26pub mod indexed;
27pub mod integration;
28pub mod renderable_image;
29pub mod renderable_macros;
30pub mod scaling;
31pub mod shapes;
32pub mod text;
33
34use crate::prelude::*;
35use crate::GraphicsError::InvalidBufferLength;
36use fnv::FnvHashMap;
37use thiserror::Error;
38
39pub mod prelude {
40 pub use crate::clipping::*;
41 pub use crate::drawable::*;
42 pub use crate::drawing::*;
43 pub use crate::image::*;
44 #[cfg(feature = "image_loading")]
45 pub use crate::image_loading::*;
46 pub use crate::indexed::*;
47 #[allow(unused_imports)]
48 pub use crate::integration::*;
49 pub use crate::shapes::collection::*;
50 pub use crate::shapes::polyline::*;
51 pub use crate::shapes::*;
52 pub use crate::text::format::*;
53 pub use crate::text::pos::*;
54 pub use crate::text::wrapping::*;
55 pub use crate::text::*;
56 pub use crate::CustomLetter;
57 pub use crate::Graphics;
58 pub use crate::GraphicsError;
59 pub use graphics_shapes::prelude::*;
60 pub use ici_files::prelude::*;
61 #[cfg(feature = "image_loading")]
62 pub use image_lib::ImageError;
63 #[cfg(feature = "image_loading")]
64 pub use image_lib::ImageFormat;
65}
66
67#[derive(Error, Debug)]
68pub enum GraphicsError {
69 #[error("Invalid buffer length, expected: {0}, found: {1}")]
70 InvalidBufferLength(usize, usize),
71 #[error("Invalid pixel array length, expected: {0}, found: {1}")]
72 ImageInitSize(usize, usize),
73 #[error("Both images must be the same size, expected: {0}x{1}, found: {2}x{3}")]
74 ImageBlendSize(usize, usize, usize, usize),
75 #[error("Over 255 colours have been drawn")]
76 TooManyColors,
77 #[error("Size is greater than 255x255: {0}x{1}")]
78 TooBig(usize, usize),
79 #[error("Creating image")]
80 ImageError(IndexedImageError),
81}
82
83pub enum GraphicsBuffer<'a> {
84 RgbaU8(&'a mut [u8]),
85 RgbaU32(&'a mut [u32]),
86 ArgbU32(&'a mut [u32]),
87}
88
89impl GraphicsBuffer<'_> {
90 pub fn to_pixels(&self) -> Vec<Color> {
91 match self {
92 GraphicsBuffer::RgbaU8(buf) => buf
93 .chunks_exact(4)
94 .map(|p| Color::new(p[0], p[1], p[2], p[3]))
95 .collect(),
96 GraphicsBuffer::RgbaU32(buf) => buf.iter().copied().map(Color::from_rgba).collect(),
97 GraphicsBuffer::ArgbU32(buf) => buf.iter().copied().map(Color::from_argb).collect(),
98 }
99 }
100
101 pub const fn pixel_size(&self) -> usize {
102 match self {
103 GraphicsBuffer::RgbaU8(_) => 4,
104 GraphicsBuffer::RgbaU32(_) => 1,
105 GraphicsBuffer::ArgbU32(_) => 1,
106 }
107 }
108
109 pub fn get_color(&self, idx: usize) -> Color {
110 match self {
111 GraphicsBuffer::RgbaU8(buf) => {
112 Color::new(buf[idx], buf[idx + 1], buf[idx + 2], buf[idx + 3])
113 }
114 GraphicsBuffer::RgbaU32(buf) => Color::from_rgba(buf[idx]),
115 GraphicsBuffer::ArgbU32(buf) => Color::from_argb(buf[idx]),
116 }
117 }
118}
119
120pub struct Graphics<'buffer> {
121 buffer: GraphicsBuffer<'buffer>,
122 width: usize,
123 height: usize,
124 translate: Coord,
126 clip: Clip,
128 pub custom_font: FnvHashMap<u8, CustomLetter>,
139 index_method: fn(usize, usize, usize) -> usize,
140 clear_method: fn(&mut GraphicsBuffer, Color),
141}
142
143impl Graphics<'_> {
144 #[inline]
146 pub fn create_buffer_u32(width: usize, height: usize) -> Vec<u32> {
147 vec![0; width * height]
148 }
149
150 #[inline]
152 pub fn create_buffer_u8(width: usize, height: usize) -> Vec<u8> {
153 vec![0; width * height * 4]
154 }
155}
156
157#[derive(Debug, Clone, Eq, PartialEq, Hash)]
159pub struct CustomLetter {
160 pub _4x4: [bool; text::font::standard_4x4::LETTER_PX_COUNT],
161 pub _4x5: [bool; text::font::standard_4x5::LETTER_PX_COUNT],
162 pub _6x7: [bool; text::font::standard_6x7::LETTER_PX_COUNT],
163 pub _7x9: [bool; text::font::outline_7x9::LETTER_PX_COUNT],
164 pub _8x8: [bool; text::font::script_8x8::LETTER_PX_COUNT],
165 pub _8x10: [bool; text::font::standard_8x10::LETTER_PX_COUNT],
166 pub _3x5: [bool; text::font::limited_3x5::LETTER_PX_COUNT],
167}
168
169impl Default for CustomLetter {
170 fn default() -> Self {
171 Self {
172 _4x4: [false; text::font::standard_4x4::LETTER_PX_COUNT],
173 _4x5: [false; text::font::standard_4x5::LETTER_PX_COUNT],
174 _6x7: [false; text::font::standard_6x7::LETTER_PX_COUNT],
175 _7x9: [false; text::font::outline_7x9::LETTER_PX_COUNT],
176 _8x8: [false; text::font::script_8x8::LETTER_PX_COUNT],
177 _8x10: [false; text::font::standard_8x10::LETTER_PX_COUNT],
178 _3x5: [false; text::font::limited_3x5::LETTER_PX_COUNT],
179 }
180 }
181}
182
183pub fn make_image<F: FnOnce(&mut Graphics)>(
195 width: usize,
196 height: usize,
197 method: F,
198) -> Result<Image, GraphicsError> {
199 let mut buffer = Graphics::create_buffer_u8(width, height);
200 let mut graphics = Graphics::new_u8_rgba(&mut buffer, width, height)?;
201 method(&mut graphics);
202 Ok(graphics.copy_to_image())
203}
204
205pub fn make_indexed_image<F: FnOnce(&mut Graphics)>(
224 width: usize,
225 height: usize,
226 simplify_palette: bool,
227 method: F,
228) -> Result<IndexedImage, GraphicsError> {
229 let mut buffer = Graphics::create_buffer_u8(width, height);
230 let mut graphics = Graphics::new_u8_rgba(&mut buffer, width, height)?;
231 method(&mut graphics);
232 graphics.copy_to_indexed_image(simplify_palette)
233}
234
235impl<'buffer> Graphics<'_> {
236 pub fn new_u8_rgba(
240 buffer: &'buffer mut [u8],
241 width: usize,
242 height: usize,
243 ) -> Result<Graphics<'buffer>, GraphicsError> {
244 let count = width * height * 4;
245 if count != buffer.len() {
246 return Err(InvalidBufferLength(count, buffer.len()));
247 }
248 let buffer = GraphicsBuffer::RgbaU8(buffer);
249 Ok(Graphics {
250 buffer,
251 width,
252 height,
253 translate: Coord::default(),
254 clip: Clip::new(width, height),
255 custom_font: FnvHashMap::default(),
256 clear_method: clear_u8,
257 index_method: index_u8,
258 })
259 }
260
261 pub fn new_u32_rgba(
265 buffer: &'buffer mut [u32],
266 width: usize,
267 height: usize,
268 ) -> Result<Graphics<'buffer>, GraphicsError> {
269 let count = width * height;
270 if count != buffer.len() {
271 return Err(InvalidBufferLength(count, buffer.len()));
272 }
273 let buffer = GraphicsBuffer::RgbaU32(buffer);
274 Ok(Graphics {
275 buffer,
276 width,
277 height,
278 translate: Coord::default(),
279 clip: Clip::new(width, height),
280 custom_font: FnvHashMap::default(),
281 clear_method: clear_u32,
282 index_method: index_u32,
283 })
284 }
285
286 pub fn new_u32_argb(
290 buffer: &'buffer mut [u32],
291 width: usize,
292 height: usize,
293 ) -> Result<Graphics<'buffer>, GraphicsError> {
294 let count = width * height;
295 if count != buffer.len() {
296 return Err(InvalidBufferLength(count, buffer.len()));
297 }
298 let buffer = GraphicsBuffer::ArgbU32(buffer);
299 Ok(Graphics {
300 buffer,
301 width,
302 height,
303 translate: Coord::default(),
304 clip: Clip::new(width, height),
305 custom_font: FnvHashMap::default(),
306 clear_method: clear_u32,
307 index_method: index_u32,
308 })
309 }
310}
311
312impl Graphics<'_> {
313 pub fn set_clip(&mut self, clip: Clip) {
315 self.clip = clip;
316 }
317
318 pub fn clip(&self) -> &Clip {
320 &self.clip
321 }
322
323 pub fn clip_mut(&mut self) -> &mut Clip {
327 &mut self.clip
328 }
329}