Gallery2: support GIF animation
This change implements a Java GIF decoder.
Change-Id: I72b6e8eb25572bba77a2a46e1754d8db8c47a0cc
Signed-off-by: Xiaojing Zhang <zhangx@codeaurora.org>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 95beee1..5204889 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -297,6 +297,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
old mode 100755
new mode 100644
index bceb16f..976549a
--- a/res/layout/view_gif_image.xml
+++ b/res/layout/view_gif_image.xml
@@ -1,46 +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_dispaly_area"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:clickable="true">
- </ImageView>
- </LinearLayout>
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-<!--
- <ImageView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/arrow_left"
- android:layout_centerVertical="true"
- android:clickable="true"
- android:src="@drawable/arrow_left">
- </ImageView>
-
- <ImageView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/arrow_right"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:clickable="true"
- android:src="@drawable/arrow_right">
- </ImageView>
-
- <TextView android:id="@+id/tv_count"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:textSize="18sp"
- android:textColor="#ffffffff"
- android:layout_marginTop="5dip"
- android:background="@drawable/text_frame">
- </TextView>
--->
+ <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/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
old mode 100644
new mode 100755
index fd3a7cf..40967a1
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -512,6 +512,13 @@
}
@Override
+ public boolean isGif(int offset) {
+ MediaItem item = getItem(mCurrentIndex + offset);
+ return (item != null) &&
+ MediaItem.MIME_TYPE_GIF.equalsIgnoreCase(item.getMimeType());
+ }
+
+ @Override
public boolean isDeletable(int offset) {
MediaItem item = getItem(mCurrentIndex + offset);
return (item == null)
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
old mode 100644
new mode 100755
index 915fdab..667ab38
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -72,6 +72,7 @@
import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.UsageStatistics;
+import com.android.gallery3d.util.ViewGifImage;
public abstract class PhotoPage extends ActivityState implements
PhotoView.Listener, AppBridge.Server, ShareActionProvider.OnShareTargetSelectedListener,
@@ -1136,6 +1137,10 @@
// 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);
@@ -1530,4 +1535,8 @@
}
}
+ 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/app/SinglePhotoDataAdapter.java b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
old mode 100644
new mode 100755
index 00f2fe7..fe39e4e
--- a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
@@ -227,6 +227,11 @@
}
@Override
+ public boolean isGif(int offset) {
+ return MediaItem.MIME_TYPE_GIF.equalsIgnoreCase(mItem.getMimeType());
+ }
+
+ @Override
public boolean isDeletable(int offset) {
return (mItem.getSupportedOperations() & MediaItem.SUPPORT_DELETE) != 0;
}
diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java
index 59ea865..92ac88d 100644
--- a/src/com/android/gallery3d/data/MediaItem.java
+++ b/src/com/android/gallery3d/data/MediaItem.java
@@ -37,6 +37,7 @@
public static final int IMAGE_ERROR = -1;
public static final String MIME_TYPE_JPEG = "image/jpeg";
+ public static final String MIME_TYPE_GIF = "image/gif";
private static final int BYTESBUFFE_POOL_SIZE = 4;
private static final int BYTESBUFFER_SIZE = 200 * 1024;
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
old mode 100644
new mode 100755
index 7afa203..5647e36
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -93,6 +93,9 @@
// Returns true if the item is a Video.
public boolean isVideo(int offset);
+ // Returns true if the item is a Gif.
+ public boolean isGif(int offset);
+
// Returns true if the item can be deleted.
public boolean isDeletable(int offset);
@@ -594,7 +597,6 @@
private boolean mIsCamera;
private boolean mIsPanorama;
private boolean mIsStaticCamera;
- private boolean mIsVideo;
private boolean mIsDeletable;
private int mLoadingState = Model.LOADING_INIT;
private Size mSize = new Size();
@@ -607,7 +609,6 @@
mIsCamera = mModel.isCamera(0);
mIsPanorama = mModel.isPanorama(0);
mIsStaticCamera = mModel.isStaticCamera(0);
- mIsVideo = mModel.isVideo(0);
mIsDeletable = mModel.isDeletable(0);
mLoadingState = mModel.getLoadingState(0);
setScreenNail(mModel.getScreenNail(0));
@@ -732,8 +733,11 @@
// Draw the play video icon and the message.
canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
- if (mIsVideo) drawVideoPlayIcon(canvas, s);
- if (mLoadingState == Model.LOADING_FAIL) {
+ //Full pic locates at index 0 of the array in PhotoDataAdapter
+ if (mModel.isVideo(0) || mModel.isGif(0)) {
+ drawVideoPlayIcon(canvas, s);
+ }
+ if (mLoadingState == Model.LOADING_FAIL ) {
drawLoadingFailMessage(canvas);
}
@@ -775,7 +779,6 @@
private boolean mIsCamera;
private boolean mIsPanorama;
private boolean mIsStaticCamera;
- private boolean mIsVideo;
private boolean mIsDeletable;
private int mLoadingState = Model.LOADING_INIT;
private Size mSize = new Size();
@@ -789,7 +792,6 @@
mIsCamera = mModel.isCamera(mIndex);
mIsPanorama = mModel.isPanorama(mIndex);
mIsStaticCamera = mModel.isStaticCamera(mIndex);
- mIsVideo = mModel.isVideo(mIndex);
mIsDeletable = mModel.isDeletable(mIndex);
mLoadingState = mModel.getLoadingState(mIndex);
setScreenNail(mModel.getScreenNail(mIndex));
@@ -853,8 +855,10 @@
invalidate();
}
int s = Math.min(drawW, drawH);
- if (mIsVideo) drawVideoPlayIcon(canvas, s);
- if (mLoadingState == Model.LOADING_FAIL) {
+ if (mModel.isVideo(mIndex) || mModel.isGif(mIndex)) {
+ drawVideoPlayIcon(canvas, s);
+ }
+ if (mLoadingState == Model.LOADING_FAIL ) {
drawLoadingFailMessage(canvas);
}
canvas.restore();
@@ -1855,4 +1859,5 @@
}
return effect;
}
+
}
diff --git a/src/com/android/gallery3d/util/GIFView.java b/src/com/android/gallery3d/util/GIFView.java
index 0e71aa3..fd4a5b2 100755
--- a/src/com/android/gallery3d/util/GIFView.java
+++ b/src/com/android/gallery3d/util/GIFView.java
@@ -1,300 +1,211 @@
package com.android.gallery3d.util;
-import java.io.InputStream;
+import com.android.gallery3d.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.net.Uri;
-import android.content.res.AssetManager;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import android.database.Cursor;
import android.widget.ImageView;
-import java.io.FileInputStream;
-
-import com.android.gallery3d.R;
-
-import android.content.ContentResolver;
import android.widget.Toast;
-public class GIFView extends ImageView implements GifAction{
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
- private static final String TAG = "GIFView";
-
- private GifDecoder gifDecoder = null;
+public class GIFView extends ImageView implements GifAction {
- private Bitmap currentImage = null;
-
- private static boolean isRun = false;
-
- private static boolean pause = true;
+ private static final String TAG = "GIFView";
+ private static final float SCALE_LIMIT = 4;
+ private static final long FRAME_DELAY = 200; //milliseconds
- private int W;
-
- private int H;
-
- private DrawThread drawThread = null;
+ private GifDecoder mGifDecoder = null;
+ private Bitmap mCurrentImage = null;
+ private DrawThread mDrawThread = null;
- Uri mUri;
- private Context mContext;
-
+ private Uri mUri;
+ private Context mContext;
+
public GIFView(Context context) {
super(context);
- mContext=context;
-
+ mContext = context;
}
- public boolean setDrawable(Uri uri){
- if (null == uri){
+ public boolean setDrawable(Uri uri) {
+ if (null == uri) {
return false;
}
- isRun = true;
- pause = false;
mUri = uri;
- int mSize = 0;
- ContentResolver cr = mContext.getContentResolver();
- InputStream input = null;
+
+ 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 {
- input = cr.openInputStream(uri);
-
- if (input instanceof FileInputStream) {
- FileInputStream f = (FileInputStream) input;
- mSize = (int) f.getChannel().size();
+ if (is instanceof FileInputStream) {
+ FileInputStream f = (FileInputStream) is;
+ size = (int) f.getChannel().size();
} else {
- while (-1 != input.read()) {
- mSize++;
+ while (-1 != is.read()) {
+ size++;
}
}
} catch (IOException e) {
-
- } finally {
-
- }
- //wss , return if file is invalid
- if(mSize == 0){
- return false;
+ Log.e(TAG, "catch exception:" + e);
}
- if(mSize > 1024*1024){ //gif must be smaller than 1MB
- if (null != input) {
- try {
- input.close();
- } catch (IOException e) {
- }
- }
- Toast.makeText(mContext, R.string.gif_image_too_large, Toast.LENGTH_LONG).show();
- return false;
+ 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);
}
-
- setGifDecoderImage(input);
-
-
-
- android.content.ContentResolver resolver = mContext.getContentResolver();
- Cursor c = resolver.query(uri, new String[]{"_data"}, null, null, null);
-
-// if ( c != null && 1 == c.getCount()){
-// c.moveToFirst();
-//
-// AssetManager am = mContext.getAssets();
-// try{
-// System.out.println(">>>>>>>>>1 "+c.getString(0));
-// setGifDecoderImage(am.open(c.getString(0), AssetManager.ACCESS_RANDOM));
-// }catch(FileNotFoundException e){
-// Log.v(TAG, "e:" + e);
-// }catch(IOException e){
-// Log.v(TAG, "e:" + e);
-// }finally{
-// c.close();
-// }
-//
-// return true;
-// }
-// else{
-// AssetManager am1 = mContext.getAssets();
-// try {
-// System.out.println(">>>>>>>>2 "+mUri.getPath());
-// setGifDecoderImage(am1.open(mUri.getPath(), AssetManager.ACCESS_UNKNOWN));
-// } catch (IOException e1) {
-// e1.printStackTrace();
-// }
-// return true;
-// }
- return true;
+ return input;
}
-
- private void setGifDecoderImage(InputStream is){
- if(gifDecoder != null){
- gifDecoder.free();
- gifDecoder= null;
- }
- gifDecoder = new GifDecoder(is,this);
- gifDecoder.start();
+
+ private void startDecode(InputStream is) {
+ freeGifDecoder();
+ mGifDecoder = new GifDecoder(is, this);
+ mGifDecoder.start();
}
-
+
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- //wangmiao add
- W = ViewGifImage.dm.widthPixels;//480;
- H = ViewGifImage.dm.heightPixels;//800;
- //Log.w(TAG,"the width is "+W +"the hight is "+H);
- if(gifDecoder == null){
+ if (mGifDecoder == null) {
return;
}
-
- if(currentImage == null){
- currentImage = gifDecoder.getImage();
+
+ if (mCurrentImage == null) {
+ mCurrentImage = mGifDecoder.getImage();
}
- if(currentImage == null){
- setImageURI(mUri); // if can not play this gif, we just try to show it as jpg by parsing mUri, bug: T81-4307
+ 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());
- //canvas.drawBitmap(currentImage, (W - currentImage.getWidth()) / 2, (H - currentImage.getHeight())/2, null);
- Rect sRect = null;
+ Rect sRect = null;
Rect dRect = null;
-
- int imageHeight = currentImage.getHeight();
- int imageWidth = currentImage.getWidth();
- //int newHeight = H/2;
- int newHeight = H;
- int newWidth = W;
-
- if (newWidth < imageWidth)
- {
- if (newHeight < imageHeight)
- {
- //h big, w big;
- //Log.w(TAG," h big, w big");
- if (imageHeight*W > imageWidth*H)
- {
- //too height
- //newHeight = H/2;
- newWidth = (imageWidth * newHeight)/imageHeight;
- //Log.w(TAG," h too big = "+ newHeight+" w big = "+newWidth);
- }
- else
- {
- //newWidth = W;
- newHeight = (imageHeight * newWidth)/imageWidth;
- //Log.w(TAG," h big = "+ newHeight+" w too big = "+newWidth);
- }
-
- //sRect = new Rect(0, 0, currentImage.getWidth(), currentImage.getHeight());
- dRect = new Rect((W - newWidth) / 2, 0, (W + newWidth) / 2, newHeight);
+ 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
- {
- //h small, w big;
- newHeight = (imageHeight * newWidth)/imageWidth;
- dRect = new Rect(0, 0, newWidth, newHeight);
- }
- canvas.drawBitmap(currentImage, sRect, dRect, null);
-
+ } 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);
}
- else if (newHeight < imageHeight)
- {
- //h big, w small;
- newWidth = (imageWidth * newHeight)/imageHeight;
- dRect = new Rect((W - newWidth) / 2, 0,
- (W + newWidth) / 2, newHeight);
- canvas.drawBitmap(currentImage, sRect, dRect, null);
- }
- else
- {
- //h small, w small;
- canvas.drawBitmap(currentImage, (W - imageWidth) / 2, (H - imageHeight) / 2, null);
- }
-
+ 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){
- if(gifDecoder != null){
- if(frameIndex == -1){
- if(gifDecoder.getFrameCount() > 1){
- if(drawThread == null){
- drawThread = new DrawThread();
- } else{
- drawThread = null;
- drawThread = new DrawThread();
- }
- drawThread.start();
- }
+
+ 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("gif","parse error");
+ } else {
+ Log.e(TAG, "parse error");
}
}
- private Handler redrawHandler = new Handler(){
- public void handleMessage(Message msg) {
- invalidate();
- }
+ private Handler mRedrawHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ invalidate();
+ }
};
-
- private class DrawThread extends Thread{
- public void run(){
- if(gifDecoder == null){
+
+ private class DrawThread extends Thread {
+ public void run() {
+ if (mGifDecoder == null) {
return;
}
-
- while(isRun){
- if(pause == false){
- if(!isShown()){
- isRun = false;
- pause = true;
- break;
- }
- GifFrame frame = gifDecoder.next();
- currentImage = frame.image;
- long sp = frame.delay;
- if(sp == 0) sp = 200; //wangmiao add merge from T92
- if(redrawHandler != null){
- Message msg = redrawHandler.obtainMessage();
- redrawHandler.sendMessage(msg);
- try{
- Thread.sleep(sp);
- } catch(InterruptedException e){}
- }else{
- break;
- }
- } else{
+
+ while (true) {
+ if (!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);
+ }
}
- isRun = true;
- pause = false;
}
+
}
- public void freeMemory()
- {
- isRun = false;
- pause = true;
- if (drawThread != null)
- {
- //drawThread.isStop = true;
- drawThread = null;
+
+ 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;
}
- if (gifDecoder != null)
- {
- Log.w(TAG," free");
- gifDecoder.free();
- gifDecoder = null;
+
+ }
+
+ public void freeMemory() {
+ if (mDrawThread != null) {
+ mDrawThread = null;
}
+ freeGifDecoder();
}
}
-
-
-
diff --git a/src/com/android/gallery3d/util/GifAction.java b/src/com/android/gallery3d/util/GifAction.java
old mode 100755
new mode 100644
index b273a17..88e3cde
--- a/src/com/android/gallery3d/util/GifAction.java
+++ b/src/com/android/gallery3d/util/GifAction.java
@@ -1,5 +1,5 @@
package com.android.gallery3d.util;
public interface GifAction {
- public void parseOk(boolean parseStatus,int frameIndex);
+ public void parseOk(boolean parseStatus, int frameIndex);
}
diff --git a/src/com/android/gallery3d/util/GifDecoder.java b/src/com/android/gallery3d/util/GifDecoder.java
index 60bc198..4235fc5 100755
--- a/src/com/android/gallery3d/util/GifDecoder.java
+++ b/src/com/android/gallery3d/util/GifDecoder.java
@@ -1,697 +1,723 @@
package com.android.gallery3d.util;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.util.Log;
-public class GifDecoder extends Thread{
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
- public static final int STATUS_PARSING = 0;
- public static final int STATUS_FORMAT_ERROR = 1;
- public static final int STATUS_OPEN_ERROR = 2;
- public static final int STATUS_FINISH = -1;
-
- private InputStream in;
- private int status;
+public class GifDecoder extends Thread {
- public int width; // full image width
- public int height; // full image height
- private boolean gctFlag; // global color table used
- private int gctSize; // size of global color table
- private int loopCount = 1; // iterations; 0 = repeat forever
+ public static final int STATUS_PARSING = 0;
+ public static final int STATUS_FORMAT_ERROR = 1;
+ public static final int STATUS_OPEN_ERROR = 2;
+ public static final int STATUS_FINISH = -1;
- private int[] gct; // global color table
- private int[] lct; // local color table
- private int[] act; // active color table
+ private InputStream mIS;
+ private int mStatus;
- private int bgIndex; // background color index
- private int bgColor; // background color
- private int lastBgColor; // previous bg color
- private int pixelAspect; // pixel aspect ratio
+ public int mWidth; // full image width
+ public int mHeight; // full image height
+ private boolean mGctFlag; // global color table used
+ private int mGctSize; // size of global color table
+ private int mLoopCount = 1; // iterations; 0 = repeat forever
- private boolean lctFlag; // local color table flag
- private boolean interlace; // interlace flag
- private int lctSize; // local color table size
+ private int[] mGct; // global color table
+ private int[] mLct; // local color table
+ private int[] mAct; // active color table
- private int ix, iy, iw, ih; // current image rectangle
- private int lrx, lry, lrw, lrh;
- private Bitmap image; // current frame
- private Bitmap lastImage; // previous frame
- private GifFrame currentFrame = null;
+ private int mBgIndex; // background color index
+ private int mBgColor; // background color
+ private int mLastBgColor; // previous bg color
+ private int mPixelAspect; // pixel aspect ratio
- private boolean isShow = false;
-
+ private boolean mLctFlag; // local color table flag
+ private boolean mInterlace; // interlace flag
+ private int mLctSize; // local color table size
- private byte[] block = new byte[256]; // current data block
- private int blockSize = 0; // block size
- private int dispose = 0;
- private int lastDispose = 0;
- private boolean transparency = false; // use transparent color
- private int delay = 0; // delay in milliseconds
- private int transIndex; // transparent color index
+ private int mIx, mIy, mIw, mIh; // current image rectangle
+ private int mLrx, mLry, mLrw, mLrh;
+ private Bitmap mImage; // current frame
+ private Bitmap mLastImage; // previous frame
+ private GifFrame mCurrentFrame = null;
- private static final int MaxStackSize = 4096;
- // max decoder pixel stack size
+ private boolean mIsShow = false;
- // LZW decoder working arrays
- private short[] prefix;
- private byte[] suffix;
- private byte[] pixelStack;
- private byte[] pixels;
+ private byte[] mBlock = new byte[256]; // current data block
+ private int mBlockSize = 0; // block size
+ private int mDispose = 0;
+ private int mLastDispose = 0;
+ private boolean mTransparency = false; // use transparent color
+ private int mDelay = 0; // delay in milliseconds
+ private int mTransIndex; // transparent color index
- private GifFrame gifFrame; // frames read from current file
- private int frameCount;
+ // max decoder pixel stack size
+ private static final int MaxStackSize = 4096;
- private GifAction action = null;
-
-
- private byte[] gifData = null;
+ // LZW decoder working arrays
+ private short[] mPrefix;
+ private byte[] mSuffix;
+ private byte[] mPixelStack;
+ private byte[] mPixels;
-
- public GifDecoder(byte[] data,GifAction act){
- gifData = data;
- action = act;
- }
-
- public GifDecoder(InputStream is,GifAction act){
- in = is;
- action = act;
- }
+ private GifFrame mGifFrame; // frames read from current file
+ private int mFrameCount;
- public void run(){
- if(in != null){
- readStream();
- }else if(gifData != null){
- readByte();
- }
- }
-
- public void free(){
- GifFrame fg = gifFrame;
- while(fg != null){
- if (fg.image != null) {
- fg.image.recycle();
+ private GifAction mGifAction = null;
+
+ private byte[] mGifData = null;
+
+ public GifDecoder(byte[] data, GifAction act) {
+ mGifData = data;
+ mGifAction = act;
+ }
+
+ public GifDecoder(InputStream is, GifAction act) {
+ mIS = is;
+ mGifAction = act;
+ }
+
+ public void run() {
+ if (mIS != null) {
+ readStream();
+ } else if (mGifData != null) {
+ readByte();
+ }
+ }
+
+ public void free() {
+ freeFrame();
+ freeIS();
+ freeImage();
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public boolean parseOk() {
+ return mStatus == STATUS_FINISH;
+ }
+
+ public int getDelay(int n) {
+ mDelay = -1;
+ if ((n >= 0) && (n < mFrameCount)) {
+ GifFrame f = getFrame(n);
+ if (f != null) {
+ mDelay = f.mDelayInMs;
}
- fg.image = null;
- fg = null;
- gifFrame = gifFrame.nextFrame;
- fg = gifFrame;
- }
- if(in != null){
- try{
- in.close();
- }catch(Exception ex){}
- in = null;
- }
- gifData = null;
- if (image != null)
- {
- image.recycle();
- image = null;
- }
- if (lastImage != null)
- {
- lastImage.recycle();
- lastImage = null;
- }
- }
-
- public int getStatus(){
- return status;
- }
-
- public boolean parseOk(){
- return status == STATUS_FINISH;
- }
-
- public int getDelay(int n) {
- delay = -1;
- if ((n >= 0) && (n < frameCount)) {
- GifFrame f = getFrame(n);
- if (f != null)
- delay = f.delay;
- }
- return delay;
- }
-
- public int[] getDelays(){
- GifFrame f = gifFrame;
- int[] d = new int[frameCount];
- int i = 0;
- while(f != null && i < frameCount){
- d[i] = f.delay;
- f = f.nextFrame;
- i++;
- }
- return d;
- }
-
- public int getFrameCount() {
- return frameCount;
- }
+ }
+ return mDelay;
+ }
- public Bitmap getImage() {
- return getFrameImage(0);
- }
+ public int[] getDelays() {
+ GifFrame f = mGifFrame;
+ int[] d = new int[mFrameCount];
+ int i = 0;
+ while (f != null && i < mFrameCount) {
+ d[i] = f.mDelayInMs;
+ f = f.mNextFrame;
+ i++;
+ }
+ return d;
+ }
- public int getLoopCount() {
- return loopCount;
- }
+ public int getFrameCount() {
+ return mFrameCount;
+ }
- private void setPixels() {
- int[] dest = new int[width * height];
- // fill in starting image contents based on last image's dispose code
- if (lastDispose > 0) {
- if (lastDispose == 3) {
- // use image before last
- int n = frameCount - 2;
- if (n > 0) {
- lastImage = getFrameImage(n - 1);
- } else {
- lastImage = null;
- }
- }
- if (lastImage != null) {
- lastImage.getPixels(dest, 0, width, 0, 0, width, height);
- // copy pixels
- if (lastDispose == 2) {
- // fill last image rect area with background color
- int c = 0;
- if (!transparency) {
- c = lastBgColor;
- }
- for (int i = 0; i < lrh; i++) {
- int n1 = (lry + i) * width + lrx;
- int n2 = n1 + lrw;
- for (int k = n1; k < n2; k++) {
- dest[k] = c;
- }
- }
- }
- }
- }
+ public Bitmap getImage() {
+ return getFrameImage(0);
+ }
- // copy each source line to the appropriate place in the destination
- int pass = 1;
- int inc = 8;
- int iline = 0;
- for (int i = 0; i < ih; i++) {
- int line = i;
- if (interlace) {
- if (iline >= ih) {
- pass++;
- switch (pass) {
- case 2:
- iline = 4;
- break;
- case 3:
- iline = 2;
- inc = 4;
- break;
- case 4:
- iline = 1;
- inc = 2;
- }
- }
- line = iline;
- iline += inc;
- }
- line += iy;
- if (line < height) {
- int k = line * width;
- int dx = k + ix; // start of line in dest
- int dlim = dx + iw; // end of dest line
- if ((k + width) < dlim) {
- dlim = k + width; // past dest edge
- }
- int sx = i * iw; // start of line in source
- while (dx < dlim) {
- // map color and insert in destination
- int index = ((int) pixels[sx++]) & 0xff;
- int c = act[index];
- if (c != 0) {
- dest[dx] = c;
- }
- dx++;
- }
- }
- }
- image = Bitmap.createBitmap(dest, width, height, Config.ARGB_4444);
- }
+ public int getLoopCount() {
+ return mLoopCount;
+ }
- public Bitmap getFrameImage(int n) {
- GifFrame frame = getFrame(n);
- if (frame == null)
- return null;
- else
- return frame.image;
- }
+ private void setPixels() {
+ int[] dest = new int[mWidth * mHeight];
+ // fill in starting image contents based on last image's dispose code
+ if (mLastDispose > 0) {
+ if (mLastDispose == 3) {
+ // use image before last
+ int n = mFrameCount - 2;
+ if (n > 0) {
+ mLastImage = getPreUndisposedImage(n - 1);
+ } else {
+ mLastImage = null;
+ }
+ }
+ if (mLastImage != null) {
+ mLastImage.getPixels(dest, 0, mWidth, 0, 0, mWidth, mHeight);
+ // copy pixels
+ if (mLastDispose == 2) {
+ // fill last image rect area with background color
+ int c = 0;
+ if (!mTransparency) {
+ c = mLastBgColor;
+ }
+ for (int i = 0; i < mLrh; i++) {
+ int n1 = (mLry + i) * mWidth + mLrx;
+ int n2 = n1 + mLrw;
+ for (int k = n1; k < n2; k++) {
+ dest[k] = c;
+ }
+ }
+ }
+ }
+ }
- public GifFrame getCurrentFrame(){
- return currentFrame;
- }
-
- public GifFrame getFrame(int n) {
- GifFrame frame = gifFrame;
- int i = 0;
- while (frame != null) {
- if (i == n) {
- return frame;
- } else {
- frame = frame.nextFrame;
- }
- i++;
- }
- return null;
- }
+ // copy each source line to the appropriate place in the destination
+ int pass = 1;
+ int inc = 8;
+ int iline = 0;
+ for (int i = 0; i < mIh; i++) {
+ int line = i;
+ if (mInterlace) {
+ if (iline >= mIh) {
+ pass++;
+ switch (pass) {
+ case 2:
+ iline = 4;
+ break;
+ case 3:
+ iline = 2;
+ inc = 4;
+ break;
+ case 4:
+ iline = 1;
+ inc = 2;
+ }
+ }
+ line = iline;
+ iline += inc;
+ }
+ line += mIy;
+ if (line < mHeight) {
+ int k = line * mWidth;
+ int dx = k + mIx; // start of line in dest
+ int dlim = dx + mIw; // end of dest line
+ if ((k + mWidth) < dlim) {
+ dlim = k + mWidth; // past dest edge
+ }
+ int sx = i * mIw; // start of line in source
+ while (dx < dlim) {
+ // map color and insert in destination
+ int index = ((int) mPixels[sx++]) & 0xff;
+ int c = mAct[index];
+ if (c != 0) {
+ dest[dx] = c;
+ }
+ dx++;
+ }
+ }
+ }
+ mImage = Bitmap.createBitmap(dest, mWidth, mHeight, Config.ARGB_4444);
+ }
- public void reset(){
- currentFrame = gifFrame;
- }
-
- public GifFrame next() {
- if(isShow == false){
- isShow = true;
- return gifFrame;
- }else{
- if(status == STATUS_PARSING){
- if(currentFrame.nextFrame != null)
- currentFrame = currentFrame.nextFrame;
- //currentFrame = gifFrame;
- }else{
- currentFrame = currentFrame.nextFrame;
- if (currentFrame == null) {
- currentFrame = gifFrame;
- }
- }
- return currentFrame;
- }
- }
+ public Bitmap getFrameImage(int n) {
+ GifFrame frame = getFrame(n);
+ if (frame == null) {
+ return null;
+ } else {
+ return frame.mImage;
+ }
+ }
- private int readByte(){
- in = new ByteArrayInputStream(gifData);
- gifData = null;
- return readStream();
- }
-
- private int readStream(){
- init();
- if(in != null){
- readHeader();
- if(!err()){
- readContents();
- if(frameCount < 0){
- status = STATUS_FORMAT_ERROR;
- action.parseOk(false,-1);
- }else{
- status = STATUS_FINISH;
- action.parseOk(true,-1);
- }
- }
- try {
- in.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }else {
- status = STATUS_OPEN_ERROR;
- action.parseOk(false,-1);
- }
- return status;
- }
+ public GifFrame getCurrentFrame() {
+ return mCurrentFrame;
+ }
- private void decodeImageData() {
- int NullCode = -1;
- int npix = iw * ih;
- int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
+ public GifFrame getFrame(int n) {
+ GifFrame frame = mGifFrame;
+ int i = 0;
+ while (frame != null) {
+ if (i == n) {
+ return frame;
+ } else {
+ frame = frame.mNextFrame;
+ }
+ i++;
+ }
+ return null;
+ }
- if ((pixels == null) || (pixels.length < npix)) {
- pixels = new byte[npix]; // allocate new pixel array
- }
- if (prefix == null) {
- prefix = new short[MaxStackSize];
- }
- if (suffix == null) {
- suffix = new byte[MaxStackSize];
- }
- if (pixelStack == null) {
- pixelStack = new byte[MaxStackSize + 1];
- }
- // Initialize GIF data stream decoder.
- data_size = read();
- clear = 1 << data_size;
- end_of_information = clear + 1;
- available = clear + 2;
- old_code = NullCode;
- code_size = data_size + 1;
- code_mask = (1 << code_size) - 1;
- for (code = 0; code < clear; code++) {
- prefix[code] = 0;
- suffix[code] = (byte) code;
- }
+ private Bitmap getPreUndisposedImage(int n) {
+ Bitmap preUndisposedImage = null;
+ GifFrame frame = mGifFrame;
+ int i = 0;
+ while (frame != null && i <= n) {
+ if (frame.mDispose == 1) {
+ preUndisposedImage = frame.mImage;
+ } else {
+ frame = frame.mNextFrame;
+ }
+ i++;
+ }
+ return preUndisposedImage;
+ }
- // Decode GIF pixel stream.
- datum = bits = count = first = top = pi = bi = 0;
- for (i = 0; i < npix;) {
- if (top == 0) {
- if (bits < code_size) {
- // Load bytes until there are enough bits for a code.
- if (count == 0) {
- // Read a new data block.
- count = readBlock();
- if (count <= 0) {
- break;
- }
- bi = 0;
- }
- datum += (((int) block[bi]) & 0xff) << bits;
- bits += 8;
- bi++;
- count--;
- continue;
- }
- // Get the next code.
- code = datum & code_mask;
- datum >>= code_size;
- bits -= code_size;
+ public void reset() {
+ mCurrentFrame = mGifFrame;
+ }
- // Interpret the code
- if ((code > available) || (code == end_of_information)) {
- break;
- }
- if (code == clear) {
- // Reset decoder.
- code_size = data_size + 1;
- code_mask = (1 << code_size) - 1;
- available = clear + 2;
- old_code = NullCode;
- continue;
- }
- if (old_code == NullCode) {
- pixelStack[top++] = suffix[code];
- old_code = code;
- first = code;
- continue;
- }
- in_code = code;
- if (code == available) {
- pixelStack[top++] = (byte) first;
- code = old_code;
- }
- while (code > clear) {
- pixelStack[top++] = suffix[code];
- code = prefix[code];
- }
- first = ((int) suffix[code]) & 0xff;
- // Add a new string to the string table,
- if (available >= MaxStackSize) {
- break;
- }
- pixelStack[top++] = (byte) first;
- prefix[available] = (short) old_code;
- suffix[available] = (byte) first;
- available++;
- if (((available & code_mask) == 0)
- && (available < MaxStackSize)) {
- code_size++;
- code_mask += available;
- }
- old_code = in_code;
- }
+ public GifFrame next() {
+ if (mIsShow == false) {
+ mIsShow = true;
+ return mGifFrame;
+ } else {
+ if (mStatus == STATUS_PARSING) {
+ if (mCurrentFrame.mNextFrame != null) {
+ mCurrentFrame = mCurrentFrame.mNextFrame;
+ }
+ } else {
+ mCurrentFrame = mCurrentFrame.mNextFrame;
+ if (mCurrentFrame == null) {
+ mCurrentFrame = mGifFrame;
+ }
+ }
+ return mCurrentFrame;
+ }
+ }
- // Pop a pixel off the pixel stack.
- top--;
- pixels[pi++] = pixelStack[top];
- i++;
- }
- for (i = pi; i < npix; i++) {
- pixels[i] = 0; // clear missing pixels
- }
- }
+ private int readByte() {
+ mIS = new ByteArrayInputStream(mGifData);
+ mGifData = null;
+ return readStream();
+ }
- private boolean err() {
- return status != STATUS_PARSING;
- }
+ private int readStream() {
+ init();
+ if (mIS != null) {
+ readHeader();
+ if (!err()) {
+ readContents();
+ if (mFrameCount < 0) {
+ mStatus = STATUS_FORMAT_ERROR;
+ mGifAction.parseOk(false, -1);
+ } else {
+ mStatus = STATUS_FINISH;
+ mGifAction.parseOk(true, -1);
+ }
+ }
+ try {
+ mIS.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ mStatus = STATUS_OPEN_ERROR;
+ mGifAction.parseOk(false, -1);
+ }
+ return mStatus;
+ }
- private void init() {
- status = STATUS_PARSING;
- frameCount = 0;
- gifFrame = null;
- gct = null;
- lct = null;
- }
+ private void decodeImageData() {
+ int NullCode = -1;
+ int npix = mIw * mIh;
+ int available, clear, code_mask, code_size, end_of_information, in_code, old_code,
+ bits, code, count, i, datum, data_size, first, top, bi, pi;
- private int read() {
- int curByte = 0;
- try {
-
- curByte = in.read();
- } catch (Exception e) {
- status = STATUS_FORMAT_ERROR;
- }
- return curByte;
- }
-
- private int readBlock() {
- blockSize = read();
- int n = 0;
- if (blockSize > 0) {
- try {
- int count = 0;
- while (n < blockSize) {
- count = in.read(block, n, blockSize - n);
- if (count == -1) {
- break;
- }
- n += count;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (n < blockSize) {
- status = STATUS_FORMAT_ERROR;
- }
- }
- return n;
- }
+ if ((mPixels == null) || (mPixels.length < npix)) {
+ mPixels = new byte[npix]; // allocate new pixel array
+ }
+ if (mPrefix == null) {
+ mPrefix = new short[MaxStackSize];
+ }
+ if (mSuffix == null) {
+ mSuffix = new byte[MaxStackSize];
+ }
+ if (mPixelStack == null) {
+ mPixelStack = new byte[MaxStackSize + 1];
+ }
+ // Initialize GIF data stream decoder.
+ data_size = read();
+ clear = 1 << data_size;
+ end_of_information = clear + 1;
+ available = clear + 2;
+ old_code = NullCode;
+ code_size = data_size + 1;
+ code_mask = (1 << code_size) - 1;
+ for (code = 0; code < clear; code++) {
+ mPrefix[code] = 0;
+ mSuffix[code] = (byte) code;
+ }
- private int[] readColorTable(int ncolors) {
- int nbytes = 3 * ncolors;
- int[] tab = null;
- byte[] c = new byte[nbytes];
- int n = 0;
- try {
- n = in.read(c);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (n < nbytes) {
- status = STATUS_FORMAT_ERROR;
- } else {
- tab = new int[256]; // max size to avoid bounds checks
- int i = 0;
- int j = 0;
- while (i < ncolors) {
- int r = ((int) c[j++]) & 0xff;
- int g = ((int) c[j++]) & 0xff;
- int b = ((int) c[j++]) & 0xff;
- tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
- }
- }
- return tab;
- }
+ // Decode GIF pixel stream.
+ datum = bits = count = first = top = pi = bi = 0;
+ for (i = 0; i < npix;) {
+ if (top == 0) {
+ if (bits < code_size) {
+ // Load bytes until there are enough bits for a code.
+ if (count == 0) {
+ // Read a new data block.
+ count = readBlock();
+ if (count <= 0) {
+ break;
+ }
+ bi = 0;
+ }
+ datum += (((int) mBlock[bi]) & 0xff) << bits;
+ bits += 8;
+ bi++;
+ count--;
+ continue;
+ }
+ // Get the next code.
+ code = datum & code_mask;
+ datum >>= code_size;
+ bits -= code_size;
- private void readContents() {
- // read GIF file content blocks
- boolean done = false;
- while (!(done || err())) {
- int code = read();
- switch (code) {
- case 0x2C: // image separator
- readImage();
- break;
- case 0x21: // extension
- code = read();
- switch (code) {
- case 0xf9: // graphics control extension
- readGraphicControlExt();
- break;
- case 0xff: // application extension
- readBlock();
- String app = "";
- for (int i = 0; i < 11; i++) {
- app += (char) block[i];
- }
- if (app.equals("NETSCAPE2.0")) {
- readNetscapeExt();
- } else {
- skip(); // don't care
- }
- break;
- default: // uninteresting extension
- skip();
- }
- break;
- case 0x3b: // terminator
- done = true;
- break;
- case 0x00: // bad byte, but keep going and see what happens
- break;
- default:
- status = STATUS_FORMAT_ERROR;
- }
- }
- }
+ // Interpret the code
+ if ((code > available) || (code == end_of_information)) {
+ break;
+ }
+ if (code == clear) {
+ // Reset decoder.
+ code_size = data_size + 1;
+ code_mask = (1 << code_size) - 1;
+ available = clear + 2;
+ old_code = NullCode;
+ continue;
+ }
+ if (old_code == NullCode) {
+ mPixelStack[top++] = mSuffix[code];
+ old_code = code;
+ first = code;
+ continue;
+ }
+ in_code = code;
+ if (code == available) {
+ mPixelStack[top++] = (byte) first;
+ code = old_code;
+ }
+ while (code > clear) {
+ mPixelStack[top++] = mSuffix[code];
+ code = mPrefix[code];
+ }
+ first = ((int) mSuffix[code]) & 0xff;
+ // Add a new string to the string table,
+ if (available >= MaxStackSize) {
+ break;
+ }
+ mPixelStack[top++] = (byte) first;
+ mPrefix[available] = (short) old_code;
+ mSuffix[available] = (byte) first;
+ available++;
+ if (((available & code_mask) == 0)
+ && (available < MaxStackSize)) {
+ code_size++;
+ code_mask += available;
+ }
+ old_code = in_code;
+ }
- private void readGraphicControlExt() {
- read(); // block size
- int packed = read(); // packed fields
- dispose = (packed & 0x1c) >> 2; // disposal method
- if (dispose == 0) {
- dispose = 1; // elect to keep old image if discretionary
- }
- transparency = (packed & 1) != 0;
- delay = readShort() * 10; // delay in milliseconds
- transIndex = read(); // transparent color index
- read(); // block terminator
- }
+ // Pop a pixel off the pixel stack.
+ top--;
+ mPixels[pi++] = mPixelStack[top];
+ i++;
+ }
+ for (i = pi; i < npix; i++) {
+ mPixels[i] = 0; // clear missing pixels
+ }
+ }
- private void readHeader() {
- String id = "";
- for (int i = 0; i < 6; i++) {
- id += (char) read();
- }
- if (!id.startsWith("GIF")) {
- status = STATUS_FORMAT_ERROR;
- return;
- }
- readLSD();
- if (gctFlag && !err()) {
- gct = readColorTable(gctSize);
- bgColor = gct[bgIndex];
- }
- }
+ private boolean err() {
+ return mStatus != STATUS_PARSING;
+ }
- private void readImage() {
- ix = readShort(); // (sub)image position & size
- iy = readShort();
- iw = readShort();
- ih = readShort();
- int packed = read();
- lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
- interlace = (packed & 0x40) != 0; // 2 - interlace flag
- // 3 - sort flag
- // 4-5 - reserved
- lctSize = 2 << (packed & 7); // 6-8 - local color table size
- if (lctFlag) {
- lct = readColorTable(lctSize); // read table
- act = lct; // make local table active
- } else {
- act = gct; // make global table active
- if (bgIndex == transIndex) {
- bgColor = 0;
- }
- }
- int save = 0;
- if (transparency) {
- save = act[transIndex];
- act[transIndex] = 0; // set transparent color if specified
- }
- if (act == null) {
- status = STATUS_FORMAT_ERROR; // no color table defined
- }
- if (err()) {
- return;
- }
+ private void init() {
+ mStatus = STATUS_PARSING;
+ mFrameCount = 0;
+ mGifFrame = null;
+ mGct = null;
+ mLct = null;
+ }
+
+ private int read() {
+ int curByte = 0;
try {
- decodeImageData(); // decode pixel data
- skip();
- if (err()) {
- return;
- }
- frameCount++;
- // create new image to receive frame data
- image = Bitmap.createBitmap(width, height, Config.ARGB_4444);
- // createImage(width, height);
- setPixels(); // transfer pixel data to image
- if (gifFrame == null) {
- gifFrame = new GifFrame(image, delay);
- currentFrame = gifFrame;
- } else {
- GifFrame f = gifFrame;
- while(f.nextFrame != null){
- f = f.nextFrame;
- }
- f.nextFrame = new GifFrame(image, delay);
- }
- // frames.addElement(new GifFrame(image, delay)); // add image to frame
- // list
- if (transparency) {
- act[transIndex] = save;
- }
- resetFrame();
- action.parseOk(true, frameCount);
- }catch (OutOfMemoryError e) {
+ curByte = mIS.read();
+ } catch (Exception e) {
+ mStatus = STATUS_FORMAT_ERROR;
+ }
+ return curByte;
+ }
+
+ private int readBlock() {
+ mBlockSize = read();
+ int n = 0;
+ if (mBlockSize > 0) {
+ try {
+ int count = 0;
+ while (n < mBlockSize) {
+ count = mIS.read(mBlock, n, mBlockSize - n);
+ if (count == -1) {
+ break;
+ }
+ n += count;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (n < mBlockSize) {
+ mStatus = STATUS_FORMAT_ERROR;
+ }
+ }
+ return n;
+ }
+
+ private int[] readColorTable(int ncolors) {
+ int nbytes = 3 * ncolors;
+ int[] tab = null;
+ byte[] c = new byte[nbytes];
+ int n = 0;
+ try {
+ n = mIS.read(c);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (n < nbytes) {
+ mStatus = STATUS_FORMAT_ERROR;
+ } else {
+ tab = new int[256]; // max size to avoid bounds checks
+ int i = 0;
+ int j = 0;
+ while (i < ncolors) {
+ int r = ((int) c[j++]) & 0xff;
+ int g = ((int) c[j++]) & 0xff;
+ int b = ((int) c[j++]) & 0xff;
+ tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
+ }
+ }
+ return tab;
+ }
+
+ private void readContents() {
+ // read GIF file content blocks
+ boolean done = false;
+ while (!(done || err())) {
+ int code = read();
+ switch (code) {
+ case 0x2C: // image separator
+ readImage();
+ break;
+ case 0x21: // extension
+ code = read();
+ switch (code) {
+ case 0xf9: // graphics control extension
+ readGraphicControlExt();
+ break;
+ case 0xff: // application extension
+ readBlock();
+ String app = "";
+ for (int i = 0; i < 11; i++) {
+ app += (char) mBlock[i];
+ }
+ if (app.equals("NETSCAPE2.0")) {
+ readNetscapeExt();
+ } else {
+ skip(); // don't care
+ }
+ break;
+ default: // uninteresting extension
+ skip();
+ }
+ break;
+ case 0x3b: // terminator
+ done = true;
+ break;
+ case 0x00: // bad byte, but keep going and see what happens
+ break;
+ default:
+ mStatus = STATUS_FORMAT_ERROR;
+ }
+ }
+ }
+
+ private void readGraphicControlExt() {
+ read(); // block size
+ int packed = read(); // packed fields
+ mDispose = (packed & 0x1c) >> 2; // disposal method
+ if (mDispose == 0) {
+ mDispose = 1; // elect to keep old image if discretionary
+ }
+ mTransparency = (packed & 1) != 0;
+ mDelay = readShort() * 10; // delay in milliseconds
+ mTransIndex = read(); // transparent color index
+ read(); // block terminator
+ }
+
+ private void readHeader() {
+ String id = "";
+ for (int i = 0; i < 6; i++) {
+ id += (char) read();
+ }
+ if (!id.startsWith("GIF")) {
+ mStatus = STATUS_FORMAT_ERROR;
+ return;
+ }
+ readLSD();
+ if (mGctFlag && !err()) {
+ mGct = readColorTable(mGctSize);
+ mBgColor = mGct[mBgIndex];
+ }
+ }
+
+ private void readImage() {
+ mIx = readShort(); // (sub)image position & size
+ mIy = readShort();
+ mIw = readShort();
+ mIh = readShort();
+ int packed = read();
+ mLctFlag = (packed & 0x80) != 0; // 1 - local color table flag
+ mInterlace = (packed & 0x40) != 0; // 2 - interlace flag
+ // 3 - sort flag
+ // 4-5 - reserved
+ mLctSize = 2 << (packed & 7); // 6-8 - local color table size
+ if (mLctFlag) {
+ mLct = readColorTable(mLctSize); // read table
+ mAct = mLct; // make local table active
+ } else {
+ mAct = mGct; // make global table active
+ if (mBgIndex == mTransIndex) {
+ mBgColor = 0;
+ }
+ }
+ int save = 0;
+ if (mTransparency) {
+ save = mAct[mTransIndex];
+ mAct[mTransIndex] = 0; // set transparent color if specified
+ }
+ if (mAct == null) {
+ mStatus = STATUS_FORMAT_ERROR; // no color table defined
+ }
+ if (err()) {
+ return;
+ }
+ try {
+ decodeImageData(); // decode pixel data
+ skip();
+ if (err()) {
+ return;
+ }
+ mFrameCount++;
+ // create new image to receive frame data
+ mImage = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_4444);
+ // createImage(mWidth, mHeight);
+ setPixels(); // transfer pixel data to image
+ if (mGifFrame == null) {
+ mGifFrame = new GifFrame(mImage, mDelay, mDispose);
+ mCurrentFrame = mGifFrame;
+ } else {
+ GifFrame f = mGifFrame;
+ while (f.mNextFrame != null) {
+ f = f.mNextFrame;
+ }
+ f.mNextFrame = new GifFrame(mImage, mDelay, mDispose);
+ }
+ // frames.addElement(new GifFrame(image, delay)); // add image to
+ // frame
+ // list
+ if (mTransparency) {
+ mAct[mTransIndex] = save;
+ }
+ resetFrame();
+ mGifAction.parseOk(true, mFrameCount);
+ } catch (OutOfMemoryError e) {
Log.e("GifDecoder", ">>> log : " + e.toString());
e.printStackTrace();
}
- }
+ }
- private void readLSD() {
- // logical screen size
- width = readShort();
- height = readShort();
- // packed fields
- int packed = read();
- gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
- // 2-4 : color resolution
- // 5 : gct sort flag
- gctSize = 2 << (packed & 7); // 6-8 : gct size
- bgIndex = read(); // background color index
- pixelAspect = read(); // pixel aspect ratio
- }
+ private void readLSD() {
+ // logical screen size
+ mWidth = readShort();
+ mHeight = readShort();
+ // packed fields
+ int packed = read();
+ mGctFlag = (packed & 0x80) != 0; // 1 : global color table flag
+ // 2-4 : color resolution
+ // 5 : gct sort flag
+ mGctSize = 2 << (packed & 7); // 6-8 : gct size
+ mBgIndex = read(); // background color index
+ mPixelAspect = read(); // pixel aspect ratio
+ }
- private void readNetscapeExt() {
- do {
- readBlock();
- if (block[0] == 1) {
- // loop count sub-block
- int b1 = ((int) block[1]) & 0xff;
- int b2 = ((int) block[2]) & 0xff;
- loopCount = (b2 << 8) | b1;
- }
- } while ((blockSize > 0) && !err());
- }
+ private void readNetscapeExt() {
+ do {
+ readBlock();
+ if (mBlock[0] == 1) {
+ // loop count sub-block
+ int b1 = ((int) mBlock[1]) & 0xff;
+ int b2 = ((int) mBlock[2]) & 0xff;
+ mLoopCount = (b2 << 8) | b1;
+ }
+ } while ((mBlockSize > 0) && !err());
+ }
- private int readShort() {
- // read 16-bit value, LSB first
- return read() | (read() << 8);
- }
+ private int readShort() {
+ // read 16-bit value, LSB first
+ return read() | (read() << 8);
+ }
- private void resetFrame() {
- lastDispose = dispose;
- lrx = ix;
- lry = iy;
- lrw = iw;
- lrh = ih;
- lastImage = image;
- lastBgColor = bgColor;
- dispose = 0;
- transparency = false;
- delay = 0;
- lct = null;
- }
+ private void resetFrame() {
+ mLastDispose = mDispose;
+ mLrx = mIx;
+ mLry = mIy;
+ mLrw = mIw;
+ mLrh = mIh;
+ mLastImage = mImage;
+ mLastBgColor = mBgColor;
+ mDispose = 0;
+ mTransparency = false;
+ mDelay = 0;
+ mLct = null;
+ }
- /**
- * Skips variable length blocks up to and including next zero length block.
- */
- private void skip() {
- do {
- readBlock();
- } while ((blockSize > 0) && !err());
- }
+ /**
+ * Skips variable length blocks up to and including next zero length block.
+ */
+ private void skip() {
+ do {
+ readBlock();
+ } while ((mBlockSize > 0) && !err());
+ }
+
+ private void freeFrame() {
+ GifFrame fg = mGifFrame;
+ while (fg != null) {
+ if (fg.mImage != null) {
+ fg.mImage.recycle();
+ }
+ fg.mImage = null;
+ fg = null;
+ mGifFrame = mGifFrame.mNextFrame;
+ fg = mGifFrame;
+ }
+ }
+
+ private void freeIS() {
+ if (mIS != null) {
+ try {
+ mIS.close();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ mIS = null;
+ }
+ mGifData = null;
+ }
+
+ private void freeImage() {
+ if (mImage != null) {
+ mImage.recycle();
+ mImage = null;
+ }
+ if (mLastImage != null) {
+ mLastImage.recycle();
+ mLastImage = null;
+ }
+ }
}
diff --git a/src/com/android/gallery3d/util/GifFrame.java b/src/com/android/gallery3d/util/GifFrame.java
index 926ed57..87d58a4 100755
--- a/src/com/android/gallery3d/util/GifFrame.java
+++ b/src/com/android/gallery3d/util/GifFrame.java
@@ -3,12 +3,15 @@
import android.graphics.Bitmap;
public class GifFrame {
- public GifFrame(Bitmap im, int del) {
- image = im;
- delay = del;
+
+ public Bitmap mImage;
+ public int mDelayInMs; //in milliseconds
+ public int mDispose;
+ public GifFrame mNextFrame = null;
+
+ public GifFrame(Bitmap bitmap, int delay, int dispose) {
+ mImage = bitmap;
+ mDelayInMs = delay;
+ mDispose = dispose;
}
-
- public Bitmap image;
- public int delay;
- public GifFrame nextFrame = null;
}
diff --git a/src/com/android/gallery3d/util/ViewGifImage.java b/src/com/android/gallery3d/util/ViewGifImage.java
index b157e2f..cdd5092 100755
--- a/src/com/android/gallery3d/util/ViewGifImage.java
+++ b/src/com/android/gallery3d/util/ViewGifImage.java
@@ -1,111 +1,67 @@
-/**
- * File property dialog
- *
- * @Author wangxuguang
- *
- * caozhe add this file, for displya gif image, 2011.3.28
- */
-
package com.android.gallery3d.util;
+import com.android.gallery3d.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.widget.ImageView;
-
-import com.android.gallery3d.R;
-import android.net.Uri;
-
-import android.view.ViewGroup.LayoutParams;
-import android.widget.LinearLayout;
import android.util.DisplayMetrics;
-import android.content.res.Configuration;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
-public class ViewGifImage extends Activity
-{
- public static final String TAG = "ViewGifImage";
- private String mFileDir;
- public static DisplayMetrics dm;
- ImageView gifView;
+public class ViewGifImage extends Activity {
+ private static final String TAG = "ViewGifImage";
+ public static final String VIEW_GIF_ACTION = "com.android.gallery3d.VIEW_GIF";
- static final int WIDTH = 320;
- static final int HEIGHT = 480;
+ public static DisplayMetrics mDM;
-
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg)
- {
- super.handleMessage(msg);
- }
- };
+ private ImageView mGifView;
@Override
- public void onCreate(Bundle savedInstanceState)
- {
- //android.util.Log.d(TAG, "=== onCreate() ===");
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_gif_image);
- dm = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(dm);
- //add end
- if(getIntent().getAction() != null
- && getIntent().getAction().equals("hisense.view_gif_image")){
- // i am called by gallery3D or other apps who want to show a 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);
-
- return;
}
-
- mFileDir = getIntent().getStringExtra("file_dir");
- Uri gifUri = getIntent().getData();
-
-
- showGifPicture(gifUri);
-
-
}
@Override
- public void onResume()
- {
- super.onResume();
- }
-
- @Override
- protected void onStop()
- {
+ protected void onStop() {
super.onStop();
finish();
}
-
- @Override
- protected void onDestroy() {
- if (gifView != null){
- if (gifView instanceof GIFView)
- {
- ((GIFView)gifView).freeMemory();
- gifView = null;
- }
- }
- super.onDestroy();
- }
- private void showGifPicture(Uri gifUri){
- gifView = new GIFView(this);
- ((LinearLayout)findViewById(R.id.image_absoluteLayout)).addView(gifView, new LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- if(!((GIFView)gifView).setDrawable(gifUri))
- 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(dm);
+ getWindowManager().getDefaultDisplay().getMetrics(mDM);
super.onConfigurationChanged(newConfig);
}
-
-
}