content: /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
  
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  
      http://www.apache.org/licenses/LICENSE-2.0
  
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  ==============================================================================*/
  
  package org.tensorflow.lite.support.image.ops;
  
  import android.graphics.Bitmap;
  import android.graphics.Matrix;
  import android.graphics.PointF;
  import org.checkerframework.checker.nullness.qual.NonNull;
  import org.tensorflow.lite.support.image.ImageOperator;
  import org.tensorflow.lite.support.image.TensorImage;
  
  /** Rotates image counter-clockwise. */
  public class Rot90Op implements ImageOperator {
  
    private final int numRotation;
  
    /** Creates a Rot90 Op which will rotate image by 90 degree counter-clockwise. */
    public Rot90Op() {
      this(1);
    }
  
    /**
     * Creates a Rot90 Op which will rotate image by 90 degree for {@code k} times counter-clockwise.
     *
     * @param k: The number of times the image is rotated by 90 degrees. If it's positive, the image
     *     will be rotated counter-clockwise. If it's negative, the op will rotate image clockwise.
     */
    public Rot90Op(int k) {
      numRotation = k % 4;
    }
  
    /**
     * Applies the defined rotation on given image and returns the result.
     *
     * <p>Note: the content of input {@code image} will change, and {@code image} is the same instance
     * with the output.
     *
     * @param image input image.
     * @return output image.
     */
    @NonNull
    @Override
    public TensorImage apply(@NonNull TensorImage image) {
      Bitmap input = image.getBitmap();
      if (numRotation == 0) {
        return image;
      }
      int w = input.getWidth();
      int h = input.getHeight();
      Matrix matrix = new Matrix();
      matrix.postTranslate(w * 0.5f, h * 0.5f);
      matrix.postRotate(-90 * numRotation);
      int newW = (numRotation % 2 == 0) ? w : h;
      int newH = (numRotation % 2 == 0) ? h : w;
      matrix.postTranslate(newW * 0.5f, newH * 0.5f);
      Bitmap output = Bitmap.createBitmap(input, 0, 0, w, h, matrix, false);
      image.load(output);
      return image;
    }
  
    @Override
    public int getOutputImageHeight(int inputImageHeight, int inputImageWidth) {
      return (numRotation % 2 == 0) ? inputImageHeight : inputImageWidth;
    }
  
    @Override
    public int getOutputImageWidth(int inputImageHeight, int inputImageWidth) {
      return (numRotation % 2 == 0) ? inputImageWidth : inputImageHeight;
    }
  
    @Override
    public PointF inverseTransform(PointF point, int inputImageHeight, int inputImageWidth) {
      int inverseNumRotation = (4 - numRotation) % 4;
      int height = getOutputImageHeight(inputImageHeight, inputImageWidth);
      int width = getOutputImageWidth(inputImageHeight, inputImageWidth);
      return transformImpl(point, height, width, inverseNumRotation);
    }
  
    private static PointF transformImpl(PointF point, int height, int width, int numRotation) {
      if (numRotation == 0) {
        return point;
      } else if (numRotation == 1) {
        return new PointF(point.y, width - point.x);
      } else if (numRotation == 2) {
        return new PointF(width - point.x, height - point.y);
      } else { // numRotation == 3
        return new PointF(height - point.y, point.x);
      }
    }
  }
  