1 /*
2  * Copyright (C) 2011 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.media.filterfw;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.graphics.Paint;
22 import android.graphics.Rect;
23 import android.graphics.RectF;
24 import androidx.media.filterfw.BackingStore.Backing;
25 
26 public class FrameImage2D extends FrameBuffer2D {
27 
28     /**
29      * Access frame's data using a TextureSource.
30      * This is a convenience method and is equivalent to calling {@code lockData} with an
31      * {@code accessFormat} of {@code ACCESS_TEXTURE}.
32      *
33      * @return The TextureSource instance holding the Frame's data.
34      */
lockTextureSource()35     public TextureSource lockTextureSource() {
36         return (TextureSource)mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_TEXTURE);
37     }
38 
39     /**
40      * Access frame's data using a RenderTarget.
41      * This is a convenience method and is equivalent to calling {@code lockData} with an
42      * {@code accessFormat} of {@code ACCESS_RENDERTARGET}.
43      *
44      * @return The RenderTarget instance holding the Frame's data.
45      */
lockRenderTarget()46     public RenderTarget lockRenderTarget() {
47         return (RenderTarget)mBackingStore.lockData(MODE_WRITE, BackingStore.ACCESS_RENDERTARGET);
48     }
49 
50     /**
51      * Assigns the pixel data of the specified bitmap.
52      *
53      * The RGBA pixel data will be extracted from the bitmap and assigned to the frame data. Note,
54      * that the colors are premultiplied with the alpha channel. If you wish to have
55      * non-premultiplied colors, you must pass the Frame through an
56      * {@code UnpremultiplyAlphaFilter}.
57      *
58      * @param bitmap The bitmap pixels to assign.
59      */
setBitmap(Bitmap bitmap)60     public void setBitmap(Bitmap bitmap) {
61         bitmap = convertToFrameType(bitmap, mBackingStore.getFrameType());
62         validateBitmapSize(bitmap, mBackingStore.getDimensions());
63         Backing backing = mBackingStore.lockBacking(MODE_WRITE, BackingStore.ACCESS_BITMAP);
64         backing.setData(bitmap);
65         mBackingStore.unlock();
66     }
67 
68     /**
69      * Returns the RGBA image contents as a Bitmap instance.
70      *
71      * @return a Bitmap instance holding the RGBA Frame image content.
72      */
toBitmap()73     public Bitmap toBitmap() {
74         Bitmap result = (Bitmap)mBackingStore.lockData(MODE_READ, BackingStore.ACCESS_BITMAP);
75         mBackingStore.unlock();
76         return result;
77     }
78 
79     /**
80      * Copies the image data from one frame to another.
81      *
82      * The source and target rectangles must be given in normalized coordinates, where 0,0 is the
83      * top-left of the image and 1,1 is the bottom-right.
84      *
85      * If the target rectangle is smaller than the target frame, the pixel values outside of the
86      * target rectangle are undefined.
87      *
88      * This method must be called within a Filter during execution. It supports both GL-enabled
89      * and GL-disabled run contexts.
90      *
91      * @param target The target frame to copy to.
92      * @param sourceRect The source rectangle in normalized coordinates.
93      * @param targetRect The target rectangle in normalized coordinates.
94      */
copyToFrame(FrameImage2D target, RectF sourceRect, RectF targetRect)95     public void copyToFrame(FrameImage2D target, RectF sourceRect, RectF targetRect) {
96         if (GraphRunner.current().isOpenGLSupported()) {
97             gpuImageCopy(this, target, sourceRect, targetRect);
98         } else {
99             cpuImageCopy(this, target, sourceRect, targetRect);
100         }
101     }
102 
create(BackingStore backingStore)103     static FrameImage2D create(BackingStore backingStore) {
104         assertCanCreate(backingStore);
105         return new FrameImage2D(backingStore);
106     }
107 
FrameImage2D(BackingStore backingStore)108     FrameImage2D(BackingStore backingStore) {
109         super(backingStore);
110     }
111 
assertCanCreate(BackingStore backingStore)112     static void assertCanCreate(BackingStore backingStore) {
113         FrameBuffer2D.assertCanCreate(backingStore);
114     }
115 
convertToFrameType(Bitmap bitmap, FrameType type)116     private static Bitmap convertToFrameType(Bitmap bitmap, FrameType type) {
117         Bitmap.Config config = bitmap.getConfig();
118         Bitmap result = bitmap;
119         switch(type.getElementId()) {
120             case FrameType.ELEMENT_RGBA8888:
121                 if (config != Bitmap.Config.ARGB_8888) {
122                     result = bitmap.copy(Bitmap.Config.ARGB_8888, false);
123                     if (result == null) {
124                         throw new RuntimeException("Could not convert bitmap to frame-type " +
125                                 "RGBA8888!");
126                     }
127                 }
128                 break;
129             default:
130                 throw new IllegalArgumentException("Unsupported frame type '" + type + "' for " +
131                         "bitmap assignment!");
132         }
133         return result;
134     }
135 
validateBitmapSize(Bitmap bitmap, int[] dimensions)136     private void validateBitmapSize(Bitmap bitmap, int[] dimensions) {
137         if (bitmap.getWidth() != dimensions[0] || bitmap.getHeight() != dimensions[1]) {
138             throw new IllegalArgumentException("Cannot assign bitmap of size " + bitmap.getWidth()
139                     + "x" + bitmap.getHeight() + " to frame of size " + dimensions[0] + "x"
140                     + dimensions[1] + "!");
141         }
142     }
143 
gpuImageCopy( FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect)144     private static void gpuImageCopy(
145             FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect) {
146         ImageShader idShader = RenderTarget.currentTarget().getIdentityShader();
147         // We briefly modify the shader
148         // TODO: Implement a safer way to save and restore a shared shader.
149         idShader.setSourceRect(srcRect);
150         idShader.setTargetRect(dstRect);
151         idShader.process(srcImage, dstImage);
152         // And reset it as others may use it as well
153         idShader.setSourceRect(0f, 0f, 1f, 1f);
154         idShader.setTargetRect(0f, 0f, 1f, 1f);
155     }
156 
cpuImageCopy( FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect)157     private static void cpuImageCopy(
158             FrameImage2D srcImage, FrameImage2D dstImage, RectF srcRect, RectF dstRect) {
159         // Convert rectangles to integer rectangles in image dimensions
160         Rect srcIRect = new Rect((int) srcRect.left * srcImage.getWidth(),
161                 (int) srcRect.top * srcImage.getHeight(),
162                 (int) srcRect.right * srcImage.getWidth(),
163                 (int) srcRect.bottom * srcImage.getHeight());
164         Rect dstIRect = new Rect((int) dstRect.left * srcImage.getWidth(),
165                 (int) dstRect.top * srcImage.getHeight(),
166                 (int) dstRect.right * srcImage.getWidth(),
167                 (int) dstRect.bottom * srcImage.getHeight());
168 
169         // Create target canvas
170         Bitmap.Config config = Bitmap.Config.ARGB_8888;
171         Bitmap dstBitmap = Bitmap.createBitmap(dstImage.getWidth(), dstImage.getHeight(), config);
172         Canvas canvas = new Canvas(dstBitmap);
173 
174         // Draw source bitmap into target canvas
175         Paint paint = new Paint();
176         paint.setFilterBitmap(true);
177         Bitmap srcBitmap = srcImage.toBitmap();
178         canvas.drawBitmap(srcBitmap, srcIRect, dstIRect, paint);
179 
180         // Assign bitmap to output frame
181         dstImage.setBitmap(dstBitmap);
182     }
183 }
184 
185