Skip to main content

read_fonts/model/glyph/
bitmap.rs

1//! Embedded bitmap glyphs.
2//!
3//! A font stores its bitmaps in strikes, one per size. [`BitmapStrikes`]
4//! reads them from the first of `sbix`, `CBDT` and `EBDT` that a
5//! [`GlyphSourceOrder`] prefers. It selects a [`Strike`] for a size, and a
6//! strike returns a [`BitmapGlyph`]. The pixels of a
7//! [`BitmapGlyph`] can be read without knowing how the table stores them.
8
9use crate::{
10    model::{
11        glyph::{GlyphSource, GlyphSourceOrder},
12        metrics::GlyphExtents,
13        Font,
14    },
15    tables::{
16        bitmap::{self as bdt, BitmapSize},
17        cbdt::Cbdt,
18        cblc::Cblc,
19        ebdt::Ebdt,
20        eblc::Eblc,
21        sbix::{self, Sbix},
22    },
23    types::{GlyphId, Tag},
24    FontData,
25};
26
27/// A table that provides bitmaps.
28#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
29pub enum BitmapSource {
30    /// PNG bitmaps in `sbix`.
31    Sbix,
32    /// Color bitmaps in `CBDT`.
33    Cbdt,
34    /// Monochrome and grayscale bitmaps in `EBDT`.
35    Ebdt,
36}
37
38impl From<BitmapSource> for GlyphSource {
39    fn from(source: BitmapSource) -> Self {
40        match source {
41            BitmapSource::Sbix => Self::Sbix,
42            BitmapSource::Cbdt => Self::Cbdt,
43            BitmapSource::Ebdt => Self::Ebdt,
44        }
45    }
46}
47
48impl TryFrom<GlyphSource> for BitmapSource {
49    /// The original source, which contains no bitmaps.
50    type Error = GlyphSource;
51
52    fn try_from(source: GlyphSource) -> Result<Self, GlyphSource> {
53        Ok(match source {
54            GlyphSource::Sbix => Self::Sbix,
55            GlyphSource::Cbdt => Self::Cbdt,
56            GlyphSource::Ebdt => Self::Ebdt,
57            _ => return Err(source),
58        })
59    }
60}
61
62#[cfg(feature = "libm")]
63#[allow(unused_imports)]
64use core_maths::CoreFloat;
65
66/// The bitmap strikes of a font, read from a single table.
67///
68/// Cheap to create and copy. It borrows tables that the font has already
69/// parsed.
70#[derive(Clone, Copy)]
71pub struct BitmapStrikes<'a> {
72    font: &'a Font,
73    kind: Kind<'a>,
74}
75
76#[derive(Clone, Copy)]
77enum Kind<'a> {
78    None,
79    Sbix(&'a Sbix<'a>),
80    Cbdt(&'a Cblc<'a>, &'a Cbdt<'a>),
81    Ebdt(&'a Eblc<'a>, &'a Ebdt<'a>),
82}
83
84impl<'a> BitmapStrikes<'a> {
85    pub(crate) fn new(font: &'a Font, order: GlyphSourceOrder) -> Self {
86        for source in order
87            .iter()
88            .filter_map(|source| BitmapSource::try_from(source).ok())
89        {
90            let kind = match source {
91                BitmapSource::Sbix => font.sbix().map(Kind::Sbix),
92                BitmapSource::Cbdt => font.cbdt().map(|(cblc, cbdt)| Kind::Cbdt(cblc, cbdt)),
93                BitmapSource::Ebdt => font.ebdt().map(|(eblc, ebdt)| Kind::Ebdt(eblc, ebdt)),
94            };
95            if let Some(kind) = kind {
96                return Self { font, kind };
97            }
98        }
99        Self {
100            font,
101            kind: Kind::None,
102        }
103    }
104
105    /// Returns the table that provides the bitmaps, or `None` if the font has
106    /// none of the requested sources.
107    pub fn source(&self) -> Option<BitmapSource> {
108        match self.kind {
109            Kind::None => None,
110            Kind::Sbix(..) => Some(BitmapSource::Sbix),
111            Kind::Cbdt(..) => Some(BitmapSource::Cbdt),
112            Kind::Ebdt(..) => Some(BitmapSource::Ebdt),
113        }
114    }
115
116    /// Returns the number of strikes, or zero if there is no bitmap table.
117    pub fn len(&self) -> u32 {
118        let count = match self.kind {
119            Kind::None => 0,
120            Kind::Sbix(sbix) => sbix.strikes().len(),
121            Kind::Cbdt(cblc, _) => cblc.bitmap_sizes().len(),
122            Kind::Ebdt(eblc, _) => eblc.bitmap_sizes().len(),
123        };
124        count as u32
125    }
126
127    /// Returns `true` if there are no strikes.
128    pub fn is_empty(&self) -> bool {
129        self.len() == 0
130    }
131
132    /// Returns the strike at `index`.
133    pub fn get(&self, index: u32) -> Option<Strike<'a>> {
134        let index = index as usize;
135        let kind = match self.kind {
136            Kind::None => return None,
137            Kind::Sbix(sbix) => {
138                let ppem = sbix.strikes().get(index).ok()?.ppem();
139                StrikeKind::Sbix(sbix, index as u32, ppem)
140            }
141            Kind::Cbdt(cblc, cbdt) => StrikeKind::Cbdt(cblc.bitmap_sizes().get(index)?, cblc, cbdt),
142            Kind::Ebdt(eblc, ebdt) => StrikeKind::Ebdt(eblc.bitmap_sizes().get(index)?, eblc, ebdt),
143        };
144        Some(Strike {
145            font: self.font,
146            kind,
147        })
148    }
149
150    /// Returns an iterator over the strikes, in index order.
151    pub fn iter(&self) -> impl Iterator<Item = Strike<'a>> + 'a {
152        let this = *self;
153        (0..this.len()).filter_map(move |index| this.get(index))
154    }
155
156    /// Returns the strike for `ppem`.
157    ///
158    /// This is the smallest strike that is at least `ppem`, or the largest
159    /// strike if none is that large. `None` or a size that is not positive
160    /// selects the largest strike.
161    pub fn select(&self, ppem: Option<f32>) -> Option<Strike<'a>> {
162        let mut best: Option<Strike<'a>> = None;
163        for strike in self.iter() {
164            if best
165                .as_ref()
166                .is_none_or(|best| closer_to(ppem, strike.ppem(), best.ppem()))
167            {
168                best = Some(strike);
169            }
170        }
171        best
172    }
173
174    /// Returns the bitmap for `glyph` from the strike selected for `ppem`.
175    ///
176    /// Strikes are ranked the same way as in [`select`](Self::select), but
177    /// only strikes that contain the glyph are considered. A strike may not
178    /// cover every glyph, so the strike [`select`](Self::select) returns is
179    /// not always the one used.
180    pub fn select_bitmap(&self, glyph: GlyphId, ppem: Option<f32>) -> Option<BitmapGlyph<'a>> {
181        let mut best: Option<BitmapGlyph<'a>> = None;
182        for strike in self.iter() {
183            if best
184                .as_ref()
185                .is_none_or(|best| closer_to(ppem, strike.ppem(), best.ppem()))
186            {
187                if let Some(bitmap) = strike.get(glyph) {
188                    best = Some(bitmap);
189                }
190            }
191        }
192        best
193    }
194}
195
196/// Returns `true` if a strike of `candidate` pixels per em is a better
197/// match for `ppem` than a strike of `best`.
198///
199/// The smallest strike that is at least
200/// `ppem` is preferred, or the largest strike if none is that large. `None`
201/// or a size that is not positive selects the largest strike.
202fn closer_to(ppem: Option<f32>, candidate: u16, best: u16) -> bool {
203    let requested = ppem.filter(|ppem| *ppem > 0.0).unwrap_or(f32::INFINITY);
204    let (candidate, best) = (candidate as f32, best as f32);
205    (requested <= candidate && candidate < best) || (requested > best && candidate > best)
206}
207
208/// A set of bitmaps at one size.
209#[derive(Clone, Copy)]
210pub struct Strike<'a> {
211    /// The font is kept for its units per em, and because an `sbix` bitmap
212    /// is positioned using the bearings of the outline it replaces.
213    font: &'a Font,
214    kind: StrikeKind<'a>,
215}
216
217#[derive(Clone, Copy)]
218enum StrikeKind<'a> {
219    /// The table, the strike's index in it, and its size.
220    Sbix(&'a Sbix<'a>, u32, u16),
221    Cbdt(&'a BitmapSize, &'a Cblc<'a>, &'a Cbdt<'a>),
222    Ebdt(&'a BitmapSize, &'a Eblc<'a>, &'a Ebdt<'a>),
223}
224
225impl<'a> Strike<'a> {
226    /// Returns the horizontal pixels per em.
227    pub fn ppem_x(&self) -> u16 {
228        match &self.kind {
229            StrikeKind::Sbix(_, _, ppem) => *ppem,
230            StrikeKind::Cbdt(size, ..) | StrikeKind::Ebdt(size, ..) => size.ppem_x() as u16,
231        }
232    }
233
234    /// Returns the vertical pixels per em.
235    pub fn ppem_y(&self) -> u16 {
236        match &self.kind {
237            StrikeKind::Sbix(_, _, ppem) => *ppem,
238            StrikeKind::Cbdt(size, ..) | StrikeKind::Ebdt(size, ..) => size.ppem_y() as u16,
239        }
240    }
241
242    /// Returns the size of the strike.
243    ///
244    /// This is the larger of the horizontal and vertical pixels per em.
245    pub fn ppem(&self) -> u16 {
246        self.ppem_x().max(self.ppem_y())
247    }
248
249    /// Returns the table that contains the strike.
250    pub fn source(&self) -> BitmapSource {
251        match &self.kind {
252            StrikeKind::Sbix(..) => BitmapSource::Sbix,
253            StrikeKind::Cbdt(..) => BitmapSource::Cbdt,
254            StrikeKind::Ebdt(..) => BitmapSource::Ebdt,
255        }
256    }
257
258    /// Returns the bitmap for `glyph`, or `None` if the strike does not
259    /// contain it.
260    ///
261    /// A strike with no size has no glyphs, because its bitmaps cannot be
262    /// placed in font units.
263    pub fn get(&self, glyph: GlyphId) -> Option<BitmapGlyph<'a>> {
264        if self.ppem_x() == 0 || self.ppem_y() == 0 {
265            return None;
266        }
267        let units_per_em = self.font.units_per_em();
268        match &self.kind {
269            StrikeKind::Sbix(sbix, index, _) => {
270                let strike = sbix.strikes().get(*index as usize).ok()?;
271                sbix_glyph(&strike, self.font, glyph)
272            }
273            StrikeKind::Cbdt(size, cblc, cbdt) => {
274                let location = size.location(cblc.offset_data(), glyph).ok()?;
275                let data = cbdt.data(&location).ok()?;
276                BitmapGlyph::from_bdt(glyph, BitmapSource::Cbdt, size, &data, units_per_em)
277            }
278            StrikeKind::Ebdt(size, eblc, ebdt) => {
279                let location = size.location(eblc.offset_data(), glyph).ok()?;
280                let data = ebdt.data(&location).ok()?;
281                BitmapGlyph::from_bdt(glyph, BitmapSource::Ebdt, size, &data, units_per_em)
282            }
283        }
284    }
285}
286
287/// The maximum number of `dupe` entries to follow.
288///
289/// This limit stops a cycle of entries from looping forever.
290const MAX_SBIX_DUPES: usize = 8;
291
292/// The eight-byte signature at the start of every PNG file.
293const PNG_SIGNATURE: [u8; 8] = [0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
294
295/// Reads a PNG glyph from an `sbix` strike.
296///
297/// A `dupe` entry resolves to the image of the glyph it names.
298fn sbix_glyph<'a>(
299    strike: &sbix::Strike<'a>,
300    font: &Font,
301    mut glyph: GlyphId,
302) -> Option<BitmapGlyph<'a>> {
303    // The bearings come from the glyph asked for, not the one a `dupe`
304    // entry points at, and are only read once a PNG is found.
305    let requested = glyph;
306    for _ in 0..=MAX_SBIX_DUPES {
307        let entry = strike.glyph_data(glyph).ok()??;
308        match entry.graphic_type() {
309            tag if tag == Tag::new(b"dupe") => {
310                glyph = GlyphId::from(FontData::new(entry.data()).read_at::<u16>(0).ok()?);
311            }
312            tag if tag == Tag::new(b"png ") => {
313                let data = entry.data();
314                if data.get(..8)? != PNG_SIGNATURE {
315                    return None;
316                }
317                // The IHDR chunk follows the signature: four bytes of length,
318                // four of type, then the width and height.
319                let reader = FontData::new(data);
320                let width = reader.read_at::<u32>(16).ok()?;
321                let height = reader.read_at::<u32>(20).ok()?;
322                // HarfBuzz refuses a dimension that an i32 of pixels would
323                // not hold once scaled.
324                if width >= 1 << 16 || height >= 1 << 16 {
325                    return None;
326                }
327                let ppem = strike.ppem();
328                let (x_bearing, y_bearing) = sbix_bearings(font, requested);
329                return Some(BitmapGlyph {
330                    glyph: requested,
331                    source: BitmapSource::Sbix,
332                    pixels: Pixels::Png(data),
333                    width,
334                    height,
335                    x_bearing,
336                    y_bearing,
337                    inner_x_bearing: entry.origin_offset_x() as i32,
338                    inner_y_bearing: entry.origin_offset_y() as i32,
339                    h_advance: None,
340                    ppem_x: ppem,
341                    ppem_y: ppem,
342                    origin: Origin::BottomLeft,
343                    units_per_em: font.units_per_em(),
344                });
345            }
346            _ => return None,
347        }
348    }
349    None
350}
351
352/// Returns the bearings used to position an `sbix` bitmap, in font units.
353///
354/// The specification places the image at the glyph origin. Skia and
355/// CoreText offset it by the `hmtx` bearing and the bottom of the outline's
356/// bounding box. This places the image where the outline would be. A glyph
357/// without an outline is placed at the origin.
358fn sbix_bearings(font: &Font, glyph: GlyphId) -> (i32, i32) {
359    let Some((glyf, loca)) = font.selected_glyf_loca() else {
360        return (0, 0);
361    };
362    let Ok(Some(outline)) = loca.get_glyf(glyph, glyf) else {
363        return (0, 0);
364    };
365    if outline.number_of_contours() == 0 {
366        return (0, 0);
367    }
368    let lsb = font
369        .h_metric_records()
370        .stated_side_bearing(glyph)
371        .map(|lsb| lsb.to_i32())
372        .unwrap_or(0);
373    (lsb, outline.y_min() as i32)
374}
375
376/// The corner of a bitmap that the inner bearings position.
377#[derive(Copy, Clone, PartialEq, Eq, Debug)]
378pub enum Origin {
379    /// The bearings position the top left corner, as in `CBDT` and `EBDT`.
380    TopLeft,
381    /// The bearings position the bottom left corner, as in `sbix`.
382    BottomLeft,
383}
384
385/// An embedded bitmap glyph.
386///
387/// A bitmap is positioned in two steps. The outer bearings offset the glyph
388/// origin in font units, so they scale with the font size. The inner
389/// bearings then position the image's corner in pixels of the strike, so
390/// they scale by the ratio of the font size to the strike size.
391#[derive(Clone, Copy)]
392pub struct BitmapGlyph<'a> {
393    glyph: GlyphId,
394    source: BitmapSource,
395    pixels: Pixels<'a>,
396    width: u32,
397    height: u32,
398    x_bearing: i32,
399    y_bearing: i32,
400    inner_x_bearing: i32,
401    inner_y_bearing: i32,
402    h_advance: Option<u32>,
403    ppem_x: u16,
404    ppem_y: u16,
405    origin: Origin,
406    /// The font's units per em, to convert pixels to font units.
407    units_per_em: u16,
408}
409
410impl<'a> BitmapGlyph<'a> {
411    fn from_bdt(
412        glyph: GlyphId,
413        source: BitmapSource,
414        size: &BitmapSize,
415        data: &bdt::BitmapData<'a>,
416        units_per_em: u16,
417    ) -> Option<Self> {
418        let (inner_x_bearing, inner_y_bearing, advance, width, height) = match data.metrics {
419            bdt::BitmapMetrics::Small(metrics) => (
420                metrics.bearing_x() as i32,
421                metrics.bearing_y() as i32,
422                metrics.advance() as u32,
423                metrics.width() as u32,
424                metrics.height() as u32,
425            ),
426            bdt::BitmapMetrics::Big(metrics) => (
427                metrics.hori_bearing_x() as i32,
428                metrics.hori_bearing_y() as i32,
429                metrics.hori_advance() as u32,
430                metrics.width() as u32,
431                metrics.height() as u32,
432            ),
433        };
434        let bdt::BitmapContent::Data(format, bytes) = &data.content else {
435            // Composite bitmaps are assembled from others, which nothing
436            // here does yet.
437            return None;
438        };
439        let bits_per_pixel = size.bit_depth();
440        let pixels = match (bits_per_pixel, format) {
441            (32, bdt::BitmapDataFormat::Png) => Pixels::Png(bytes),
442            // Color bitmaps are a byte per channel, so they are never packed.
443            (32, bdt::BitmapDataFormat::ByteAligned) => {
444                Pixels::Bgra(Bgra::new(bytes, width, height)?)
445            }
446            (1 | 2 | 4 | 8, bdt::BitmapDataFormat::ByteAligned) => {
447                Pixels::Mask(Mask::new(bytes, width, height, bits_per_pixel, false)?)
448            }
449            (1 | 2 | 4 | 8, bdt::BitmapDataFormat::BitAligned) => {
450                Pixels::Mask(Mask::new(bytes, width, height, bits_per_pixel, true)?)
451            }
452            _ => return None,
453        };
454        Some(Self {
455            glyph,
456            source,
457            pixels,
458            width,
459            height,
460            x_bearing: 0,
461            y_bearing: 0,
462            inner_x_bearing,
463            inner_y_bearing,
464            h_advance: Some(advance),
465            ppem_x: size.ppem_x() as u16,
466            ppem_y: size.ppem_y() as u16,
467            origin: Origin::TopLeft,
468            units_per_em,
469        })
470    }
471
472    /// Returns the glyph ID of this bitmap.
473    ///
474    /// For an `sbix` entry that reuses another glyph's image, this is the
475    /// requested glyph.
476    pub fn glyph(&self) -> GlyphId {
477        self.glyph
478    }
479
480    /// Returns the table that contains the bitmap.
481    pub fn source(&self) -> BitmapSource {
482        self.source
483    }
484
485    /// Returns the pixel data.
486    pub fn pixels(&self) -> Pixels<'a> {
487        self.pixels
488    }
489
490    /// Returns the width in pixels.
491    pub fn width(&self) -> u32 {
492        self.width
493    }
494
495    /// Returns the height in pixels.
496    pub fn height(&self) -> u32 {
497        self.height
498    }
499
500    /// Returns the horizontal offset of the image's origin from the glyph
501    /// origin, in font units.
502    ///
503    /// This is the `hmtx` bearing for an `sbix` glyph with an outline, and
504    /// zero otherwise.
505    pub fn x_bearing(&self) -> i32 {
506        self.x_bearing
507    }
508
509    /// Returns the vertical offset of the image's origin above the glyph
510    /// origin, in font units.
511    ///
512    /// This is the bottom of the outline's bounding box for an `sbix` glyph
513    /// with an outline, and zero otherwise.
514    pub fn y_bearing(&self) -> i32 {
515        self.y_bearing
516    }
517
518    /// Returns the horizontal position of the [`origin`](Self::origin)
519    /// corner, in pixels to the right of the image's origin.
520    pub fn inner_x_bearing(&self) -> i32 {
521        self.inner_x_bearing
522    }
523
524    /// Returns the vertical position of the [`origin`](Self::origin) corner,
525    /// in pixels above the image's origin.
526    pub fn inner_y_bearing(&self) -> i32 {
527        self.inner_y_bearing
528    }
529
530    /// Returns the advance width in pixels, if the table provides one.
531    ///
532    /// This is always `None` for `sbix`.
533    pub fn h_advance(&self) -> Option<u32> {
534        self.h_advance
535    }
536
537    /// Returns the horizontal pixels per em of the strike that contains the
538    /// glyph.
539    pub fn ppem_x(&self) -> u16 {
540        self.ppem_x
541    }
542
543    /// Returns the vertical pixels per em of the strike that contains the
544    /// glyph.
545    pub fn ppem_y(&self) -> u16 {
546        self.ppem_y
547    }
548
549    /// Returns the corner that the inner bearings position.
550    pub fn origin(&self) -> Origin {
551        self.origin
552    }
553
554    /// Returns the size of the strike.
555    ///
556    /// This is the larger of the horizontal and vertical pixels per em.
557    pub fn ppem(&self) -> u16 {
558        self.ppem_x.max(self.ppem_y)
559    }
560
561    /// Returns the ink extents of the bitmap, in font units.
562    ///
563    /// Each value is scaled from pixels by the strike size and rounded to a
564    /// whole unit. The bearing is to the top left corner, whatever corner the
565    /// table uses. The table's outer bearings are not included.
566    pub fn extents(&self) -> GlyphExtents<f32> {
567        let pixels = self.pixel_extents();
568        let units_per_em = self.units_per_em as f32;
569        // A strike with no size has no glyphs, so neither size is zero.
570        let scale = |value: i32, ppem: u16| (value as f32 * units_per_em / ppem as f32).round();
571        GlyphExtents {
572            x_bearing: scale(pixels.x_bearing, self.ppem_x),
573            y_bearing: scale(pixels.y_bearing, self.ppem_y),
574            width: scale(pixels.width, self.ppem_x),
575            height: scale(pixels.height, self.ppem_y),
576        }
577    }
578
579    /// The ink extents in pixels from the image's origin, with the bearing
580    /// to the top left corner.
581    fn pixel_extents(&self) -> GlyphExtents<i32> {
582        let y_bearing = match self.origin {
583            Origin::TopLeft => self.inner_y_bearing,
584            Origin::BottomLeft => self.inner_y_bearing + self.height as i32,
585        };
586        GlyphExtents {
587            x_bearing: self.inner_x_bearing,
588            y_bearing,
589            width: self.width as i32,
590            height: self.height as i32,
591        }
592    }
593}
594
595/// Pixel data for a bitmap glyph.
596#[derive(Clone, Copy)]
597pub enum Pixels<'a> {
598    /// A PNG image.
599    Png(&'a [u8]),
600    /// Uncompressed 32-bit color.
601    Bgra(Bgra<'a>),
602    /// A single channel coverage mask.
603    Mask(Mask<'a>),
604}
605
606/// Uncompressed 32-bit color pixels.
607///
608/// Pixels are premultiplied, in BGRA order and in the sRGB color space.
609#[derive(Clone, Copy)]
610pub struct Bgra<'a> {
611    data: &'a [u8],
612    width: u32,
613    height: u32,
614}
615
616impl<'a> Bgra<'a> {
617    /// Creates a view of `data` as `width` by `height` pixels of four bytes
618    /// each.
619    ///
620    /// Returns `None` if `data` is too short.
621    pub fn new(data: &'a [u8], width: u32, height: u32) -> Option<Self> {
622        let len = (width as usize)
623            .checked_mul(height as usize)?
624            .checked_mul(4)?;
625        (data.len() >= len).then(|| Self {
626            data: &data[..len],
627            width,
628            height,
629        })
630    }
631
632    /// Returns the width in pixels.
633    pub fn width(&self) -> u32 {
634        self.width
635    }
636
637    /// Returns the height in pixels.
638    pub fn height(&self) -> u32 {
639        self.height
640    }
641
642    /// Returns the pixel data, row by row, four bytes per pixel.
643    pub fn data(&self) -> &'a [u8] {
644        self.data
645    }
646
647    /// Returns an iterator over the rows.
648    ///
649    /// Each row contains `width` pixels of four bytes.
650    pub fn rows(&self) -> impl Iterator<Item = &'a [u8]> + 'a {
651        let row_len = (self.width as usize * 4).max(1);
652        self.data.chunks_exact(row_len)
653    }
654
655    /// Returns the pixel at column `x` of row `y`, as blue, green, red and
656    /// alpha.
657    pub fn pixel(&self, x: u32, y: u32) -> Option<[u8; 4]> {
658        if x >= self.width || y >= self.height {
659            return None;
660        }
661        let start = (y as usize * self.width as usize + x as usize) * 4;
662        self.data.get(start..start + 4)?.try_into().ok()
663    }
664}
665
666/// A single channel coverage mask of 1, 2, 4 or 8 bits per pixel.
667///
668/// The data is kept in the table's packing. The accessors decode it, so a
669/// caller does not need to know the bit depth or whether rows are padded.
670#[derive(Clone, Copy)]
671pub struct Mask<'a> {
672    data: &'a [u8],
673    width: u32,
674    height: u32,
675    bits_per_pixel: u8,
676    packed: bool,
677}
678
679impl<'a> Mask<'a> {
680    /// Creates a view of `data` as `width` by `height` pixels of
681    /// `bits_per_pixel` each.
682    ///
683    /// In a packed mask, each row begins at the bit that follows the previous
684    /// row. In an unpacked mask, each row is padded to a whole byte.
685    ///
686    /// Returns `None` if the bit depth is not 1, 2, 4 or 8, or if `data` is
687    /// too short.
688    pub fn new(
689        data: &'a [u8],
690        width: u32,
691        height: u32,
692        bits_per_pixel: u8,
693        packed: bool,
694    ) -> Option<Self> {
695        if !matches!(bits_per_pixel, 1 | 2 | 4 | 8) {
696            return None;
697        }
698        let bits = bits_per_pixel as usize;
699        let len = if packed {
700            (width as usize)
701                .checked_mul(height as usize)?
702                .checked_mul(bits)?
703                .div_ceil(8)
704        } else {
705            (width as usize)
706                .checked_mul(bits)?
707                .div_ceil(8)
708                .checked_mul(height as usize)?
709        };
710        (data.len() >= len).then(|| Self {
711            data: &data[..len],
712            width,
713            height,
714            bits_per_pixel,
715            packed,
716        })
717    }
718
719    /// Returns the width in pixels.
720    pub fn width(&self) -> u32 {
721        self.width
722    }
723
724    /// Returns the height in pixels.
725    pub fn height(&self) -> u32 {
726        self.height
727    }
728
729    /// Returns the number of bits per pixel: 1, 2, 4 or 8.
730    pub fn bits_per_pixel(&self) -> u8 {
731        self.bits_per_pixel
732    }
733
734    /// Returns `true` if rows are not padded to a whole byte.
735    pub fn is_packed(&self) -> bool {
736        self.packed
737    }
738
739    /// Returns the raw pixel data from the table.
740    pub fn data(&self) -> &'a [u8] {
741        self.data
742    }
743
744    /// Returns the size in bytes of the decoded mask, one byte per pixel.
745    ///
746    /// This is the length [`decode_into`](Self::decode_into) writes.
747    pub fn decoded_len(&self) -> usize {
748        self.width as usize * self.height as usize
749    }
750
751    /// Returns `true` if there are no pixels.
752    pub fn is_empty(&self) -> bool {
753        self.decoded_len() == 0
754    }
755
756    /// Returns the coverage of the pixel at column `x` of row `y`.
757    ///
758    /// The value ranges from 0 to 255 for any bit depth.
759    pub fn pixel(&self, x: u32, y: u32) -> Option<u8> {
760        if x >= self.width || y >= self.height {
761            return None;
762        }
763        Some(self.pixel_at(self.bit_offset(x, y)))
764    }
765
766    /// Returns an iterator over the coverage of each pixel, row by row.
767    pub fn pixels(&self) -> impl Iterator<Item = u8> + 'a {
768        let mask = *self;
769        (0..mask.height).flat_map(move |y| mask.row(y).pixels())
770    }
771
772    /// Returns an iterator over the rows, from top to bottom.
773    pub fn rows(&self) -> impl Iterator<Item = MaskRow<'a>> + 'a {
774        let mask = *self;
775        (0..mask.height).map(move |y| mask.row(y))
776    }
777
778    /// Writes the coverage of each pixel to `dst`, row by row, one byte per
779    /// pixel.
780    ///
781    /// `dst` must hold at least [`decoded_len`](Self::decoded_len) bytes, and
782    /// any bytes past that are left as they were. Returns `false`, writing
783    /// nothing, if `dst` is shorter.
784    pub fn decode_into(&self, dst: &mut [u8]) -> bool {
785        let len = self.decoded_len();
786        let Some(dst) = dst.get_mut(..len) else {
787            return false;
788        };
789        if self.bits_per_pixel == 8 {
790            // A byte per pixel is already the output, packed or not.
791            dst.copy_from_slice(&self.data[..len]);
792            return true;
793        }
794        for (out, pixel) in dst.iter_mut().zip(self.pixels()) {
795            *out = pixel;
796        }
797        true
798    }
799
800    fn row(&self, y: u32) -> MaskRow<'a> {
801        MaskRow { mask: *self, y }
802    }
803
804    /// Returns the bit offset of the pixel at column `x` of row `y`.
805    fn bit_offset(&self, x: u32, y: u32) -> usize {
806        let bits = self.bits_per_pixel as usize;
807        if self.packed {
808            (y as usize * self.width as usize + x as usize) * bits
809        } else {
810            let row_bits = (self.width as usize * bits).div_ceil(8) * 8;
811            y as usize * row_bits + x as usize * bits
812        }
813    }
814
815    /// Returns the coverage of the pixel at `bit`, scaled to 0 to 255.
816    fn pixel_at(&self, bit: usize) -> u8 {
817        let byte = self.data.get(bit >> 3).copied().unwrap_or(0);
818        match self.bits_per_pixel {
819            1 => ((byte >> (7 - (bit & 7))) & 1) * 255,
820            2 => ((byte >> (6 - (bit & 7))) & 3) * 85,
821            4 => ((byte >> (4 - (bit & 7))) & 15) * 17,
822            _ => byte,
823        }
824    }
825}
826
827/// One row of a [`Mask`].
828#[derive(Clone, Copy)]
829pub struct MaskRow<'a> {
830    mask: Mask<'a>,
831    y: u32,
832}
833
834impl<'a> MaskRow<'a> {
835    /// Returns the index of the row, counting from the top.
836    pub fn y(&self) -> u32 {
837        self.y
838    }
839
840    /// Returns the coverage of the pixel at column `x`, from 0 to 255.
841    pub fn pixel(&self, x: u32) -> Option<u8> {
842        self.mask.pixel(x, self.y)
843    }
844
845    /// Returns an iterator over the coverage of each pixel, left to right.
846    pub fn pixels(&self) -> impl Iterator<Item = u8> + 'a {
847        let mask = self.mask;
848        let start = mask.bit_offset(0, self.y);
849        let bits = mask.bits_per_pixel as usize;
850        (0..mask.width as usize).map(move |x| mask.pixel_at(start + x * bits))
851    }
852}
853
854#[cfg(test)]
855mod tests {
856    use super::*;
857    use crate::model::glyph::GlyphSource;
858    use alloc::vec::Vec;
859
860    fn font(data: &[u8]) -> Font {
861        Font::new(data.to_vec(), 0).unwrap()
862    }
863
864    #[test]
865    fn cbdt_strikes() {
866        let font = font(font_test_data::CBDT);
867        let bitmaps = font.bitmap_strikes();
868        assert_eq!(bitmaps.source(), Some(BitmapSource::Cbdt));
869        assert_eq!(bitmaps.len(), 3);
870        let ppems: Vec<_> = bitmaps.iter().map(|strike| strike.ppem()).collect();
871        assert_eq!(ppems, [16, 64, 128]);
872    }
873
874    #[test]
875    fn cbdt_glyph_metrics() {
876        let font = font(font_test_data::CBDT);
877        let bitmaps = font.bitmap_strikes();
878        let zero = bitmaps.get(0).unwrap().get(GlyphId::new(0)).unwrap();
879        assert_eq!(zero.glyph(), GlyphId::new(0));
880        assert_eq!(zero.source(), BitmapSource::Cbdt);
881        assert_eq!((zero.width(), zero.height()), (11, 13));
882        assert_eq!((zero.x_bearing(), zero.y_bearing()), (0, 0));
883        assert_eq!((zero.inner_x_bearing(), zero.inner_y_bearing()), (1, 13));
884        assert_eq!(zero.h_advance(), Some(12));
885        assert_eq!(zero.origin(), Origin::TopLeft);
886        assert_eq!(
887            zero.pixel_extents(),
888            GlyphExtents {
889                x_bearing: 1,
890                y_bearing: 13,
891                width: 11,
892                height: 13,
893            }
894        );
895        let two = bitmaps.get(1).unwrap().get(GlyphId::new(2)).unwrap();
896        assert_eq!((two.width(), two.height()), (39, 52));
897        assert_eq!((two.x_bearing(), two.y_bearing()), (0, 0));
898        assert_eq!((two.inner_x_bearing(), two.inner_y_bearing()), (6, 52));
899        assert_eq!(two.h_advance(), Some(51));
900        assert!(matches!(two.pixels(), Pixels::Png(_)));
901    }
902
903    #[test]
904    fn strike_selection_follows_harfbuzz() {
905        let font = font(font_test_data::CBDT);
906        let bitmaps = font.bitmap_strikes();
907        let strike_for = |ppem| bitmaps.select(ppem).unwrap().ppem();
908        // The smallest strike at least as large as the size.
909        assert_eq!(strike_for(Some(12.0)), 16);
910        assert_eq!(strike_for(Some(16.0)), 16);
911        assert_eq!(strike_for(Some(17.0)), 64);
912        assert_eq!(strike_for(Some(60.0)), 64);
913        // Past every strike, the largest.
914        assert_eq!(strike_for(Some(200.0)), 128);
915        // A fractional size takes the strike at least as large.
916        assert_eq!(strike_for(Some(16.5)), 64);
917        assert_eq!(strike_for(Some(15.5)), 16);
918        // No size, or one HarfBuzz would read as zero, picks the largest.
919        assert_eq!(strike_for(None), 128);
920        for ppem in [0.0, -0.0, -12.0, f32::NAN] {
921            assert_eq!(strike_for(Some(ppem)), 128, "{ppem}");
922        }
923        assert_eq!(
924            bitmaps
925                .select_bitmap(GlyphId::new(2), Some(12.0))
926                .unwrap()
927                .ppem_x(),
928            16
929        );
930        assert_eq!(
931            bitmaps
932                .select_bitmap(GlyphId::new(2), Some(0.0))
933                .unwrap()
934                .ppem_x(),
935            128
936        );
937    }
938
939    #[test]
940    fn a_glyph_comes_from_the_best_strike_that_has_it() {
941        let font = font(font_test_data::CBDT);
942        let bitmaps = font.bitmap_strikes();
943        let gid = GlyphId::new(2);
944        for (ppem, expected) in [
945            (Some(12.0), 16),
946            (Some(17.0), 64),
947            (Some(60.0), 64),
948            (None, 128),
949        ] {
950            assert_eq!(
951                bitmaps.select_bitmap(gid, ppem).unwrap().ppem(),
952                expected,
953                "{ppem:?}"
954            );
955        }
956        // The ranking alone, as a strike missing a glyph would apply it.
957        assert!(closer_to(Some(12.0), 16, 64));
958        assert!(!closer_to(Some(12.0), 64, 16));
959        assert!(closer_to(Some(200.0), 128, 64));
960        assert!(closer_to(None, 128, 16));
961        assert!(!closer_to(None, 16, 128));
962    }
963
964    #[test]
965    fn extents_scale_to_font_units_as_harfbuzz_does() {
966        let font = font(font_test_data::CBDT);
967        let glyph = font
968            .bitmap_strikes()
969            .select_bitmap(GlyphId::new(0), Some(16.0))
970            .unwrap();
971        let extents = glyph.extents();
972        let scale = font.units_per_em() as f32 / 16.0;
973        assert_eq!(extents.x_bearing, (1.0 * scale).round());
974        assert_eq!(extents.y_bearing, (13.0 * scale).round());
975        assert_eq!(extents.width, (11.0 * scale).round());
976        assert_eq!(extents.height, (13.0 * scale).round());
977    }
978
979    // A strike with no size has no glyphs, as in HarfBuzz, so the search
980    // moves on to the next strike.
981    #[test]
982    fn a_strike_with_no_size_has_no_glyphs() {
983        use crate::{model::Blob, types::Tag, FontRef};
984        use alloc::sync::Arc;
985        let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(|tag| {
986            let font = FontRef::new(font_test_data::CBDT).ok()?;
987            let mut data = font.table_data(tag)?.as_bytes().to_vec();
988            if tag == Tag::new(b"CBLC") {
989                // The first size record's horizontal ppem: an eight byte
990                // header, then 44 bytes into the record.
991                data[52] = 0;
992            }
993            Some(Blob::from(data))
994        });
995        let font = Font::new(source, 0).unwrap();
996        let bitmaps = font.bitmap_strikes();
997        let strike = bitmaps.get(0).unwrap();
998        assert_eq!(strike.ppem_x(), 0);
999        assert!(strike.get(GlyphId::new(0)).is_none());
1000        let glyph = bitmaps.select_bitmap(GlyphId::new(0), Some(1.0)).unwrap();
1001        assert!(glyph.ppem_x() > 0);
1002        let extents = glyph.extents();
1003        assert!(extents.width.is_finite() && extents.width > 0.0);
1004    }
1005
1006    #[test]
1007    fn sbix_glyphs() {
1008        let font = font(font_test_data::NOTO_HANDWRITING_SBIX);
1009        let bitmaps = font.bitmap_strikes();
1010        assert_eq!(bitmaps.source(), Some(BitmapSource::Sbix));
1011        assert_eq!(bitmaps.len(), 1);
1012        let strike = bitmaps.get(0).unwrap();
1013        assert_eq!(strike.ppem(), 109);
1014        let glyph = strike.get(GlyphId::new(7)).unwrap();
1015        assert_eq!(glyph.glyph(), GlyphId::new(7));
1016        assert_eq!(glyph.source(), BitmapSource::Sbix);
1017        // The glyph has no outline, so nothing moves its origin.
1018        assert_eq!((glyph.x_bearing(), glyph.y_bearing()), (0, 0));
1019        assert_eq!((glyph.inner_x_bearing(), glyph.inner_y_bearing()), (4, -27));
1020        assert_eq!(glyph.origin(), Origin::BottomLeft);
1021        assert!(matches!(glyph.pixels(), Pixels::Png(_)));
1022        assert!(glyph.width() > 0 && glyph.height() > 0);
1023        // The bearing to the top of the image is the height above the offset.
1024        assert_eq!(glyph.pixel_extents().y_bearing, glyph.height() as i32 - 27);
1025        assert_eq!(glyph.h_advance(), None);
1026    }
1027
1028    #[test]
1029    fn sbix_bearings_come_from_the_outline_the_image_stands_in_for() {
1030        let font = font(font_test_data::NOTO_HANDWRITING_SBIX);
1031        // Glyph 0 is the only glyph with contours: its `hmtx` bearing is 51
1032        // and its stated box reaches down to -250.
1033        assert_eq!(sbix_bearings(&font, GlyphId::new(0)), (51, -250));
1034        // Glyph 7 has an image but no contours, so its origin stays put.
1035        assert_eq!(sbix_bearings(&font, GlyphId::new(7)), (0, 0));
1036    }
1037
1038    #[test]
1039    fn ebdt_masks() {
1040        let font = font(font_test_data::EMBEDDED_BITMAPS);
1041        let bitmaps = font.bitmap_strikes_with(GlyphSourceOrder::new(&[GlyphSource::Ebdt]));
1042        assert_eq!(bitmaps.source(), Some(BitmapSource::Ebdt));
1043        let strike = bitmaps.get(0).unwrap();
1044        assert_eq!((strike.ppem_x(), strike.ppem_y()), (7, 7));
1045        let glyph = strike.get(GlyphId::new(1)).unwrap();
1046        assert_eq!(glyph.glyph(), GlyphId::new(1));
1047        assert_eq!(glyph.source(), BitmapSource::Ebdt);
1048        assert_eq!((glyph.width(), glyph.height()), (3, 8));
1049        let Pixels::Mask(mask) = &glyph.pixels() else {
1050            panic!("expected a mask");
1051        };
1052        assert_eq!(mask.bits_per_pixel(), 1);
1053        assert!(mask.is_packed());
1054        assert_eq!(mask.decoded_len(), 24);
1055        // The data is 0xee, 0xae, 0xea: three columns of eight rows.
1056        let pixels: Vec<_> = mask.pixels().map(|p| (p == 255) as u8).collect();
1057        assert_eq!(
1058            pixels,
1059            [1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0]
1060        );
1061        assert_eq!(mask.rows().count(), 8);
1062        assert_eq!(
1063            mask.rows().nth(1).unwrap().pixels().collect::<Vec<_>>(),
1064            [0, 255, 255]
1065        );
1066        let mut decoded = [0u8; 24];
1067        assert!(mask.decode_into(&mut decoded));
1068        assert_eq!(decoded[..3], [255, 255, 255]);
1069        assert!(!mask.decode_into(&mut [0u8; 23]));
1070    }
1071
1072    #[test]
1073    fn default_order_prefers_cbdt_over_ebdt() {
1074        let font = font(font_test_data::EMBEDDED_BITMAPS);
1075        assert_eq!(font.bitmap_strikes().source(), Some(BitmapSource::Cbdt));
1076        assert_eq!(
1077            font.bitmap_strikes_with(GlyphSourceOrder::OUTLINES)
1078                .source(),
1079            None
1080        );
1081    }
1082
1083    // A font with only EBDT bitmaps still has bitmaps, though the extents
1084    // order leaves EBDT out.
1085    #[test]
1086    fn a_font_with_only_ebdt_has_bitmaps() {
1087        use crate::{model::Blob, types::Tag, FontRef};
1088        use alloc::sync::Arc;
1089        let source: Arc<dyn Fn(Tag) -> Option<Blob> + Send + Sync> = Arc::new(|tag| {
1090            if tag == Tag::new(b"CBDT") || tag == Tag::new(b"CBLC") {
1091                return None;
1092            }
1093            let font = FontRef::new(font_test_data::EMBEDDED_BITMAPS).ok()?;
1094            Some(Blob::from(font.table_data(tag)?.as_bytes().to_vec()))
1095        });
1096        let font = Font::new(source, 0).unwrap();
1097        let bitmaps = font.bitmap_strikes();
1098        assert_eq!(bitmaps.source(), Some(BitmapSource::Ebdt));
1099        assert!(!bitmaps.is_empty());
1100        assert!(!GlyphSourceOrder::DEFAULT.contains(GlyphSource::Ebdt));
1101    }
1102
1103    #[test]
1104    fn unpacked_mask_pixels() {
1105        // 4 by 2, 1 bit per pixel, each row a byte: 1010 then 0101.
1106        let mask = Mask::new(&[0xA0, 0x50], 4, 2, 1, false).unwrap();
1107        let pixels: Vec<_> = mask.pixels().collect();
1108        assert_eq!(pixels, [255, 0, 255, 0, 0, 255, 0, 255]);
1109        assert_eq!(mask.pixel(3, 1), Some(255));
1110        assert_eq!(mask.pixel(4, 1), None);
1111        // 3 by 2, 4 bits per pixel, rows padded to two bytes.
1112        let mask = Mask::new(&[0xF8, 0x40, 0x05, 0xA0], 3, 2, 4, false).unwrap();
1113        let pixels: Vec<_> = mask.pixels().collect();
1114        assert_eq!(pixels, [255, 136, 68, 0, 85, 170]);
1115        // 4 by 1, 2 bits per pixel.
1116        let mask = Mask::new(&[0xE4], 4, 1, 2, false).unwrap();
1117        assert_eq!(mask.pixels().collect::<Vec<_>>(), [255, 170, 85, 0]);
1118    }
1119
1120    #[test]
1121    fn packed_mask_pixels() {
1122        // 3 by 3, 1 bit per pixel, nine bits run together.
1123        let mask = Mask::new(&[0xAB, 0x00], 3, 3, 1, true).unwrap();
1124        let pixels: Vec<_> = mask.pixels().collect();
1125        assert_eq!(pixels, [255, 0, 255, 0, 255, 0, 255, 255, 0]);
1126        // 5 by 2, 2 bits per pixel.
1127        let mask = Mask::new(&[0xE4, 0xC6, 0xC0], 5, 2, 2, true).unwrap();
1128        assert_eq!(
1129            mask.pixels().collect::<Vec<_>>(),
1130            [255, 170, 85, 0, 255, 0, 85, 170, 255, 0]
1131        );
1132        // 3 by 2, 4 bits per pixel.
1133        let mask = Mask::new(&[0xF0, 0x84, 0xA5], 3, 2, 4, true).unwrap();
1134        assert_eq!(
1135            mask.pixels().collect::<Vec<_>>(),
1136            [255, 0, 136, 68, 170, 85]
1137        );
1138        // A byte per pixel copies through.
1139        let mask = Mask::new(&[100, 200, 50, 0, 128, 255], 3, 2, 8, true).unwrap();
1140        let mut out = [0u8; 6];
1141        assert!(mask.decode_into(&mut out));
1142        assert_eq!(out, [100, 200, 50, 0, 128, 255]);
1143    }
1144
1145    #[test]
1146    fn short_or_odd_masks_are_refused() {
1147        assert!(Mask::new(&[0xFF], 3, 3, 1, true).is_none());
1148        assert!(Mask::new(&[1, 2, 3], 4, 2, 8, false).is_none());
1149        assert!(Mask::new(&[0; 16], 2, 2, 3, false).is_none());
1150        let empty = Mask::new(&[], 0, 5, 8, false).unwrap();
1151        assert!(empty.is_empty());
1152        assert_eq!(empty.pixels().count(), 0);
1153    }
1154
1155    #[test]
1156    fn bgra_rows_and_pixels() {
1157        let data: Vec<u8> = (0..16).collect();
1158        let bgra = Bgra::new(&data, 2, 2).unwrap();
1159        assert_eq!(bgra.rows().count(), 2);
1160        assert_eq!(bgra.pixel(1, 1), Some([12, 13, 14, 15]));
1161        assert_eq!(bgra.pixel(2, 0), None);
1162        assert!(Bgra::new(&data, 3, 2).is_none());
1163    }
1164}