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
13use lazy_static::lazy_static;
14use pathfinder_geometry::rect::RectI;
15use pathfinder_geometry::vector::Vector2I;
16use std::cmp;
17use std::fmt;
18
19use crate::utils;
20
21lazy_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.
38pub 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
49impl 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 /// Blits to a rectangle with origin at `dst_point` and size according to `src_size`.
89 /// If the target area overlaps the boundaries of the canvas, only the drawable region is blitted.
90 /// `dst_point` and `src_size` are specified in pixels. `src_stride` is specified in bytes.
91 /// `src_stride` must be equal or larger than the actual data length.
92 #[allow(dead_code)]
93 pub(crate) fn blit_from(
94 &mut self,
95 dst_point: Vector2I,
96 src_bytes: &[u8],
97 src_size: Vector2I,
98 src_stride: usize,
99 src_format: Format,
100 ) {
101 assert_eq!(
102 src_stride * src_size.y() as usize,
103 src_bytes.len(),
104 "Number of pixels in src_bytes does not match stride and size."
105 );
106 assert!(
107 src_stride >= src_size.x() as usize * src_format.bytes_per_pixel() as usize,
108 "src_stride must be >= than src_size.x()"
109 );
110
111 let dst_rect = RectI::new(dst_point, src_size);
112 let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size));
113 let dst_rect = match dst_rect {
114 Some(dst_rect) => dst_rect,
115 None => return,
116 };
117
118 match (self.format, src_format) {
119 (Format::A8, Format::A8)
120 | (Format::Rgb24, Format::Rgb24)
121 | (Format::Rgba32, Format::Rgba32) => {
122 self.blit_from_with::<BlitMemcpy>(dst_rect, src_bytes, src_stride, src_format)
123 }
124 (Format::A8, Format::Rgb24) => {
125 self.blit_from_with::<BlitRgb24ToA8>(dst_rect, src_bytes, src_stride, src_format)
126 }
127 (Format::Rgb24, Format::A8) => {
128 self.blit_from_with::<BlitA8ToRgb24>(dst_rect, src_bytes, src_stride, src_format)
129 }
130 (Format::Rgb24, Format::Rgba32) => self
131 .blit_from_with::<BlitRgba32ToRgb24>(dst_rect, src_bytes, src_stride, src_format),
132 (Format::Rgba32, Format::Rgb24) => self
133 .blit_from_with::<BlitRgb24ToRgba32>(dst_rect, src_bytes, src_stride, src_format),
134 (Format::Rgba32, Format::A8) | (Format::A8, Format::Rgba32) => unimplemented!(),
135 }
136 }
137
138 #[allow(dead_code)]
139 pub(crate) fn blit_from_bitmap_1bpp(
140 &mut self,
141 dst_point: Vector2I,
142 src_bytes: &[u8],
143 src_size: Vector2I,
144 src_stride: usize,
145 ) {
146 if self.format != Format::A8 {
147 unimplemented!()
148 }
149
150 let dst_rect = RectI::new(dst_point, src_size);
151 let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size));
152 let dst_rect = match dst_rect {
153 Some(dst_rect) => dst_rect,
154 None => return,
155 };
156
157 let size = dst_rect.size();
158
159 let dest_bytes_per_pixel = self.format.bytes_per_pixel() as usize;
160 let dest_row_stride = size.x() as usize * dest_bytes_per_pixel;
161 let src_row_stride = utils::div_round_up(size.x() as usize, 8);
162
163 for y in 0..size.y() {
164 let (dest_row_start, src_row_start) = (
165 (y + dst_rect.origin_y()) as usize * self.stride
166 + dst_rect.origin_x() as usize * dest_bytes_per_pixel,
167 y as usize * src_stride,
168 );
169 let dest_row_end = dest_row_start + dest_row_stride;
170 let src_row_end = src_row_start + src_row_stride;
171 let dest_row_pixels = &mut self.pixels[dest_row_start..dest_row_end];
172 let src_row_pixels = &src_bytes[src_row_start..src_row_end];
173 for x in 0..src_row_stride {
174 let pattern = &BITMAP_1BPP_TO_8BPP_LUT[src_row_pixels[x] as usize];
175 let dest_start = x * 8;
176 let dest_end = cmp::min(dest_start + 8, dest_row_stride);
177 let src = &pattern[0..(dest_end - dest_start)];
178 dest_row_pixels[dest_start..dest_end].clone_from_slice(src);
179 }
180 }
181 }
182
183 /// Blits to area `rect` using the data given in the buffer `src_bytes`.
184 /// `src_stride` must be specified in bytes.
185 /// The dimensions of `rect` must be in pixels.
186 fn blit_from_with<B: Blit>(
187 &mut self,
188 rect: RectI,
189 src_bytes: &[u8],
190 src_stride: usize,
191 src_format: Format,
192 ) {
193 let src_bytes_per_pixel = src_format.bytes_per_pixel() as usize;
194 let dest_bytes_per_pixel = self.format.bytes_per_pixel() as usize;
195
196 for y in 0..rect.height() {
197 let (dest_row_start, src_row_start) = (
198 (y + rect.origin_y()) as usize * self.stride
199 + rect.origin_x() as usize * dest_bytes_per_pixel,
200 y as usize * src_stride,
201 );
202 let dest_row_end = dest_row_start + rect.width() as usize * dest_bytes_per_pixel;
203 let src_row_end = src_row_start + rect.width() as usize * src_bytes_per_pixel;
204 let dest_row_pixels = &mut self.pixels[dest_row_start..dest_row_end];
205 let src_row_pixels = &src_bytes[src_row_start..src_row_end];
206 B::blit(dest_row_pixels, src_row_pixels)
207 }
208 }
209}
210
211impl fmt::Debug for Canvas {
212 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213 f.debug_struct("Canvas")
214 .field("pixels", &self.pixels.len()) // Do not dump a vector content.
215 .field("size", &self.size)
216 .field("stride", &self.stride)
217 .field("format", &self.format)
218 .finish()
219 }
220}
221
222/// The image format for the canvas.
223#[derive(Clone, Copy, Debug, PartialEq)]
224pub enum Format {
225 /// Premultiplied R8G8B8A8, little-endian.
226 Rgba32,
227 /// R8G8B8, little-endian.
228 Rgb24,
229 /// A8.
230 A8,
231}
232
233impl Format {
234 /// Returns the number of bits per pixel that this image format corresponds to.
235 #[inline]
236 pub fn bits_per_pixel(self) -> u8 {
237 match self {
238 Format::Rgba32 => 32,
239 Format::Rgb24 => 24,
240 Format::A8 => 8,
241 }
242 }
243
244 /// Returns the number of color channels per pixel that this image format corresponds to.
245 #[inline]
246 pub fn components_per_pixel(self) -> u8 {
247 match self {
248 Format::Rgba32 => 4,
249 Format::Rgb24 => 3,
250 Format::A8 => 1,
251 }
252 }
253
254 /// Returns the number of bits per color channel that this image format contains.
255 #[inline]
256 pub fn bits_per_component(self) -> u8 {
257 self.bits_per_pixel() / self.components_per_pixel()
258 }
259
260 /// Returns the number of bytes per pixel that this image format corresponds to.
261 #[inline]
262 pub fn bytes_per_pixel(self) -> u8 {
263 self.bits_per_pixel() / 8
264 }
265}
266
267/// The antialiasing strategy that should be used when rasterizing glyphs.
268#[derive(Clone, Copy, Debug, PartialEq)]
269pub enum RasterizationOptions {
270 /// "Black-and-white" rendering. Each pixel is either entirely on or off.
271 Bilevel,
272 /// Grayscale antialiasing. Only one channel is used.
273 GrayscaleAa,
274 /// Subpixel RGB antialiasing, for LCD screens.
275 SubpixelAa,
276}
277
278trait Blit {
279 fn blit(dest: &mut [u8], src: &[u8]);
280}
281
282struct BlitMemcpy;
283
284impl Blit for BlitMemcpy {
285 #[inline]
286 fn blit(dest: &mut [u8], src: &[u8]) {
287 dest.clone_from_slice(src)
288 }
289}
290
291struct BlitRgb24ToA8;
292
293impl Blit for BlitRgb24ToA8 {
294 #[inline]
295 fn blit(dest: &mut [u8], src: &[u8]) {
296 // TODO(pcwalton): SIMD.
297 for (dest, src) in dest.iter_mut().zip(src.chunks(3)) {
298 *dest = src[1]
299 }
300 }
301}
302
303struct BlitA8ToRgb24;
304
305impl Blit for BlitA8ToRgb24 {
306 #[inline]
307 fn blit(dest: &mut [u8], src: &[u8]) {
308 for (dest, src) in dest.chunks_mut(3).zip(src.iter()) {
309 dest[0] = *src;
310 dest[1] = *src;
311 dest[2] = *src;
312 }
313 }
314}
315
316struct BlitRgba32ToRgb24;
317
318impl Blit for BlitRgba32ToRgb24 {
319 #[inline]
320 fn blit(dest: &mut [u8], src: &[u8]) {
321 // TODO(pcwalton): SIMD.
322 for (dest, src) in dest.chunks_mut(3).zip(src.chunks(4)) {
323 dest.copy_from_slice(&src[0..3])
324 }
325 }
326}
327
328struct BlitRgb24ToRgba32;
329
330impl Blit for BlitRgb24ToRgba32 {
331 fn blit(dest: &mut [u8], src: &[u8]) {
332 for (dest, src) in dest.chunks_mut(4).zip(src.chunks(3)) {
333 dest[0] = src[0];
334 dest[1] = src[1];
335 dest[2] = src[2];
336 dest[3] = 255;
337 }
338 }
339}