2024-03-11 01:33:47 +08:00

106 lines
4.0 KiB
TypeScript

/*
* @Author: ShawnPhang
* @Date: 2023-10-05 16:33:07
* @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-08 11:09:55
*/
import { GLOBAL_COMPOSITE_OPERATION_DESTINATION_IN, GLOBAL_COMPOSITE_OPERATION_SOURCE_OVER } from '../constants'
import { MattingDrawingConfig, RenderInterpolationConfig } from '../types/drawing'
import { createContext2D, drawBrushPoint, transformedDrawImage } from './dom-helper'
import { computeInterpolationStep, computeMovements, needDrawInterpolation, needDrawInterpolationPoint } from './drawing-compute'
import { fixed } from './util'
/** 批量执行抠图(修补/擦除)绘制 */
export function executeMattingDrawing(drawingConfigs: MattingDrawingConfig[]) {
for (const config of drawingConfigs) {
const { radius } = config
const { maxMovement, unsignedMovementX, unsignedMovementY } = computeMovements(config)
if (needDrawInterpolation(maxMovement, radius)) {
renderMattingInterpolation({ drawingConfig: config, unsignedMovementX, unsignedMovementY, maxMovement })
} else {
drawMattingPoint(config)
}
drawResultArea(config)
}
}
/** 隐藏的辅助插值绘制的绘制上下文对象 */
const interpolationCtx = createContext2D()
/** 渲染插值图像区域 */
function renderMattingInterpolation(interpolationConfig: RenderInterpolationConfig) {
const { drawingConfig, maxMovement } = interpolationConfig
const { step, stepBase, drawingCtx, radius, hardness } = drawingConfig
let { x, y } = drawingConfig
const { stepX, stepY } = computeInterpolationStep(interpolationConfig)
resetInterpolationCtx(drawingCtx)
for (let movement = 0, moved = movement; movement < maxMovement; movement += stepBase, x += stepX, y += stepY) {
if (needDrawInterpolationPoint(movement, moved, step)) {
moved = movement
drawBrushPoint({ ctx: interpolationCtx, x: fixed(x), y: fixed(y), radius, hardness })
}
}
drawMattingInterpolationTrack(drawingConfig)
}
/** 绘制插值轨迹 */
function drawMattingInterpolationTrack(drawingConfig: MattingDrawingConfig) {
const { isErasing, hiddenCtx, drawingCtx } = drawingConfig
if (isErasing) {
hiddenCtx.value.drawImage(interpolationCtx.canvas, 0, 0)
} else {
drawMattingTrack(drawingConfig, () => {
drawingCtx.drawImage(interpolationCtx.canvas, 0, 0)
})
}
}
/** 重置用于插值绘制的画板 */
function resetInterpolationCtx(drawingCtx: CanvasRenderingContext2D) {
const { width, height } = drawingCtx.canvas
interpolationCtx.canvas.width = width
interpolationCtx.canvas.height = height
interpolationCtx.clearRect(0, 0, width, height)
}
/** 绘制擦补/抠图区域的圆点 */
function drawMattingPoint(drawingConfig: MattingDrawingConfig) {
const { isErasing, hiddenCtx, drawingCtx } = drawingConfig
const { x, y, radius, hardness } = drawingConfig
if (isErasing) {
drawBrushPoint({ ctx: hiddenCtx.value, x, y, radius, hardness })
} else {
drawMattingTrack(drawingConfig, () => {
drawBrushPoint({ ctx: drawingCtx, x, y, radius, hardness })
})
}
}
/** TODO: 绘制修补/扣除的轨迹 */
async function drawMattingTrack(drawingConfig: MattingDrawingConfig, drawingCallback: VoidFunction) {
const { hiddenCtx, drawingCtx, mattingSource } = drawingConfig
drawingCtx.globalCompositeOperation = GLOBAL_COMPOSITE_OPERATION_SOURCE_OVER
drawingCtx.drawImage(mattingSource, 0, 0)
drawingCtx.globalCompositeOperation = GLOBAL_COMPOSITE_OPERATION_DESTINATION_IN
drawingCallback()
hiddenCtx.value.drawImage(drawingCtx.canvas, 0, 0)
}
/** 在呈现的画布上绘制图像 */
function drawResultArea(drawingConfig: MattingDrawingConfig) {
const { ctx, hiddenCtx, positionRange, scaleRatio, isErasing } = drawingConfig
transformedDrawImage({
ctx: ctx.value as CanvasRenderingContext2D,
hiddenCtx: hiddenCtx.value,
positionRange,
scaleRatio,
withBorder: isInResultBoard(isErasing),
})
}
/** 绘制图像的画板是否为输出画板 */
export function isInResultBoard(isErasing: boolean | undefined) {
return isErasing !== undefined
}