1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.testing.impl.fakes; 18 19 import static androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.graphics.Bitmap; 24 import android.graphics.Canvas; 25 import android.graphics.ColorMatrixColorFilter; 26 import android.graphics.Paint; 27 28 import androidx.camera.core.CameraEffect; 29 import androidx.camera.core.ImageProcessor; 30 import androidx.camera.core.ImageProxy; 31 import androidx.camera.core.imagecapture.RgbaImageProxy; 32 33 import org.jspecify.annotations.NonNull; 34 import org.jspecify.annotations.Nullable; 35 36 /** 37 * Create a grayscale image effect for testing. 38 */ 39 public class GrayscaleImageEffect extends CameraEffect { 40 GrayscaleImageEffect()41 public GrayscaleImageEffect() { 42 super(IMAGE_CAPTURE, 43 directExecutor(), 44 new GrayscaleProcessor(), 45 throwable -> { 46 }); 47 } 48 getInputImage()49 public @Nullable ImageProxy getInputImage() { 50 return ((GrayscaleProcessor) requireNonNull(getImageProcessor())).getInputImage(); 51 } 52 53 static class GrayscaleProcessor implements ImageProcessor { 54 55 private ImageProxy mImageIn; 56 57 @Override process(@onNull Request request)58 public @NonNull Response process(@NonNull Request request) { 59 mImageIn = requireNonNull(request.getInputImage()); 60 Bitmap bitmapIn = ((RgbaImageProxy) mImageIn).createBitmap(); 61 Bitmap bitmapOut = createProcessedBitmap(bitmapIn); 62 return () -> createOutputImage(bitmapOut, mImageIn); 63 } 64 getInputImage()65 @Nullable ImageProxy getInputImage() { 66 return mImageIn; 67 } 68 69 /** 70 * Creates output image 71 */ createOutputImage(Bitmap newBitmap, ImageProxy imageIn)72 private ImageProxy createOutputImage(Bitmap newBitmap, ImageProxy imageIn) { 73 return new RgbaImageProxy( 74 newBitmap, 75 imageIn.getCropRect(), 76 imageIn.getImageInfo().getRotationDegrees(), 77 imageIn.getImageInfo().getSensorToBufferTransformMatrix(), 78 imageIn.getImageInfo().getTimestamp() 79 ); 80 } 81 82 /** 83 * Apply grayscale on the [Bitmap] 84 */ createProcessedBitmap(Bitmap bitmapIn)85 private Bitmap createProcessedBitmap(Bitmap bitmapIn) { 86 Paint paint = new Paint(); 87 // R/G/B = 1/3 R + 1/3 G + 1/3 B 88 float oneThird = 1 / 3F; 89 paint.setColorFilter(new ColorMatrixColorFilter(new float[]{ 90 oneThird, oneThird, oneThird, 0F, 0F, 91 oneThird, oneThird, oneThird, 0F, 0F, 92 oneThird, oneThird, oneThird, 0F, 0F, 93 0F, 0F, 0F, 1F, 0F 94 })); 95 Bitmap bitmapOut = Bitmap.createBitmap(bitmapIn.getWidth(), bitmapIn.getHeight(), 96 Bitmap.Config.ARGB_8888); 97 Canvas canvas = new Canvas(bitmapOut); 98 canvas.drawBitmap(bitmapIn, 0F, 0F, paint); 99 return bitmapOut; 100 } 101 } 102 } 103