@shopify/react-native-skia
Version:
High-performance React Native Graphics using Skia
146 lines (128 loc) • 3.58 kB
text/typescript
import type {
Skia,
SkCanvas,
SkColorFilter,
SkPaint,
SkShader,
SkImageFilter,
SkPathEffect,
} from "../../skia/types";
export const createDrawingContext = (
Skia: Skia,
paintPool: SkPaint[],
canvas: SkCanvas
) => {
"worklet";
// State (formerly class fields)
const paints: SkPaint[] = [];
const colorFilters: SkColorFilter[] = [];
const shaders: SkShader[] = [];
const imageFilters: SkImageFilter[] = [];
const pathEffects: SkPathEffect[] = [];
const paintDeclarations: SkPaint[] = [];
const opacities: number[] = [];
let nextPaintIndex = 1;
// Initialize first paint and opacity
paintPool[0] = Skia.Paint();
paints.push(paintPool[0]);
opacities.push(1);
// Methods (formerly class methods)
const savePaint = () => {
// Get next available paint from pool or create new one if needed
if (nextPaintIndex >= paintPool.length) {
paintPool.push(Skia.Paint());
}
const nextPaint = paintPool[nextPaintIndex];
nextPaint.assign(getCurrentPaint()); // Reuse allocation by copying properties
paints.push(nextPaint);
opacities.push(opacities[opacities.length - 1]);
nextPaintIndex++;
};
const getOpacity = () => {
return opacities[opacities.length - 1];
};
const setOpacity = (newOpacity: number) => {
opacities[opacities.length - 1] = Math.max(0, Math.min(1, newOpacity));
};
const saveBackdropFilter = () => {
let imageFilter: SkImageFilter | null = null;
const imgf = imageFilters.pop();
if (imgf) {
imageFilter = imgf;
} else {
const cf = colorFilters.pop();
if (cf) {
imageFilter = Skia.ImageFilter.MakeColorFilter(cf, null);
}
}
canvas.saveLayer(undefined, null, imageFilter);
canvas.restore();
};
// Equivalent to the `get paint()` getter in the original class
const getCurrentPaint = () => {
return paints[paints.length - 1];
};
const restorePaint = () => {
opacities.pop();
return paints.pop();
};
const materializePaint = () => {
// Color Filters
if (colorFilters.length > 0) {
getCurrentPaint().setColorFilter(
colorFilters.reduceRight((inner, outer) =>
inner ? Skia.ColorFilter.MakeCompose(outer, inner) : outer
)
);
}
// Shaders
if (shaders.length > 0) {
getCurrentPaint().setShader(shaders[shaders.length - 1]);
}
// Image Filters
if (imageFilters.length > 0) {
getCurrentPaint().setImageFilter(
imageFilters.reduceRight((inner, outer) =>
inner ? Skia.ImageFilter.MakeCompose(outer, inner) : outer
)
);
}
// Path Effects
if (pathEffects.length > 0) {
getCurrentPaint().setPathEffect(
pathEffects.reduceRight((inner, outer) =>
inner ? Skia.PathEffect.MakeCompose(outer, inner) : outer
)
);
}
// Clear arrays
colorFilters.length = 0;
shaders.length = 0;
imageFilters.length = 0;
pathEffects.length = 0;
};
// Return an object containing the Skia reference, the canvas, and the methods
return {
// Public fields
Skia,
canvas,
paints,
colorFilters,
shaders,
imageFilters,
pathEffects,
paintDeclarations,
paintPool,
// Public methods
savePaint,
saveBackdropFilter,
get paint() {
return paints[paints.length - 1];
}, // the "getter" for the current paint
restorePaint,
materializePaint,
getOpacity,
setOpacity,
};
};
export type DrawingContext = ReturnType<typeof createDrawingContext>;