font_kit/
canvas.rs

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}