[Fontations-backend] Color bitmap support.

Retrieve, place, scale and draw CBDT/CBLC and SBIX bitmap PNG glyphs.

Add ffi code to specifically handle format differences for SBIX and CBDT
and navigate the respective read_fonts retrieval functions.

Tested locally to closely match FreeType output with a modified
gm/fontations_ft_compare.cpp test.

When [1] lands, tested through various gm's that load CBDT and SBIX
fonts, for example gm/coloremoji.cpp, scaledemoji.cpp and
scaledemoji_rendering.cpp.

[1] https://skia-review.googlesource.com/c/skia/+/820216

Bug: skia:301560994
Cq-Include-Trybots: luci.skia.skia.primary:Build-Debian10-Clang-x86_64-Debug-Fontations,Build-Mac-Clang-x86_64-Debug-Fontations,Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Fontations,Test-Mac12-Clang-MacBookPro16.2-CPU-AppleIntel-x86_64-Debug-All-Fontations
Change-Id: Ic2966555e8fb787c057d6c72c5beb8313b5c1203
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/809056
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Dominik Röttsches <drott@google.com>
Commit-Queue: Dominik Röttsches <drott@google.com>
diff --git a/gm/colrv1.cpp b/gm/colrv1.cpp
index e9c680c..fb1e229 100644
--- a/gm/colrv1.cpp
+++ b/gm/colrv1.cpp
@@ -23,8 +23,9 @@
 #include "tools/ToolUtils.h"
 #include "tools/fonts/FontToolUtils.h"
 
+#if defined(SK_TYPEFACE_FACTORY_FONTATIONS)
 #include "include/ports/SkTypeface_fontations.h"
-// #include "include/ports/SkFontMgr_empty.h"
+#endif
 
 #include <string.h>
 #include <initializer_list>
diff --git a/gm/fontations.cpp b/gm/fontations.cpp
index 35784e3..4d59667 100644
--- a/gm/fontations.cpp
+++ b/gm/fontations.cpp
@@ -15,6 +15,7 @@
 #include "include/ports/SkTypeface_fontations.h"
 #include "tools/Resources.h"
 
+
 namespace skiagm {
 
 namespace {
diff --git a/src/ports/SkTypeface_fontations.cpp b/src/ports/SkTypeface_fontations.cpp
index 594cb2e..1db3c3e 100644
--- a/src/ports/SkTypeface_fontations.cpp
+++ b/src/ports/SkTypeface_fontations.cpp
@@ -6,10 +6,13 @@
  */
 #include "include/ports/SkTypeface_fontations.h"
 
+#include "include/codec/SkCodec.h"
+#include "include/codec/SkPngDecoder.h"
 #include "include/core/SkBitmap.h"
 #include "include/core/SkCanvas.h"
 #include "include/core/SkData.h"
 #include "include/core/SkFontMetrics.h"
+#include "include/core/SkImage.h"
 #include "include/core/SkPictureRecorder.h"
 #include "include/core/SkStream.h"
 #include "include/effects/SkGradientShader.h"
@@ -294,6 +297,7 @@
         static const constexpr value_type PATH = 1;
         static const constexpr value_type COLRv0 = 2;
         static const constexpr value_type COLRv1 = 3;
+        static const constexpr value_type BITMAP = 4;
     };
 
     GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
@@ -322,6 +326,8 @@
                 fontations_ffi::has_colrv1_glyph(fBridgeFontRef, glyph.getGlyphID());
         bool has_colrv0_glyph =
                 fontations_ffi::has_colrv0_glyph(fBridgeFontRef, glyph.getGlyphID());
+        bool has_bitmap_glyph =
+                fontations_ffi::has_bitmap_glyph(fBridgeFontRef, glyph.getGlyphID());
 
         if (has_colrv1_glyph || has_colrv0_glyph) {
             mx.extraBits = has_colrv1_glyph ? ScalerContextBits::COLRv1 : ScalerContextBits::COLRv0;
@@ -367,6 +373,44 @@
                     }
                 }
             }
