// Copyright 2026 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.
let max_paint_walk_depth : Int = 64
///|
/// Visitor callback for COLRv1 paint traversal.
pub type PaintVisitor = (@paint.Paint, Int) -> Bool
///|
/// Walk a COLRv1 paint graph, visiting each node and returning count.
pub fn ColrTable::walk_paint(
self : ColrTable,
paint : @paint.Paint,
visit : PaintVisitor,
max_depth? : Int = max_paint_walk_depth,
) -> Result[Int, ColorError] {
walk_paint_impl(self, paint, visit, 0, max_depth)
}
fn walk_paint_impl(
colr : ColrTable,
paint : @paint.Paint,
visit : PaintVisitor,
depth : Int,
max_depth : Int,
) -> Result[Int, ColorError] {
if depth > max_depth {
return Err(InvalidFormat)
}
let should_recurse = visit(paint, depth)
let mut count = 1
if !should_recurse {
return Ok(count)
}
match paint {
@paint.Paint::ColrLayers(first_layer_index, num_layers) => {
if num_layers < 0 {
return Err(InvalidFormat)
}
for i in 0.. return Err(err)
Ok(None) => ()
Ok(Some(child)) => {
let child_count = walk_paint_impl(colr, child, visit, depth + 1, max_depth)
match child_count {
Err(err) => return Err(err)
Ok(value) => count = count + value
}
}
}
}
}
@paint.Paint::Glyph(_, child) => {
let child_count = walk_paint_impl(colr, child, visit, depth + 1, max_depth)
match child_count {
Err(err) => return Err(err)
Ok(value) => count = count + value
}
}
@paint.Paint::ColrGlyph(glyph_id) => {
let glyph_paint = colr.paint_for_glyph(glyph_id)
match glyph_paint {
Err(err) => return Err(err)
Ok(None) => ()
Ok(Some(child)) => {
let child_count = walk_paint_impl(colr, child, visit, depth + 1, max_depth)
match child_count {
Err(err) => return Err(err)
Ok(value) => count = count + value
}
}
}
}
@paint.Paint::Transform(_, child)
| @paint.Paint::Translate(_, _, _, child)
| @paint.Paint::Scale(_, _, _, child)
| @paint.Paint::ScaleAroundCenter(_, _, _, _, _, child)
| @paint.Paint::ScaleUniform(_, _, child)
| @paint.Paint::ScaleUniformAroundCenter(_, _, _, _, child)
| @paint.Paint::Rotate(_, _, child)
| @paint.Paint::RotateAroundCenter(_, _, _, _, child)
| @paint.Paint::Skew(_, _, _, child)
| @paint.Paint::SkewAroundCenter(_, _, _, _, _, child) => {
let child_count = walk_paint_impl(colr, child, visit, depth + 1, max_depth)
match child_count {
Err(err) => return Err(err)
Ok(value) => count = count + value
}
}
@paint.Paint::Composite(_, src, backdrop) => {
let src_count = walk_paint_impl(colr, src, visit, depth + 1, max_depth)
match src_count {
Err(err) => return Err(err)
Ok(value) => count = count + value
}
let backdrop_count = walk_paint_impl(colr, backdrop, visit, depth + 1, max_depth)
match backdrop_count {
Err(err) => return Err(err)
Ok(value) => count = count + value
}
}
_ => ()
}
Ok(count)
}