mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 16:25:49 +08:00
144 lines
4.9 KiB
Go
144 lines
4.9 KiB
Go
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
|
//
|
|
// 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.
|
|
|
|
package ofdgo
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"slices"
|
|
)
|
|
|
|
// RasterScene 固定尺寸、DPI、字体和资源的可重复绘制场景
|
|
// 每次Render返回独立图像;源图像只读借用,修改页面或配置后应重新准备
|
|
type RasterScene interface {
|
|
Render() (image.Image, error)
|
|
}
|
|
|
|
// RasterSceneBackend 可选的光栅预编译能力,不改变后端选择
|
|
type RasterSceneBackend interface {
|
|
RasterBackend
|
|
Prepare(page *RasterPage) (RasterScene, error)
|
|
}
|
|
|
|
// RasterSceneUpdater 可选的增量场景能力,保持原后端与显式几何配置
|
|
// Update按新页面的完整内容及DPI使派生数据失效,失败时原场景仍可绘制
|
|
// 图片仍为只读借用,编辑图片应提供新资源;修改后端配置应重新Prepare
|
|
type RasterSceneUpdater interface {
|
|
RasterScene
|
|
Update(page *RasterPage) error
|
|
}
|
|
|
|
// RasterSceneReuseBackend 可选的跨快照复用能力,不改变显式后端选择
|
|
// 实现必须验证前次场景及配置,返回独立快照,不能修改仍可使用的旧场景
|
|
type RasterSceneReuseBackend interface {
|
|
RasterSceneBackend
|
|
PrepareReuse(page *RasterPage, previous RasterScene) (RasterScene, error)
|
|
}
|
|
|
|
// rasterPreparationKey 隔离渲染器私有的前次场景
|
|
type rasterPreparationKey struct{}
|
|
|
|
// rasterPreparationState 记录前次场景及实际后端配置
|
|
type rasterPreparationState struct {
|
|
backend RasterBackend
|
|
scene RasterScene
|
|
}
|
|
|
|
// rasterScene 复用后端无关指令,图片保持只读引用
|
|
type rasterScene struct {
|
|
backend RasterBackend
|
|
page *RasterPage
|
|
}
|
|
|
|
// PreparePage 编译并准备当前配置的独立页面快照,按后端能力复用未修改指令
|
|
// 每次重新解释源页面,按内容使模板、签章、字体及样式派生数据失效
|
|
// 入参: page 源页面
|
|
// 返回: RasterScene 可重复绘制场景, error 编译或准备错误
|
|
func (r *Renderer) PreparePage(page *PageContent) (RasterScene, error) {
|
|
if r.backends.Raster == nil {
|
|
return nil, fmt.Errorf("raster scene: %w", ErrBackendUnavailable)
|
|
}
|
|
compiled, err := r.CompilePage(page)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if backend, ok := r.backends.Raster.(RasterSceneReuseBackend); ok {
|
|
state, _ := r.backendStates[rasterPreparationKey{}].(rasterPreparationState)
|
|
var previous RasterScene
|
|
if sameBackend(state.backend, r.backends.Raster) {
|
|
previous = state.scene
|
|
}
|
|
scene, err := backend.PrepareReuse(compiled, previous)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r.backendStates[rasterPreparationKey{}] = rasterPreparationState{backend: r.backends.Raster, scene: scene}
|
|
return scene, nil
|
|
}
|
|
return PrepareRasterScene(r.backends.Raster, compiled)
|
|
}
|
|
|
|
// PrepareRasterScene 准备绘制快照,未实现预编译的后端复用公共指令
|
|
// 入参: backend 光栅后端, page 绘制页面
|
|
// 返回: RasterScene 可重复绘制场景, error 准备错误
|
|
func PrepareRasterScene(backend RasterBackend, page *RasterPage) (RasterScene, error) {
|
|
if backend == nil {
|
|
return nil, fmt.Errorf("raster scene: %w", ErrBackendUnavailable)
|
|
}
|
|
if _, _, err := page.PixelSize(); err != nil {
|
|
return nil, err
|
|
}
|
|
if provider, ok := backend.(RasterSceneBackend); ok {
|
|
return provider.Prepare(page)
|
|
}
|
|
return &rasterScene{backend: backend, page: cloneRasterPage(page)}, nil
|
|
}
|
|
|
|
// Render 绘制固定快照
|
|
// 返回: image.Image 独立图像, error 绘制错误
|
|
func (s *rasterScene) Render() (image.Image, error) { return s.backend.Render(s.page) }
|
|
|
|
// cloneRasterPage 隔离可变指令,源图片按接口约定只读借用
|
|
// 入参: page 绘制页面
|
|
// 返回: *RasterPage 指令快照
|
|
func cloneRasterPage(page *RasterPage) *RasterPage {
|
|
result := *page
|
|
result.Commands = slices.Clone(page.Commands)
|
|
for i := range result.Commands {
|
|
cmd := &result.Commands[i]
|
|
cmd.Path, cmd.Clip = slices.Clone(cmd.Path), slices.Clone(cmd.Clip)
|
|
if cmd.Stroke != nil {
|
|
stroke := *cmd.Stroke
|
|
stroke.Dashes = slices.Clone(stroke.Dashes)
|
|
cmd.Stroke = &stroke
|
|
}
|
|
if cmd.Paint.Gradient != nil {
|
|
gradient := *cmd.Paint.Gradient
|
|
gradient.Stops = slices.Clone(gradient.Stops)
|
|
if gradient.Mesh != nil {
|
|
mesh := *gradient.Mesh
|
|
mesh.Triangles = slices.Clone(mesh.Triangles)
|
|
gradient.Mesh = &mesh
|
|
}
|
|
if gradient.Spread != nil {
|
|
spread := *gradient.Spread
|
|
gradient.Spread = &spread
|
|
}
|
|
cmd.Paint.Gradient = &gradient
|
|
}
|
|
}
|
|
return &result
|
|
}
|