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