chromium/ash/webui/camera_app_ui/resources/js/device/mode/scan.ts

// Copyright 2021 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} from '../../assert.js';
import {Point} from '../../geometry.js';
import {
  Facing,
  PreviewVideo,
  Resolution,
} from '../../type.js';
import {StreamConstraints} from '../stream_constraints.js';

import {ModeBase, ModeFactory} from './mode_base.js';
import {
  Photo,
  PhotoHandler,
  PhotoResult,
} from './photo.js';

/**
 * @param size Size of image to be cropped document from.
 */
export function getDefaultScanCorners(size: Resolution): Point[] {
  // No initial guess from scan API, position corners in box of portrait A4
  // size occupied with 80% center area.
  const WIDTH_A4 = 210;
  const HEIGHT_A4 = 297;
  const {width: w, height: h} = size;
  const [width, height] = size.aspectRatio > WIDTH_A4 / HEIGHT_A4 ?
      [h / w * WIDTH_A4 / HEIGHT_A4 * 0.8, 0.8] :
      [0.8, w / h * HEIGHT_A4 / WIDTH_A4 * 0.8];
  return [
    new Point(0.5 - width / 2, 0.5 - height / 2),
    new Point(0.5 - width / 2, 0.5 + height / 2),
    new Point(0.5 + width / 2, 0.5 + height / 2),
    new Point(0.5 + width / 2, 0.5 - height / 2),
  ];
}

/**
 * Provides external dependency functions used by scan mode and handles the
 * captured result photo.
 */
export interface ScanHandler extends PhotoHandler {
  onDocumentCaptureDone(pendingPhotoResult: Promise<PhotoResult>):
      Promise<void>;
}

class DocumentPhotoHandler implements PhotoHandler {
  constructor(private readonly handler: ScanHandler) {}

  playShutterEffect(): void {
    this.handler.playShutterEffect();
  }

  onPhotoError(): void {
    this.handler.onPhotoError();
  }

  onPhotoCaptureDone(pendingPhotoResult: Promise<PhotoResult>): Promise<void> {
    return this.handler.onDocumentCaptureDone(pendingPhotoResult);
  }

  shouldUsePreviewAsPhoto(): boolean {
    return this.handler.shouldUsePreviewAsPhoto();
  }
}

/**
 * Scan mode capture controller.
 */
export class Scan extends Photo {
  constructor(
      video: PreviewVideo, facing: Facing, captureResolution: Resolution|null,
      scanHandler: ScanHandler) {
    super(
        video, facing, captureResolution,
        new DocumentPhotoHandler(scanHandler));
  }
}

/**
 * Factory for creating scan mode capture object.
 */
export class ScanFactory extends ModeFactory {
  /**
   * @param constraints Constraints for preview stream.
   */
  constructor(
      constraints: StreamConstraints, captureResolution: Resolution|null,
      protected readonly handler: ScanHandler) {
    super(constraints, captureResolution);
  }

  produce(): ModeBase {
    assert(this.previewVideo !== null);
    assert(this.facing !== null);
    return new Scan(
        this.previewVideo,
        this.facing,
        this.captureResolution,
        this.handler,
    );
  }
}