NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java

780 lines
28 KiB
Java
Raw Normal View History

/*
2019-01-23 18:03:33 +01:00
* This is the source code of Telegram for Android v. 5.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
2019-01-23 18:03:33 +01:00
* Copyright Nikolai Kudashov, 2013-2018.
*/
package org.telegram.ui.Components;
import android.graphics.Bitmap;
2016-04-22 15:49:00 +02:00
import android.graphics.BitmapShader;
import android.graphics.Canvas;
2016-04-22 15:49:00 +02:00
import android.graphics.Matrix;
import android.graphics.Paint;
2020-01-23 07:15:40 +01:00
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
2016-04-22 15:49:00 +02:00
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.BitmapDrawable;
2019-12-31 14:08:08 +01:00
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import org.telegram.messenger.AndroidUtilities;
2019-03-03 21:40:48 +01:00
import org.telegram.messenger.AnimatedFileDrawableStream;
import org.telegram.messenger.DispatchQueue;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.FileLog;
2020-07-26 10:03:38 +02:00
import org.telegram.messenger.ImageLocation;
2019-03-03 21:40:48 +01:00
import org.telegram.tgnet.TLRPC;
import java.io.File;
2020-07-26 10:03:38 +02:00
import java.util.ArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
2017-03-31 01:58:05 +02:00
import java.util.concurrent.TimeUnit;
public class AnimatedFileDrawable extends BitmapDrawable implements Animatable {
2019-08-22 01:53:26 +02:00
private static native long createDecoder(String src, int[] params, int account, long streamFileSize, Object readCallback, boolean preview);
2018-07-30 04:07:02 +02:00
private static native void destroyDecoder(long ptr);
2019-03-03 21:40:48 +01:00
private static native void stopDecoder(long ptr);
private static native int getVideoFrame(long ptr, Bitmap bitmap, int[] params, int stride, boolean preview, float startTimeSeconds, float endTimeSeconds);
2019-08-22 01:53:26 +02:00
private static native void seekToMs(long ptr, long ms, boolean precise);
2020-07-26 10:03:38 +02:00
private static native int getFrameAtTime(long ptr, long ms, Bitmap bitmap, int[] data, int stride);
2019-03-03 21:40:48 +01:00
private static native void prepareToSeek(long ptr);
2019-12-31 14:08:08 +01:00
private static native void getVideoInfo(int sdkVersion, String src, int[] params);
2019-05-14 14:08:05 +02:00
2019-12-31 14:08:08 +01:00
public final static int PARAM_NUM_SUPPORTED_VIDEO_CODEC = 0;
2019-05-14 14:08:05 +02:00
public final static int PARAM_NUM_WIDTH = 1;
public final static int PARAM_NUM_HEIGHT = 2;
public final static int PARAM_NUM_BITRATE = 3;
public final static int PARAM_NUM_DURATION = 4;
public final static int PARAM_NUM_AUDIO_FRAME_SIZE = 5;
public final static int PARAM_NUM_VIDEO_FRAME_SIZE = 6;
public final static int PARAM_NUM_FRAMERATE = 7;
public final static int PARAM_NUM_ROTATION = 8;
2019-12-31 14:08:08 +01:00
public final static int PARAM_NUM_SUPPORTED_AUDIO_CODEC = 9;
public final static int PARAM_NUM_HAS_AUDIO = 10;
public final static int PARAM_NUM_COUNT = 11;
private long lastFrameTime;
private int lastTimeStamp;
private int invalidateAfter = 50;
2019-03-03 21:40:48 +01:00
private final int[] metaData = new int[5];
private Runnable loadFrameTask;
private Bitmap renderingBitmap;
2019-03-03 21:40:48 +01:00
private int renderingBitmapTime;
private Bitmap nextRenderingBitmap;
2019-03-03 21:40:48 +01:00
private int nextRenderingBitmapTime;
private Bitmap backgroundBitmap;
2019-03-03 21:40:48 +01:00
private int backgroundBitmapTime;
private boolean destroyWhenDone;
private boolean decoderCreated;
2017-07-08 18:32:04 +02:00
private boolean decodeSingleFrame;
private boolean singleFrameDecoded;
2020-07-26 10:03:38 +02:00
private boolean forceDecodeAfterNextFrame;
private File path;
2019-03-03 21:40:48 +01:00
private long streamFileSize;
private int currentAccount;
private boolean recycleWithSecond;
2019-03-03 21:40:48 +01:00
private volatile long pendingSeekTo = -1;
private volatile long pendingSeekToUI = -1;
private boolean pendingRemoveLoading;
private int pendingRemoveLoadingFramesReset;
private final Object sync = new Object();
2020-07-26 10:03:38 +02:00
private boolean invalidateParentViewWithSecond;
2017-03-31 01:58:05 +02:00
private long lastFrameDecodeTime;
private RectF actualDrawRect = new RectF();
2016-04-22 15:49:00 +02:00
private BitmapShader renderingShader;
private BitmapShader nextRenderingShader;
private BitmapShader backgroundShader;
2020-01-23 07:15:40 +01:00
private int[] roundRadius = new int[4];
private int[] roundRadiusBackup;
2016-04-22 15:49:00 +02:00
private Matrix shaderMatrix = new Matrix();
2020-01-23 07:15:40 +01:00
private Path roundPath = new Path();
private static float[] radii = new float[8];
2016-04-22 15:49:00 +02:00
private float scaleX = 1.0f;
private float scaleY = 1.0f;
private boolean applyTransformation;
private final android.graphics.Rect dstRect = new android.graphics.Rect();
private static final Handler uiHandler = new Handler(Looper.getMainLooper());
private volatile boolean isRunning;
private volatile boolean isRecycled;
2019-03-03 21:40:48 +01:00
public volatile long nativePtr;
private DispatchQueue decodeQueue;
private float startTime;
private float endTime;
2019-08-22 01:53:26 +02:00
private View parentView;
2020-07-26 10:03:38 +02:00
private ArrayList<View> secondParentViews = new ArrayList<>();
2019-03-03 21:40:48 +01:00
private AnimatedFileDrawableStream stream;
2019-05-14 14:08:05 +02:00
private boolean useSharedQueue;
private boolean invalidatePath = true;
2019-05-14 14:08:05 +02:00
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2, new ThreadPoolExecutor.DiscardPolicy());
2018-08-27 10:33:11 +02:00
protected final Runnable mInvalidateTask = () -> {
2020-07-26 10:03:38 +02:00
if (!secondParentViews.isEmpty()) {
for (int a = 0, N = secondParentViews.size(); a < N; a++) {
secondParentViews.get(a).invalidate();
}
}
if ((secondParentViews.isEmpty() || invalidateParentViewWithSecond) && parentView != null) {
2018-08-27 10:33:11 +02:00
parentView.invalidate();
}
};
2019-01-23 18:03:33 +01:00
private Runnable uiRunnableNoFrame = new Runnable() {
@Override
public void run() {
if (destroyWhenDone && nativePtr != 0) {
destroyDecoder(nativePtr);
nativePtr = 0;
}
if (nativePtr == 0) {
if (renderingBitmap != null) {
renderingBitmap.recycle();
renderingBitmap = null;
}
if (backgroundBitmap != null) {
backgroundBitmap.recycle();
backgroundBitmap = null;
}
2019-03-03 21:40:48 +01:00
if (decodeQueue != null) {
decodeQueue.recycle();
decodeQueue = null;
}
2019-01-23 18:03:33 +01:00
return;
}
loadFrameTask = null;
scheduleNextGetFrame();
}
};
private Runnable uiRunnable = new Runnable() {
@Override
public void run() {
if (destroyWhenDone && nativePtr != 0) {
destroyDecoder(nativePtr);
nativePtr = 0;
}
if (nativePtr == 0) {
2017-03-31 01:58:05 +02:00
if (renderingBitmap != null) {
renderingBitmap.recycle();
renderingBitmap = null;
}
if (backgroundBitmap != null) {
backgroundBitmap.recycle();
2016-04-22 15:49:00 +02:00
backgroundBitmap = null;
}
2019-03-03 21:40:48 +01:00
if (decodeQueue != null) {
decodeQueue.recycle();
decodeQueue = null;
}
return;
}
2019-03-03 21:40:48 +01:00
if (stream != null && pendingRemoveLoading) {
FileLoader.getInstance(currentAccount).removeLoadingVideo(stream.getDocument(), false, false);
}
if (pendingRemoveLoadingFramesReset <= 0) {
pendingRemoveLoading = true;
} else {
pendingRemoveLoadingFramesReset--;
}
2020-07-26 10:03:38 +02:00
if (!forceDecodeAfterNextFrame) {
singleFrameDecoded = true;
} else {
forceDecodeAfterNextFrame = false;
}
loadFrameTask = null;
nextRenderingBitmap = backgroundBitmap;
2019-03-03 21:40:48 +01:00
nextRenderingBitmapTime = backgroundBitmapTime;
2016-04-22 15:49:00 +02:00
nextRenderingShader = backgroundShader;
2016-10-11 13:57:01 +02:00
if (metaData[3] < lastTimeStamp) {
lastTimeStamp = startTime > 0 ? (int) (startTime * 1000) : 0;
}
2016-10-11 13:57:01 +02:00
if (metaData[3] - lastTimeStamp != 0) {
invalidateAfter = metaData[3] - lastTimeStamp;
}
2019-03-03 21:40:48 +01:00
if (pendingSeekToUI >= 0 && pendingSeekTo == -1) {
pendingSeekToUI = -1;
invalidateAfter = 0;
}
2016-10-11 13:57:01 +02:00
lastTimeStamp = metaData[3];
2020-07-26 10:03:38 +02:00
if (!secondParentViews.isEmpty()) {
for (int a = 0, N = secondParentViews.size(); a < N; a++) {
secondParentViews.get(a).invalidate();
}
}
if ((secondParentViews.isEmpty() || invalidateParentViewWithSecond) && parentView != null) {
parentView.invalidate();
}
2017-03-31 01:58:05 +02:00
scheduleNextGetFrame();
}
};
private Runnable loadFrameRunnable = new Runnable() {
@Override
public void run() {
if (!isRecycled) {
if (!decoderCreated && nativePtr == 0) {
2019-08-22 01:53:26 +02:00
nativePtr = createDecoder(path.getAbsolutePath(), metaData, currentAccount, streamFileSize, stream, false);
2020-03-30 14:00:09 +02:00
if (nativePtr != 0 && (metaData[0] > 3840 || metaData[1] > 3840)) {
destroyDecoder(nativePtr);
nativePtr = 0;
}
decoderCreated = true;
}
try {
2019-01-23 18:03:33 +01:00
if (nativePtr != 0 || metaData[0] == 0 || metaData[1] == 0) {
2019-03-03 21:40:48 +01:00
if (backgroundBitmap == null && metaData[0] > 0 && metaData[1] > 0) {
2019-01-23 18:03:33 +01:00
try {
backgroundBitmap = Bitmap.createBitmap(metaData[0], metaData[1], Bitmap.Config.ARGB_8888);
} catch (Throwable e) {
FileLog.e(e);
}
2020-01-23 07:15:40 +01:00
if (backgroundShader == null && backgroundBitmap != null && hasRoundRadius()) {
2019-01-23 18:03:33 +01:00
backgroundShader = new BitmapShader(backgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
}
2019-03-03 21:40:48 +01:00
boolean seekWas = false;
if (pendingSeekTo >= 0) {
metaData[3] = (int) pendingSeekTo;
long seekTo = pendingSeekTo;
synchronized(sync) {
pendingSeekTo = -1;
}
seekWas = true;
2019-05-14 14:08:05 +02:00
if (stream != null) {
stream.reset();
}
2019-08-22 01:53:26 +02:00
seekToMs(nativePtr, seekTo, true);
2019-03-03 21:40:48 +01:00
}
2019-01-23 18:03:33 +01:00
if (backgroundBitmap != null) {
lastFrameDecodeTime = System.currentTimeMillis();
if (getVideoFrame(nativePtr, backgroundBitmap, metaData, backgroundBitmap.getRowBytes(), false, startTime, endTime) == 0) {
2019-01-23 18:03:33 +01:00
AndroidUtilities.runOnUIThread(uiRunnableNoFrame);
return;
}
2019-03-03 21:40:48 +01:00
if (seekWas) {
lastTimeStamp = metaData[3];
}
backgroundBitmapTime = metaData[3];
2016-04-22 15:49:00 +02:00
}
2019-01-23 18:03:33 +01:00
} else {
AndroidUtilities.runOnUIThread(uiRunnableNoFrame);
return;
}
} catch (Throwable e) {
2017-03-31 01:58:05 +02:00
FileLog.e(e);
}
}
AndroidUtilities.runOnUIThread(uiRunnable);
}
};
2018-08-27 10:33:11 +02:00
private final Runnable mStartTask = () -> {
2020-07-26 10:03:38 +02:00
if (!secondParentViews.isEmpty()) {
for (int a = 0, N = secondParentViews.size(); a < N; a++) {
secondParentViews.get(a).invalidate();
}
}
if ((secondParentViews.isEmpty() || invalidateParentViewWithSecond) && parentView != null) {
2018-08-27 10:33:11 +02:00
parentView.invalidate();
}
};
2020-07-26 10:03:38 +02:00
public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, TLRPC.Document document, ImageLocation location, Object parentObject, long seekTo, int account, boolean preview) {
path = file;
2019-03-03 21:40:48 +01:00
streamFileSize = streamSize;
currentAccount = account;
2020-07-26 10:03:38 +02:00
getPaint().setFlags(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
if (streamSize != 0 && (document != null || location != null)) {
stream = new AnimatedFileDrawableStream(document, location, parentObject, account, preview);
2019-03-03 21:40:48 +01:00
}
if (createDecoder) {
2019-08-22 01:53:26 +02:00
nativePtr = createDecoder(file.getAbsolutePath(), metaData, currentAccount, streamFileSize, stream, preview);
2020-03-30 14:00:09 +02:00
if (nativePtr != 0 && (metaData[0] > 3840 || metaData[1] > 3840)) {
destroyDecoder(nativePtr);
nativePtr = 0;
}
decoderCreated = true;
}
2020-07-26 10:03:38 +02:00
if (seekTo != 0) {
seekTo(seekTo, false);
}
}
2019-08-22 01:53:26 +02:00
public Bitmap getFrameAtTime(long ms) {
2020-07-26 10:03:38 +02:00
return getFrameAtTime(ms, false);
}
public Bitmap getFrameAtTime(long ms, boolean precise) {
2019-08-22 01:53:26 +02:00
if (!decoderCreated || nativePtr == 0) {
return null;
}
if (stream != null) {
stream.cancel(false);
stream.reset();
}
2020-07-26 10:03:38 +02:00
if (!precise) {
seekToMs(nativePtr, ms, precise);
}
2019-08-22 01:53:26 +02:00
if (backgroundBitmap == null) {
backgroundBitmap = Bitmap.createBitmap(metaData[0], metaData[1], Bitmap.Config.ARGB_8888);
}
2020-07-26 10:03:38 +02:00
int result;
if (precise) {
result = getFrameAtTime(nativePtr, ms, backgroundBitmap, metaData, backgroundBitmap.getRowBytes());
} else {
result = getVideoFrame(nativePtr, backgroundBitmap, metaData, backgroundBitmap.getRowBytes(), true, 0, 0);
}
2019-08-22 01:53:26 +02:00
return result != 0 ? backgroundBitmap : null;
}
public void setParentView(View view) {
2019-03-03 21:40:48 +01:00
if (parentView != null) {
return;
}
parentView = view;
}
2020-07-26 10:03:38 +02:00
public void setInvalidateParentViewWithSecond(boolean value) {
invalidateParentViewWithSecond = value;
}
public void addSecondParentView(View view) {
if (view == null || secondParentViews.contains(view)) {
return;
2020-01-23 07:15:40 +01:00
}
2020-07-26 10:03:38 +02:00
secondParentViews.add(view);
}
public void removeSecondParentView(View view) {
secondParentViews.remove(view);
if (secondParentViews.isEmpty()) {
if (recycleWithSecond) {
recycle();
} else {
if (roundRadiusBackup != null) {
setRoundRadius(roundRadiusBackup);
}
}
}
}
2017-07-08 18:32:04 +02:00
public void setAllowDecodeSingleFrame(boolean value) {
decodeSingleFrame = value;
if (decodeSingleFrame) {
scheduleNextGetFrame();
}
}
2019-03-03 21:40:48 +01:00
public void seekTo(long ms, boolean removeLoading) {
2020-07-26 10:03:38 +02:00
seekTo(ms, removeLoading, false);
}
public void seekTo(long ms, boolean removeLoading, boolean force) {
2019-03-03 21:40:48 +01:00
synchronized (sync) {
pendingSeekTo = ms;
pendingSeekToUI = ms;
2020-07-26 10:03:38 +02:00
if (nativePtr != 0) {
prepareToSeek(nativePtr);
}
2019-03-03 21:40:48 +01:00
if (decoderCreated && stream != null) {
stream.cancel(removeLoading);
pendingRemoveLoading = removeLoading;
pendingRemoveLoadingFramesReset = pendingRemoveLoading ? 0 : 10;
}
2020-07-26 10:03:38 +02:00
if (force && decodeSingleFrame) {
singleFrameDecoded = false;
if (loadFrameTask == null) {
scheduleNextGetFrame();
} else {
forceDecodeAfterNextFrame = true;
}
}
2019-03-03 21:40:48 +01:00
}
}
public void recycle() {
2020-07-26 10:03:38 +02:00
if (!secondParentViews.isEmpty()) {
recycleWithSecond = true;
return;
}
isRunning = false;
isRecycled = true;
if (loadFrameTask == null) {
if (nativePtr != 0) {
destroyDecoder(nativePtr);
nativePtr = 0;
}
2017-03-31 01:58:05 +02:00
if (renderingBitmap != null) {
renderingBitmap.recycle();
renderingBitmap = null;
}
if (nextRenderingBitmap != null) {
nextRenderingBitmap.recycle();
nextRenderingBitmap = null;
}
2019-03-03 21:40:48 +01:00
if (decodeQueue != null) {
decodeQueue.recycle();
decodeQueue = null;
}
} else {
destroyWhenDone = true;
}
2019-03-03 21:40:48 +01:00
if (stream != null) {
stream.cancel(true);
}
}
2019-08-22 01:53:26 +02:00
public void resetStream(boolean stop) {
if (stream != null) {
stream.cancel(true);
}
if (nativePtr != 0) {
if (stop) {
stopDecoder(nativePtr);
} else {
prepareToSeek(nativePtr);
}
}
}
protected static void runOnUiThread(Runnable task) {
if (Looper.myLooper() == uiHandler.getLooper()) {
task.run();
} else {
uiHandler.post(task);
}
}
2019-05-14 14:08:05 +02:00
public void setUseSharedQueue(boolean value) {
useSharedQueue = value;
}
@Override
protected void finalize() throws Throwable {
try {
recycle();
} finally {
super.finalize();
}
}
@Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
@Override
public void start() {
if (isRunning) {
return;
}
isRunning = true;
2017-03-31 01:58:05 +02:00
scheduleNextGetFrame();
runOnUiThread(mStartTask);
}
2019-03-03 21:40:48 +01:00
public float getCurrentProgress() {
if (metaData[4] == 0) {
return 0;
}
if (pendingSeekToUI >= 0) {
return pendingSeekToUI / (float) metaData[4];
}
return metaData[3] / (float) metaData[4];
}
public int getCurrentProgressMs() {
if (pendingSeekToUI >= 0) {
return (int) pendingSeekToUI;
}
return nextRenderingBitmapTime != 0 ? nextRenderingBitmapTime : renderingBitmapTime;
}
public int getDurationMs() {
return metaData[4];
}
2016-04-22 15:49:00 +02:00
private void scheduleNextGetFrame() {
2017-07-08 18:32:04 +02:00
if (loadFrameTask != null || nativePtr == 0 && decoderCreated || destroyWhenDone || !isRunning && (!decodeSingleFrame || decodeSingleFrame && singleFrameDecoded)) {
return;
}
2017-03-31 01:58:05 +02:00
long ms = 0;
if (lastFrameDecodeTime != 0) {
ms = Math.min(invalidateAfter, Math.max(0, invalidateAfter - (System.currentTimeMillis() - lastFrameDecodeTime)));
}
2019-05-14 14:08:05 +02:00
if (useSharedQueue) {
executor.schedule(loadFrameTask = loadFrameRunnable, ms, TimeUnit.MILLISECONDS);
} else {
2019-03-03 21:40:48 +01:00
if (decodeQueue == null) {
decodeQueue = new DispatchQueue("decodeQueue" + this);
}
decodeQueue.postRunnable(loadFrameTask = loadFrameRunnable, ms);
}
}
2019-05-14 14:08:05 +02:00
public boolean isLoadingStream() {
return stream != null && stream.isWaitingForLoad();
}
@Override
public void stop() {
isRunning = false;
}
@Override
public boolean isRunning() {
return isRunning;
}
@Override
public int getIntrinsicHeight() {
2019-01-23 18:03:33 +01:00
int height = decoderCreated ? (metaData[2] == 90 || metaData[2] == 270 ? metaData[0] : metaData[1]) : 0;
if (height == 0) {
return AndroidUtilities.dp(100);
}
return height;
}
@Override
public int getIntrinsicWidth() {
2019-01-23 18:03:33 +01:00
int width = decoderCreated ? (metaData[2] == 90 || metaData[2] == 270 ? metaData[1] : metaData[0]) : 0;
if (width == 0) {
return AndroidUtilities.dp(100);
}
return width;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
applyTransformation = true;
}
@Override
public void draw(Canvas canvas) {
if (nativePtr == 0 && decoderCreated || destroyWhenDone) {
return;
}
2017-03-31 01:58:05 +02:00
long now = System.currentTimeMillis();
if (isRunning) {
if (renderingBitmap == null && nextRenderingBitmap == null) {
2016-04-22 15:49:00 +02:00
scheduleNextGetFrame();
2019-03-03 21:40:48 +01:00
} else if (nextRenderingBitmap != null && (renderingBitmap == null || Math.abs(now - lastFrameTime) >= invalidateAfter)) {
renderingBitmap = nextRenderingBitmap;
renderingBitmapTime = nextRenderingBitmapTime;
renderingShader = nextRenderingShader;
nextRenderingBitmap = null;
nextRenderingBitmapTime = 0;
nextRenderingShader = null;
lastFrameTime = now;
}
2017-07-08 18:32:04 +02:00
} else if (!isRunning && decodeSingleFrame && Math.abs(now - lastFrameTime) >= invalidateAfter && nextRenderingBitmap != null) {
renderingBitmap = nextRenderingBitmap;
2019-03-03 21:40:48 +01:00
renderingBitmapTime = nextRenderingBitmapTime;
2017-07-08 18:32:04 +02:00
renderingShader = nextRenderingShader;
nextRenderingBitmap = null;
2019-03-03 21:40:48 +01:00
nextRenderingBitmapTime = 0;
2017-07-08 18:32:04 +02:00
nextRenderingShader = null;
lastFrameTime = now;
}
if (renderingBitmap != null) {
if (applyTransformation) {
2016-10-11 13:57:01 +02:00
int bitmapW = renderingBitmap.getWidth();
int bitmapH = renderingBitmap.getHeight();
if (metaData[2] == 90 || metaData[2] == 270) {
int temp = bitmapW;
bitmapW = bitmapH;
bitmapH = temp;
}
dstRect.set(getBounds());
2016-10-11 13:57:01 +02:00
scaleX = (float) dstRect.width() / bitmapW;
scaleY = (float) dstRect.height() / bitmapH;
applyTransformation = false;
}
2020-01-23 07:15:40 +01:00
if (hasRoundRadius()) {
2016-04-22 15:49:00 +02:00
float scale = Math.max(scaleX, scaleY);
if (renderingShader == null) {
renderingShader = new BitmapShader(backgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
2019-05-14 14:08:05 +02:00
Paint paint = getPaint();
paint.setShader(renderingShader);
2016-04-22 15:49:00 +02:00
shaderMatrix.reset();
2019-05-14 14:08:05 +02:00
shaderMatrix.setTranslate(dstRect.left, dstRect.top);
if (metaData[2] == 90) {
shaderMatrix.preRotate(90);
shaderMatrix.preTranslate(0, -dstRect.width());
} else if (metaData[2] == 180) {
shaderMatrix.preRotate(180);
shaderMatrix.preTranslate(-dstRect.width(), -dstRect.height());
} else if (metaData[2] == 270) {
shaderMatrix.preRotate(270);
shaderMatrix.preTranslate(-dstRect.height(), 0);
}
shaderMatrix.preScale(scaleX, scaleY);
2016-04-22 15:49:00 +02:00
renderingShader.setLocalMatrix(shaderMatrix);
if (invalidatePath) {
invalidatePath = false;
for (int a = 0; a < roundRadius.length; a++) {
radii[a * 2] = roundRadius[a];
radii[a * 2 + 1] = roundRadius[a];
}
roundPath.reset();
roundPath.addRoundRect(actualDrawRect, radii, Path.Direction.CW);
roundPath.close();
2020-01-23 07:15:40 +01:00
}
canvas.drawPath(roundPath, paint);
2016-04-22 15:49:00 +02:00
} else {
canvas.translate(dstRect.left, dstRect.top);
2016-10-11 13:57:01 +02:00
if (metaData[2] == 90) {
canvas.rotate(90);
canvas.translate(0, -dstRect.width());
} else if (metaData[2] == 180) {
canvas.rotate(180);
canvas.translate(-dstRect.width(), -dstRect.height());
} else if (metaData[2] == 270) {
canvas.rotate(270);
canvas.translate(-dstRect.height(), 0);
}
2016-04-22 15:49:00 +02:00
canvas.scale(scaleX, scaleY);
canvas.drawBitmap(renderingBitmap, 0, 0, getPaint());
}
if (isRunning) {
2017-03-31 01:58:05 +02:00
long timeToNextFrame = Math.max(1, invalidateAfter - (now - lastFrameTime) - 17);
uiHandler.removeCallbacks(mInvalidateTask);
uiHandler.postDelayed(mInvalidateTask, Math.min(timeToNextFrame, invalidateAfter));
}
}
}
@Override
public int getMinimumHeight() {
2019-01-23 18:03:33 +01:00
int height = decoderCreated ? (metaData[2] == 90 || metaData[2] == 270 ? metaData[0] : metaData[1]) : 0;
if (height == 0) {
return AndroidUtilities.dp(100);
}
return height;
}
@Override
public int getMinimumWidth() {
2019-01-23 18:03:33 +01:00
int width = decoderCreated ? (metaData[2] == 90 || metaData[2] == 270 ? metaData[1] : metaData[0]) : 0;
if (width == 0) {
return AndroidUtilities.dp(100);
}
return width;
}
2019-03-03 21:40:48 +01:00
public Bitmap getRenderingBitmap() {
return renderingBitmap;
}
public Bitmap getNextRenderingBitmap() {
return nextRenderingBitmap;
}
public Bitmap getBackgroundBitmap() {
return backgroundBitmap;
}
public Bitmap getAnimatedBitmap() {
if (renderingBitmap != null) {
return renderingBitmap;
} else if (nextRenderingBitmap != null) {
return nextRenderingBitmap;
}
return null;
}
2019-05-14 14:08:05 +02:00
public void setActualDrawRect(float x, float y, float width, float height) {
float bottom = y + height;
float right = x + width;
if (actualDrawRect.left != x || actualDrawRect.top != y || actualDrawRect.right != right || actualDrawRect.bottom != bottom) {
actualDrawRect.set(x, y, right, bottom);
invalidatePath = true;
}
2017-03-31 01:58:05 +02:00
}
2020-01-23 07:15:40 +01:00
public void setRoundRadius(int[] value) {
2020-07-26 10:03:38 +02:00
if (!secondParentViews.isEmpty()) {
2020-01-23 07:15:40 +01:00
if (roundRadiusBackup == null) {
roundRadiusBackup = new int[4];
}
System.arraycopy(roundRadius, 0, roundRadiusBackup, 0, roundRadiusBackup.length);
}
for (int i = 0; i < 4; i++) {
2020-07-26 10:03:38 +02:00
if (!invalidatePath && value[i] != roundRadius[i]) {
invalidatePath = true;
}
roundRadius[i] = value[i];
}
2016-04-22 15:49:00 +02:00
}
2020-01-23 07:15:40 +01:00
private boolean hasRoundRadius() {
for (int a = 0; a < roundRadius.length; a++) {
if (roundRadius[a] != 0) {
return true;
}
}
return false;
}
public boolean hasBitmap() {
return nativePtr != 0 && (renderingBitmap != null || nextRenderingBitmap != null);
}
2016-10-11 13:57:01 +02:00
public int getOrientation() {
return metaData[2];
}
public AnimatedFileDrawable makeCopy() {
2019-03-03 21:40:48 +01:00
AnimatedFileDrawable drawable;
if (stream != null) {
2020-07-26 10:03:38 +02:00
drawable = new AnimatedFileDrawable(path, false, streamFileSize, stream.getDocument(), stream.getLocation(), stream.getParentObject(), pendingSeekToUI, currentAccount, stream != null && stream.isPreview());
2019-03-03 21:40:48 +01:00
} else {
2020-07-26 10:03:38 +02:00
drawable = new AnimatedFileDrawable(path, false, streamFileSize, null, null, null, pendingSeekToUI, currentAccount, stream != null && stream.isPreview());
2019-03-03 21:40:48 +01:00
}
drawable.metaData[0] = metaData[0];
drawable.metaData[1] = metaData[1];
return drawable;
}
2019-12-31 14:08:08 +01:00
public static void getVideoInfo(String src, int[] params) {
getVideoInfo(Build.VERSION.SDK_INT, src, params);
}
public void setStartEndTime(long startTime, long endTime) {
this.startTime = startTime / 1000f;
this.endTime = endTime / 1000f;
if (getCurrentProgressMs() < startTime) {
seekTo(startTime, true);
}
}
public long getStartTime() {
return (long) (startTime * 1000);
}
}