chromium/chrome/browser/resources/lens/overlay/post_selection_paint_worklet.ts

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {assert, assertInstanceof} from '//resources/js/assert.js';

class PostSelectionWorklet {
  // TODO(b/335858693): Ideally, there should not be different corner lengths,
  // and instead should handle this using an inputted percent value. However,
  // this adds extra complexity to the rendering logic.
  static get inputProperties() {
    return [
      `--post-selection-corner-horizontal-length`,
      `--post-selection-corner-vertical-length`,
      `--post-selection-corner-radius`,
      `--post-selection-corner-width`,
    ];
  }

  paint(
      ctx: PaintRenderingContext2D, size: PaintSize,
      properties: StylePropertyMapReadOnly) {
    // Get inputted properties.
    const cornerLengthHorizontalProp =
        properties.get('--post-selection-corner-horizontal-length');
    const cornerLengthVerticalProp =
        properties.get('--post-selection-corner-vertical-length');
    const cornerWidthProp = properties.get('--post-selection-corner-width');
    const cornerRadiusProp = properties.get('--post-selection-corner-radius');

    // Ensure the values are in the correct format
    assertInstanceof(cornerLengthHorizontalProp, CSSUnitValue);
    assertInstanceof(cornerLengthVerticalProp, CSSUnitValue);
    assertInstanceof(cornerWidthProp, CSSUnitValue);
    assertInstanceof(cornerRadiusProp, CSSUnitValue);
    assert(
        cornerLengthHorizontalProp.unit === 'px',
        '--post-selection-corner-horizontal-length must be a pixel value');
    assert(
        cornerLengthVerticalProp.unit === 'px',
        '--post-selection-corner-vertical-length must be a pixel value');
    assert(
        cornerWidthProp.unit === 'px',
        '--post-selection-corner-width must be a pixel value');
    assert(
        cornerRadiusProp.unit === 'px',
        '--post-selection-corner-radius must be a pixel value');

    // Convert properties to integers so they are easier to use.
    const cornerLengthHorizontal = cornerLengthHorizontalProp.value;
    const cornerLengthVertical = cornerLengthVerticalProp.value;

    const cornerWidth = cornerWidthProp.value;
    // Handle cases where radius is larger than width or height
    const cornerRadius = Math.min(
        cornerRadiusProp.value,
        Math.abs(size.width / 2),
        Math.abs(size.height / 2),
    );

    const minX = cornerWidth / 2;
    const minY = cornerWidth / 2;
    // Need to subtract 1 to account for indexing by zero
    const maxX = size.width - (cornerWidth / 2) - 1;
    const maxY = size.height - (cornerWidth / 2) - 1;

    if (cornerLengthHorizontal <= 0 || cornerLengthVertical <= 0 ||
        cornerWidth <= 0) {
      return;
    }

    ctx.lineWidth = cornerWidth;
    ctx.beginPath;

    // Top-Left Corner
    ctx.moveTo(minX, cornerLengthVertical);
    ctx.arcTo(minX, minY, cornerLengthHorizontal, minY, cornerRadius);
    ctx.lineTo(cornerLengthHorizontal, minY);

    // Top-Right
    ctx.moveTo(maxX - cornerLengthHorizontal, minY);
    ctx.arcTo(maxX, minY, maxX, cornerLengthVertical, cornerRadius);
    ctx.lineTo(maxX, cornerLengthVertical);

    // Bottom-Right
    ctx.moveTo(maxX, maxY - cornerLengthVertical);
    ctx.arcTo(maxX, maxY, maxX - cornerLengthHorizontal, maxY, cornerRadius);
    ctx.lineTo(maxX - cornerLengthHorizontal, maxY);

    // Bottom-Left
    ctx.moveTo(minX + cornerLengthHorizontal, maxY);
    ctx.arcTo(minX, maxY, minX, maxY - cornerLengthVertical, cornerRadius);
    ctx.lineTo(minX, maxY - cornerLengthVertical);

    ctx.strokeStyle = 'white';
    ctx.stroke();
  }
}

registerPaint('post-selection', PostSelectionWorklet);