+        } else if (has_bitmap_glyph) {
+            mx.maskFormat = SkMask::kARGB32_Format;
+            mx.neverRequestPath = true;
+            mx.extraBits = ScalerContextBits::BITMAP;
+
+            rust::cxxbridge1::Box<fontations_ffi::BridgeBitmapGlyph> bitmap_glyph =
+                    fontations_ffi::bitmap_glyph(fBridgeFontRef, glyph.getGlyphID(), scale.fY);
+            rust::cxxbridge1::Slice<const uint8_t> png_data =
+                    fontations_ffi::png_data(*bitmap_glyph);
+            SkASSERT(png_data.size());
+
+            const fontations_ffi::BitmapMetrics bitmap_metrics =
+                    fontations_ffi::bitmap_metrics(*bitmap_glyph);
+
+            const bool& placement_origin_bottom_left = bitmap_metrics.placement_origin_bottom_left;
+
+            std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(
+                    SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr);
+            if (!codec) {
+                return mx;
+            }
+
+            SkRect bounds = SkRect::MakeEmpty();
+            SkImageInfo info = codec->getInfo();
+            float height_adjustment = placement_origin_bottom_left ? info.height() : 0;
+
+            bounds = SkRect::Make(info.bounds());
+            SkMatrix matrix = remainingMatrix;
+            SkScalar ratio = scale.fY / bitmap_metrics.ppem_y;
+            matrix.preScale(ratio, ratio);
+            matrix.preTranslate(bitmap_metrics.bearing_x,
+                                -bitmap_metrics.bearing_y - height_adjustment);
+            if (this->isSubpixel()) {
+                matrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
+                                     SkFixedToScalar(glyph.getSubYFixed()));
+            }
+            matrix.mapRect(&bounds);
+            mx.bounds = SkRect::Make(bounds.roundOut());
         } else {
             // TODO: Retrieve from read_fonts and Skrifa - TrueType bbox or from path with
             // hinting?
@@ -376,6 +420,63 @@
         return mx;
     }
 
+    void generatePngImage(const SkGlyph& glyph, void* imageBuffer) {
+        SkASSERT(glyph.maskFormat() == SkMask::kARGB32_Format);
+        SkBitmap dstBitmap;
+        dstBitmap.setInfo(
+                SkImageInfo::Make(
+                        glyph.width(), glyph.height(), kN32_SkColorType, kPremul_SkAlphaType),
+                glyph.rowBytes());
+        dstBitmap.setPixels(imageBuffer);
+
+        SkCanvas canvas(dstBitmap);
+
+        canvas.translate(-glyph.left(), -glyph.top());
+
+        SkVector scale;
+        SkMatrix remainingMatrix;
+        if (!fRec.computeMatrices(
+                    SkScalerContextRec::PreMatrixScale::kVertical, &scale, &remainingMatrix)) {
+            return;
+        }
+
+        rust::cxxbridge1::Box<fontations_ffi::BridgeBitmapGlyph> bitmap_glyph =
+                fontations_ffi::bitmap_glyph(fBridgeFontRef, glyph.getGlyphID(), scale.fY);
+        rust::cxxbridge1::Slice<const uint8_t> png_data = fontations_ffi::png_data(*bitmap_glyph);
+        SkASSERT(png_data.size());
+
+        std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode(
+                SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr);
+
+        if (!codec) {
+            return;
+        }
+
+        auto [glyph_image, result] = codec->getImage();
+        if (result != SkCodec::Result::kSuccess) {
+            return;
+        }
+
+        canvas.clear(SK_ColorTRANSPARENT);
+        canvas.concat(remainingMatrix);
+
+        if (this->isSubpixel()) {
+            canvas.translate(SkFixedToScalar(glyph.getSubXFixed()),
+                             SkFixedToScalar(glyph.getSubYFixed()));
+        }
+        const fontations_ffi::BitmapMetrics bitmapMetrics =
+                fontations_ffi::bitmap_metrics(*bitmap_glyph);
+        SkScalar ratio = scale.fY / bitmapMetrics.ppem_y;
+        canvas.scale(ratio, ratio);
+
+        float height_adjustment =
+                bitmapMetrics.placement_origin_bottom_left ? glyph_image->height() : 0;
+        canvas.translate(bitmapMetrics.bearing_x, -bitmapMetrics.bearing_y - height_adjustment);
+
+        SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNearest);
+        canvas.drawImage(glyph_image, 0, 0, sampling);
+    }
+
     void generateImage(const SkGlyph& glyph, void* imageBuffer) override {
         ScalerContextBits::value_type format = glyph.extraBits();
         if (format == ScalerContextBits::PATH) {
@@ -410,6 +511,8 @@
             canvas.translate(-glyph.left(), -glyph.top());
 
             drawCOLRGlyph(glyph, fRec.fForegroundColor, &canvas);
+        } else if (format == ScalerContextBits::BITMAP) {
+            generatePngImage(glyph, imageBuffer);
         } else {
             SK_ABORT("Bad format");
         }
@@ -481,6 +584,9 @@
             }
         };
         ScalerContextBits::value_type format = glyph.extraBits();
