Avoiding anti-aliasing with round painting in react-konva

224 Views Asked by At

I am trying to create a app with Konva and react-konva where the user can freely draw anything he wants on the stage, but mainly to use the mouse as a pen or a erasor.

The user can only use two colors: red and green. Although, the user have to see the background. So the colors I am using are actually rgba(255,0,0,125) and rgba(0,255,0,125), so they can see the image in the Konva.Stage while they draw on it.

The free drawing region:

const Region = forwardRef(({ lines }, ref) => {
  const [stroke, setStroke] = useState(30);
  const line1Ref = useRef(null);
  const line2Ref = useRef(null);
  console.log(line1Ref?.current);
  return (
    <Layer ref={ref}>
      {lines.map((line, i) => (
        <>
          <Line
            ref={line1Ref}
            points={line.points}
            stroke="black"
            strokeWidth={stroke}
            lineCap="round"
            lineJoin="round"
            tension={0.5}
            globalCompositeOperation="destination-out"
          />
          <Line
            ref={line2Ref}
            points={line.points}
            stroke={line.color}
            strokeWidth={stroke}
            tension={0.5}
            lineCap="round"
            lineJoin="round"
          />
        </>
      ))}
    </Layer>
  );
});

The stage is drawn as follows

const Konva = (props) => {

const regionRef = useRef(null);
  const [drawing, setDrawing] = useState(false);
  const [lines, setLines] = useState([]);
  const [finish, setFinish] = useState(false);
  const [image, setImage] = useState(null);
  const [color, setColor] = useState('rgba(255,0,0,0.49)');
  const stageRef = useRef(null);

 useEffect(() => {
    if (props.url) {
      const { url } = props;
      const img = new Image();
      img.onload = () => {
        setImage(img);
        setFinish(true);
      };
      img.src = url;
    }
  }, [matchingFrame]);

const handleMouseDown = (e) => {
    setDrawing(true);
    const pos = e.target.getStage().getPointerPosition();
    setLines((prevLines) => {
      return [...prevLines, { color, points: [pos.x, pos.y] }];
    });
  };
  const handleMouseUp = () => {
    setDrawing(false);
  };

  const handleMouseMove = (e) => {
    if (!drawing) {
      return;
    }
    const stage = e.target.getStage();
    const point = stage.getPointerPosition();
    const lastLine = lines[lines.length - 1];
    // add point
    lastLine.points = lastLine.points.concat([point.x, point.y]);

    // replace last
    lines.splice(lines.length - 1, 1, lastLine);
    setLines(lines.concat());
  };
  
  if(!image) return null; 
  
  return (<Stage
              ref={stageRef}
              width={image.width}
              height={image.height}
              onMouseDown={handleMouseDown}
              onMouseUp={handleMouseUp}
              onMouseMove={handleMouseMove}
              onMouseLeave={handleMouseUp}
            >
              <Layer ref={imgRef}>
                <KonvaImage image={image} />
              </Layer>

              <Region ref={regionRef} lines={lines} color={color} />
            </Stage>)
  }

After the drawing I convert the data to an encoded array mask. Althought, the joint points between the colors have different rgba besides the ones I desired due to the anti-aliasing that make the pen looks curved. If I ignore this mix of colors, there will be pixels with no color when they should have, like in the image removed mixed colors in the pixel

Is there any way to avoid this colors interpolation? The idea I have now is to set this pixels into a nearest neighbor approach where I would fill them with the nearest color, red or green.

The code developed is based on the demos developed by Anton Lavrenov. Image labeling and Free drawing

0

There are 0 best solutions below