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, None)
330    }
331
332    /// Creates a new builder for constructing a scaler with this context,
333    /// specified font and a custom unique identifier.
334    pub fn builder_with_id<'a>(
335        &'a mut self,
336        font: impl Into<FontRef<'a>>,
337        id: [u64; 2],
338    ) -> ScalerBuilder<'a> {
339        ScalerBuilder::new(self, font, Some(id))
340    }
341}
342
343impl Default for ScaleContext {
344    fn default() -> Self {
345        Self::new()
346    }
347}
348
349/// Builder for configuring a scaler.
350pub struct ScalerBuilder<'a> {
351    state: &'a mut State,
352    hinting_cache: &'a mut HintingCache,
353    font: FontRef<'a>,
354    outlines: Option<OutlineGlyphCollection<'a>>,
355    proxy: &'a ScalerProxy,
356    id: [u64; 2],
357    coords: &'a mut Vec<SkrifaNormalizedCoord>,
358    size: f32,
359    hint: bool,
360}
361
362impl<'a> ScalerBuilder<'a> {
363    fn new(
364        context: &'a mut ScaleContext,
365        font: impl Into<FontRef<'a>>,
366        id: Option<[u64; 2]>,
367    ) -> Self {
368        let font = font.into();
369        let (id, proxy) = context.fonts.get(&font, id, ScalerProxy::from_font);
370        let skrifa_font = if font.offset == 0 {
371            skrifa::FontRef::new(font.data).ok()
372        } else {
373            // TODO: make this faster
374            let index = crate::FontDataRef::new(font.data)
375                .and_then(|font_data| font_data.fonts().position(|f| f.offset == font.offset));
376            index.and_then(|index| skrifa::FontRef::from_index(font.data, index as u32).ok())
377        };
378        let outlines = skrifa_font.map(|font_ref| font_ref.outline_glyphs());
379        Self {
380            state: &mut context.state,
381            hinting_cache: &mut context.hinting_cache,
382            font,
383            outlines,
384            proxy,
385            id,
386            coords: &mut context.coords,
387            size: 0.,
388            hint: false,
389        }
390    }
391
392    /// Specifies the font size in pixels per em. The default value is `0` which will produce
393    /// unscaled glyphs in original font units.
394    pub fn size(mut self, ppem: f32) -> Self {
395        self.size = ppem.max(0.);
396        self
397    }
398
399    /// Specifies whether to apply hinting to outlines. The default value is `false`.
400    pub fn hint(mut self, yes: bool) -> Self {
401        self.hint = yes;
402        self
403    }
404
405    /// Adds variation settings to the scaler.
406    pub fn variations<I>(self, settings: I) -> Self
407    where
408        I: IntoIterator,
409        I::Item: Into<Setting<f32>>,
410    {
411        if self.proxy.coord_count != 0 {
412            let vars = self.font.variations();
413            self.coords.resize(vars.len(), Default::default());
414            for setting in settings {
415                let setting = setting.into();
416                for var in vars {
417                    if var.tag() == setting.tag {
418                        let value = var.normalize(setting.value);
419                        if let Some(c) = self.coords.get_mut(var.index()) {
420                            *c = SkrifaNormalizedCoord::from_bits(value);
421                        }
422                    }
423                }
424            }
425        }
426        self
427    }
428
429    /// Specifies the variation settings in terms of normalized coordinates. This will replace
430    /// any previous variation settings.
431    pub fn normalized_coords<I>(self, coords: I) -> Self
432    where
433        I: IntoIterator,
434        I::Item: Borrow<NormalizedCoord>,
435    {
436        self.coords.clear();
437        self.coords.extend(
438            coords
439                .into_iter()
440                .map(|c| SkrifaNormalizedCoord::from_bits(*c.borrow())),
441        );
442        self
443    }
444
445    /// Builds a scaler for the current configuration.
446    pub fn build(self) -> Scaler<'a> {
447        let upem = self.proxy.metrics.units_per_em();
448        let skrifa_size = if self.size != 0.0 && upem != 0 {
449            SkrifaSize::new(self.size)
450        } else {
451            SkrifaSize::unscaled()
452        };
453        let hinting_instance = match (self.hint, &self.outlines) {
454            (true, Some(outlines)) => {
455                let key = hinting_cache::HintingKey {
456                    id: self.id,
457                    outlines,
458                    size: skrifa_size,
459                    coords: self.coords,
460                };
461                self.hinting_cache.get(&key)
462            }
463            _ => None,
464        };
465        Scaler {
466            state: self.state,
467            font: self.font,
468            outlines: self.outlines,
469            hinting_instance,
470            proxy: self.proxy,
471            coords: &self.coords[..],
472            size: self.size,
473            skrifa_size,
474        }
475    }
476}
477
478/// Scales outline and bitmap glyphs.
479///
480/// See the module level [documentation](index.html#outlines-and-bitmaps) for detail.
481pub struct Scaler<'a> {
482    state: &'a mut State,
483    font: FontRef<'a>,
484    outlines: Option<OutlineGlyphCollection<'a>>,
485    hinting_instance: Option<&'a skrifa::outline::HintingInstance>,
486    proxy: &'a ScalerProxy,
487    coords: &'a [SkrifaNormalizedCoord],
488    size: f32,
489    skrifa_size: SkrifaSize,
490}
491
492impl<'a> Scaler<'a> {
493    /// Returns true if scalable glyph outlines are available.
494    pub fn has_outlines(&self) -> bool {
495        self.outlines
496            .as_ref()
497            .map(|outlines| outlines.format().is_some())
498            .unwrap_or_default()
499    }
500
501    /// Scales an outline for the specified glyph into the provided outline.
502    pub fn scale_outline_into(&mut self, glyph_id: GlyphId, outline: &mut Outline) -> bool {
503        outline.clear();
504        self.scale_outline_impl(glyph_id, None, Some(outline))
505    }
506
507    /// Scales an outline for the specified glyph.
508    pub fn scale_outline(&mut self, glyph_id: GlyphId) -> Option<Outline> {
509        let mut outline = Outline::new();
510        if self.scale_outline_into(glyph_id, &mut outline) {
511            Some(outline)
512        } else {
513            None
514        }
515    }
516
517    /// Returns true if scalable color glyph outlines are available.
518    pub fn has_color_outlines(&self) -> bool {
519        self.proxy.color.colr != 0 && self.proxy.color.cpal != 0
520    }
521
522    /// Scales a color outline for the specified glyph into the provided outline.
523    pub fn scale_color_outline_into(&mut self, glyph_id: GlyphId, outline: &mut Outline) -> bool {
524        outline.clear();
525        if !self.has_color_outlines() {
526            return false;
527        }
528        let layers = match self.proxy.color.layers(self.font.data, glyph_id) {
529            Some(layers) => layers,
530            _ => return false,
531        };
532        for i in 0..layers.len() {
533            let layer = match layers.get(i) {
534                Some(layer) => layer,
535                _ => return false,
536            };
537            if !self.scale_outline_impl(layer.glyph_id, layer.color_index, Some(outline)) {
538                return false;
539            }
540        }
541        outline.set_color(true);
542        true
543    }
544
545    /// Scales a color outline for the specified glyph.
546    pub fn scale_color_outline(&mut self, glyph_id: GlyphId) -> Option<Outline> {
547        let mut outline = Outline::new();
548        if self.scale_color_outline_into(glyph_id, &mut outline) {
549            Some(outline)
550        } else {
551            None
552        }
553    }
554
555    fn scale_outline_impl(
556        &mut self,
557        glyph_id: GlyphId,
558        color_index: Option<u16>,
559        outline: Option<&mut Outline>,
560    ) -> bool {
561        let mut outline = match outline {
562            Some(x) => x,
563            _ => &mut self.state.outline,
564        };
565        if let Some(outlines) = &self.outlines {
566            if let Some(glyph) = outlines.get(SkrifaGlyphId::from(glyph_id)) {
567                outline.begin_layer(color_index);
568                let settings: skrifa::outline::DrawSettings =
569                    if let Some(hinting_instance) = &self.hinting_instance {
570                        (*hinting_instance).into()
571                    } else {
572                        (
573                            self.skrifa_size,
574                            skrifa::instance::LocationRef::new(self.coords),
575                        )
576                            .into()
577                    };
578                if glyph
579                    .draw(settings, &mut OutlineWriter(&mut outline))
580                    .is_ok()
581                {
582                    outline.maybe_close();
583                    outline.finish();
584                    return true;
585                }
586            }
587        }
588        false
589    }
590
591    // Unused when render feature is disabled.
592    #[allow(dead_code)]
593    fn scale_color_outline_impl(&mut self, glyph_id: GlyphId) -> bool {
594        if !self.has_color_outlines() {
595            return false;
596        }
597        let layers = match self.proxy.color.layers(self.font.data, glyph_id) {
598            Some(layers) => layers,
599            _ => return false,
600        };
601        self.state.outline.clear();
602        for i in 0..layers.len() {
603            let layer = match layers.get(i) {
604                Some(layer) => layer,
605                _ => return false,
606            };
607            if !self.scale_outline_impl(layer.glyph_id, layer.color_index, None) {
608                return false;
609            }
610        }
611        true
612    }
613
614    /// Returns true if alpha bitmaps are available.
615    pub fn has_bitmaps(&self) -> bool {
616        self.proxy.bitmaps.has_alpha()
617    }
618
619    /// Scales a bitmap for the specified glyph and mode into the provided image.
620    pub fn scale_bitmap_into(
621        &mut self,
622        glyph_id: u16,
623        strike: StrikeWith,
624        image: &mut Image,
625    ) -> bool {
626        self.scale_bitmap_impl(glyph_id, false, strike, image) == Some(true)
627    }
628
629    /// Scales a bitmap for the specified glyph and mode.
630    pub fn scale_bitmap(&mut self, glyph_id: u16, strike: StrikeWith) -> Option<Image> {
631        let mut image = Image::new();
632        if self.scale_bitmap_into(glyph_id, strike, &mut image) {
633            Some(image)
634        } else {
635            None
636        }
637    }
638
639    /// Returns true if color bitmaps are available.
640    pub fn has_color_bitmaps(&self) -> bool {
641        self.proxy.bitmaps.has_color()
642    }
643
644    /// Scales a color bitmap for the specified glyph and mode into the provided image.
645    pub fn scale_color_bitmap_into(
646        &mut self,
647        glyph_id: u16,
648        strike: StrikeWith,
649        image: &mut Image,
650    ) -> bool {
651        self.scale_bitmap_impl(glyph_id, true, strike, image) == Some(true)
652    }
653
654    /// Scales a color bitmap for the specified glyph and mode.
655    pub fn scale_color_bitmap(&mut self, glyph_id: u16, strike: StrikeWith) -> Option<Image> {
656        let mut image = Image::new();
657        if self.scale_color_bitmap_into(glyph_id, strike, &mut image) {
658            Some(image)
659        } else {
660            None
661        }
662    }
663
664    fn scale_bitmap_impl(
665        &mut self,
666        glyph_id: GlyphId,
667        color: bool,
668        strike: StrikeWith,
669        image: &mut Image,
670    ) -> Option<bool> {
671        image.clear();
672        let size = self.size;
673        let mut strikes = if color {
674            self.proxy.bitmaps.materialize_color(&self.font)
675        } else {
676            self.proxy.bitmaps.materialize_alpha(&self.font)
677        };
678        let bitmap = match strike {
679            StrikeWith::ExactSize => {
680                if self.size == 0. {
681                    None
682                } else {
683                    strikes
684                        .find_by_exact_ppem(size as u16, glyph_id)?
685                        .get(glyph_id)
686                }
687            }
688            StrikeWith::BestFit => {
689                if self.size == 0. {
690                    None
691                } else {
692                    strikes
693                        .find_by_nearest_ppem(size as u16, glyph_id)?
694                        .get(glyph_id)
695                }
696            }
697            StrikeWith::LargestSize => strikes.find_by_largest_ppem(glyph_id)?.get(glyph_id),
698            StrikeWith::Index(i) => strikes
699                .nth(i as usize)
700                .and_then(|strike| strike.get(glyph_id)),
701        }?;
702        if bitmap.ppem == 0 {
703            return None;
704        }
705        let (_, _, bufsize) = bitmap.scaled_size(size);
706        image.data.resize(bufsize, 0);
707        self.state.scratch0.clear();
708        self.state.scratch1.clear();
709        let mut w = bitmap.width;
710        let mut h = bitmap.height;
711        let scale = size / bitmap.ppem as f32;
712        image.placement = if size != 0. && scale != 1. {
713            self.state
714                .scratch0
715                .resize(bitmap.format.buffer_size(w, h), 0);
716            w = (w as f32 * scale) as u32;
717            h = (h as f32 * scale) as u32;
718            image.data.resize(bitmap.format.buffer_size(w, h), 0);
719            if !bitmap.decode(Some(&mut self.state.scratch1), &mut self.state.scratch0) {
720                return None;
721            }
722            if !bitmap::resize(
723                &self.state.scratch0,
724                bitmap.width,
725                bitmap.height,
726                bitmap.format.channels(),
727                &mut image.data,
728                w,
729                h,
730                bitmap::Filter::Mitchell,
731                Some(&mut self.state.scratch1),
732            ) {
733                return None;
734            }
735            let left = (bitmap.left as f32 * scale) as i32;
736            let top = (bitmap.top as f32 * scale) as i32;
737            Placement {
738                left,
739                top,
740                width: w,
741                height: h,
742            }
743        } else {
744            image.data.resize(bitmap.format.buffer_size(w, h), 0);
745            if !bitmap.decode(Some(&mut self.state.scratch1), &mut image.data) {
746                return None;
747            }
748            Placement {
749                left: bitmap.left,
750                top: bitmap.top,
751                width: w,
752                height: h,
753            }
754        };
755        image.source = match color {
756            true => Source::ColorBitmap(strike),
757            false => Source::Bitmap(strike),
758        };
759        image.content = match bitmap.format.channels() {
760            1 => Content::Mask,
761            _ => Content::Color,
762        };
763        // let mut advance = bitmap.advance() as f32;
764        // if options.size != 0. && options.size as u16 != bitmap.ppem() {
765        //     advance *= options.size / bitmap.ppem() as f32;
766        // }
767        Some(true)
768    }
769}
770
771/// Builder type for rendering a glyph into an image.
772///
773/// See the module level [documentation](index.html#rendering) for detail.
774#[cfg(feature = "render")]
775pub struct Render<'a> {
776    sources: &'a [Source],
777    format: Format,
778    offset: Point,
779    transform: Option<Transform>,
780    embolden: f32,
781    foreground: [u8; 4],
782    style: Style<'a>,
783}
784
785#[cfg(feature = "render")]
786impl<'a> Render<'a> {
787    /// Creates a new builder for configuring rendering using the specified
788    /// prioritized list of sources.
789    pub fn new(sources: &'a [Source]) -> Self {
790        Self {
791            sources,
792            format: Format::Alpha,
793            offset: Point::new(0., 0.),
794            transform: None,
795            embolden: 0.,
796            foreground: [128, 128, 128, 255],
797            style: Style::default(),
798        }
799    }
800
801    /// Specifies the target format for rasterizing an outline. Default is
802    /// [`Format::Alpha`].
803    pub fn format(&mut self, format: Format) -> &mut Self {
804        self.format = format;
805        self
806    }
807
808    /// Specifies the path style to use when rasterizing an outline. Default is
809    /// [`Fill::NonZero`](zeno::Fill::NonZero).
810    pub fn style(&mut self, style: impl Into<Style<'a>>) -> &mut Self {
811        self.style = style.into();
812        self
813    }
814
815    /// Specifies an additional offset to apply when rasterizing an outline.
816    /// Default is `(0, 0)`.
817    pub fn offset(&mut self, offset: Vector) -> &mut Self {
818        self.offset = offset;
819        self
820    }
821
822    /// Specifies a transformation matrix to apply when rasterizing an
823    /// outline. Default is `None`.
824    pub fn transform(&mut self, transform: Option<Transform>) -> &mut Self {
825        self.transform = transform;
826        self
827    }
828
829    /// Specifies the strength of a faux bold transform to apply when
830    /// rasterizing an outline. Default is `0`.
831    pub fn embolden(&mut self, strength: f32) -> &mut Self {
832        self.embolden = strength;
833        self
834    }
835
836    /// Specifies an RGBA color to use when rasterizing layers of a color
837    /// outline that do not directly reference a palette color. Default is
838    /// `[128, 128, 128, 255]`.
839    pub fn default_color(&mut self, color: [u8; 4]) -> &mut Self {
840        self.foreground = color;
841        self
842    }
843
844    /// Renders the specified glyph using the current configuration into the
845    /// provided image.
846    pub fn render_into(&self, scaler: &mut Scaler, glyph_id: GlyphId, image: &mut Image) -> bool {
847        for source in self.sources {
848            match source {
849                Source::Outline => {
850                    if !scaler.has_outlines() {
851                        continue;
852                    }
853                    scaler.state.outline.clear();
854                    if scaler.scale_outline_impl(glyph_id, None, None) {
855                        let state = &mut scaler.state;
856                        let rcx = &mut state.rcx;
857                        let outline = &mut state.outline;
858                        if self.embolden != 0. {
859                            outline.embolden(self.embolden, self.embolden);
860                        }
861                        if let Some(transform) = &self.transform {
862                            outline.transform(transform);
863                        }
864                        let placement = Mask::with_scratch(outline.path(), rcx)
865                            .format(self.format)
866                            .origin(Origin::BottomLeft)
867                            .style(self.style)
868                            .offset(self.offset)
869                            .render_offset(self.offset)
870                            .inspect(|fmt, w, h| {
871                                image.data.resize(fmt.buffer_size(w, h), 0);
872                            })
873                            .render_into(&mut image.data[..], None);
874                        image.placement = placement;
875                        image.content = if self.format == Format::Alpha {
876                            Content::Mask
877                        } else {
878                            Content::SubpixelMask
879                        };
880                        image.source = Source::Outline;
881                        return true;
882                    }
883                }
884                Source::ColorOutline(palette_index) => {
885                    if !scaler.has_color_outlines() {
886                        continue;
887                    }
888                    scaler.state.outline.clear();
889                    if scaler.scale_color_outline_impl(glyph_id) {
890                        let font = &scaler.font;
891                        let proxy = &scaler.proxy;
892                        let state = &mut scaler.state;
893                        let scratch = &mut state.scratch0;
894                        let rcx = &mut state.rcx;
895                        let outline = &mut state.outline;
896                        // Cool effect, but probably not generally desirable.
897                        // Maybe expose a separate option?
898                        // if self.embolden != 0. {
899                        //     outline.embolden(self.embolden, self.embolden);
900                        // }
901                        if let Some(transform) = &self.transform {
902                            outline.transform(transform);
903                        }
904                        let palette = proxy.color.palette(font, *palette_index);
905
906                        let total_bounds = outline.bounds();
907
908                        // need to take offset into account when placing glyph
909                        let base_x = (total_bounds.min.x + self.offset.x).floor() as i32;
910                        let base_y = (total_bounds.min.y + self.offset.y).ceil() as i32;
911                        let base_w = total_bounds.width().ceil() as u32;
912                        let base_h = total_bounds.height().ceil() as u32;
913
914                        image.data.resize((base_w * base_h * 4) as usize, 0);
915                        image.placement.left = base_x;
916                        image.placement.top = base_h as i32 + base_y;
917                        image.placement.width = total_bounds.width().ceil() as u32;
918                        image.placement.height = total_bounds.height().ceil() as u32;
919
920                        let mut ok = true;
921                        for i in 0..outline.len() {
922                            let layer = match outline.get(i) {
923                                Some(layer) => layer,
924                                _ => {
925                                    ok = false;
926                                    break;
927                                }
928                            };
929
930                            scratch.clear();
931                            let placement = Mask::with_scratch(layer.path(), rcx)
932                                .origin(Origin::BottomLeft)
933                                .style(self.style)
934                                .offset(self.offset)
935                                .render_offset(self.offset)
936                                .inspect(|fmt, w, h| {
937                                    scratch.resize(fmt.buffer_size(w, h), 0);
938                                })
939                                .render_into(&mut scratch[..], None);
940                            let color = layer
941                                .color_index()
942                                .and_then(|i| palette.map(|p| p.get(i)))
943                                .unwrap_or(self.foreground);
944                            bitmap::blit(
945                                &scratch[..],
946                                placement.width,
947                                placement.height,
948                                placement.left.wrapping_sub(base_x),
949                                (base_h as i32 + base_y).wrapping_sub(placement.top),
950                                color,
951                                &mut image.data,
952                                base_w,
953                                base_h,
954                            );
955                        }
956                        if ok {
957                            image.source = Source::ColorOutline(*palette_index);
958                            image.content = Content::Color;
959                            return true;
960                        }
961                    }
962                }
963                Source::Bitmap(mode) => {
964                    if !scaler.has_bitmaps() {
965                        continue;
966                    }
967                    if scaler.scale_bitmap_into(glyph_id, *mode, image) {
968                        return true;
969                    }
970                }
971                Source::ColorBitmap(mode) => {
972                    if !scaler.has_color_bitmaps() {
973                        continue;
974                    }
975                    if scaler.scale_color_bitmap_into(glyph_id, *mode, image) {
976                        return true;
977                    }
978                }
979            }
980        }
981        false
982    }
983
984    /// Renders the specified glyph using the current configuration.
985    pub fn render(&self, scaler: &mut Scaler, glyph_id: GlyphId) -> Option<Image> {
986        let mut image = Image::new();
987        if self.render_into(scaler, glyph_id, &mut image) {
988            Some(image)
989        } else {
990            None
991        }
992    }
993}