summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLikai Ding <likaid@codeaurora.org>2013-08-13 14:07:48 +0800
committerLuK1337 <priv.luk@gmail.com>2019-10-21 21:40:56 +0200
commitb2e91006f4ba715c7d7fe97dee4181cb56127090 (patch)
treec26f5e06368e35489f955823db3d1c534d26641b
parent518f760ae7493f09ecab528487c6c81f709dee0a (diff)
downloadandroid_packages_apps_Gallery2-b2e91006f4ba715c7d7fe97dee4181cb56127090.tar.gz
android_packages_apps_Gallery2-b2e91006f4ba715c7d7fe97dee4181cb56127090.tar.bz2
android_packages_apps_Gallery2-b2e91006f4ba715c7d7fe97dee4181cb56127090.zip
Gallery2: Support GIF animation
This change implements a Java GIF decoder. Change-Id: I227cef76cbacd66b7e87bc59b4f07d518b70a859 Signed-off-by: Xiaojing Zhang <zhangx@codeaurora.org>
-rwxr-xr-xAndroidManifest.xml10
-rw-r--r--res/layout/view_gif_image.xml17
-rwxr-xr-xsrc/com/android/gallery3d/app/PhotoPage.java10
-rwxr-xr-xsrc/com/android/gallery3d/ui/PhotoView.java4
-rwxr-xr-xsrc/com/android/gallery3d/util/GIFView.java207
-rwxr-xr-xsrc/com/android/gallery3d/util/GifDecoder.java6
-rwxr-xr-xsrc/com/android/gallery3d/util/ViewGifImage.java67
7 files changed, 315 insertions, 6 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 60211c7ac..faa1e0f59 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -307,6 +307,16 @@
android:theme="@style/Theme.Gallery"
android:configChanges="orientation|keyboardHidden|screenSize" />
+ <activity android:name="com.android.gallery3d.util.ViewGifImage"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+ android:configChanges="orientation|keyboardHidden|screenSize|keyboard|navigation">
+ <intent-filter>
+ <action android:name="com.android.gallery3d.VIEW_GIF" />
+ <data android:mimeType="image/gif" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<provider android:name="com.android.gallery3d.provider.GalleryProvider"
android:syncable="false"
android:grantUriPermissions="true"
diff --git a/res/layout/view_gif_image.xml b/res/layout/view_gif_image.xml
new file mode 100644
index 000000000..976549a6b
--- /dev/null
+++ b/res/layout/view_gif_image.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/image_absoluteLayout"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+ <ImageView android:id="@+id/image_display_area"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:clickable="true">
+ </ImageView>
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 90e47ab82..793b1d75a 100755
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -81,6 +81,7 @@ import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.util.GDepth;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.UsageStatistics;
+import com.android.gallery3d.util.ViewGifImage;
import java.util.List;
import java.util.Locale;
@@ -1343,6 +1344,10 @@ public abstract class PhotoPage extends ActivityState implements
// item is not ready or it is camera preview, ignore
return;
}
+ if (item.getMimeType().equals(MediaItem.MIME_TYPE_GIF)) {
+ viewAnimateGif((Activity) mActivity, item.getContentUri());
+ return;
+ }
int supported = item.getSupportedOperations();
boolean playVideo = ((supported & MediaItem.SUPPORT_PLAY) != 0);
@@ -1842,4 +1847,9 @@ public abstract class PhotoPage extends ActivityState implements
m3DButton.refresh();
}
}
+
+ private static void viewAnimateGif(Activity activity, Uri uri) {
+ Intent intent = new Intent(ViewGifImage.VIEW_GIF_ACTION, uri);
+ activity.startActivity(intent);
+ }
}
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index e3b72eead..246e647b4 100755
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -734,7 +734,7 @@ public class PhotoView extends GLView {
canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
//Full pic locates at index 0 of the array in PhotoDataAdapter
- if (mModel.isVideo(0)) {
+ if (mModel.isVideo(0) || mModel.isGif(0)) {
drawVideoPlayIcon(canvas, s);
}
if (mLoadingState == Model.LOADING_FAIL ) {
@@ -864,7 +864,7 @@ public class PhotoView extends GLView {
invalidate();
}
int s = Math.min(drawW, drawH);
- if (mModel.isVideo(mIndex)) {
+ if (mModel.isVideo(mIndex) || mModel.isGif(mIndex)) {
drawVideoPlayIcon(canvas, s);
}
diff --git a/src/com/android/gallery3d/util/GIFView.java b/src/com/android/gallery3d/util/GIFView.java
new file mode 100755
index 000000000..7bcc52d1b
--- /dev/null
+++ b/src/com/android/gallery3d/util/GIFView.java
@@ -0,0 +1,207 @@
+package com.android.gallery3d.util;
+
+import org.codeaurora.gallery.R;
+
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.res.AssetManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+
+public class GIFView extends ImageView implements GifAction {
+
+ private static final String TAG = "GIFView";
+ private static final float SCALE_LIMIT = 4;
+ private static final long FRAME_DELAY = 200; //milliseconds
+
+ private GifDecoder mGifDecoder = null;
+ private Bitmap mCurrentImage = null;
+ private DrawThread mDrawThread = null;
+
+ private Uri mUri;
+ private Context mContext;
+
+ public GIFView(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ public boolean setDrawable(Uri uri) {
+ if (null == uri) {
+ return false;
+ }
+ mUri = uri;
+
+ InputStream is = getInputStream(uri);
+ if (is == null || (getFileSize (is) == 0)) {
+ return false;
+ }
+ startDecode(is);
+ return true;
+ }
+
+ private int getFileSize (InputStream is) {
+ if(is == null) return 0;
+
+ int size = 0;
+ try {
+ if (is instanceof FileInputStream) {
+ FileInputStream f = (FileInputStream) is;
+ size = (int) f.getChannel().size();
+ } else {
+ while (-1 != is.read()) {
+ size++;
+ }
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "catch exception:" + e);
+ }
+
+ return size;
+
+ }
+
+ private InputStream getInputStream (Uri uri) {
+ ContentResolver cr = mContext.getContentResolver();
+ InputStream input = null;
+ try {
+ input = cr.openInputStream(uri);
+ } catch (IOException e) {
+ Log.e(TAG, "catch exception:" + e);
+ }
+ return input;
+ }
+
+ private void startDecode(InputStream is) {
+ freeGifDecoder();
+ mGifDecoder = new GifDecoder(is, this);
+ mGifDecoder.start();
+ }
+
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mGifDecoder == null) {
+ return;
+ }
+
+ if (mCurrentImage == null) {
+ mCurrentImage = mGifDecoder.getImage();
+ }
+ if (mCurrentImage == null) {
+ // if this gif can not be displayed, just try to show it as jpg by parsing mUri
+ setImageURI(mUri);
+ return;
+ }
+ setImageURI(null);
+ int saveCount = canvas.getSaveCount();
+ canvas.save();
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+ Rect sRect = null;
+ Rect dRect = null;
+
+ int imageHeight = mCurrentImage.getHeight();
+ int imageWidth = mCurrentImage.getWidth();
+
+ int displayHeight = ViewGifImage.mDM.heightPixels;
+ int displayWidth = ViewGifImage.mDM.widthPixels;
+
+ int width, height;
+ if (imageWidth >= displayWidth || imageHeight >= displayHeight) {
+ // scale-down the image
+ if (imageWidth * displayHeight > displayWidth * imageHeight) {
+ width = displayWidth;
+ height = (imageHeight * width) / imageWidth;
+ } else {
+ height = displayHeight;
+ width = (imageWidth * height) / imageHeight;
+ }
+ } else {
+ // scale-up the image
+ float scale = Math.min(SCALE_LIMIT, Math.min(displayWidth / (float) imageWidth,
+ displayHeight / (float) imageHeight));
+ width = (int) (imageWidth * scale);
+ height = (int) (imageHeight * scale);
+ }
+ dRect = new Rect((displayWidth - width) / 2, (displayHeight - height) / 2,
+ (displayWidth + width) / 2, (displayHeight + height) / 2);
+ canvas.drawBitmap(mCurrentImage, sRect, dRect, null);
+ canvas.restoreToCount(saveCount);
+ }
+
+ public void parseOk(boolean parseStatus, int frameIndex) {
+ if (parseStatus) {
+ //indicates the start of a new GIF
+ if (mGifDecoder != null && frameIndex == -1
+ && mGifDecoder.getFrameCount() > 1) {
+ if (mDrawThread != null) {
+ mDrawThread = null;
+ }
+ mDrawThread = new DrawThread();
+ mDrawThread.start();
+ }
+ } else {
+ Log.e(TAG, "parse error");
+ }
+ }
+
+ private Handler mRedrawHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ invalidate();
+ }
+ };
+
+ private class DrawThread extends Thread {
+ public void run() {
+ while (true) {
+ if (mGifDecoder == null || !isShown() || mRedrawHandler == null) {
+ break;
+ }
+ GifFrame frame = mGifDecoder.next();
+ mCurrentImage = frame.mImage;
+
+ Message msg = mRedrawHandler.obtainMessage();
+ mRedrawHandler.sendMessage(msg);
+ try {
+ Thread.sleep(getDelay(frame));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "catch exception:" + e);
+ }
+ }
+ }
+
+ }
+
+ private long getDelay (GifFrame frame) {
+ //in milliseconds
+ return frame.mDelayInMs == 0 ? FRAME_DELAY : frame.mDelayInMs;
+ }
+
+ private void freeGifDecoder () {
+ if (mGifDecoder != null) {
+ mGifDecoder.free();
+ mGifDecoder = null;
+ }
+
+ }
+
+ public void freeMemory() {
+ if (mDrawThread != null) {
+ mDrawThread = null;
+ }
+ freeGifDecoder();
+ }
+}
diff --git a/src/com/android/gallery3d/util/GifDecoder.java b/src/com/android/gallery3d/util/GifDecoder.java
index 1342355e6..5b076238d 100755
--- a/src/com/android/gallery3d/util/GifDecoder.java
+++ b/src/com/android/gallery3d/util/GifDecoder.java
@@ -7,7 +7,7 @@ import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
-public class GifDecoder {
+public class GifDecoder extends Thread {
public static final int STATUS_PARSING = 0;
public static final int STATUS_FORMAT_ERROR = 1;
@@ -71,16 +71,14 @@ public class GifDecoder {
public GifDecoder(byte[] data, GifAction act) {
mGifData = data;
mGifAction = act;
- startDecoder();
}
public GifDecoder(InputStream is, GifAction act) {
mIS = is;
mGifAction = act;
- startDecoder();
}
- public void startDecoder() {
+ public void run() {
if (mIS != null) {
readStream();
} else if (mGifData != null) {
diff --git a/src/com/android/gallery3d/util/ViewGifImage.java b/src/com/android/gallery3d/util/ViewGifImage.java
new file mode 100755
index 000000000..a85af5638
--- /dev/null
+++ b/src/com/android/gallery3d/util/ViewGifImage.java
@@ -0,0 +1,67 @@
+package com.android.gallery3d.util;
+
+import org.codeaurora.gallery.R;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+public class ViewGifImage extends Activity {
+ private static final String TAG = "ViewGifImage";
+ public static final String VIEW_GIF_ACTION = "com.android.gallery3d.VIEW_GIF";
+
+ public static DisplayMetrics mDM;
+
+ private ImageView mGifView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_gif_image);
+ mDM = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(mDM);
+ if (getIntent().getAction() != null
+ && getIntent().getAction().equals(VIEW_GIF_ACTION)) {
+ Uri gifUri = getIntent().getData();
+ showGifPicture(gifUri);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mGifView != null && mGifView instanceof GIFView) {
+ ((GIFView) mGifView).freeMemory();
+ mGifView = null;
+ }
+ super.onDestroy();
+ }
+
+ private void showGifPicture(Uri uri) {
+ mGifView = new GIFView(this);
+ ((LinearLayout) findViewById(R.id.image_absoluteLayout)).addView(mGifView,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ if (((GIFView) mGifView).setDrawable(uri)) return;
+
+ finish();
+
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ getWindowManager().getDefaultDisplay().getMetrics(mDM);
+ super.onConfigurationChanged(newConfig);
+ }
+}