swash/
strike.rs

1//! Alpha and color bitmaps.
2
3use super::internal::*;
4use super::{FontRef, GlyphId};
5
6#[cfg(feature = "scale")]
7use alloc::vec::Vec;
8#[cfg(all(feature = "libm", feature = "scale"))]
9use core_maths::CoreFloat;
10
11/// Proxy for rematerializing strike collections.
12#[derive(Copy, Clone)]
13pub struct BitmapStrikesProxy {
14    bitmaps: (u32, u32),
15    color_bitmaps: (u32, u32),
16    upem: u16,
17    is_apple: bool,
18}
19
20impl BitmapStrikesProxy {
21    /// Creates strike collection proxy from the specified font.
22    pub fn from_font<'a>(font: &FontRef<'a>) -> Self {
23        let upem = font.head().map(|h| h.units_per_em()).unwrap_or(1);
24        let mut bitmaps = (0, 0);
25        let eblc = font.table_offset(raw_tag(b"EBLC"));
26        if eblc != 0 {
27            let ebdt = font.table_offset(raw_tag(b"EBDT"));
28            if ebdt != 0 {
29                bitmaps = (eblc, ebdt);
30            }
31        }
32        let mut color_bitmaps = (0, 0);
33        let sbix = font.table_offset(raw_tag(b"sbix"));
34        let mut is_apple = false;
35        if sbix != 0 {
36            color_bitmaps = (sbix, sbix);
37            use super::string::StringId::Family;
38            if let Some(name) = font.localized_strings().find_by_id(Family, None) {
39                is_apple = name.chars().eq("Apple Color Emoji".chars());
40            }
41        } else {
42            let cblc = font.table_offset(raw_tag(b"CBLC"));
43            if cblc != 0 {
44                let cbdt = font.table_offset(raw_tag(b"CBDT"));
45                if cbdt != 0 {
46                    color_bitmaps = (cblc, cbdt);
47                }
48            }
49        }
50        Self {
51            bitmaps,
52            color_bitmaps,
53            upem,
54            is_apple,
55        }
56    }
57
58    /// Returns true if the font has alpha bitmap strikes.
59    pub fn has_alpha(&self) -> bool {
60        self.bitmaps.0 != 0
61    }
62
63    /// Returns true if the font has color bitmap strikes.
64    pub fn has_color(&self) -> bool {
65        self.color_bitmaps.0 != 0
66    }
67
68    /// Materializes an alpha strike iterator for the specified font. This
69    /// proxy must have been created from the same font.
70    pub fn materialize_alpha<'a>(&self, font: &FontRef<'a>) -> BitmapStrikes<'a> {
71        self.materialize_impl(font.data, self.bitmaps.0, self.bitmaps.1, self.upem, false)
72    }
73
74    /// Materializes a color strike iterator for the specified font. This
75    /// proxy must have been created from the same font.
76    pub fn materialize_color<'a>(&self, font: &FontRef<'a>) -> BitmapStrikes<'a> {
77        self.materialize_impl(
78            font.data,
79            self.color_bitmaps.0,
80            self.color_bitmaps.1,
81            self.upem,
82            self.is_apple,
83        )
84    }
85
86    fn materialize_impl<'a>(
87        &self,
88        data: &'a [u8],
89        loc: u32,
90        dat: u32,
91        upem: u16,
92        is_apple: bool,
93    ) -> BitmapStrikes<'a> {
94        if loc == 0 {
95            BitmapStrikes::new(&[], &[], upem, false, false)
96        } else if loc == dat {
97            let loc = data.get(loc as usize..).unwrap_or(&[]);
98            BitmapStrikes::new(loc, loc, upem, true, is_apple)
99        } else {
100            let loc = data.get(loc as usize..).unwrap_or(&[]);
101            let dat = data.get(dat as usize..).unwrap_or(&[]);
102            BitmapStrikes::new(loc, dat, upem, false, false)
103        }
104    }
105}
106
107/// Iterator over a collection of bitmap strikes.
108#[derive(Copy, Clone)]
109pub struct BitmapStrikes<'a> {
110    data: Bytes<'a>,
111    bitmap_data: &'a [u8],
112    is_sbix: bool,
113    is_apple: bool,
114    upem: u16,
115    len: usize,
116    pos: usize,
117}
118
119impl<'a> BitmapStrikes<'a> {
120    fn new(
121        data: &'a [u8],
122        bitmap_data: &'a [u8],
123        upem: u16,
124        is_sbix: bool,
125        is_apple: bool,
126    ) -> Self {
127        let data = Bytes::new(data);
128        Self {
129            data,
130            bitmap_data,
131            upem,
132            is_sbix,
133            is_apple,
134            len: data.read_or_default::<u32>(4) as usize,
135            pos: 0,
136        }
137    }
138
139    /// Returns the bitmap strike at the specified index.
140    fn get(&self, index: usize) -> Option<BitmapStrike<'a>> {
141        if index >= self.len {
142            return None;
143        }
144        let offset = if self.is_sbix {
145            self.data.read::<u32>(8 + index * 4)? as usize
146        } else {
147            8 + index * 48
148        };
149        Some(BitmapStrike {
150            data: self.data,
151            bitmap_data: self.bitmap_data,
152            upem: self.upem,
153            is_sbix: self.is_sbix,
154            is_apple: self.is_apple,
155            offset,
156        })
157    }
158
159    /// Searches for a strike that matches the specified size and glyph
160    /// identifier. Returns the strike of the nearest suitable size, preferring
161    /// larger strikes if no exact match is available.
162    ///
163    /// ## Iteration behavior
164    /// This function searches the entire strike collection without regard
165    /// for the current state of the iterator.
166    pub fn find_by_nearest_ppem(&self, ppem: u16, glyph_id: GlyphId) -> Option<BitmapStrike<'a>> {
167        let mut best = None;
168        let mut best_size = 0;
169        for i in 0..self.len {
170            let strike = match self.get(i) {
171                Some(strike) => strike,
172                _ => continue,
173            };
174            if !strike.contains(glyph_id) {
175                continue;
176            }
177            best = Some(strike);
178            let strike_ppem = strike.ppem();
179            if strike_ppem > best_size {
180                best = Some(strike);
181                best_size = strike_ppem;
182            }
183            if strike_ppem >= ppem {
184                return Some(strike);
185            }
186        }
187        best
188    }
189
190    /// Searches for a strike that exactly matches the specified size and glyph
191    /// identifier.
192    ///
193    /// ## Iteration behavior
194    /// This function searches the entire strike collection without regard
195    /// for the current state of the iterator.
196    pub fn find_by_exact_ppem(&self, ppem: u16, glyph_id: GlyphId) -> Option<BitmapStrike<'a>> {
197        for i in 0..self.len {
198            let strike = match self.get(i) {
199                Some(strike) => strike,
200                _ => continue,
201            };
202            if !strike.contains(glyph_id) {
203                continue;
204            }
205            if strike.ppem() == ppem {
206                return Some(strike);
207            }
208        }
209        None
210    }
211
212    /// Searches for a strike with the largest size that contains the specified
213    /// glyph.
214    ///
215    /// ## Iteration behavior
216    /// This function searches the entire strike collection without regard
217    /// for the current state of the iterator.
218    pub fn find_by_largest_ppem(&self, glyph_id: GlyphId) -> Option<BitmapStrike<'a>> {
219        let mut largest = None;
220        let mut largest_ppem = 0;
221        for i in 0..self.len {
222            let strike = match self.get(i) {
223                Some(strike) => strike,
224                _ => continue,
225            };
226            if !strike.contains(glyph_id) {
227                continue;
228            }
229            let strike_ppem = strike.ppem();
230            if largest.is_none() || strike_ppem > largest_ppem {
231                largest = Some(strike);
232                largest_ppem = strike_ppem;
233            }
234        }
235        largest
236    }
237}
238
239impl_iter!(BitmapStrikes, BitmapStrike);
240
241/// Collection of bitmaps of a specific size and format.
242#[derive(Copy, Clone)]
243#[allow(dead_code)]
244pub struct BitmapStrike<'a> {
245    data: Bytes<'a>,
246    bitmap_data: &'a [u8],
247    offset: usize,
248    upem: u16,
249    is_sbix: bool,
250    is_apple: bool,
251}
252
253impl<'a> BitmapStrike<'a> {
254    /// Returns the device pixel density for which the strike was designed.
255    pub fn ppi(&self) -> u16 {
256        if self.is_sbix {
257            self.data.read_or_default::<u16>(self.offset + 2)
258        } else {
259            72
260        }
261    }
262
263    /// Returns the bit depth of the strike.
264    pub fn bit_depth(&self) -> u8 {
265        if self.is_sbix {
266            return 32;
267        }
268        self.data.read_or_default::<u8>(self.offset + 46)
269    }
270
271    /// Returns the size of the strike in pixels per em.
272    pub fn ppem(&self) -> u16 {
273        if self.is_sbix {
274            self.data.read_or_default::<u16>(self.offset)
275        } else {
276            self.data.read_or_default::<u8>(self.offset + 45) as u16
277        }
278    }
279
280    /// Returns true if the specified glyph is covered by the strike.
281    pub fn contains(&self, glyph_id: GlyphId) -> bool {
282        get_coverage(&self.data, self.offset, self.is_sbix, glyph_id).unwrap_or(false)
283    }
284
285    /// Returns the bitmap for the specified glyph, if available.
286    #[cfg(feature = "scale")]
287    pub(crate) fn get(&self, glyph_id: GlyphId) -> Option<Bitmap<'a>> {
288        let loc = get_location(&self.data, self.offset, self.is_sbix, glyph_id)?;
289        loc.get(self.bitmap_data, self.upem, self.is_apple)
290    }
291}
292
293/// Format of a bitmap.
294#[cfg(feature = "scale")]
295#[derive(Copy, Clone, PartialEq, Eq, Debug)]
296pub enum BitmapFormat {
297    /// Alpha mask with the specified number of bits.
298    Alpha(u8),
299    /// Packed alpha mask with the specified number of bits.
300    Packed(u8),
301    /// 32-bit RGBA color.
302    Color,
303    /// PNG encoded.
304    Png,
305}
306
307#[cfg(feature = "scale")]
308impl BitmapFormat {
309    /// Returns the number of channels.
310    pub fn channels(&self) -> u32 {
311        match self {
312            Self::Alpha(..) | Self::Packed(..) => 1,
313            _ => 4,
314        }
315    }
316
317    /// Returns the buffer size for a bitmap of the specified dimensions in this
318    /// format.
319    pub fn buffer_size(&self, width: u32, height: u32) -> usize {
320        (width * height * self.channels()) as usize
321    }
322}
323
324/// Bitmap glyph.
325#[cfg(feature = "scale")]
326#[derive(Copy, Clone)]
327pub struct Bitmap<'a> {
328    pub format: BitmapFormat,
329    pub ppem: u16,
330    pub width: u32,
331    pub height: u32,
332    pub left: i32,
333    pub top: i32,
334    pub data: &'a [u8],
335}
336
337#[cfg(feature = "scale")]
338impl<'a> Bitmap<'a> {
339    fn new(data: &BitmapData<'a>, upem: u16, is_apple: bool) -> Option<Self> {
340        let format = if data.is_packed {
341            BitmapFormat::Packed(data.bit_depth)
342        } else if data.is_png {
343            BitmapFormat::Png
344        } else if data.bit_depth == 32 {
345            BitmapFormat::Color
346        } else {
347            BitmapFormat::Alpha(data.bit_depth)
348        };
349        let (width, height, left, top) = if data.is_png {
350            let png = Bytes::new(data.data);
351            let width = png.read::<u32>(16)?;
352            let height = png.read::<u32>(20)?;
353            let (left, mut top) = (data.metrics.x as i32, data.metrics.y as i32);
354            if data.is_sbix {
355                if top == 0 && is_apple {
356                    let s = data.ppem as f32 / upem as f32;
357                    top = (-100. * s).round() as i32;
358                }
359                top += height as i32;
360            }
361            (width, height, left, top)
362        } else {
363            (
364                data.width as u32,
365                data.height as u32,
366                data.metrics.x as i32,
367                data.metrics.y as i32,
368            )
369        };
370        Some(Bitmap {
371            format,
372            ppem: data.ppem,
373            width,
374            height,
375            left,
376            top,
377            data: data.data,
378        })
379    }
380
381    /// Returns the size of an image buffer necessary for storing the decoded bitmap.
382    pub fn decoded_size(&self) -> usize {
383        self.format.buffer_size(self.width, self.height)
384    }
385
386    /// Returns the width, height and buffer size for a scaled version of the bitmap.
387    pub fn scaled_size(&self, size: f32) -> (u32, u32, usize) {
388        let mut w = self.width;
389        let mut h = self.height;
390        if size != 0. {
391            let scale = size / self.ppem as f32;
392            w = (w as f32 * scale) as u32;
393            h = (h as f32 * scale) as u32;
394        }
395        (w, h, self.format.buffer_size(w, h))
396    }
397
398    /// Decodes the bitmap into the specified target buffer.
399    pub fn decode(&self, scratch: Option<&mut Vec<u8>>, target: &mut [u8]) -> bool {
400        let mut tmp = Vec::new();
401        let scratch = if let Some(scratch) = scratch {
402            scratch
403        } else {
404            &mut tmp
405        };
406        let size = self.decoded_size();
407        if target.len() < size {
408            return false;
409        }
410        let w = self.width as usize;
411        let h = self.height as usize;
412        let src = self.data;
413        let dst = &mut *target;
414        match self.format {
415            BitmapFormat::Packed(bits) => match bits {
416                1 => {
417                    for x in 0..(w * h) {
418                        dst[x] = (src[x >> 3] >> (!x & 7) & 1) * 255;
419                    }
420                }
421                2 => {
422                    for x in 0..(w * h) {
423                        dst[x] = (src[x >> 2] >> (!(x * 2) & 2) & 3) * 85;
424                    }
425                }
426                4 => {
427                    for x in 0..(w * h) {
428                        dst[x] = (src[x >> 1] >> (!(x * 4) & 4) & 15) * 17;
429                    }
430                }
431                8 | 32 => {
432                    dst.copy_from_slice(src);
433                }
434                _ => return false,
435            },
436            BitmapFormat::Alpha(bits) => match bits {
437                1 => {
438                    let mut dst_idx = 0;
439                    for row in src.chunks(((w * bits as usize) + 7) / 8) {
440                        for x in 0..w {
441                            dst[dst_idx] = (row[x >> 3] >> (!x & 7) & 1) * 255;
442                            dst_idx += 1;
443                        }
444                    }
445                }
446                2 => {
447                    let mut dst_idx = 0;
448                    for row in src.chunks(((w * bits as usize) + 7) / 8) {
449                        for x in 0..w {
450                            dst[dst_idx] = (row[x >> 2] >> (!(x * 2) & 2) & 3) * 85;
451                            dst_idx += 1;
452                        }
453                    }
454                }
455                4 => {
456                    let mut dst_idx = 0;
457                    for row in src.chunks(((w * bits as usize) + 7) / 8) {
458                        for x in 0..w {
459                            dst[dst_idx] = (row[x >> 1] >> (!(x * 4) & 4) & 15) * 17;
460                            dst_idx += 1;
461                        }
462                    }
463                }
464                8 | 32 => {
465                    dst.copy_from_slice(src);
466                }
467                _ => return false,
468            },
469
470            BitmapFormat::Color => {
471                dst.copy_from_slice(src);
472            }
473            BitmapFormat::Png => {
474                use super::scale::decode_png;
475                scratch.clear();
476                if decode_png(src, scratch, target).is_none() {
477                    return false;
478                }
479            }
480        }
481        true
482    }
483}
484
485/// The location of a bitmap in the bitmap data table.
486#[cfg(feature = "scale")]
487#[derive(Copy, Clone)]
488struct Location {
489    format: u8,
490    flags: u8,
491    offset: u32,
492    size: u32,
493    ppem: u16,
494    bit_depth: u8,
495    width: u8,
496    height: u8,
497    metrics: Metrics,
498    vertical_metrics: Metrics,
499}
500
501#[cfg(feature = "scale")]
502impl Location {
503    /// Gets a bitmap from this location in the specified data source.
504    pub fn get<'a>(&self, data: &'a [u8], upem: u16, is_apple: bool) -> Option<Bitmap<'a>> {
505        Bitmap::new(&get_data(data, self)?, upem, is_apple)
506    }
507}
508
509fn get_coverage(table: &[u8], strike_base: usize, is_sbix: bool, glyph_id: u16) -> Option<bool> {
510    if is_sbix {
511        return Some(sbix_range(table, strike_base, glyph_id, 0).is_some());
512    }
513    let b = Bytes::with_offset(table, strike_base)?;
514    if glyph_id < b.read(40)? || glyph_id > b.read(42)? {
515        return None;
516    }
517    let count = b.read::<u32>(8)? as usize;
518    let array_offset = b.read::<u32>(0)? as usize;
519    let b = Bytes::with_offset(table, array_offset)?;
520    for i in 0..count {
521        let offset = i * 8;
522        let first = b.read::<u16>(offset)?;
523        if glyph_id < first {
524            return None;
525        }
526        if glyph_id > b.read::<u16>(offset + 2)? {
527            continue;
528        }
529        return Some(true);
530    }
531    None
532}
533
534#[cfg(feature = "scale")]
535fn get_location(
536    table: &[u8],
537    strike_base: usize,
538    is_sbix: bool,
539    glyph_id: u16,
540) -> Option<Location> {
541    let d = Bytes::new(table);
542    if is_sbix {
543        let (start, end) = sbix_range(table, strike_base, glyph_id, 0)?;
544        let len = (end - start) as usize;
545        let start = start as usize;
546        let x = d.read::<i16>(start)?;
547        let y = d.read::<i16>(start + 2)?;
548        let ppem = d.read::<u16>(strike_base)?;
549        return Some(Location {
550            ppem,
551            bit_depth: 32,
552            width: 0,
553            height: 0,
554            metrics: Metrics {
555                x: x as i8,
556                y: y as i8,
557                advance: 0,
558            },
559            vertical_metrics: Metrics::default(),
560            format: 0xFF,
561            flags: 1,
562            offset: start as u32 + 8,
563            size: len as u32 - 8,
564        });
565    }
566    if glyph_id < d.read(strike_base + 40)? || glyph_id > d.read(strike_base + 42)? {
567        return None;
568    }
569    let count = d.read::<u32>(strike_base + 8)? as usize;
570    let ppem = d.read::<u8>(strike_base + 45)? as u16;
571    let bit_depth = d.read::<u8>(strike_base + 46)?;
572    let flags = d.read::<u8>(strike_base + 47)?;
573    let array_offset = d.read::<u32>(strike_base)? as usize;
574    for i in 0..count {
575        let offset = array_offset + i * 8;
576        let first = d.read::<u16>(offset)?;
577        if glyph_id < first {
578            return None;
579        }
580        if glyph_id > d.read::<u16>(offset + 2)? {
581            continue;
582        }
583        let offset = array_offset + d.read::<u32>(offset + 4)? as usize;
584        let index_format = d.read::<u16>(offset)?;
585        let image_format = d.read::<u16>(offset + 2)? as u8;
586        let image_offset = d.read::<u32>(offset + 4)?;
587        let base = offset + 8;
588        let mut loc = Location {
589            ppem,
590            bit_depth,
591            width: 0,
592            height: 0,
593            metrics: Metrics::default(),
594            vertical_metrics: Metrics::default(),
595            format: image_format,
596            flags,
597            offset: 0,
598            size: 0,
599        };
600        match index_format {
601            1 => {
602                loc.offset =
603                    image_offset + d.read::<u32>(base + (glyph_id - first) as usize * 4)?;
604                return Some(loc);
605            }
606            2 => {
607                loc.size = d.read::<u32>(base)?;
608                loc.offset = image_offset + loc.size * (glyph_id - first) as u32;
609                let (w, h) = get_metrics(
610                    &d,
611                    base + 4,
612                    flags,
613                    true,
614                    &mut loc.metrics,
615                    &mut loc.vertical_metrics,
616                )?;
617                loc.width = w;
618                loc.height = h;
619                return Some(loc);
620            }
621            3 => {
622                loc.offset =
623                    image_offset + d.read::<u16>(base + (glyph_id - first) as usize * 2)? as u32;
624                return Some(loc);
625            }
626            4 => {
627                let mut l = 0;
628                let mut h = d.read::<u32>(base)? as usize;
629                while l < h {
630                    use core::cmp::Ordering::*;
631                    let i = (l + h) / 2;
632                    let rec = base + i * 4;
633                    let id = d.read::<u16>(rec)?;
634                    match glyph_id.cmp(&id) {
635                        Less => h = i,
636                        Greater => l = i + i,
637                        Equal => {
638                            let offset1 = d.read::<u16>(rec + 2)? as u32;
639                            let offset2 = d.read::<u16>(rec + 6)? as u32;
640                            if offset2 <= offset1 {
641                                return None;
642                            }
643                            loc.offset = image_offset + offset1;
644                            loc.size = offset2 - offset1;
645                            return Some(loc);
646                        }
647                    }
648                }
649            }
650            _ => {
651                return None;
652            }
653        }
654    }
655    None
656}
657
658#[cfg(feature = "scale")]
659fn get_data<'a>(table: &'a [u8], loc: &Location) -> Option<BitmapData<'a>> {
660    let depth = loc.bit_depth as usize;
661    let mut bitmap = BitmapData {
662        data: &[],
663        ppem: loc.ppem,
664        bit_depth: loc.bit_depth,
665        width: loc.width,
666        height: loc.height,
667        metrics: loc.metrics,
668        vertical_metrics: loc.vertical_metrics,
669        is_packed: false,
670        is_png: false,
671        is_sbix: false,
672    };
673    let d = &Bytes::new(table);
674    let offset = loc.offset as usize;
675    let flags = loc.flags;
676    let size = loc.size as usize;
677    match loc.format {
678        0xFF => {
679            bitmap.data = d.read_bytes(offset, size)?;
680            bitmap.is_png = true;
681            bitmap.is_sbix = true;
682            Some(bitmap)
683        }
684        1 => {
685            bitmap.read_metrics(d, offset, flags, false)?;
686            let w = (bitmap.width as usize * depth + 7) / 8;
687            let h = bitmap.height as usize;
688            bitmap.data = d.read_bytes(offset + 5, w * h)?;
689            Some(bitmap)
690        }
691        2 => {
692            bitmap.read_metrics(d, offset, flags, false)?;
693            let w = bitmap.width as usize * depth;
694            let h = bitmap.height as usize;
695            bitmap.data = d.read_bytes(offset + 5, (w * h + 7) / 8)?;
696            bitmap.is_packed = true;
697            Some(bitmap)
698        }
699        5 => {
700            bitmap.data = d.read_bytes(offset, size)?;
701            bitmap.is_packed = true;
702            Some(bitmap)
703        }
704        6 => {
705            bitmap.read_metrics(d, offset, flags, true)?;
706            let w = (bitmap.width as usize * depth + 7) / 8;
707            let h = bitmap.height as usize;
708            bitmap.data = d.read_bytes(offset + 8, w * h)?;
709            Some(bitmap)
710        }
711        7 => {
712            bitmap.read_metrics(d, offset, flags, true)?;
713            let w = bitmap.width as usize * depth;
714            let h = bitmap.height as usize;
715            bitmap.data = d.read_bytes(offset + 8, (w * h + 7) / 8)?;
716            bitmap.is_packed = true;
717            Some(bitmap)
718        }
719        17 => {
720            bitmap.read_metrics(d, offset, flags, false)?;
721            let size = d.read::<u32>(offset + 5)? as usize;
722            bitmap.data = d.read_bytes(offset + 9, size)?;
723            bitmap.is_png = true;
724            Some(bitmap)
725        }
726        18 => {
727            bitmap.read_metrics(d, offset, flags, true)?;
728            let size = d.read::<u32>(offset + 8)? as usize;
729            bitmap.data = d.read_bytes(offset + 12, size)?;
730            bitmap.is_png = true;
731            Some(bitmap)
732        }
733        19 => {
734            let size = d.read::<u32>(offset)? as usize;
735            bitmap.data = d.read_bytes(offset + 4, size)?;
736            bitmap.is_png = true;
737            Some(bitmap)
738        }
739        _ => None,
740    }
741}
742
743fn sbix_range(table: &[u8], strike_base: usize, glyph_id: u16, recurse: i32) -> Option<(u32, u32)> {
744    const DUPE: RawTag = raw_tag(b"dupe");
745    const PNG: RawTag = raw_tag(b"png ");
746    if recurse > 1 {
747        return None;
748    }
749    let b = Bytes::new(table);
750    let id = glyph_id as usize;
751    let base = strike_base + 4;
752    let mut start = b.read::<u32>(base + id * 4)?;
753    let mut end = b.read::<u32>(base + (id + 1) * 4)?;
754    if end <= start {
755        return None;
756    }
757    start += strike_base as u32;
758    end += strike_base as u32;
759    let tag = b.read::<u32>(start as usize + 4)?;
760    if tag == DUPE {
761        let dupe = b.read::<u16>(start as usize + 8)?;
762        sbix_range(table, strike_base, dupe, recurse + 1)
763    } else if tag == PNG {
764        Some((start, end))
765    } else {
766        None
767    }
768}
769
770#[cfg(feature = "scale")]
771#[derive(Copy, Clone)]
772struct BitmapData<'a> {
773    pub data: &'a [u8],
774    pub ppem: u16,
775    pub bit_depth: u8,
776    pub width: u8,
777    pub height: u8,
778    pub metrics: Metrics,
779    pub vertical_metrics: Metrics,
780    pub is_packed: bool,
781    pub is_png: bool,
782    pub is_sbix: bool,
783}
784
785#[cfg(feature = "scale")]
786impl<'a> BitmapData<'a> {
787    fn read_metrics(&mut self, d: &Bytes, offset: usize, flags: u8, big: bool) -> Option<()> {
788        let (w, h) = get_metrics(
789            d,
790            offset,
791            flags,
792            big,
793            &mut self.metrics,
794            &mut self.vertical_metrics,
795        )?;
796        self.width = w;
797        self.height = h;
798        Some(())
799    }
800}
801
802#[cfg(feature = "scale")]
803#[derive(Copy, Clone, Default)]
804struct Metrics {
805    pub x: i8,
806    pub y: i8,
807    pub advance: u8,
808}
809
810#[cfg(feature = "scale")]
811fn get_metrics(
812    d: &Bytes,
813    offset: usize,
814    flags: u8,
815    big: bool,
816    h: &mut Metrics,
817    v: &mut Metrics,
818) -> Option<(u8, u8)> {
819    let width = d.read::<u8>(offset + 1)?;
820    let height = d.read::<u8>(offset)?;
821    if big {
822        h.x = d.read::<i8>(offset + 2)?;
823        h.y = d.read::<i8>(offset + 3)?;
824        h.advance = d.read::<u8>(offset + 4)?;
825        v.x = d.read::<i8>(offset + 5)?;
826        v.y = d.read::<i8>(offset + 6)?;
827        v.advance = d.read::<u8>(offset + 7)?;
828    } else {
829        let m = if flags & 2 != 0 { v } else { h };
830        m.x = d.read::<i8>(offset + 2)?;
831        m.y = d.read::<i8>(offset + 3)?;
832        m.advance = d.read::<u8>(offset + 4)?;
833    }
834    Some((width, height))
835}