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.Rect;
20 import android.graphics.RectF;
21 import android.view.View;
22 
23 /**
24  * TODO: Move this to filterpacks/base?
25  */
26 public abstract class ViewFilter extends Filter {
27 
28     public static final int SCALE_STRETCH = 1;
29     public static final int SCALE_FIT = 2;
30     public static final int SCALE_FILL = 3;
31 
32     protected int mScaleMode = SCALE_FIT;
33     protected float[] mClearColor = new float[] { 0f, 0f, 0f, 1f };
34     protected boolean mFlipVertically = true;
35 
36     private String mRequestedScaleMode = null;
37 
ViewFilter(MffContext context, String name)38     protected ViewFilter(MffContext context, String name) {
39         super(context, name);
40     }
41 
42     /**
43      * Binds the filter to a view.
44      * View filters support visualizing data to a view. Check the specific filter documentation
45      * for details. The view may be bound only if the filter's graph is not running.
46      *
47      * @param view the view to bind to.
48      * @throws IllegalStateException if the method is called while the graph is running.
49      */
bindToView(View view)50     public void bindToView(View view) {
51         if (isRunning()) {
52             throw new IllegalStateException("Attempting to bind filter to view while it is "
53                 + "running!");
54         }
55         onBindToView(view);
56     }
57 
setScaleMode(int scaleMode)58     public void setScaleMode(int scaleMode) {
59         if (isRunning()) {
60             throw new IllegalStateException("Attempting to change scale mode while filter is "
61                 + "running!");
62         }
63         mScaleMode = scaleMode;
64     }
65 
66     @Override
getSignature()67     public Signature getSignature() {
68         return new Signature()
69             .addInputPort("scaleMode", Signature.PORT_OPTIONAL, FrameType.single(String.class))
70             .addInputPort("flip", Signature.PORT_OPTIONAL, FrameType.single(boolean.class));
71     }
72 
73     /**
74      * Subclasses must override this method to bind their filter to the specified view.
75      *
76      * When this method is called, Filter implementations may assume that the graph is not
77      * currently running.
78      */
onBindToView(View view)79     protected abstract void onBindToView(View view);
80 
81     /**
82      * TODO: Document.
83      */
getTargetRect(Rect frameRect, Rect bufferRect)84     protected RectF getTargetRect(Rect frameRect, Rect bufferRect) {
85         RectF result = new RectF();
86         if (bufferRect.width() > 0 && bufferRect.height() > 0) {
87             float frameAR = (float)frameRect.width() / frameRect.height();
88             float bufferAR = (float)bufferRect.width() / bufferRect.height();
89             float relativeAR = bufferAR / frameAR;
90             switch (mScaleMode) {
91                 case SCALE_STRETCH:
92                     result.set(0f, 0f, 1f, 1f);
93                     break;
94                 case SCALE_FIT:
95                     if (relativeAR > 1.0f) {
96                         float x = 0.5f - 0.5f / relativeAR;
97                         float y = 0.0f;
98                         result.set(x, y, x + 1.0f / relativeAR, y + 1.0f);
99                     } else {
100                         float x = 0.0f;
101                         float y = 0.5f - 0.5f * relativeAR;
102                         result.set(x, y, x + 1.0f, y + relativeAR);
103                     }
104                     break;
105                 case SCALE_FILL:
106                     if (relativeAR > 1.0f) {
107                         float x = 0.0f;
108                         float y = 0.5f - 0.5f * relativeAR;
109                         result.set(x, y, x + 1.0f, y + relativeAR);
110                     } else {
111                         float x = 0.5f - 0.5f / relativeAR;
112                         float y = 0.0f;
113                         result.set(x, y, x + 1.0f / relativeAR, y + 1.0f);
114                     }
115                     break;
116             }
117         }
118         return result;
119     }
120 
connectViewInputs(InputPort port)121     protected void connectViewInputs(InputPort port) {
122         if (port.getName().equals("scaleMode")) {
123             port.bindToListener(mScaleModeListener);
124             port.setAutoPullEnabled(true);
125         } else if (port.getName().equals("flip")) {
126             port.bindToFieldNamed("mFlipVertically");
127             port.setAutoPullEnabled(true);
128         }
129     }
130 
setupShader(ImageShader shader, Rect frameRect, Rect outputRect)131     protected void setupShader(ImageShader shader, Rect frameRect, Rect outputRect) {
132         shader.setTargetRect(getTargetRect(frameRect, outputRect));
133         shader.setClearsOutput(true);
134         shader.setClearColor(mClearColor);
135         if (mFlipVertically) {
136             shader.setSourceRect(0f, 1f, 1f, -1f);
137         }
138     }
139 
140     private InputPort.FrameListener mScaleModeListener = new InputPort.FrameListener() {
141         @Override
142         public void onFrameReceived(InputPort port, Frame frame) {
143             String scaleMode = (String)frame.asFrameValue().getValue();
144             if (!scaleMode.equals(mRequestedScaleMode)) {
145                 mRequestedScaleMode = scaleMode;
146                 if (scaleMode.equals("stretch")) {
147                     mScaleMode = SCALE_STRETCH;
148                 } else if (scaleMode.equals("fit")) {
149                     mScaleMode = SCALE_FIT;
150                 } else if (scaleMode.equals("fill")) {
151                     mScaleMode = SCALE_FILL;
152                 } else {
153                     throw new RuntimeException("Unknown scale-mode '" + scaleMode + "'!");
154                 }
155             }
156         }
157     };
158 }
159 
160