Source: RotateComponent.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

import {core} from 'metal';
import Component from 'metal-component';
import Soy from 'metal-soy';

import componentTemplates from './RotateComponent.soy';

import './RotateControls.soy';

/**
 * Creates a Rotate component.
 */
class RotateComponent extends Component {
	/**
	 * @inheritDoc
	 */
	attached() {
		this.cache_ = {};
		this.rotationAngle_ = 0;
	}

	/**
	 * @inheritDoc
	 */
	detached() {
		this.cache_ = {};
	}

	/**
	 * Rotates the image to the current selected rotation angle.
	 *
	 * @param  {ImageData} imageData The image data representation of the image.
	 * @return {Promise} A promise that resolves when processing is
	 * complete.
	 */
	preview(imageData) {
		return this.process(imageData);
	}

	/**
	 * Rotates the image to the current selected rotation angle.
	 *
	 * @param  {ImageData} imageData The image data representation of the image.
	 * @return {Promise} A promise that resolves when processing is
	 * complete.
	 */
	process(imageData) {
		let promise = this.cache_[this.rotationAngle_];

		if (!promise) {
			promise = this.rotate_(imageData, this.rotationAngle_);

			this.cache_[this.rotationAngle_] = promise;
		}

		return promise;
	}

	/**
	 * Rotates the passed image data to the current rotation angle.
	 *
	 * @param  {ImageData} imageData The image data to rotate.
	 * @param  {number} rotationAngle The normalized rotation angle (in degrees)
	 * in the range [0-360).
	 * @protected
	 * @return {Promise} A promise that resolves when the image is
	 * rotated.
	 */
	rotate_(imageData, rotationAngle) {
		const cancellablePromise = new Promise(resolve => {
			const imageWidth = imageData.width;
			const imageHeight = imageData.height;

			const swapDimensions = (rotationAngle / 90) % 2;

			const imageCanvas = document.createElement('canvas');
			imageCanvas.width = imageWidth;
			imageCanvas.height = imageHeight;
			imageCanvas.getContext('2d').putImageData(imageData, 0, 0);

			const offscreenCanvas = document.createElement('canvas');
			offscreenCanvas.width = swapDimensions ? imageHeight : imageWidth;
			offscreenCanvas.height = swapDimensions ? imageWidth : imageHeight;

			const offscreenContext = offscreenCanvas.getContext('2d');
			offscreenContext.save();
			offscreenContext.translate(
				offscreenCanvas.width / 2,
				offscreenCanvas.height / 2
			);
			offscreenContext.rotate((rotationAngle * Math.PI) / 180);
			offscreenContext.drawImage(
				imageCanvas,
				-imageCanvas.width / 2,
				-imageCanvas.height / 2
			);
			offscreenContext.restore();

			resolve(
				offscreenContext.getImageData(
					0,
					0,
					offscreenCanvas.width,
					offscreenCanvas.height
				)
			);
		});

		return cancellablePromise;
	}

	/**
	 * Rotates the image 90º counter-clockwise.
	 */
	rotateLeft() {
		this.rotationAngle_ = (this.rotationAngle_ - 90) % 360;
		this.requestImageEditorPreview();
	}

	/**
	 * Rotates the image 90º clockwise.
	 */
	rotateRight() {
		this.rotationAngle_ = (this.rotationAngle_ + 90) % 360;
		this.requestImageEditorPreview();
	}
}

/**
 * State definition.
 *
 * @static
 * @type {!Object}
 */
RotateComponent.STATE = {
	/**
	 * Path of this module.
	 *
	 * @type {Function}
	 */
	modulePath: {
		validator: core.isString,
	},

	/**
	 * Injected method that notifies the editor that this component wants to
	 * generate a preview version of the image.
	 *
	 * @type {Function}
	 */
	requestImageEditorPreview: {
		validator: core.isFunction,
	},
};

Soy.register(RotateComponent, componentTemplates);

export default RotateComponent;