Document the relationship between offscreen canvas and pixel density
wcandillon opened this issue · comments
yes this is very likely:
Could you share the example with me?
@wcandillon Here is the code. You can see the density issue and paint stroke goes different if setting strokeWidth is skipped on the two textures.
I attached the result image (above: offscreen, below: pictureRecorder) and code.
Thanks :)
Versions:
RN skia: 1.1.0
react-native: 0.73.6
import {
Canvas,
Group,
Image,
Line,
PaintStyle,
Picture,
rect,
SkCanvas,
Skia,
vec,
} from '@shopify/react-native-skia';
import { Dimensions } from 'react-native';
import { useDerivedValue } from 'react-native-reanimated';
const { width: stageWidth, height: stageHeight } = Dimensions.get('screen');
const stageHeightHalf = stageHeight / 2;
const TOTAL = 1000;
const RADIUS = 10;
const BALL_STYLE = Skia.Paint();
BALL_STYLE.setStyle(PaintStyle.Stroke);
// BALL_STYLE.setStrokeWidth(1); // * If you skip setting strokeWidth, the two textures shows different result.
const BALLS = Array.from({ length: TOTAL }, () => ({
x: RADIUS + Math.random() * (stageWidth - RADIUS),
y: RADIUS + Math.random() * (stageHeightHalf - RADIUS),
r: RADIUS,
}));
const Page = () => {
const drawBalls = (canvas: SkCanvas) => {
'worklet';
canvas.save();
for (let i = 0; i < BALLS.length; i++) {
const ball = BALLS[i];
canvas.drawCircle(ball.x, ball.y, ball.r, BALL_STYLE);
}
canvas.restore();
};
const offscreen = useDerivedValue(() => {
const offscreen = Skia.Surface.MakeOffscreen(stageWidth, stageHeightHalf)!;
const canvas = offscreen.getCanvas();
drawBalls(canvas);
return offscreen.makeImageSnapshot();
}, []);
const pictureRecorder = useDerivedValue(() => {
const recorder = Skia.PictureRecorder();
const canvas = recorder.beginRecording(rect(0, 0, stageWidth, stageHeightHalf));
drawBalls(canvas);
return recorder.finishRecordingAsPicture();
}, []);
return (
<Canvas
style={{
width: stageWidth,
height: stageHeight,
backgroundColor: 'lightblue',
}}
>
<Group transform={[{ translate: [0, 0] }]}>
<Image image={offscreen} width={stageWidth} height={stageHeightHalf} />
</Group>
<Group transform={[{ translate: [0, stageHeightHalf] }]}>
<Picture picture={pictureRecorder} />
</Group>
<Group>
<Line p1={vec(0, stageHeightHalf)} p2={vec(stageWidth, stageHeightHalf)} strokeWidth={10} color="blue" />
</Group>
</Canvas>
);
};
export default Page;
I think this is the expected result.
This is how you need to write the code:
const pd = PixelRatio.get();
const offscreen = useDerivedValue(() => {
const off = Skia.Surface.MakeOffscreen(
stageWidth * pd,
stageHeightHalf * pd
)!;
const canvas = off.getCanvas();
canvas.scale(pd, pd);
drawBalls(canvas);
canvas.restore();
return off.makeImageSnapshot();
}, []);
@wcandillon Nice :) Thanks for the insight ! 🥰
@wcandillon Oh I have a question, so you are planning to fix this offscreen density issue to make users don't need to add the pixel density configuration for offscreen canvas?
I don't think it makes sense to add it in this API because the offscreen API has nothing to do with the screen.
But now I am trying to see if this causes inconsistencies with higher-level API (e.g useTexture) which are indeed on screen.
Got it! Thanks 🙏🥰
What we will do as a first step is document this better.
I'm closing it for now, as the current situation seems to make sense.