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
17package androidx.media.filterfw;
18
19import android.graphics.Rect;
20import android.graphics.RectF;
21import android.view.View;
22
23/**
24 * TODO: Move this to filterpacks/base?
25 */
26public 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
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     */
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
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
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     */
79    protected abstract void onBindToView(View view);
80
81    /**
82     * TODO: Document.
83     */
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
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
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