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    #[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.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("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, src) in dest.iter_mut().zip(src.chunks(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, src) in dest.chunks_mut(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, src) in dest.chunks_mut(3).zip(src.chunks(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, src) in dest.chunks_mut(4).zip(src.chunks(3)) {
316            dest[0] = src[0];
317            dest[1] = src[1];
318            dest[2] = src[2];
319            dest[3] = 255;
320        }
321    }
322}