+        if (format == ScalerContextBits::BITMAP) {
+            return nullptr;
+        }
         SkASSERT(format == ScalerContextBits::COLRv1 || format == ScalerContextBits::COLRv0);
         return sk_sp<SkDrawable>(new ColrGlyphDrawable(this, glyph));
     }
diff --git a/src/ports/fontations/BUILD.bazel b/src/ports/fontations/BUILD.bazel
index f28985d..82b9bdd 100644
--- a/src/ports/fontations/BUILD.bazel
+++ b/src/ports/fontations/BUILD.bazel
@@ -42,6 +42,8 @@
         ":bridge_rust_side",
         ":fontations_ffi",
         ":path_bridge_include",
+        # For color bitmap fonts.
+        "//src/codec:png_decode",
     ],
 )
 
diff --git a/src/ports/fontations/src/ffi.rs b/src/ports/fontations/src/ffi.rs
index c0fdd87..880deeb 100644
--- a/src/ports/fontations/src/ffi.rs
+++ b/src/ports/fontations/src/ffi.rs
@@ -5,6 +5,7 @@
 use font_types::{BoundingBox, GlyphId, Pen};
 use read_fonts::{tables::colr::CompositeMode, FileRef, FontRef, ReadError, TableProvider};
 use skrifa::{
+    attribute::Style,
     color::{Brush, ColorGlyphFormat, ColorPainter, Transform},
     instance::{Location, Size},
     metrics::{GlyphMetrics, Metrics},
@@ -15,11 +16,11 @@
 };
 use std::pin::Pin;
 
-use skrifa::attribute::{Style};
+use crate::bitmap::{bitmap_glyph, bitmap_metrics, has_bitmap_glyph, png_data, BridgeBitmapGlyph};
 
 use crate::ffi::{
     AxisWrapper, BridgeScalerMetrics, ColorPainterWrapper, ColorStop, PaletteOverride, PathWrapper,
-    SkiaDesignCoordinate,
+    SkiaDesignCoordinate,BridgeFontStyle
 };
 
 fn lookup_glyph_or_zero(font_ref: &BridgeFontRef, codepoint: u32) -> u16 {
@@ -715,8 +716,6 @@
     return color_stops.num_stops;
 }
 
-use crate::ffi::BridgeFontStyle;
-
 fn get_font_style(font_ref: &BridgeFontRef, style: &mut BridgeFontStyle) -> bool {
     font_ref
         .with_font(|f| {
@@ -751,7 +750,7 @@
         .unwrap_or_default()
 }
 
-struct BridgeFontRef<'a>(Option<FontRef<'a>>);
+pub struct BridgeFontRef<'a>(Option<FontRef<'a>>);
 
 impl<'a> BridgeFontRef<'a> {
     fn with_font<T>(&'a self, f: impl FnOnce(&'a FontRef) -> Option<T>) -> Option<T> {
@@ -778,6 +777,194 @@
     pub num_stops: usize,
 }
 
