/*
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
*
* 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 com.danxx.mdplayer.widget.media;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import com.danxx.mdplayer.R;
import java.lang.ref.WeakReference;
public final class MeasureHelper {
private WeakReference<View> mWeakView;
private int mVideoWidth;
private int mVideoHeight;
private int mVideoSarNum;
private int mVideoSarDen;
private int mVideoRotationDegree;
private int mMeasuredWidth;
private int mMeasuredHeight;
private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;
public MeasureHelper(View view) {
mWeakView = new WeakReference<View>(view);
}
public View getView() {
if (mWeakView == null)
return null;
return mWeakView.get();
}
public void setVideoSize(int videoWidth, int videoHeight) {
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
}
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
mVideoSarNum = videoSarNum;
mVideoSarDen = videoSarDen;
}
public void setVideoRotation(int videoRotationDegree) {
mVideoRotationDegree = videoRotationDegree;
}
/**
* Must be called by View.onMeasure(int, int)
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
// + MeasureSpec.toString(heightMeasureSpec) + ")");
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
int tempSpec = widthMeasureSpec;
widthMeasureSpec = heightMeasureSpec;
heightMeasureSpec = tempSpec;
}
int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
width = widthMeasureSpec;
height = heightMeasureSpec;
} else if (mVideoWidth > 0 && mVideoHeight > 0) {
int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
float displayAspectRatio;
switch (mCurrentAspectRatio) {
case IRenderView.AR_16_9_FIT_PARENT:
displayAspectRatio = 16.0f / 9.0f;
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
displayAspectRatio = 1.0f / displayAspectRatio;
break;
case IRenderView.AR_4_3_FIT_PARENT:
displayAspectRatio = 4.0f / 3.0f;
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
displayAspectRatio = 1.0f / displayAspectRatio;
break;
case IRenderView.AR_ASPECT_FIT_PARENT:
case IRenderView.AR_ASPECT_FILL_PARENT:
case IRenderView.AR_ASPECT_WRAP_CONTENT:
default:
displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
if (mVideoSarNum > 0 && mVideoSarDen > 0)
displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
break;
}
boolean shouldBeWider = displayAspectRatio > specAspectRatio;
switch (mCurrentAspectRatio) {
case IRenderView.AR_ASPECT_FIT_PARENT:
case IRenderView.AR_16_9_FIT_PARENT:
case IRenderView.AR_4_3_FIT_PARENT:
if (shouldBeWider) {
// too wide, fix width
width = widthSpecSize;
height = (int) (width / displayAspectRatio);
} else {
// too high, fix height
height = heightSpecSize;
width = (int) (height * displayAspectRatio);
}
break;
case IRenderView.AR_ASPECT_FILL_PARENT:
if (shouldBeWider) {
// not high enough, fix height
height = heightSpecSize;
width = (int) (height * displayAspectRatio);
} else {
// not wide enough, fix width
width = widthSpecSize;
height = (int) (width / displayAspectRatio);
}
break;
case IRenderView.AR_ASPECT_WRAP_CONTENT:
default:
if (shouldBeWider) {
// too wide, fix width
width = Math.min(mVideoWidth, widthSpecSize);
height = (int) (width / displayAspectRatio);
} else {
// too high, fix height
height = Math.min(mVideoHeight, heightSpecSize);
width = (int) (height * displayAspectRatio);
}
break;
}
} else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
// the size is fixed
width = widthSpecSize;
height = heightSpecSize;
// for compatibility, we adjust size based on aspect ratio
if (mVideoWidth * height < width * mVideoHeight) {
//Log.i("@@@", "image too wide, correcting");
width = height * mVideoWidth / mVideoHeight;
} else if (mVideoWidth * height > width * mVideoHeight) {
//Log.i("@@@", "image too tall, correcting");
height = width * mVideoHeight / mVideoWidth;
}
} else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
// only the width is fixed, adjust the height to match aspect ratio if possible
width = widthSpecSize;
height = width * mVideoHeight / mVideoWidth;
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
// couldn't match aspect ratio within the constraints
height = heightSpecSize;
}
} else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
// only the height is fixed, adjust the width to match aspect ratio if possible
height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
// couldn't match aspect ratio within the constraints
width = widthSpecSize;
}
} else {
// neither the width nor the height are fixed, try to use actual video size
width = mVideoWidth;
height = mVideoHeight;
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
// too tall, decrease both width and height
height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
}
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
// too wide, decrease both width and height
width = widthSpecSize;
height = width * mVideoHeight / mVideoWidth;
}
}
} else {
// no size yet, just adopt the given spec sizes
}
mMeasuredWidth = width;
mMeasuredHeight = height;
}
public int getMeasuredWidth() {
return mMeasuredWidth;
}
public int getMeasuredHeight() {
return mMeasuredHeight;
}
public void setAspectRatio(int aspectRatio) {
mCurrentAspectRatio = aspectRatio;
}
@NonNull
public static String getAspectRatioText(Context context, int aspectRatio) {
String text;
switch (aspectRatio) {
case IRenderView.AR_ASPECT_FIT_PARENT:
text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
break;
case IRenderView.AR_ASPECT_FILL_PARENT:
text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
break;
case IRenderView.AR_ASPECT_WRAP_CONTENT:
text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
break;
case IRenderView.AR_MATCH_PARENT:
text = context.getString(R.string.VideoView_ar_match_parent);
break;
case IRenderView.AR_16_9_FIT_PARENT:
text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
break;
case IRenderView.AR_4_3_FIT_PARENT:
text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
break;
default:
text = context.getString(R.string.N_A);
break;
}
return text;
}
}