swash/scale/
mod.rs

1/*!
2Scaling, hinting and rasterization of visual glyph representations.
3
4Scaling is the process of generating an appropriately sized visual
5representation of a glyph. The scaler can produce rendered glyph
6[images](Image) from outlines, layered color outlines and embedded
7bitmaps. Alternatively, you can request raw, optionally hinted
8[outlines](Outline) that can then be further processed by [zeno] or
9fed into other crates like [lyon](https://github.com/nical/lyon) or
10[pathfinder](https://github.com/servo/pathfinder) for tessellation and
11GPU rendering.
12
13# Building the scaler
14
15All scaling in this crate takes place within the purview of a
16[`ScaleContext`]. This opaque struct manages internal LRU caches and scratch
17buffers that are necessary for the scaling process. Generally, you'll
18want to keep an instance with your glyph cache, or if doing multithreaded
19glyph rasterization, one instance per thread.
20
21The only method available on the context is [`builder`](ScaleContext::builder)
22which takes a type that can be converted into a [`FontRef`] as an argument
23and produces a [`ScalerBuilder`] that provides options for configuring and
24building a [`Scaler`].
25
26Here, we'll create a context and build a scaler for a size of 14px with
27hinting enabled:
28```
29# use swash::{FontRef, CacheKey, scale::*};
30# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
31// let font = ...;
32let mut context = ScaleContext::new();
33let mut scaler = context.builder(font)
34    .size(14.)
35    .hint(true)
36    .build();
37```
38
39You can specify variation settings by calling the [`variations`](ScalerBuilder::variations)
40method with an iterator that yields a sequence of values that are convertible
41to [`Setting<f32>`]. Tuples of (&str, f32) will work in a pinch. For example,
42you can request a variation of the weight axis like this:
43```
44# use swash::{FontRef, CacheKey, scale::*};
45# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
46// let font = ...;
47let mut context = ScaleContext::new();
48let mut scaler = context.builder(font)
49    .size(14.)
50    .hint(true)
51    .variations(&[("wght", 520.5)])
52    .build();
53```
54
55Alternatively, you can specify variations using the
56[`normalized_coords`](ScalerBuilder::normalized_coords) method which takes an iterator
57that yields [`NormalizedCoord`]s (a type alias for `i16` which is a fixed point value
58in 2.14 format). This method is faster than specifying variations by tag and value, but
59the difference is likely negligible outside of microbenchmarks. The real advantage
60is that a sequence of `i16` is more compact and easier to fold into a key in a glyph
61cache. You can compute these normalized coordinates by using the
62[`Variation::normalize`](crate::Variation::normalize) method for each available axis in
63the font. The best strategy, however, is to simply capture these during shaping with
64the [`Shaper::normalized_coords`](crate::shape::Shaper::normalized_coords) method which
65will have already computed them for you.
66
67See [`ScalerBuilder`] for available options and default values.
68
69# Outlines and bitmaps
70
71The [`Scaler`] struct essentially provides direct access to the outlines and embedded
72bitmaps that are available in the font. In the case of outlines, it can produce the
73raw outline in font units or an optionally hinted, scaled outline. For example, to
74extract the raw outline for the letter 'Q':
75```
76# use swash::{FontRef, CacheKey, scale::*};
77# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
78// let font = ...;
79let mut context = ScaleContext::new();
80let mut scaler = context.builder(font).build();
81let glyph_id = font.charmap().map('Q');
82let outline = scaler.scale_outline(glyph_id);
83```
84
85For the same, but hinted at 12px:
86```
87# use swash::{FontRef, CacheKey, scale::*};
88# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
89// let font = ...;
90let mut context = ScaleContext::new();
91let mut scaler = context.builder(font)
92    .hint(true)
93    .size(12.)
94    .build();
95let glyph_id = font.charmap().map('Q');
96let outline = scaler.scale_outline(glyph_id);
97```
98The [`scale_outline`](Scaler::scale_outline) method returns an [`Outline`] wrapped
99in an option. It will return `None` if an outline was not available or if there was
100an error during the scaling process. Note that
101[`scale_color_outline`](Scaler::scale_color_outline) can be used to access layered
102color outlines such as those included in the Microsoft _Segoe UI Emoji_ font. Finally,
103the `_into` variants of these methods ([`scale_outline_into`](Scaler::scale_outline_into)
104and [`scale_color_outline_into`](Scaler::scale_color_outline_into)) will return
105their results in a previously allocated outline avoiding the extra allocations.
106
107Similar to outlines, bitmaps can be retrieved with the [`scale_bitmap`](Scaler::scale_bitmap)
108and [`scale_color_bitmap`](Scaler::scale_color_bitmap) for alpha and color bitmaps,
109respectively. These methods return an [`Image`] wrapped in an option. The associated
110`_into` variants are also available.
111
112Unlike outlines, bitmaps are available in [`strike`](crate::BitmapStrike)s of various sizes.
113When requesting a bitmap, you specify the strategy for strike selection using the
114[`StrikeWith`] enum.
115
116For example, if we want the largest available unscaled image for the fire emoji:
117```
118# use swash::{FontRef, CacheKey, scale::*};
119# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
120// let font = ...;
121let mut context = ScaleContext::new();
122let mut scaler = context.builder(font).build();
123let glyph_id = font.charmap().map('🔥');
124let image = scaler.scale_color_bitmap(glyph_id, StrikeWith::LargestSize);
125```
126
127Or, to produce a scaled image for a size of 18px:
128```
129# use swash::{FontRef, CacheKey, scale::*};
130# let font: FontRef = FontRef { data: &[], offset: 0, key: CacheKey::new() };
131// let font = ...;
132let mut context = ScaleContext::new();
133let mut scaler = context.builder(font)
134    .size(18.)
135    .build();
136let glyph_id = font.charmap().map('🔥');
137let image = scaler.scale_color_bitmap(glyph_id, StrikeWith::BestFit);
138```
139This will select the best strike for the requested size and return
140a bitmap that is scaled appropriately for an 18px run of text.
141
142Alpha bitmaps should generally be avoided unless you're rendering small East
143Asian text where these are sometimes still preferred over scalable outlines. In
144this case, you should only use [`StrikeWith::ExactSize`] to select the strike,
145falling back to an outline if a bitmap is unavailable.
146
147# Rendering
148
149In the general case of text rendering, you'll likely not care about the specific
150details of outlines or bitmaps and will simply want an appropriately sized
151image that represents your glyph. For this purpose, you'll want to use the
152[`Render`] struct which is a builder that provides options for rendering an image.
153This struct is constructed with a slice of [`Source`]s in priority order and
154will iterate through them until it finds one that satisfies the request. Typically,
155you'll want to use the following order:
156```
157# use swash::scale::*;
158Render::new(&[
159    // Color outline with the first palette
160    Source::ColorOutline(0),
161    // Color bitmap with best fit selection mode
162    Source::ColorBitmap(StrikeWith::BestFit),
163    // Standard scalable outline
164    Source::Outline,
165]);
166```
167
168The [`Render`] struct offers several options that control rasterization of
169outlines such as [`format`](Render::format) for selecting a subpixel rendering mode,
170[`offset`](Render::offset) for applying fractional positioning, and others. See the
171struct documentation for detail.
172
173After selecting your options, call the [`render`](Render::render) method, passing your
174configured [`Scaler`] and the requested glyph identifier to produce an [`Image`].
175Let's put it all together by writing a simple function that will render subpixel glyphs
176with fractional positioning:
177```
178# use swash::{scale::{*, image::Image}, FontRef, GlyphId};
179fn render_glyph(
180    context: &mut ScaleContext,
181    font: &FontRef,
182    size: f32,
183    hint: bool,
184    glyph_id: GlyphId,
185    x: f32,
186    y: f32,
187) -> Option<Image> {
188    use zeno::{Format, Vector};
189    // Build the scaler
190    let mut scaler = context.builder(*font).size(size).hint(hint).build();
191    // Compute the fractional offset-- you'll likely want to quantize this
192    // in a real renderer
193    let offset = Vector::new(x.fract(), y.fract());
194    // Select our source order
195    Render::new(&[
196        Source::ColorOutline(0),
197        Source::ColorBitmap(StrikeWith::BestFit),
198        Source::Outline,
199    ])
200    // Select a subpixel format
201    .format(Format::Subpixel)
202    // Apply the fractional offset
203    .offset(offset)
204    // Render the image
205    .render(&mut scaler, glyph_id)
206}
207```
208Note that rendering also takes care of correctly scaling, rasterizing and
209compositing layered color outlines for us.
210
211There are other options available for emboldening, transforming with an
212affine matrix, and applying path effects. See the methods on [`Render`] for
213more detail.
214*/
215
216pub mod image;
217pub mod outline;
218
219mod bitmap;
220mod color;
221mod hinting_cache;
222mod proxy;
223
224use hinting_cache::HintingCache;
225use image::*;
226use outline::*;
227use skrifa::{
228    instance::{NormalizedCoord as SkrifaNormalizedCoord, Size as SkrifaSize},
229    outline::OutlineGlyphCollection,
230    GlyphId as SkrifaGlyphId, MetadataProvider,
231};
232
233use super::internal;
234use super::{cache::FontCache, setting::Setting, FontRef, GlyphId, NormalizedCoord};
235use alloc::vec::Vec;
236use core::borrow::Borrow;
237#[cfg(all(feature = "libm", feature = "render"))]
238use core_maths::CoreFloat;
239use proxy::*;
240use zeno::Placement;
241#[cfg(feature = "render")]
242use zeno::{Format, Mask, Origin, Point, Scratch, Style, Transform, Vector};
243
244pub(crate) use bitmap::decode_png;
245
246/// Index of a color palette.
247pub type PaletteIndex = u16;
248
249/// Index of a bitmap strike.
250pub type StrikeIndex = u32;
251
252/// Bitmap strike selection mode.
253#[derive(Copy, Clone, Debug)]
254pub enum StrikeWith {
255    /// Load a bitmap only if the exact size is available.
256    ExactSize,
257    /// Load a bitmap of the best available size.
258    BestFit,
259    /// Loads a bitmap of the largest size available.
260    LargestSize,
261    /// Load a bitmap from the specified strike.
262    Index(StrikeIndex),
263}
264
265/// Glyph sources for the renderer.
266#[derive(Copy, Clone, Debug)]
267pub enum Source {
268    /// Scalable outlines.
269    Outline,
270    /// Layered color scalable outlines.
271    ColorOutline(PaletteIndex),
272    /// Embedded alpha bitmaps.
273    Bitmap(StrikeWith),
274    /// Embedded color bitmaps.
275    ColorBitmap(StrikeWith),
276}
277
278impl Default for Source {
279    fn default() -> Self {
280        Self::Outline
281    }
282}
283
284/// Context that manages caches and scratch buffers for scaling.
285///
286/// See the module level [documentation](index.html#building-the-scaler) for detail.
287pub struct ScaleContext {
288    fonts: FontCache<ScalerProxy>,
289    state: State,
290    hinting_cache: HintingCache,
291    coords: Vec<SkrifaNormalizedCoord>,
292}
293
294struct State {
295    scratch0: Vec<u8>,
296    scratch1: Vec<u8>,
297    outline: Outline,
298    #[cfg(feature = "render")]
299    rcx: Scratch,
300}
301
302impl ScaleContext {
303    /// Creates a new scaling context.
304    pub fn new() -> Self {
305        Self::with_max_entries(8)
306    }
307
308    /// Creates a new scaling context with the specified maximum number of
309    /// cache entries.
310    pub fn with_max_entries(max_entries: usize) -> Self {
311        let max_entries = max_entries.clamp(1, 64);
312        Self {
313            fonts: FontCache::new(max_entries),
314            state: State {
315                scratch0: Vec::new(),
316                scratch1: Vec::new(),
317                outline: Outline::new(),
318                #[cfg(feature = "render")]
319                rcx: Scratch::new(),
320            },
321            hinting_cache: HintingCache::default(),
322            coords: Vec::new(),
323        }
324    }
325
326    /// Creates a new builder for constructing a scaler with this context
327    /// and the specified font.
328    pub fn builder<'a>(&'a mut self, font: impl Into<FontRef<'a>>) -> ScalerBuilder<'a> {
329        ScalerBuilder::new(self, font)
330    }
331}
332
333impl Default for ScaleContext {
334    fn default() -> Self {
335        Self::new()
336    }
337}
338
339/// Builder for configuring a scaler.
340pub struct ScalerBuilder<'a> {
341    state: &'a mut State,
342    hinting_cache: &'a mut HintingCache,
343    font: FontRef<'a>,
344    outlines: Option<OutlineGlyphCollection<'a>>,
345    proxy: &'a ScalerProxy,
346    id: [u64; 2],
347    coords: &'a mut Vec<SkrifaNormalizedCoord>,
348    size: f32,
349    hint: bool,
350}
351
352impl<'a> ScalerBuilder<'a> {
353    fn new(context: &'a mut ScaleContext, font: impl Into<FontRef<'a>>) -> Self {
354        let font = font.into();
355        let (id, proxy) = context.fonts.get(&font, None, ScalerProxy::from_font);
356        let skrifa_font = if font.offset == 0 {
357            skrifa::FontRef::new(font.data).ok()
358        } else {
359            // TODO: make this faster
360            let index = crate::FontDataRef::new(font.data)
361                .and_then(|font_data| font_data.fonts().position(|f| f.offset == font.offset));
362            index.and_then(|index| skrifa::FontRef::from_index(font.data, index as u32).ok())
363        };
364        let outlines = skrifa_font.map(|font_ref| font_ref.outline_glyphs());
365        Self {
366            state: &mut context.state,
367            hinting_cache: &mut context.hinting_cache,
368            font,
369            outlines,
370            proxy,
371            id,
372            coords: &mut context.coords,
373            size: 0.,
374            hint: false,
375        }
376    }
377
378    /// Specifies the font size in pixels per em. The default value is `0` which will produce
379    /// unscaled glyphs in original font units.
380    pub fn size(mut self, ppem: f32) -> Self {
381        self.size = ppem.max(0.);
382        self
383    }
384
385    /// Specifies whether to apply hinting to outlines. The default value is `false`.
386    pub fn hint(mut self, yes: bool) -> Self {
387        self.hint = yes;
388        self
389    }
390
391    /// Adds variation settings to the scaler.
392    pub fn variations<I>(self, settings: I) -> Self
393    where
394        I: IntoIterator,
395        I::Item: Into<Setting<f32>>,
396    {
397        if self.proxy.coord_count != 0 {
398            let vars = self.font.variations();
399            self.coords.resize(vars.len(), Default::default());
400            for setting in settings {
401                let setting = setting.into();
402                for var in vars {
403                    if var.tag() == setting.tag {
404                        let value = var.normalize(setting.value);
405                        if let Some(c) = self.coords.get_mut(var.index()) {
406                            *c = SkrifaNormalizedCoord::from_bits(value);
407                        }
408                    }
409                }
410            }
411        }
412        self
413    }
414
415    /// Specifies the variation settings in terms of normalized coordinates. This will replace
416    /// any previous variation settings.
417    pub fn normalized_coords<I>(self, coords: I) -> Self
418    where
419        I: IntoIterator,
420        I::Item: Borrow<NormalizedCoord>,
421    {
422        self.coords.clear();
423        self.coords.extend(
424            coords
425                .into_iter()
426                .map(|c| SkrifaNormalizedCoord::from_bits(*c.borrow())),
427        );
428        self
429    }
430
431    /// Builds a scaler for the current configuration.
432    pub fn build(self) -> Scaler<'a> {
433        let upem = self.proxy.metrics.units_per_em();
434        let skrifa_size = if self.size != 0.0 && upem != 0 {
435            SkrifaSize::new(self.size)
436        } else {
437            SkrifaSize::unscaled()
438        };
439        let hinting_instance = match (self.hint, &self.outlines) {
440            (true, Some(outlines)) => {
441                let key = hinting_cache::HintingKey {
442                    id: self.id,
443                    outlines,
444                    size: skrifa_size,
445                    coords: self.coords,
446                };
447                self.hinting_cache.get(&key)
448            }
449            _ => None,
450        };
451        Scaler {
452            state: self.state,
453            font: self.font,
454            outlines: self.outlines,
455            hinting_instance,
456            proxy: self.proxy,
457            coords: &self.coords[..],
458            size: self.size,
459            skrifa_size,
460        }
461    }
462}
463
464/// Scales outline and bitmap glyphs.
465///
466/// See the module level [documentation](index.html#outlines-and-bitmaps) for detail.
467pub struct Scaler<'a> {
468    state: &'a mut State,
469    font: FontRef<'a>,
470    outlines: Option<OutlineGlyphCollection<'a>>,
471    hinting_instance: Option<&'a skrifa::outline::HintingInstance>,
472    proxy: &'a ScalerProxy,
473    coords: &'a [SkrifaNormalizedCoord],
474    size: f32,
475    skrifa_size: SkrifaSize,
476}
477
478impl<'a> Scaler<'a> {
479    /// Returns true if scalable glyph outlines are available.
480    pub fn has_outlines(&self) -> bool {
481        self.outlines
482            .as_ref()
483            .map(|outlines| outlines.format().is_some())
484            .unwrap_or_default()
485    }
486
487    /// Scales an outline for the specified glyph into the provided outline.
488    pub fn scale_outline_into(&mut self, glyph_id: GlyphId, outline: &mut Outline) -> bool {
489        outline.clear();
490        self.scale_outline_impl(glyph_id, None, Some(outline))
491    }
492
493    /// Scales an outline for the specified glyph.
494    pub fn scale_outline(&mut self, glyph_id: GlyphId) -> Option<Outline> {
495        let mut outline = Outline::new();
496        if self.scale_outline_into(glyph_id, &mut outline) {
497            Some(outline)
498        } else {
499            None
500        }
501    }
502
503    /// Returns true if scalable color glyph outlines are available.
504    pub fn has_color_outlines(&self) -> bool {
505        self.proxy.color.colr != 0 && self.proxy.color.cpal != 0
506    }
507
508    /// Scales a color outline for the specified glyph into the provided outline.
509    pub fn scale_color_outline_into(&mut self, glyph_id: GlyphId, outline: &mut Outline) -> bool {
510        outline.clear();
511        if !self.has_color_outlines() {
512            return false;
513        }
514        let layers = match self.proxy.color.layers(self.font.data, glyph_id) {
515            Some(layers) => layers,
516            _ => return false,
517        };
518        for i in 0..layers.len() {
519            let layer = match layers.get(i) {
520                Some(layer) => layer,
521                _ => return false,
522            };
523            if !self.scale_outline_impl(layer.glyph_id, layer.color_index, Some(outline)) {
524                return false;
525            }
526        }
527        outline.set_color(true);
528        true
529    }
530
531    /// Scales a color outline for the specified glyph.
532    pub fn scale_color_outline(&mut self, glyph_id: GlyphId) -> Option<Outline> {
533        let mut outline = Outline::new();
534        if self.scale_color_outline_into(glyph_id, &mut outline) {
535            Some(outline)
536        } else {
537            None
538        }
539    }
540
541    fn scale_outline_impl(
542        &mut self,
543        glyph_id: GlyphId,
544        color_index: Option<u16>,
545        outline: Option<&mut Outline>,
546    ) -> bool {
547        let outline = match outline {
548            Some(x) => x,
549            _ => &mut self.state.outline,
550        };
551        if let Some(outlines) = &self.outlines {
552            if let Some(glyph) = outlines.get(SkrifaGlyphId::from(glyph_id)) {
553                outline.begin_layer(color_index);
554                let settings: skrifa::outline::DrawSettings =
555                    if let Some(hinting_instance) = &self.hinting_instance {
556                        (*hinting_instance).into()
557                    } else {
558                        (
559                            self.skrifa_size,
560                            skrifa::instance::LocationRef::new(self.coords),
561                        )
562                            .into()
563                    };
564                if glyph.draw(settings, outline).is_ok() {
565                    outline.maybe_close();
566                    outline.finish();
567                    return true;
568                }
569            }
570        }
571        false
572    }
573
574    // Unused when render feature is disabled.
575    #[allow(dead_code)]
576    fn scale_color_outline_impl(&mut self, glyph_id: GlyphId) -> bool {
577        if !self.has_color_outlines() {
578            return false;
579        }
580        let layers = match self.proxy.color.layers(self.font.data, glyph_id) {
581            Some(layers) => layers,
582            _ => return false,
583        };
584        self.state.outline.clear();
585        for i in 0..layers.len() {
586            let layer = match layers.get(i) {
587                Some(layer) => layer,
588                _ => return false,
589            };
590            if !self.scale_outline_impl(layer.glyph_id, layer.color_index, None) {
591                return false;
592            }
593        }
594        true
595    }
596
597    /// Returns true if alpha bitmaps are available.
598    pub fn has_bitmaps(&self) -> bool {
599        self.proxy.bitmaps.has_alpha()
600    }
601
602    /// Scales a bitmap for the specified glyph and mode into the provided image.
603    pub fn scale_bitmap_into(
604        &mut self,
605        glyph_id: u16,
606        strike: StrikeWith,
607        image: &mut Image,
608    ) -> bool {
609        self.scale_bitmap_impl(glyph_id, false, strike, image) == Some(true)
610    }
611
612    /// Scales a bitmap for the specified glyph and mode.
613    pub fn scale_bitmap(&mut self, glyph_id: u16, strike: StrikeWith) -> Option<Image> {
614        let mut image = Image::new();
615        if self.scale_bitmap_into(glyph_id, strike, &mut image) {
616            Some(image)
617        } else {
618            None
619        }
620    }
621
622    /// Returns true if color bitmaps are available.
623    pub fn has_color_bitmaps(&self) -> bool {
624        self.proxy.bitmaps.has_color()
625    }
626
627    /// Scales a color bitmap for the specified glyph and mode into the provided image.
628    pub fn scale_color_bitmap_into(
629        &mut self,
630        glyph_id: u16,
631        strike: StrikeWith,
632        image: &mut Image,
633    ) -> bool {
634        self.scale_bitmap_impl(glyph_id, true, strike, image) == Some(true)
635    }
636
637    /// Scales a color bitmap for the specified glyph and mode.
638    pub fn scale_color_bitmap(&mut self, glyph_id: u16, strike: StrikeWith) -> Option<Image> {
639        let mut image = Image::new();
640        if self.scale_color_bitmap_into(glyph_id, strike, &mut image) {
641            Some(image)
642        } else {
643            None
644        }
645    }
646
647    fn scale_bitmap_impl(
648        &mut self,
649        glyph_id: GlyphId,
650        color: bool,
651        strike: StrikeWith,
652        image: &mut Image,
653    ) -> Option<bool> {
654        image.clear();
655        let size = self.size;
656        let mut strikes = if color {
657            self.proxy.bitmaps.materialize_color(&self.font)
658        } else {
659            self.proxy.bitmaps.materialize_alpha(&self.font)
660        };
661        let bitmap = match strike {
662            StrikeWith::ExactSize => {
663                if self.size == 0. {
664                    None
665                } else {
666                    strikes
667                        .find_by_exact_ppem(size as u16, glyph_id)?
668                        .get(glyph_id)
669                }
670            }
671            StrikeWith::BestFit => {
672                if self.size == 0. {
673                    None
674                } else {
675                    strikes
676                        .find_by_nearest_ppem(size as u16, glyph_id)?
677                        .get(glyph_id)
678                }
679            }
680            StrikeWith::LargestSize => strikes.find_by_largest_ppem(glyph_id)?.get(glyph_id),
681            StrikeWith::Index(i) => strikes
682                .nth(i as usize)
683                .and_then(|strike| strike.get(glyph_id)),
684        }?;
685        if bitmap.ppem == 0 {
686            return None;
687        }
688        let (_, _, bufsize) = bitmap.scaled_size(size);
689        image.data.resize(bufsize, 0);
690        self.state.scratch0.clear();
691        self.state.scratch1.clear();
692        let mut w = bitmap.width;
693        let mut h = bitmap.height;
694        let scale = size / bitmap.ppem as f32;
695        image.placement = if size != 0. && scale != 1. {
696            self.state
697                .scratch0
698                .resize(bitmap.format.buffer_size(w, h), 0);
699            w = (w as f32 * scale) as u32;
700            h = (h as f32 * scale) as u32;
701            image.data.resize(bitmap.format.buffer_size(w, h), 0);
702            if !bitmap.decode(Some(&mut self.state.scratch1), &mut self.state.scratch0) {
703                return None;
704            }
705            if !bitmap::resize(
706                &self.state.scratch0,
707                bitmap.width,
708                bitmap.height,
709                bitmap.format.channels(),
710                &mut image.data,
711                w,
712                h,
713                bitmap::Filter::Mitchell,
714                Some(&mut self.state.scratch1),
715            ) {
716                return None;
717            }
718            let left = (bitmap.left as f32 * scale) as i32;
719            let top = (bitmap.top as f32 * scale) as i32;
720            Placement {
721                left,
722                top,
723                width: w,
724                height: h,
725            }
726        } else {
727            image.data.resize(bitmap.format.buffer_size(w, h), 0);
728            if !bitmap.decode(Some(&mut self.state.scratch1), &mut image.data) {
729                return None;
730            }
731            Placement {
732                left: bitmap.left,
733                top: bitmap.top,
734                width: w,
735                height: h,
736            }
737        };
738        image.source = match color {
739            true => Source::ColorBitmap(strike),
740            false => Source::Bitmap(strike),
741        };
742        image.content = match bitmap.format.channels() {
743            1 => Content::Mask,
744            _ => Content::Color,
745        };
746        // let mut advance = bitmap.advance() as f32;
747        // if options.size != 0. && options.size as u16 != bitmap.ppem() {
748        //     advance *= options.size / bitmap.ppem() as f32;
749        // }
750        Some(true)
751    }
752}
753
754/// Builder type for rendering a glyph into an image.
755///
756/// See the module level [documentation](index.html#rendering) for detail.
757#[cfg(feature = "render")]
758pub struct Render<'a> {
759    sources: &'a [Source],
760    format: Format,
761    offset: Point,
762    transform: Option<Transform>,
763    embolden: f32,
764    foreground: [u8; 4],
765    style: Style<'a>,
766}
767
768#[cfg(feature = "render")]
769impl<'a> Render<'a> {
770    /// Creates a new builder for configuring rendering using the specified
771    /// prioritized list of sources.
772    pub fn new(sources: &'a [Source]) -> Self {
773        Self {
774            sources,
775            format: Format::Alpha,
776            offset: Point::new(0., 0.),
777            transform: None,
778            embolden: 0.,
779            foreground: [128, 128, 128, 255],
780            style: Style::default(),
781        }
782    }
783
784    /// Specifies the target format for rasterizing an outline. Default is
785    /// [`Format::Alpha`].
786    pub fn format(&mut self, format: Format) -> &mut Self {
787        self.format = format;
788        self
789    }
790
791    /// Specifies the path style to use when rasterizing an outline. Default is
792    /// [`Fill::NonZero`](zeno::Fill::NonZero).
793    pub fn style(&mut self, style: impl Into<Style<'a>>) -> &mut Self {
794        self.style = style.into();
795        self
796    }
797
798    /// Specifies an additional offset to apply when rasterizing an outline.
799    /// Default is `(0, 0)`.
800    pub fn offset(&mut self, offset: Vector) -> &mut Self {
801        self.offset = offset;
802        self
803    }
804
805    /// Specifies a transformation matrix to apply when rasterizing an
806    /// outline. Default is `None`.
807    pub fn transform(&mut self, transform: Option<Transform>) -> &mut Self {
808        self.transform = transform;
809        self
810    }
811
812    /// Specifies the strength of a faux bold transform to apply when
813    /// rasterizing an outline. Default is `0`.
814    pub fn embolden(&mut self, strength: f32) -> &mut Self {
815        self.embolden = strength;
816        self
817    }
818
819    /// Specifies an RGBA color to use when rasterizing layers of a color
820    /// outline that do not directly reference a palette color. Default is
821    /// `[128, 128, 128, 255]`.
822    pub fn default_color(&mut self, color: [u8; 4]) -> &mut Self {
823        self.foreground = color;
824        self
825    }
826
827    /// Renders the specified glyph using the current configuration into the
828    /// provided image.
829    pub fn render_into(&self, scaler: &mut Scaler, glyph_id: GlyphId, image: &mut Image) -> bool {
830        for source in self.sources {
831            match source {
832                Source::Outline => {
833                    if !scaler.has_outlines() {
834                        continue;
835                    }
836                    scaler.state.outline.clear();
837                    if scaler.scale_outline_impl(glyph_id, None, None) {
838                        let state = &mut scaler.state;
839                        let rcx = &mut state.rcx;
840                        let outline = &mut state.outline;
841                        if self.embolden != 0. {
842                            outline.embolden(self.embolden, self.embolden);
843                        }
844                        if let Some(transform) = &self.transform {
845                            outline.transform(transform);
846                        }
847                        let placement = Mask::with_scratch(outline.path(), rcx)
848                            .format(self.format)
849                            .origin(Origin::BottomLeft)
850                            .style(self.style)
851                            .render_offset(self.offset)
852                            .inspect(|fmt, w, h| {
853                                image.data.resize(fmt.buffer_size(w, h), 0);
854                            })
855                            .render_into(&mut image.data[..], None);
856                        image.placement = placement;
857                        image.content = if self.format == Format::Alpha {
858                            Content::Mask
859                        } else {
860                            Content::SubpixelMask
861                        };
862                        image.source = Source::Outline;
863                        return true;
864                    }
865                }
866                Source::ColorOutline(palette_index) => {
867                    if !scaler.has_color_outlines() {
868                        continue;
869                    }
870                    scaler.state.outline.clear();
871                    if scaler.scale_color_outline_impl(glyph_id) {
872                        let font = &scaler.font;
873                        let proxy = &scaler.proxy;
874                        let state = &mut scaler.state;
875                        let scratch = &mut state.scratch0;
876                        let rcx = &mut state.rcx;
877                        let outline = &mut state.outline;
878                        // Cool effect, but probably not generally desirable.
879                        // Maybe expose a separate option?
880                        // if self.embolden != 0. {
881                        //     outline.embolden(self.embolden, self.embolden);
882                        // }
883                        if let Some(transform) = &self.transform {
884                            outline.transform(transform);
885                        }
886                        let palette = proxy.color.palette(font, *palette_index);
887
888                        let total_bounds = outline.bounds();
889
890                        // need to take offset into account when placing glyph
891                        let base_x = (total_bounds.min.x + self.offset.x).floor() as i32;
892                        let base_y = (total_bounds.min.y + self.offset.y).ceil() as i32;
893                        let base_w = total_bounds.width().ceil() as u32;
894                        let base_h = total_bounds.height().ceil() as u32;
895
896                        image.data.resize((base_w * base_h * 4) as usize, 0);
897                        image.placement.left = base_x;
898                        image.placement.top = base_h as i32 + base_y;
899                        image.placement.width = total_bounds.width().ceil() as u32;
900                        image.placement.height = total_bounds.height().ceil() as u32;
901
902                        let mut ok = true;
903                        for i in 0..outline.len() {
904                            let layer = match outline.get(i) {
905                                Some(layer) => layer,
906                                _ => {
907                                    ok = false;
908                                    break;
909                                }
910                            };
911
912                            scratch.clear();
913                            let placement = Mask::with_scratch(layer.path(), rcx)
914                                .origin(Origin::BottomLeft)
915                                .style(self.style)
916                                .render_offset(self.offset)
917                                .inspect(|fmt, w, h| {
918                                    scratch.resize(fmt.buffer_size(w, h), 0);
919                                })
920                                .render_into(&mut scratch[..], None);
921                            let color = layer
922                                .color_index()
923                                .and_then(|i| palette.map(|p| p.get(i)))
924                                .unwrap_or(self.foreground);
925                            bitmap::blit(
926                                &scratch[..],
927                                placement.width,
928                                placement.height,
929                                placement.left.wrapping_sub(base_x),
930                                (base_h as i32 + base_y).wrapping_sub(placement.top),
931                                color,
932                                &mut image.data,
933                                base_w,
934                                base_h,
935                            );
936                        }
937                        if ok {
938                            image.source = Source::ColorOutline(*palette_index);
939                            image.content = Content::Color;
940                            return true;
941                        }
942                    }
943                }
944                Source::Bitmap(mode) => {
945                    if !scaler.has_bitmaps() {
946                        continue;
947                    }
948                    if scaler.scale_bitmap_into(glyph_id, *mode, image) {
949                        return true;
950                    }
951                }
952                Source::ColorBitmap(mode) => {
953                    if !scaler.has_color_bitmaps() {
954                        continue;
955                    }
956                    if scaler.scale_color_bitmap_into(glyph_id, *mode, image) {
957                        return true;
958                    }
959                }
960            }
961        }
962        false
963    }
964
965    /// Renders the specified glyph using the current configuration.
966    pub fn render(&self, scaler: &mut Scaler, glyph_id: GlyphId) -> Option<Image> {
967        let mut image = Image::new();
968        if self.render_into(scaler, glyph_id, &mut image) {
969            Some(image)
970        } else {
971            None
972        }
973    }
974}