+mod bitmap {
+
+    use read_fonts::{
+        tables::{
+            bitmap::{BitmapContent, BitmapData, BitmapDataFormat, BitmapMetrics, BitmapSize},
+            sbix::{GlyphData, Strike},
+        },
+        FontRef, TableProvider,
+    };
+
+    use font_types::GlyphId;
+
+    use crate::{ffi::BitmapMetrics as FfiBitmapMetrics, BridgeFontRef};
+
+    pub enum BitmapPixelData<'a> {
+        PngData(&'a [u8]),
+    }
+
+    struct CblcGlyph<'a> {
+        bitmap_data: BitmapData<'a>,
+        ppem_x: u8,
+        ppem_y: u8,
+    }
+
+    struct SbixGlyph<'a> {
+        glyph_data: GlyphData<'a>,
+        ppem: u16,
+    }
+
+    #[derive(Default)]
+    pub struct BridgeBitmapGlyph<'a> {
+        pub data: Option<BitmapPixelData<'a>>,
+        pub metrics: FfiBitmapMetrics,
+    }
+
+    trait StrikeSizeRetrievable {
+        fn strike_size(&self) -> f32;
+    }
+
+    impl StrikeSizeRetrievable for &BitmapSize {
+        fn strike_size(&self) -> f32 {
+            self.ppem_y() as f32
+        }
+    }
+
+    impl StrikeSizeRetrievable for Strike<'_> {
+        fn strike_size(&self) -> f32 {
+            self.ppem() as f32
+        }
+    }
+
+    // Find the nearest larger strike size, or if no larger one is available, the nearest smaller.
+    fn best_strike_size<T>(strikes: impl Iterator<Item = T>, font_size: f32) -> Option<T>
+    where
+        T: StrikeSizeRetrievable,
+    {
+        // After a bigger strike size is found, the order of strike sizes smaller
+        // than the requested font size does not matter anymore. A new strike size
+        // is only an improvement if it gets closer to the requested font size (and
+        // is smaller than the current best, but bigger than font size). And vice
+        // versa: As long as we have found only smaller ones so far, only any strike
+        // size matters that is bigger than the current best.
+        strikes.reduce(|best, entry| {
+            let entry_size = entry.strike_size();
+            if (entry_size >= font_size && entry_size < best.strike_size())
+                || (best.strike_size() < font_size && entry_size > best.strike_size())
+            {
+                entry
+            } else {
+                best
+            }
+        })
+    }
+
+    fn sbix_glyph<'a>(
+        font_ref: &'a FontRef,
+        glyph_id: GlyphId,
+        font_size: Option<f32>,
+    ) -> Option<SbixGlyph<'a>> {
+        let sbix = font_ref.sbix().ok()?;
+        let mut strikes = sbix.strikes().iter().filter_map(|strike| strike.ok());
+
+        let best_strike = match font_size {
+            Some(size) => best_strike_size(strikes, size),
+            _ => strikes.next(),
+        }?;
+
+        Some(SbixGlyph {
+            ppem: best_strike.ppem(),
+            glyph_data: best_strike.glyph_data(glyph_id).ok()??,
+        })
+    }
+
+    fn cblc_glyph<'a>(
+        font_ref: &'a FontRef,
+        glyph_id: GlyphId,
+        font_size: Option<f32>,
+    ) -> Option<CblcGlyph<'a>> {
+        let cblc = font_ref.cblc().ok()?;
+        let cbdt = font_ref.cbdt().ok()?;
+
+        let strikes = &cblc.bitmap_sizes();
+        let best_strike = font_size
+            .and_then(|size| best_strike_size(strikes.iter(), size))
+            .or(strikes.get(0))?;
+
+        let location = best_strike.location(cblc.offset_data(), glyph_id).ok()?;
+
+        Some(CblcGlyph {
+            bitmap_data: cbdt.data(&location).ok()?,
+            ppem_x: best_strike.ppem_x,
+            ppem_y: best_strike.ppem_y,
+        })
+    }
+
+    pub fn has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool {
+        let glyph_id = GlyphId::new(glyph_id);
+        font_ref
+            .with_font(|font| {
+                let has_sbix = sbix_glyph(font, glyph_id, None).is_some();
+                let has_cblc = cblc_glyph(font, glyph_id, None).is_some();
+                Some(has_sbix || has_cblc)
+            })
+            .unwrap_or_default()
+    }
+
+    pub unsafe fn bitmap_glyph<'a>(
+        font_ref: &'a BridgeFontRef,
+        glyph_id: u16,
+        font_size: f32,
+    ) -> Box<BridgeBitmapGlyph<'a>> {
+        let glyph_id = GlyphId::new(glyph_id);
+        font_ref
+            .with_font(|font| {
+                if let Some(sbix_glyph) = sbix_glyph(font, glyph_id, Some(font_size)) {
+                    return Some(Box::new(BridgeBitmapGlyph {
+                        data: Some(BitmapPixelData::PngData(sbix_glyph.glyph_data.data())),
+                        metrics: FfiBitmapMetrics {
+                            bearing_x: sbix_glyph.glyph_data.origin_offset_x() as f32,
+                            bearing_y: sbix_glyph.glyph_data.origin_offset_y() as f32,
+                            ppem_x: sbix_glyph.ppem as f32,
+                            ppem_y: sbix_glyph.ppem as f32,
+                            placement_origin_bottom_left: true,
+                        },
+                    }));
+                } else if let Some(cblc_glyph) = cblc_glyph(font, glyph_id, Some(font_size)) {
+                    let (bearing_x, bearing_y) = match cblc_glyph.bitmap_data.metrics {
+                        BitmapMetrics::Small(small_metrics) => (
+                            small_metrics.bearing_x() as f32,
+                            small_metrics.bearing_y() as f32,
+                        ),
+                        BitmapMetrics::Big(big_metrics) => (
+                            big_metrics.hori_bearing_x() as f32,
+                            big_metrics.hori_bearing_y() as f32,
+                        ),
+                    };
+                    if let BitmapContent::Data(BitmapDataFormat::Png, png_buffer) =
+                        cblc_glyph.bitmap_data.content
+                    {
+                        return Some(Box::new(BridgeBitmapGlyph {
+                            data: Some(BitmapPixelData::PngData(png_buffer)),
+                            metrics: FfiBitmapMetrics {
+                                bearing_x,
+                                bearing_y,
+                                ppem_x: cblc_glyph.ppem_x as f32,
+                                ppem_y: cblc_glyph.ppem_y as f32,
+                                ..Default::default()
+                            },
+                        }));
+                    }
+                }
+                None
+            })
+            .unwrap_or_default()
+    }
+
+    pub unsafe fn png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8] {
+        match bitmap_glyph.data {
+            Some(BitmapPixelData::PngData(glyph_data)) => glyph_data,
+            _ => &[],
+        }
+    }
+
+    pub unsafe fn bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a FfiBitmapMetrics {
+        &bitmap_glyph.metrics
+    }
+}
+
 #[cxx::bridge(namespace = "fontations_ffi")]
 mod ffi {
     struct ColorStop {
@@ -866,6 +1053,21 @@
         pub width: i32,
     }
 
+    #[derive(Default)]
+    struct BitmapMetrics {
+        bearing_x: f32,
+        bearing_y: f32,
+        // Not returning CBDT/CBLC encoded width and height values as these
+        // should be retrieved from the PNG, which is avoids a potential
+        // mismatch between stored metrics and actual image dimensions.  ppem_*
+        // values are used to compute a scale factor on the client side.
+        ppem_x: f32,
+        ppem_y: f32,
+        // Account for the fact that Sbix and CBDT/CBLC have a different origin
+        // definition.
+        placement_origin_bottom_left: bool,
+    }
+
     extern "Rust" {
         type BridgeFontRef<'a>;
         unsafe fn make_font_ref<'a>(font_data: &'a [u8], index: u32) -> Box<BridgeFontRef<'a>>;
@@ -933,6 +1135,16 @@
             clip_box: &mut ClipBox,
         ) -> bool;
 
+        type BridgeBitmapGlyph<'a>;
+        fn has_bitmap_glyph(font_ref: &BridgeFontRef, glyph_id: u16) -> bool;
+        unsafe fn bitmap_glyph<'a>(
+            font_ref: &'a BridgeFontRef,
+            glyph_id: u16,
+            font_size: f32,
+        ) -> Box<BridgeBitmapGlyph<'a>>;
+        unsafe fn png_data<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a [u8];
+        unsafe fn bitmap_metrics<'a>(bitmap_glyph: &'a BridgeBitmapGlyph) -> &'a BitmapMetrics;
+
         fn table_data(font_ref: &BridgeFontRef, tag: u32, offset: usize, data: &mut [u8]) -> usize;
         fn table_tags(font_ref: &BridgeFontRef, tags: &mut [u32]) -> u16;
         fn variation_position(