/* * 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.Rect; import android.graphics.RectF; import android.view.View; /** * TODO: Move this to filterpacks/base? */ public abstract class ViewFilter extends Filter { public static final int SCALE_STRETCH = 1; public static final int SCALE_FIT = 2; public static final int SCALE_FILL = 3; protected int mScaleMode = SCALE_FIT; protected float[] mClearColor = new float[] { 0f, 0f, 0f, 1f }; protected boolean mFlipVertically = true; private String mRequestedScaleMode = null; protected ViewFilter(MffContext context, String name) { super(context, name); } /** * Binds the filter to a view. * View filters support visualizing data to a view. Check the specific filter documentation * for details. The view may be bound only if the filter's graph is not running. * * @param view the view to bind to. * @throws IllegalStateException if the method is called while the graph is running. */ public void bindToView(View view) { if (isRunning()) { throw new IllegalStateException("Attempting to bind filter to view while it is " + "running!"); } onBindToView(view); } public void setScaleMode(int scaleMode) { if (isRunning()) { throw new IllegalStateException("Attempting to change scale mode while filter is " + "running!"); } mScaleMode = scaleMode; } @Override public Signature getSignature() { return new Signature() .addInputPort("scaleMode", Signature.PORT_OPTIONAL, FrameType.single(String.class)) .addInputPort("flip", Signature.PORT_OPTIONAL, FrameType.single(boolean.class)); } /** * Subclasses must override this method to bind their filter to the specified view. * * When this method is called, Filter implementations may assume that the graph is not * currently running. */ protected abstract void onBindToView(View view); /** * TODO: Document. */ protected RectF getTargetRect(Rect frameRect, Rect bufferRect) { RectF result = new RectF(); if (bufferRect.width() > 0 && bufferRect.height() > 0) { float frameAR = (float)frameRect.width() / frameRect.height(); float bufferAR = (float)bufferRect.width() / bufferRect.height(); float relativeAR = bufferAR / frameAR; switch (mScaleMode) { case SCALE_STRETCH: result.set(0f, 0f, 1f, 1f); break; case SCALE_FIT: if (relativeAR > 1.0f) { float x = 0.5f - 0.5f / relativeAR; float y = 0.0f; result.set(x, y, x + 1.0f / relativeAR, y + 1.0f); } else { float x = 0.0f; float y = 0.5f - 0.5f * relativeAR; result.set(x, y, x + 1.0f, y + relativeAR); } break; case SCALE_FILL: if (relativeAR > 1.0f) { float x = 0.0f; float y = 0.5f - 0.5f * relativeAR; result.set(x, y, x + 1.0f, y + relativeAR); } else { float x = 0.5f - 0.5f / relativeAR; float y = 0.0f; result.set(x, y, x + 1.0f / relativeAR, y + 1.0f); } break; } } return result; } protected void connectViewInputs(InputPort port) { if (port.getName().equals("scaleMode")) { port.bindToListener(mScaleModeListener); port.setAutoPullEnabled(true); } else if (port.getName().equals("flip")) { port.bindToFieldNamed("mFlipVertically"); port.setAutoPullEnabled(true); } } protected void setupShader(ImageShader shader, Rect frameRect, Rect outputRect) { shader.setTargetRect(getTargetRect(frameRect, outputRect)); shader.setClearsOutput(true); shader.setClearColor(mClearColor); if (mFlipVertically) { shader.setSourceRect(0f, 1f, 1f, -1f); } } private InputPort.FrameListener mScaleModeListener = new InputPort.FrameListener() { @Override public void onFrameReceived(InputPort port, Frame frame) { String scaleMode = (String)frame.asFrameValue().getValue(); if (!scaleMode.equals(mRequestedScaleMode)) { mRequestedScaleMode = scaleMode; if (scaleMode.equals("stretch")) { mScaleMode = SCALE_STRETCH; } else if (scaleMode.equals("fit")) { mScaleMode = SCALE_FIT; } else if (scaleMode.equals("fill")) { mScaleMode = SCALE_FILL; } else { throw new RuntimeException("Unknown scale-mode '" + scaleMode + "'!"); } } } }; }