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 #[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
194impl 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)]
207pub enum Format {
208 /// Premultiplied R8G8B8A8, little-endian.
209 Rgba32,
210 /// R8G8B8, little-endian.
211 Rgb24,
212 /// A8.
213 A8,
214}
215
216impl 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)]
252pub 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
261trait Blit {
262 fn blit(dest: &mut [u8], src: &[u8]);
263}
264
265struct BlitMemcpy;
266
267impl Blit for BlitMemcpy {
268 #[inline]
269 fn blit(dest: &mut [u8], src: &[u8]) {
270 dest.clone_from_slice(src)
271 }
272}
273
274struct BlitRgb24ToA8;
275
276impl 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
286struct BlitA8ToRgb24;
287
288impl 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
299struct BlitRgba32ToRgb24;
300
301impl 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
311struct BlitRgb24ToRgba32;
312
313impl 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