///|
/// Value-layer request for shaping a text run with stable cache identity and
/// explicit font/fallback dependencies.
pub(all) struct TextShapingDescriptor {
run : TextRunDescriptor
font_key : RendererResourceKey
width : Float
} derive(Debug, Eq)
///|
pub fn TextShapingDescriptor::new(
run? : TextRunDescriptor = TextRunDescriptor::new(),
font_key? : RendererResourceKey = RendererResourceKey::font(b""),
width? : Float = 1000000000.0,
) -> TextShapingDescriptor {
{ run, font_key, width: normalized_text_measurement_width(width) }
}
///|
pub fn TextShapingDescriptor::with_run(
self : TextShapingDescriptor,
run : TextRunDescriptor,
) -> TextShapingDescriptor {
{ ..self, run, }
}
///|
pub fn TextShapingDescriptor::with_font_key(
self : TextShapingDescriptor,
font_key : RendererResourceKey,
) -> TextShapingDescriptor {
{ ..self, font_key, }
}
///|
pub fn TextShapingDescriptor::with_width(
self : TextShapingDescriptor,
width : Float,
) -> TextShapingDescriptor {
{ ..self, width: normalized_text_measurement_width(width) }
}
///|
pub fn TextShapingDescriptor::measurement_descriptor(
self : TextShapingDescriptor,
) -> TextMeasurementDescriptor {
TextMeasurementDescriptor::new(
run=self.run,
font_key=self.font_key,
width=self.width,
measure_bounds=false,
shape=true,
)
}
///|
pub fn TextShapingDescriptor::resource_key(
self : TextShapingDescriptor,
text : Bytes,
) -> RendererResourceKey {
RendererResourceKey::text_run(
b"moui_skia:text-shaping:v1" +
text_run_cache_bytes(self.run.resource_key(text, font_key=self.font_key).id) +
self.width.to_be_bytes(),
)
}
///|
pub fn TextShapingDescriptor::resource_descriptor(
self : TextShapingDescriptor,
text : Bytes,
byte_size? : Int64 = -1L,
) -> RendererResourceDescriptor {
let id = self.resource_key(text).id
let measured_byte_size = if byte_size < 0L {
id.length().to_int64()
} else {
byte_size
}
RendererResourceDescriptor::text_run(id, byte_size=measured_byte_size)
}
///|
pub fn TextShapingDescriptor::resource_plan(
self : TextShapingDescriptor,
text : Bytes,
shaping_byte_size? : Int64 = -1L,
text_byte_size? : Int64 = -1L,
font_byte_size? : Int64 = 0L,
fallback_byte_size? : Int64 = -1L,
) -> RendererResourcePlan {
let descriptors : Array[RendererResourceDescriptor] = Array::new()
descriptors.push(self.resource_descriptor(text, byte_size=shaping_byte_size))
for
descriptor in self.run.resource_plan(
text,
font_key=self.font_key,
text_byte_size~,
font_byte_size~,
fallback_byte_size~,
).descriptors {
descriptors.push(descriptor)
}
RendererResourcePlan::new(descriptors)
}
///|
fn normalized_shaped_text_advance(advance : Point) -> Point {
if advance.is_finite() {
advance
} else {
Point::zero()
}
}
///|
/// Value-layer metadata for a native shaped text run result.
pub(all) struct ShapedTextRunDescriptor {
shaping : TextShapingDescriptor
glyph_count : Int
cluster_count : Int
advance : Point
} derive(Debug, Eq)
///|
pub fn ShapedTextRunDescriptor::new(
shaping? : TextShapingDescriptor = TextShapingDescriptor::new(),
glyph_count? : Int = 0,
cluster_count? : Int = 0,
advance? : Point = Point::zero(),
) -> ShapedTextRunDescriptor {
{
shaping,
glyph_count: Int::max(0, glyph_count),
cluster_count: Int::max(0, cluster_count),
advance: normalized_shaped_text_advance(advance),
}
}
///|
pub fn TextShapingDescriptor::shaped_run_descriptor(
self : TextShapingDescriptor,
glyph_count? : Int = 0,
cluster_count? : Int = 0,
advance? : Point = Point::zero(),
) -> ShapedTextRunDescriptor {
ShapedTextRunDescriptor::new(
shaping=self,
glyph_count~,
cluster_count~,
advance~,
)
}
///|
pub fn ShapedTextRunDescriptor::is_valid(
self : ShapedTextRunDescriptor,
) -> Bool {
self.glyph_count > 0 && self.cluster_count >= 0 && self.advance.is_finite()
}
///|
fn ShapedTextRunDescriptor::resource_id(
self : ShapedTextRunDescriptor,
text : Bytes,
) -> Bytes {
if !self.is_valid() {
b""
} else {
b"moui_skia:shaped-text-run:v1" +
text_run_cache_bytes(self.shaping.resource_key(text).id) +
text_run_cache_u64(self.glyph_count) +
text_run_cache_u64(self.cluster_count) +
self.advance.x.to_be_bytes() +
self.advance.y.to_be_bytes()
}
}
///|
pub fn ShapedTextRunDescriptor::resource_key(
self : ShapedTextRunDescriptor,
text : Bytes,
) -> RendererResourceKey {
RendererResourceKey::text_run(self.resource_id(text))
}
///|
pub fn ShapedTextRunDescriptor::resource_descriptor(
self : ShapedTextRunDescriptor,
text : Bytes,
byte_size? : Int64 = -1L,
) -> RendererResourceDescriptor {
let id = self.resource_id(text)
let measured_byte_size = if byte_size < 0L {
id.length().to_int64()
} else {
byte_size
}
RendererResourceDescriptor::text_run(id, byte_size=measured_byte_size)
}
///|
pub fn ShapedTextRunDescriptor::resource_plan(
self : ShapedTextRunDescriptor,
text : Bytes,
shaped_byte_size? : Int64 = -1L,
shaping_byte_size? : Int64 = -1L,
text_byte_size? : Int64 = -1L,
font_byte_size? : Int64 = 0L,
fallback_byte_size? : Int64 = -1L,
) -> RendererResourcePlan {
let descriptors : Array[RendererResourceDescriptor] = Array::new()
descriptors.push(self.resource_descriptor(text, byte_size=shaped_byte_size))
if self.is_valid() {
for
descriptor in self.shaping.resource_plan(
text,
shaping_byte_size~,
text_byte_size~,
font_byte_size~,
fallback_byte_size~,
).descriptors {
descriptors.push(descriptor)
}
}
RendererResourcePlan::new(descriptors)
}
///|
fn normalized_shaped_glyph_run_count(
glyph_ids : Array[Int],
positions : Array[Point],
clusters : Array[Int],
) -> Int {
let mut max_count = glyph_ids.length()
if positions.length() < max_count {
max_count = positions.length()
}
if clusters.length() < max_count {
max_count = clusters.length()
}
let mut count = 0
for index in 0.. count = count + 1
_ => return count
}
}
count
}
///|
fn normalized_shaped_glyph_ids(
glyph_ids : Array[Int],
count : Int,
) -> Array[Int] {
let result : Array[Int] = Array::new(capacity=count)
for index in 0.. result.push(Int::max(0, glyph_id))
None => return result
}
}
result
}
///|
fn normalized_shaped_glyph_positions(
positions : Array[Point],
count : Int,
) -> Array[Point] {
let result : Array[Point] = Array::new(capacity=count)
for index in 0..
result.push(if position.is_finite() { position } else { Point::zero() })
None => return result
}
}
result
}
///|
fn normalized_shaped_glyph_clusters(
clusters : Array[Int],
count : Int,
) -> Array[Int] {
let result : Array[Int] = Array::new(capacity=count)
for index in 0.. result.push(Int::max(0, cluster))
None => return result
}
}
result
}
///|
fn normalized_shaped_glyph_text_run(
shaped_run : ShapedTextRunDescriptor,
glyph_count : Int,
cluster_count : Int,
) -> ShapedTextRunDescriptor {
ShapedTextRunDescriptor::new(
shaping=shaped_run.shaping,
glyph_count~,
cluster_count~,
advance=shaped_run.advance,
)
}
///|
/// Detailed glyph IDs, positions, and clusters for a shaped text run.
pub(all) struct ShapedGlyphRunDescriptor {
shaped_run : ShapedTextRunDescriptor
glyph_ids : Array[Int]
positions : Array[Point]
clusters : Array[Int]
} derive(Debug, Eq)
///|
pub fn ShapedGlyphRunDescriptor::new(
shaped_run? : ShapedTextRunDescriptor = ShapedTextRunDescriptor::new(),
glyph_ids? : Array[Int] = [],
positions? : Array[Point] = [],
clusters? : Array[Int] = [],
) -> ShapedGlyphRunDescriptor {
let count = normalized_shaped_glyph_run_count(glyph_ids, positions, clusters)
let glyph_ids = normalized_shaped_glyph_ids(glyph_ids, count)
let positions = normalized_shaped_glyph_positions(positions, count)
let clusters = normalized_shaped_glyph_clusters(clusters, count)
{
shaped_run: normalized_shaped_glyph_text_run(
shaped_run,
count,
clusters.length(),
),
glyph_ids,
positions,
clusters,
}
}
///|
pub fn ShapedTextRunDescriptor::glyph_run_descriptor(
self : ShapedTextRunDescriptor,
glyph_ids? : Array[Int] = [],
positions? : Array[Point] = [],
clusters? : Array[Int] = [],
) -> ShapedGlyphRunDescriptor {
ShapedGlyphRunDescriptor::new(
shaped_run=self,
glyph_ids~,
positions~,
clusters~,
)
}
///|
pub fn ShapedGlyphRunDescriptor::glyph_count(
self : ShapedGlyphRunDescriptor,
) -> Int {
self.glyph_ids.length()
}
///|
pub fn ShapedGlyphRunDescriptor::cluster_count(
self : ShapedGlyphRunDescriptor,
) -> Int {
self.clusters.length()
}
///|
pub fn ShapedGlyphRunDescriptor::is_empty(
self : ShapedGlyphRunDescriptor,
) -> Bool {
self.glyph_ids.is_empty()
}
///|
pub fn ShapedGlyphRunDescriptor::is_valid(
self : ShapedGlyphRunDescriptor,
) -> Bool {
let glyph_count = self.glyph_count()
let mut valid = self.shaped_run.is_valid() &&
glyph_count > 0 &&
self.positions.length() == glyph_count &&
self.clusters.length() == glyph_count &&
self.shaped_run.glyph_count == glyph_count &&
self.shaped_run.cluster_count == self.cluster_count()
for glyph_id in self.glyph_ids {
if glyph_id < 0 {
valid = false
}
}
for position in self.positions {
if !position.is_finite() {
valid = false
}
}
for cluster in self.clusters {
if cluster < 0 {
valid = false
}
}
valid
}
///|
fn ShapedGlyphRunDescriptor::resource_id(
self : ShapedGlyphRunDescriptor,
text : Bytes,
) -> Bytes {
if !self.is_valid() {
b""
} else {
let mut id = b"moui_skia:shaped-glyph-run:v1" +
text_run_cache_bytes(self.shaped_run.resource_key(text).id) +
text_run_cache_u64(self.glyph_count())
for glyph_id in self.glyph_ids {
id = id + text_run_cache_u64(glyph_id)
}
for position in self.positions {
id = id + position.x.to_be_bytes() + position.y.to_be_bytes()
}
for cluster in self.clusters {
id = id + text_run_cache_u64(cluster)
}
id
}
}
///|
pub fn ShapedGlyphRunDescriptor::resource_key(
self : ShapedGlyphRunDescriptor,
text : Bytes,
) -> RendererResourceKey {
RendererResourceKey::text_run(self.resource_id(text))
}
///|
pub fn ShapedGlyphRunDescriptor::resource_descriptor(
self : ShapedGlyphRunDescriptor,
text : Bytes,
byte_size? : Int64 = -1L,
) -> RendererResourceDescriptor {
let id = self.resource_id(text)
let measured_byte_size = if byte_size < 0L {
id.length().to_int64()
} else {
byte_size
}
RendererResourceDescriptor::text_run(id, byte_size=measured_byte_size)
}
///|
pub fn ShapedGlyphRunDescriptor::resource_plan(
self : ShapedGlyphRunDescriptor,
text : Bytes,
glyph_run_byte_size? : Int64 = -1L,
shaped_byte_size? : Int64 = -1L,
shaping_byte_size? : Int64 = -1L,
text_byte_size? : Int64 = -1L,
font_byte_size? : Int64 = 0L,
fallback_byte_size? : Int64 = -1L,
) -> RendererResourcePlan {
let descriptors : Array[RendererResourceDescriptor] = Array::new()
descriptors.push(
self.resource_descriptor(text, byte_size=glyph_run_byte_size),
)
if self.is_valid() {
for
descriptor in self.shaped_run.resource_plan(
text,
shaped_byte_size~,
shaping_byte_size~,
text_byte_size~,
font_byte_size~,
fallback_byte_size~,
).descriptors {
descriptors.push(descriptor)
}
}
RendererResourcePlan::new(descriptors)
}