openpencil/tests/engine/render/canvas/image-fill.test.ts
2026-06-30 10:54:32 +03:00

104 lines
2.9 KiB
TypeScript

import { describe, expect, mock, test } from 'bun:test'
import type { Fill, SceneNode } from '@open-pencil/scene-graph'
import { makeImageFillLocalMatrix, patternTileLayout } from '#core/canvas/fills'
import type { SkiaRenderer } from '#core/canvas/renderer'
function createRenderer() {
return {
ck: {
Matrix: {
identity: mock(() => ['identity']),
invert: mock((matrix) => ['invert', matrix]),
multiply: mock((...matrices) => ['multiply', ...matrices]),
scaled: mock((x, y) => ['scaled', x, y]),
translated: mock((x, y) => ['translated', x, y])
}
}
} as SkiaRenderer
}
const node = {
width: 120,
height: 80
} as SceneNode
describe('canvas pattern fills', () => {
test('uses scale, spacing, and alignment for rectangular pattern tiles', () => {
const layout = patternTileLayout(
{ width: 20, height: 10 } as SceneNode,
{
type: 'PATTERN',
scale: 2,
patternSpacing: { x: 0.25, y: 0.5 },
horizontalAlignment: 'CENTER',
verticalAlignment: 'END',
color: { r: 0, g: 0, b: 0, a: 1 },
opacity: 1,
visible: true
} as Fill
)
expect(layout).toEqual({
rect: { x: 0, y: 0, width: 50, height: 30 },
scale: 2,
positions: [{ x: -5, y: -10 }]
})
})
test('adds an offset source copy for hexagonal pattern tiles', () => {
const layout = patternTileLayout(
{ width: 20, height: 10 } as SceneNode,
{
type: 'PATTERN',
patternTileType: 'HORIZONTAL_HEXAGONAL',
patternSpacing: { x: 0, y: 0 },
color: { r: 0, g: 0, b: 0, a: 1 },
opacity: 1,
visible: true
} as Fill
)
expect(layout.positions).toEqual([
{ x: 0, y: 0 },
{ x: 10, y: 5 }
])
})
})
describe('canvas image fills', () => {
test('keeps untransformed tile fills in image pixel space', () => {
const renderer = createRenderer()
const fill = { type: 'IMAGE', imageScaleMode: 'TILE' } as Fill
const matrix = makeImageFillLocalMatrix(renderer, fill, node, 24, 16)
expect(matrix).toEqual(['identity'])
expect(renderer.ck.Matrix.identity).toHaveBeenCalled()
expect(renderer.ck.Matrix.scaled).not.toHaveBeenCalled()
})
test('uses the full imported affine transform for patterned image fills', () => {
const renderer = createRenderer()
const transform = { m00: 0.5, m01: 0.1, m02: 0.25, m10: -0.2, m11: 0.25, m12: 0.5 }
const fill = {
type: 'IMAGE',
imageScaleMode: 'TILE',
imageTransform: transform
} as Fill
const matrix = makeImageFillLocalMatrix(renderer, fill, node, 40, 40)
expect(renderer.ck.Matrix.invert).toHaveBeenCalledWith([
0.5, 0.1, 0.25, -0.2, 0.25, 0.5, 0, 0, 1
])
expect(matrix).toEqual([
'multiply',
['scaled', 120, 80],
['invert', [0.5, 0.1, 0.25, -0.2, 0.25, 0.5, 0, 0, 1]],
['scaled', 0.025, 0.025]
])
})
})