// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Color scalable outline helpers (COLR/CPAL).
///
/// Ported from `upstream swash/src/scale/color.rs`.
///|
struct ColorProxy {
colr : UInt
cpal : UInt
}
///|
pub fn ColorProxy::from_font(font : @swash.FontRef) -> ColorProxy {
ColorProxy::{
colr: @internal.table_offset(font, 0x434f4c52), // "COLR"
cpal: @internal.table_offset(font, 0x4350414c), // "CPAL"
}
}
///|
pub fn ColorProxy::layers(
self : ColorProxy,
data : Bytes,
glyph_id : UInt16,
) -> ColrLayers? {
if self.colr == 0 {
return None
}
let colr_off = self.colr.reinterpret_as_int()
let b_opt = @internal.BeBytes::with_offset(data, colr_off)
if b_opt is None {
return None
}
let b = b_opt.unwrap()
// COLR v0 header:
// u16 version, u16 numBaseGlyphRecords, u32 baseGlyphRecordsOffset,
// u32 layerRecordsOffset, u16 numLayerRecords
let count_u = b.read_u16(2).unwrap_or(0)
let count = count_u.reinterpret_as_int()
let base_off_u = b.read_u32(4).unwrap_or(0)
let base_off = base_off_u.reinterpret_as_int()
let mut l = 0
let mut h = count
while l < h {
let i = l + (h - l) / 2
let rec = base_off + i * 6
match b.read_u16(rec) {
None => return None
Some(id_u) => {
let id = id_u.to_uint16()
if glyph_id < id {
h = i
} else if glyph_id > id {
l = i + 1
} else {
let first = b.read_u16(rec + 2).unwrap_or(0).reinterpret_as_int()
let layer_records_off = b
.read_u32(8)
.unwrap_or(0)
.reinterpret_as_int()
let offset = layer_records_off + first * 4
let len = b.read_u16(rec + 4).unwrap_or(0).to_uint16()
return Some(ColrLayers::{ data: b, offset, len })
}
}
}
}
None
}
///|
struct ColrLayers {
data : @internal.BeBytes
offset : Int
len : UInt16
}
///|
pub fn ColrLayers::len(self : ColrLayers) -> UInt16 {
self.len
}
///|
pub fn ColrLayers::get(self : ColrLayers, index : UInt16) -> ColrLayer? {
if index >= self.len {
return None
}
let base = self.offset + index.to_int() * 4
let glyph_id_u = match self.data.read_u16(base) {
None => return None
Some(u) => u
}
let color_u = match self.data.read_u16(base + 2) {
None => return None
Some(u) => u
}
let glyph_id = glyph_id_u.to_uint16()
let color_index = if color_u == 0xFFFF {
None
} else {
Some(color_u.to_uint16())
}
Some(ColrLayer::{ glyph_id, color_index })
}
///|
struct ColrLayer {
glyph_id : UInt16
color_index : UInt16?
}
///|
pub fn ColrLayer::glyph_id(self : ColrLayer) -> UInt16 {
self.glyph_id
}
///|
pub fn ColrLayer::color_index(self : ColrLayer) -> UInt16? {
self.color_index
}