diff options
author | Likai Ding <likaid@codeaurora.org> | 2013-08-13 14:43:40 +0800 |
---|---|---|
committer | emancebo <emancebo@cyngn.com> | 2014-09-04 10:40:17 -0700 |
commit | 3ac7264b02d7c009607bbf9f25ab085abfee7293 (patch) | |
tree | 20d02cb735306ef3bebdeb447de7e96dc402e699 /src | |
parent | 10a68d3cfadf0b0e93e1c22e5f6b18bb85a0e810 (diff) | |
download | android_packages_apps_Gallery2-3ac7264b02d7c009607bbf9f25ab085abfee7293.tar.gz android_packages_apps_Gallery2-3ac7264b02d7c009607bbf9f25ab085abfee7293.tar.bz2 android_packages_apps_Gallery2-3ac7264b02d7c009607bbf9f25ab085abfee7293.zip |
Gallery2: support GIF animation
This change implements a Java GIF decoder.
Change-Id: I72b6e8eb25572bba77a2a46e1754d8db8c47a0cc
CRs-Fixed: 517575
Diffstat (limited to 'src')
-rwxr-xr-x[-rw-r--r--] | src/com/android/gallery3d/app/PhotoDataAdapter.java | 7 | ||||
-rwxr-xr-x[-rw-r--r--] | src/com/android/gallery3d/app/PhotoPage.java | 9 | ||||
-rwxr-xr-x[-rw-r--r--] | src/com/android/gallery3d/app/SinglePhotoDataAdapter.java | 5 | ||||
-rw-r--r-- | src/com/android/gallery3d/data/MediaItem.java | 1 | ||||
-rwxr-xr-x[-rw-r--r--] | src/com/android/gallery3d/ui/PhotoView.java | 21 | ||||
-rwxr-xr-x | src/com/android/gallery3d/util/GIFView.java | 511 | ||||
-rw-r--r--[-rwxr-xr-x] | src/com/android/gallery3d/util/GifAction.java | 10 | ||||
-rwxr-xr-x | src/com/android/gallery3d/util/GifDecoder.java | 1420 | ||||
-rwxr-xr-x | src/com/android/gallery3d/util/GifFrame.java | 31 | ||||
-rwxr-xr-x | src/com/android/gallery3d/util/ViewGifImage.java | 176 |
10 files changed, 1057 insertions, 1134 deletions
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java index 6fca527cb..1e47ab0b7 100644..100755 --- a/src/com/android/gallery3d/app/PhotoDataAdapter.java +++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java @@ -512,6 +512,13 @@ public class PhotoDataAdapter implements PhotoPage.Model { } @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 index 909fdd09c..7f4671364 100644..100755 --- 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.SelectionManager; 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, @@ -1146,6 +1147,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); @@ -1540,4 +1545,8 @@ public abstract class PhotoPage extends ActivityState implements } } + 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 index b5c424e08..e450ae0a1 100644..100755 --- a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java +++ b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java @@ -234,6 +234,11 @@ public class SinglePhotoDataAdapter extends TileImageViewAdapter } @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 59ea86551..92ac88dc6 100644 --- a/src/com/android/gallery3d/data/MediaItem.java +++ b/src/com/android/gallery3d/data/MediaItem.java @@ -37,6 +37,7 @@ public abstract class MediaItem extends MediaObject { 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 index fffb31910..586bd03f7 100644..100755 --- a/src/com/android/gallery3d/ui/PhotoView.java +++ b/src/com/android/gallery3d/ui/PhotoView.java @@ -93,6 +93,9 @@ public class PhotoView extends GLView { // 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 @@ public class PhotoView extends GLView { 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 @@ public class PhotoView extends GLView { 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 @@ public class PhotoView extends GLView { // 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 @@ public class PhotoView extends GLView { 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 @@ public class PhotoView extends GLView { 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 @@ public class PhotoView extends GLView { 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(); @@ -1857,4 +1861,5 @@ public class PhotoView extends GLView { } return effect; } + } diff --git a/src/com/android/gallery3d/util/GIFView.java b/src/com/android/gallery3d/util/GIFView.java index 9eeb86bd4..3fdd0a535 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 android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-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{
-
- private static final String TAG = "GIFView";
-
- private GifDecoder gifDecoder = null;
-
- private Bitmap currentImage = null;
-
- private static boolean isRun = false;
-
- private static boolean pause = true;
-
- private int W;
-
- private int H;
-
- private DrawThread drawThread = null;
-
- Uri mUri;
- private Context mContext;
-
- public GIFView(Context context) {
- super(context);
- mContext=context;
-
- }
-
- 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;
- try {
- input = cr.openInputStream(uri);
-
- if (input instanceof FileInputStream) {
- FileInputStream f = (FileInputStream) input;
- mSize = (int) f.getChannel().size();
- } else {
- while (-1 != input.read()) {
- mSize++;
- }
- }
-
- } catch (IOException e) {
-
- } finally {
-
- }
- //wss , return if file is invalid
- if(mSize == 0){
- return false;
- }
-
- 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;
- }
-
- 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;
- }
-
- private void setGifDecoderImage(InputStream is){
- if(gifDecoder != null){
- gifDecoder.free();
- gifDecoder= null;
- }
- gifDecoder = new GifDecoder(is,this);
- gifDecoder.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){
- return;
- }
-
- if(currentImage == null){
- currentImage = gifDecoder.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
- 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 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);
- }
- else
- {
- //h small, w big;
- newHeight = (imageHeight * newWidth)/imageWidth;
- dRect = new Rect(0, 0, newWidth, newHeight);
- }
- canvas.drawBitmap(currentImage, sRect, dRect, null);
-
- }
- 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);
- }
-
- 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();
- }
- }
- }
- }else{
- Log.e("gif","parse error");
- }
- }
-
- private Handler redrawHandler = new Handler(){
- public void handleMessage(Message msg) {
- invalidate();
- }
- };
-
- private class DrawThread extends Thread{
- public void run(){
- if(gifDecoder == 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{
- break;
- }
- }
- isRun = true;
- pause = false;
- }
- }
- public void freeMemory()
- {
- isRun = false;
- pause = true;
- if (drawThread != null)
- {
- //drawThread.isStop = true;
- drawThread = null;
- }
- if (gifDecoder != null)
- {
- Log.w(TAG," free");
- gifDecoder.free();
- gifDecoder = null;
- }
- }
-}
-
-
-
+package com.android.gallery3d.util; + +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.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() { + if (mGifDecoder == null) { + return; + } + + 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); + } + } + } + + } + + 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/GifAction.java b/src/com/android/gallery3d/util/GifAction.java index 1eee020cd..88e3cdee0 100755..100644 --- 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);
-}
+package com.android.gallery3d.util; + +public interface GifAction { + 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 d67e416f8..4235fc5a5 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{
-
- 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 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
-
- private int[] gct; // global color table
- private int[] lct; // local color table
- private int[] act; // active color table
-
- private int bgIndex; // background color index
- private int bgColor; // background color
- private int lastBgColor; // previous bg color
- private int pixelAspect; // pixel aspect ratio
-
- private boolean lctFlag; // local color table flag
- private boolean interlace; // interlace flag
- private int lctSize; // local color table size
-
- 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 boolean isShow = false;
-
-
- 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 static final int MaxStackSize = 4096;
- // max decoder pixel stack size
-
- // LZW decoder working arrays
- private short[] prefix;
- private byte[] suffix;
- private byte[] pixelStack;
- private byte[] pixels;
-
- private GifFrame gifFrame; // frames read from current file
- private int frameCount;
-
- private GifAction action = null;
-
-
- private byte[] gifData = null;
-
-
- public GifDecoder(byte[] data,GifAction act){
- gifData = data;
- action = act;
- }
-
- public GifDecoder(InputStream is,GifAction act){
- in = is;
- action = act;
- }
-
- 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();
- }
- 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;
- }
-
- public Bitmap getImage() {
- return getFrameImage(0);
- }
-
- public int getLoopCount() {
- return loopCount;
- }
-
- 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;
- }
- }
- }
- }
- }
-
- // 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 Bitmap getFrameImage(int n) {
- GifFrame frame = getFrame(n);
- if (frame == null)
- return null;
- else
- return frame.image;
- }
-
- 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;
- }
-
- 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;
- }
- }
-
- 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;
- }
-
- 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;
-
- 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;
- }
-
- // 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;
-
- // 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;
- }
-
- // 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 boolean err() {
- return status != STATUS_PARSING;
- }
-
- private void init() {
- status = STATUS_PARSING;
- frameCount = 0;
- gifFrame = null;
- gct = null;
- lct = null;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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;
- }
- }
- }
-
- 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
- }
-
- 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 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;
- }
- 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) {
- 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 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 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;
- }
-
- /**
- * Skips variable length blocks up to and including next zero length block.
- */
- private void skip() {
- do {
- readBlock();
- } while ((blockSize > 0) && !err());
- }
-}
+package com.android.gallery3d.util; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +public class GifDecoder extends Thread { + + 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 mIS; + private int mStatus; + + 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 int[] mGct; // global color table + private int[] mLct; // local color table + private int[] mAct; // active color table + + 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 mLctFlag; // local color table flag + private boolean mInterlace; // interlace flag + private int mLctSize; // local color table size + + 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 boolean mIsShow = false; + + 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 + + // max decoder pixel stack size + private static final int MaxStackSize = 4096; + + // LZW decoder working arrays + private short[] mPrefix; + private byte[] mSuffix; + private byte[] mPixelStack; + private byte[] mPixels; + + private GifFrame mGifFrame; // frames read from current file + private int mFrameCount; + + 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; + } + } + return mDelay; + } + + 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 getFrameCount() { + return mFrameCount; + } + + public Bitmap getImage() { + return getFrameImage(0); + } + + public int getLoopCount() { + return mLoopCount; + } + + 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; + } + } + } + } + } + + // 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 Bitmap getFrameImage(int n) { + GifFrame frame = getFrame(n); + if (frame == null) { + return null; + } else { + return frame.mImage; + } + } + + public GifFrame getCurrentFrame() { + return mCurrentFrame; + } + + 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; + } + + 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; + } + + public void reset() { + mCurrentFrame = mGifFrame; + } + + 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; + } + } + + private int readByte() { + mIS = new ByteArrayInputStream(mGifData); + mGifData = null; + return readStream(); + } + + 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 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; + + 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; + } + + // 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; + + // 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; + } + + // 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 boolean err() { + return mStatus != STATUS_PARSING; + } + + private void init() { + mStatus = STATUS_PARSING; + mFrameCount = 0; + mGifFrame = null; + mGct = null; + mLct = null; + } + + private int read() { + int curByte = 0; + try { + 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 + 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 (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 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 ((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 54ddd31d8..87d58a40d 100755 --- a/src/com/android/gallery3d/util/GifFrame.java +++ b/src/com/android/gallery3d/util/GifFrame.java @@ -1,14 +1,17 @@ -package com.android.gallery3d.util;
-
-import android.graphics.Bitmap;
-
-public class GifFrame {
- public GifFrame(Bitmap im, int del) {
- image = im;
- delay = del;
- }
-
- public Bitmap image;
- public int delay;
- public GifFrame nextFrame = null;
-}
+package com.android.gallery3d.util; + +import android.graphics.Bitmap; + +public class GifFrame { + + 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; + } +} diff --git a/src/com/android/gallery3d/util/ViewGifImage.java b/src/com/android/gallery3d/util/ViewGifImage.java index ed765b13a..cdd509280 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 android.app.Activity;
-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;
-
-public class ViewGifImage extends Activity
-{
- public static final String TAG = "ViewGifImage";
- private String mFileDir;
- public static DisplayMetrics dm;
- ImageView gifView;
-
- static final int WIDTH = 320;
- static final int HEIGHT = 480;
-
-
- private Handler mHandler = new Handler() {
- public void handleMessage(Message msg)
- {
- super.handleMessage(msg);
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- //android.util.Log.d(TAG, "=== onCreate() ===");
- 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
- Uri gifUri = getIntent().getData();
- showGifPicture(gifUri);
-
- return;
- }
-
- mFileDir = getIntent().getStringExtra("file_dir");
- Uri gifUri = getIntent().getData();
-
-
- showGifPicture(gifUri);
-
-
- }
-
- @Override
- public void onResume()
- {
- super.onResume();
- }
-
+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.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 - 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
- public void onConfigurationChanged(Configuration newConfig) {
- getWindowManager().getDefaultDisplay().getMetrics(dm);
- super.onConfigurationChanged(newConfig);
- }
-
-
-}
+ 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); + } +} |