blob: bca94f77ba1922027cb44bb01463c1b31124a9bd [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 androidx.media.filterfw;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import androidx.media.filterfw.BackingStore.Backing;
public class FrameImage2D extends FrameBuffer2D {
/**
* Access frame's data using a TextureSource.
* This is a convenience method and is equivalent to calling {@code lockData} with an
* {@code accessFormat} of {@code ACCESS_TEXTURE}.
*
* @return The TextureSource instance holding the Frame's data.
*/
public TextureSource lockTextureSource() {
return (TextureSource)mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_TEXTURE);
}
/**
* Access frame's data using a RenderTarget.
* This is a convenience method and is equivalent to calling {@code lockData} with an
* {@code accessFormat} of {@code ACCESS_RENDERTARGET}.
*
* @return The RenderTarget instance holding the Frame's data.
*/
public RenderTarget lockRenderTarget() {
return (RenderTarget)mBackingStore.lockData(MODE_WRITE, BackingStore.ACCESS_RENDERTARGET);
}
/**
* Assigns the pixel data of the specified bitmap.
*
* The RGBA pixel data will be extracted from the bitmap and assigned to the frame data. Note,
* that the colors are premultiplied with the alpha channel. If you wish to have
* non-premultiplied colors, you must pass the Frame through an
* {@code UnpremultiplyAlphaFilter}.
*
* @param bitmap The bitmap pixels to assign.
*/
public void setBitmap(Bitmap bitmap) {
bitmap = convertToFrameType(bitmap, mBackingStore.getFrameType());
validateBitmapSize(bitmap, mBackingStore.getDimensions());
Backing backing = mBackingStore.lockBacking(MODE_WRITE, BackingStore.ACCESS_BITMAP);
backing.setData(bitmap);
mBackingStore.unlock();
}
/**
* Returns the RGBA image contents as a Bitmap instance.
*
* @return a Bitmap instance holding the RGBA Frame image content.
*/
public Bitmap toBitmap() {
Bitmap result = (Bitmap)mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_BITMAP);
mBackingStore.unlock();
return result;
}
/**
* Copies the image data from one frame to another.
*
* The source and target rectangles must be given in normalized coordinates, where 0,0 is the
* top-left of the image and 1,1 is the bottom-right.
*
* If the target rectangle is smaller than the target frame, the pixel values outside of the
* target rectangle are undefined.
*
* This method must be called within a Filter during execution. It supports both GL-enabled
* and GL-disabled run contexts.
*
* @param target The target frame to copy to.
* @param sourceRect The source rectangle in normalized coordinates.
* @param targetRect The target rectangle in normalized coordinates.
*/
public void copyToFrame(FrameImage2D target, RectF sourceRect, RectF targetRect) {
if (GraphRunner.current().isOpenGLSupported()) {
gpuImageCopy(this, target, sourceRect, targetRect);
} else {
cpuImageCopy(this, target, sourceRect, targetRect);
}
}
static FrameImage2D create(BackingStore backingStore) {
assertCanCreate(backingStore);
return new FrameImage2D(backingStore);
}
FrameImage2D(BackingStore backingStore) {
super(backingStore);
}
static void assertCanCreate(BackingStore backingStore) {
FrameBuffer2D.assertCanCreate(backingStore);
}
private static Bitmap convertToFrameType(Bitmap bitmap, FrameType type) {
Bitmap.Config config = bitmap.getConfig();
Bitmap result = bitmap;
switch(type.getElementId()) {
case FrameType.ELEMENT_RGBA8888:
if (config != Bitmap.Config.ARGB_8888) {
result = bitmap.copy(Bitmap.Config.ARGB_8888, false);
if (result == null) {
throw new RuntimeException("Could not convert bitmap to frame-type " +
"RGBA8888!");
}
}
break;
default:
throw new IllegalArgumentException("Unsupported frame type '" + type + "' for " +
"bitmap assignment!");
}
return result;
}
private void validateBitmapSize(Bitmap bitmap, int[] dimensions) {
if (bitmap.getWidth() != dimensions[0] || bitmap.getHeight() != dimensions[1]) {
throw new IllegalArgumentException("Cannot assign bitmap of size " + bitmap.getWidth()
+ "x" + bitmap.getHeight() + " to frame of size " + dimensions[0] + "x"
+ dimensions[1] + "!");
}
}
private static void gpuImageCopy(
FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect) {
ImageShader idShader = RenderTarget.currentTarget().getIdentityShader();
// We briefly modify the shader
// TODO: Implement a safer way to save and restore a shared shader.
idShader.setSourceRect(srcRect);
idShader.setTargetRect(dstRect);
idShader.process(srcImage, dstImage);
// And reset it as others may use it as well
idShader.setSourceRect(0f, 0f, 1f, 1f);
idShader.setTargetRect(0f, 0f, 1f, 1f);
}
private static void cpuImageCopy(
FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect) {
// Convert rectangles to integer rectangles in image dimensions
Rect srcIRect = new Rect((int) srcRect.left * srcImage.getWidth(),
(int) srcRect.top * srcImage.getHeight(),
(int) srcRect.right * srcImage.getWidth(),
(int) srcRect.bottom * srcImage.getHeight());
Rect dstIRect = new Rect((int) dstRect.left * srcImage.getWidth(),
(int) dstRect.top * srcImage.getHeight(),
(int) dstRect.right * srcImage.getWidth(),
(int) dstRect.bottom * srcImage.getHeight());
// Create target canvas
Bitmap.Config config = Bitmap.Config.ARGB_8888;
Bitmap dstBitmap = Bitmap.createBitmap(dstImage.getWidth(), dstImage.getHeight(), config);
Canvas canvas = new Canvas(dstBitmap);
// Draw source bitmap into target canvas
Paint paint = new Paint();
paint.setFilterBitmap(true);
Bitmap srcBitmap = srcImage.toBitmap();
canvas.drawBitmap(srcBitmap, srcIRect, dstIRect, paint);
// Assign bitmap to output frame
dstImage.setBitmap(dstBitmap);
}
}