1 | // font-kit/src/canvas.rs |
2 | // |
3 | // Copyright © 2018 The Pathfinder Project Developers. |
4 | // |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | // option. This file may not be copied, modified, or distributed |
9 | // except according to those terms. |
10 | |
11 | //! An in-memory bitmap surface for glyph rasterization. |
12 | |
13 | use lazy_static::lazy_static; |
14 | use pathfinder_geometry::rect::RectI; |
15 | use pathfinder_geometry::vector::Vector2I; |
16 | use std::cmp; |
17 | use std::fmt; |
18 | |
19 | use crate::utils; |
20 | |
21 | lazy_static! { |
22 | static ref BITMAP_1BPP_TO_8BPP_LUT: [[u8; 8]; 256] = { |
23 | let mut lut = [[0; 8]; 256]; |
24 | for byte in 0..0x100 { |
25 | let mut value = [0; 8]; |
26 | for bit in 0..8 { |
27 | if (byte & (0x80 >> bit)) != 0 { |
28 | value[bit] = 0xff; |
29 | } |
30 | } |
31 | lut[byte] = value |
32 | } |
33 | lut |
34 | }; |
35 | } |
36 | |
37 | /// An in-memory bitmap surface for glyph rasterization. |
38 | pub struct Canvas { |
39 | /// The raw pixel data. |
40 | pub pixels: Vec<u8>, |
41 | /// The size of the buffer, in pixels. |
42 | pub size: Vector2I, |
43 | /// The number of *bytes* between successive rows. |
44 | pub stride: usize, |
45 | /// The image format of the canvas. |
46 | pub format: Format, |
47 | } |
48 | |
49 | impl Canvas { |
50 | /// Creates a new blank canvas with the given pixel size and format. |
51 | /// |
52 | /// Stride is automatically calculated from width. |
53 | /// |
54 | /// The canvas is initialized with transparent black (all values 0). |
55 | #[inline ] |
56 | pub fn new(size: Vector2I, format: Format) -> Canvas { |
57 | Canvas::with_stride( |
58 | size, |
59 | size.x() as usize * format.bytes_per_pixel() as usize, |
60 | format, |
61 | ) |
62 | } |
63 | |
64 | /// Creates a new blank canvas with the given pixel size, stride (number of bytes between |
65 | /// successive rows), and format. |
66 | /// |
67 | /// The canvas is initialized with transparent black (all values 0). |
68 | pub fn with_stride(size: Vector2I, stride: usize, format: Format) -> Canvas { |
69 | Canvas { |
70 | pixels: vec![0; stride * size.y() as usize], |
71 | size, |
72 | stride, |
73 | format, |
74 | } |
75 | } |
76 | |
77 | #[allow (dead_code)] |
78 | pub(crate) fn blit_from_canvas(&mut self, src: &Canvas) { |
79 | self.blit_from( |
80 | Vector2I::default(), |
81 | &src.pixels, |
82 | src.size, |
83 | src.stride, |
84 | src.format, |
85 | ) |
86 | } |
87 | |
88 | #[allow (dead_code)] |
89 | pub(crate) fn blit_from( |
90 | &mut self, |
91 | dst_point: Vector2I, |
92 | src_bytes: &[u8], |
93 | src_size: Vector2I, |
94 | src_stride: usize, |
95 | src_format: Format, |
96 | ) { |
97 | let dst_rect = RectI::new(dst_point, src_size); |
98 | let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size)); |
99 | let dst_rect = match dst_rect { |
100 | Some(dst_rect) => dst_rect, |
101 | None => return, |
102 | }; |
103 | |
104 | match (self.format, src_format) { |
105 | (Format::A8, Format::A8) |
106 | | (Format::Rgb24, Format::Rgb24) |
107 | | (Format::Rgba32, Format::Rgba32) => { |
108 | self.blit_from_with::<BlitMemcpy>(dst_rect, src_bytes, src_stride, src_format) |
109 | } |
110 | (Format::A8, Format::Rgb24) => { |
111 | self.blit_from_with::<BlitRgb24ToA8>(dst_rect, src_bytes, src_stride, src_format) |
112 | } |
113 | (Format::Rgb24, Format::A8) => { |
114 | self.blit_from_with::<BlitA8ToRgb24>(dst_rect, src_bytes, src_stride, src_format) |
115 | } |
116 | (Format::Rgb24, Format::Rgba32) => self |
117 | .blit_from_with::<BlitRgba32ToRgb24>(dst_rect, src_bytes, src_stride, src_format), |
118 | (Format::Rgba32, Format::Rgb24) => self |
119 | .blit_from_with::<BlitRgb24ToRgba32>(dst_rect, src_bytes, src_stride, src_format), |
120 | (Format::Rgba32, Format::A8) | (Format::A8, Format::Rgba32) => unimplemented!(), |
121 | } |
122 | } |
123 | |
124 | #[allow (dead_code)] |
125 | pub(crate) fn blit_from_bitmap_1bpp( |
126 | &mut self, |
127 | dst_point: Vector2I, |
128 | src_bytes: &[u8], |
129 | src_size: Vector2I, |
130 | src_stride: usize, |
131 | ) { |
132 | if self.format != Format::A8 { |
133 | unimplemented!() |
134 | } |
135 | |
136 | let dst_rect = RectI::new(dst_point, src_size); |
137 | let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size)); |
138 | let dst_rect = match dst_rect { |
139 | Some(dst_rect) => dst_rect, |
140 | None => return, |
141 | }; |
142 | |
143 | let size = dst_rect.size(); |
144 | |
145 | let dest_bytes_per_pixel = self.format.bytes_per_pixel() as usize; |
146 | let dest_row_stride = size.x() as usize * dest_bytes_per_pixel; |
147 | let src_row_stride = utils::div_round_up(size.x() as usize, 8); |
148 | |
149 | for y in 0..size.y() { |
150 | let (dest_row_start, src_row_start) = ( |
151 | (y + dst_rect.origin_y()) as usize * self.stride |
152 | + dst_rect.origin_x() as usize * dest_bytes_per_pixel, |
153 | y as usize * src_stride, |
154 | ); |
155 | let dest_row_end = dest_row_start + dest_row_stride; |
156 | let src_row_end = src_row_start + src_row_stride; |
157 | let dest_row_pixels = &mut self.pixels[dest_row_start..dest_row_end]; |
158 | let src_row_pixels = &src_bytes[src_row_start..src_row_end]; |
159 | for x in 0..src_row_stride { |
160 | let pattern = &BITMAP_1BPP_TO_8BPP_LUT[src_row_pixels[x] as usize]; |
161 | let dest_start = x * 8; |
162 | let dest_end = cmp::min(dest_start + 8, dest_row_stride); |
163 | let src = &pattern[0..(dest_end - dest_start)]; |
164 | dest_row_pixels[dest_start..dest_end].clone_from_slice(src); |
165 | } |
166 | } |
167 | } |
168 | |
169 | fn blit_from_with<B: Blit>( |
170 | &mut self, |
171 | rect: RectI, |
172 | src_bytes: &[u8], |
173 | src_stride: usize, |
174 | src_format: Format, |
175 | ) { |
176 | let src_bytes_per_pixel = src_format.bytes_per_pixel() as usize; |
177 | let dest_bytes_per_pixel = self.format.bytes_per_pixel() as usize; |
178 | |
179 | for y in 0..rect.height() { |
180 | let (dest_row_start, src_row_start) = ( |
181 | (y + rect.origin_y()) as usize * self.stride |
182 | + rect.origin_x() as usize * dest_bytes_per_pixel, |
183 | y as usize * src_stride, |
184 | ); |
185 | let dest_row_end = dest_row_start + rect.width() as usize * dest_bytes_per_pixel; |
186 | let src_row_end = src_row_start + rect.width() as usize * src_bytes_per_pixel; |
187 | let dest_row_pixels = &mut self.pixels[dest_row_start..dest_row_end]; |
188 | let src_row_pixels = &src_bytes[src_row_start..src_row_end]; |
189 | B::blit(dest_row_pixels, src_row_pixels) |
190 | } |
191 | } |
192 | } |
193 | |
194 | impl fmt::Debug for Canvas { |
195 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
196 | f&mut DebugStruct<'_, '_>.debug_struct("Canvas" ) |
197 | .field("pixels" , &self.pixels.len()) // Do not dump a vector content. |
198 | .field("size" , &self.size) |
199 | .field("stride" , &self.stride) |
200 | .field(name:"format" , &self.format) |
201 | .finish() |
202 | } |
203 | } |
204 | |
205 | /// The image format for the canvas. |
206 | #[derive (Clone, Copy, Debug, PartialEq)] |
207 | pub enum Format { |
208 | /// Premultiplied R8G8B8A8, little-endian. |
209 | Rgba32, |
210 | /// R8G8B8, little-endian. |
211 | Rgb24, |
212 | /// A8. |
213 | A8, |
214 | } |
215 | |
216 | impl Format { |
217 | /// Returns the number of bits per pixel that this image format corresponds to. |
218 | #[inline ] |
219 | pub fn bits_per_pixel(self) -> u8 { |
220 | match self { |
221 | Format::Rgba32 => 32, |
222 | Format::Rgb24 => 24, |
223 | Format::A8 => 8, |
224 | } |
225 | } |
226 | |
227 | /// Returns the number of color channels per pixel that this image format corresponds to. |
228 | #[inline ] |
229 | pub fn components_per_pixel(self) -> u8 { |
230 | match self { |
231 | Format::Rgba32 => 4, |
232 | Format::Rgb24 => 3, |
233 | Format::A8 => 1, |
234 | } |
235 | } |
236 | |
237 | /// Returns the number of bits per color channel that this image format contains. |
238 | #[inline ] |
239 | pub fn bits_per_component(self) -> u8 { |
240 | self.bits_per_pixel() / self.components_per_pixel() |
241 | } |
242 | |
243 | /// Returns the number of bytes per pixel that this image format corresponds to. |
244 | #[inline ] |
245 | pub fn bytes_per_pixel(self) -> u8 { |
246 | self.bits_per_pixel() / 8 |
247 | } |
248 | } |
249 | |
250 | /// The antialiasing strategy that should be used when rasterizing glyphs. |
251 | #[derive (Clone, Copy, Debug, PartialEq)] |
252 | pub enum RasterizationOptions { |
253 | /// "Black-and-white" rendering. Each pixel is either entirely on or off. |
254 | Bilevel, |
255 | /// Grayscale antialiasing. Only one channel is used. |
256 | GrayscaleAa, |
257 | /// Subpixel RGB antialiasing, for LCD screens. |
258 | SubpixelAa, |
259 | } |
260 | |
261 | trait Blit { |
262 | fn blit(dest: &mut [u8], src: &[u8]); |
263 | } |
264 | |
265 | struct BlitMemcpy; |
266 | |
267 | impl Blit for BlitMemcpy { |
268 | #[inline ] |
269 | fn blit(dest: &mut [u8], src: &[u8]) { |
270 | dest.clone_from_slice(src) |
271 | } |
272 | } |
273 | |
274 | struct BlitRgb24ToA8; |
275 | |
276 | impl Blit for BlitRgb24ToA8 { |
277 | #[inline ] |
278 | fn blit(dest: &mut [u8], src: &[u8]) { |
279 | // TODO(pcwalton): SIMD. |
280 | for (dest: &mut u8, src: &[u8]) in dest.iter_mut().zip(src.chunks(chunk_size:3)) { |
281 | *dest = src[1] |
282 | } |
283 | } |
284 | } |
285 | |
286 | struct BlitA8ToRgb24; |
287 | |
288 | impl Blit for BlitA8ToRgb24 { |
289 | #[inline ] |
290 | fn blit(dest: &mut [u8], src: &[u8]) { |
291 | for (dest: &mut [u8], src: &u8) in dest.chunks_mut(chunk_size:3).zip(src.iter()) { |
292 | dest[0] = *src; |
293 | dest[1] = *src; |
294 | dest[2] = *src; |
295 | } |
296 | } |
297 | } |
298 | |
299 | struct BlitRgba32ToRgb24; |
300 | |
301 | impl Blit for BlitRgba32ToRgb24 { |
302 | #[inline ] |
303 | fn blit(dest: &mut [u8], src: &[u8]) { |
304 | // TODO(pcwalton): SIMD. |
305 | for (dest: &mut [u8], src: &[u8]) in dest.chunks_mut(chunk_size:3).zip(src.chunks(chunk_size:4)) { |
306 | dest.copy_from_slice(&src[0..3]) |
307 | } |
308 | } |
309 | } |
310 | |
311 | struct BlitRgb24ToRgba32; |
312 | |
313 | impl Blit for BlitRgb24ToRgba32 { |
314 | fn blit(dest: &mut [u8], src: &[u8]) { |
315 | for (dest: &mut [u8], src: &[u8]) in dest.chunks_mut(chunk_size:4).zip(src.chunks(chunk_size:3)) { |
316 | dest[0] = src[0]; |
317 | dest[1] = src[1]; |
318 | dest[2] = src[2]; |
319 | dest[3] = 255; |
320 | } |
321 | } |
322 | } |
323 | |