http://blog.csdn.net/yihongyuelan/article/details/8045748

概要

前两篇文章:

《Android 2.3 Gallery3D添加gif支持——概要(一)》

《Android 2.3 Gallery3D添加gif支持——图片显示(二)》

看过Gallery3D代码的童鞋都知道,其代码不仅多而且很复杂,因此对于代码这里不会做过于详细的分析,重点是流程。毕竟关键的方法就那么几个,其他修改不外乎为了完善它,仅此而已。

欢迎转载,请务必注明出处:http://blog.csdn.net/yihongyuelan/

对于让Android显示gif图片,其实有很多种做法,比如:

(1). 以Java的方式对gif进行解析并显示。

在Gallery3D中,当获取到该图片为gif格式时,调用一套单独的解析显示流程。解析包括获取当前gif中每一张图片,以及每一张图片之间的显示间隔等等;显示包括用一个循环的方式让每一张图片。当然我们可以将解析方式以Jar包的方式引入,网上的现成例子有很多。

(2).以C/C++的方式对gif进行解析并显示。

Android 2.3 集成了Skia这个强大的图形显示引擎,有多强大呢?(那么那么强大!!o(╯□╰)o还是自己百度吧),Skia是支持对gif解析的。但为什么google没有使用Skia来进行gif解析呢?这是有多方面原因的,Android默认开发都是针对模拟器的,但模拟器的性能大家都知道吧,CPU资源少,内存资源少等等,总的来讲google为了保证模拟器的“正常运行”就没有加入gif的显示。

回到文章主题,也就是说我们可以通过Skia对gif进行解码,然后修改一下Gallery3D显示逻辑,从而完成gif的显示。这样子不用我说,大家也知道效率比Java高很多倍了吧!!既然能够通过Skia来解析gif图片,自然可以通过类似的方式来改造ImageView等等。

基本框架

本文采用第二种方案,大致流程如图1所示:

图1

框架解析

通过图1我们可以知道,我们需要修改的代码包括三个部分,Gallery3D以及Framework中添加接口,最后调用Native方法去解析gif并返回。整个流程看似简单,但要完善整个功能需要修改的文件多达15个啊( ⊙ o ⊙ )!废话不多讲,我们先从APP层的Gallery3D入手。

APP层

这里所说的APP层,实际上就是com.android.cooliris下的修改。位于:AndroidSource/packages/appa/Gallery3D
主要思路:Gallery3D是如何显示一张图片的?
无论是jpeg还是bmp,当我们在Gallery3D中点击缩略图时,它们走的流程应该是一致的,即根据缩略图信息查找到数据库中对应的真实地址并对图片进行解析。因此在Gallery3D需要显示一张图片时,如果我判断到当前的图片类型是gif格式,那么我们让该gif图片走一套单独的显示流程即可完成gif的播放。这套独立的显示流程包括以下几步:
(1). 检测图片类型,即判断是否是gif图片(isGifImage)。
(2). 如果是gif图片,那么我需要为播放gif图片做好准备,解析图片(decodeOneFrame)、一张图片显示时长(getFrameDuration)。
(3). 准备完成之后,需要有播放(playGif)和停止播放(stopGif)。
(4). 显示完一张gif中的图片之后,需要显示下一张图片(displayNextFrame)。
整个流程如图2:
图2
单独显示一张图片,最终调用到com/cooliris/media/GridDrawManager.java中的drawFocusItems(),以下代码“+”号表示添加代码,“-”表示删除代码,如下:
[java] view plaincopy
  1. void drawFocusItems(RenderView view, GL11 gl, float zoomValue, boolean slideshowMode, float timeElapsedSinceView) {
  2. int selectedSlotIndex = mSelectedSlot;
  3. GridDrawables drawables = mDrawables;
  4. int firstBufferedVisibleSlot = mBufferedVisibleRange.begin;
  5. int lastBufferedVisibleSlot = mBufferedVisibleRange.end;
  6. boolean isCameraZAnimating = mCamera.isZAnimating();
  7. +        //如果不满足gif播放条件则停止播放
  8. +        boolean playGifMode = false;
  9. for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
  10. +               DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
  11. +               if (i != selectedSlotIndex && displayItem != null) {
  12. +                displayItem.stopGif();
  13. +            }
  14. if (selectedSlotIndex != Shared.INVALID && (i >= selectedSlotIndex - 2 && i <= selectedSlotIndex + 2)) {
  15. continue;
  16. }
  17. -            DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
  18. if (displayItem != null) {
  19. displayItem.clearScreennailImage();
  20. }
  21. return;
  22. }
  23. boolean focusItemTextureLoaded = false;
  24. -            Texture centerTexture = centerDisplayItem.getScreennailImage(view.getContext());
  25. +            //获取图片纹理信息
  26. +            Texture centerTexture = centerDisplayItem.getScreennailImage(view);
  27. if (centerTexture != null && centerTexture.isLoaded()) {
  28. focusItemTextureLoaded = true;
  29. }
  30. view.setAlpha(1.0f);
  31. gl.glEnable(GL11.GL_BLEND);
  32. gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
  33. +            int selectedIndexInDrawnArray = (selectedSlotIndex - firstBufferedVisibleSlot)
  34. +                    * GridLayer.MAX_ITEMS_PER_SLOT;
  35. +            DisplayItem selectedDisplayItem = displayItems[selectedIndexInDrawnArray];
  36. +            MediaItem selectedItem = selectedDisplayItem.mItemRef;
  37. +            if (selectedItem.isGifImage() && !slideshowMode) {
  38. +                playGifMode = true;
  39. +                mDisplayGifItem = selectedDisplayItem;
  40. +            }
  41. float backupImageTheta = 0.0f;
  42. for (int i = -1; i <= 1; ++i) {
  43. if (slideshowMode && timeElapsedSinceView > 1.0f && i != 0)
  44. continue;
  45. +                //添加是否是GifMode判断条件
  46. +                if (playGifMode && i != 0) {
  47. +                    continue;
  48. +                }
  49. float viewAspect = camera.mAspectRatio;
  50. int selectedSlotToUse = selectedSlotIndex + i;
  51. if (selectedSlotToUse >= 0 && selectedSlotToUse <= lastBufferedVisibleSlot) {
  52. DisplayItem displayItem = displayItems[indexInDrawnArray];
  53. MediaItem item = displayItem.mItemRef;
  54. final Texture thumbnailTexture = displayItem.getThumbnailImage(view.getContext(), sThumbnailConfig);
  55. -                    Texture texture = displayItem.getScreennailImage(view.getContext());
  56. -                    if (isCameraZAnimating && (texture == null || !texture.isLoaded())) {
  57. +                    //获取下一张图片
  58. +                    Texture texture = displayItem.getScreennailImage(view);
  59. +                    Texture nextFrameTexture = displayItem.getNextFrame();
  60. +                    if (playGifMode) {
  61. +                        long now = SystemClock.uptimeMillis();
  62. +                        if ((now - mPreTime) > item.getFrameDuration() && nextFrameTexture != null
  63. +                                && nextFrameTexture.isLoaded()) {
  64. +                            item.displayNextFrame();
  65. +                            mPreTime = now;
  66. +                        }
  67. +                        view.requestRender();
  68. +                    }
  69. +                    //添加是否是GifMode判断条件
  70. +                    if (!playGifMode && isCameraZAnimating && (texture == null || !texture.isLoaded())) {
  71. texture = thumbnailTexture;
  72. mSelectedMixRatio.setValue(0f);
  73. mSelectedMixRatio.animateValue(1f, 0.75f, view.getFrameTime());
  74. }
  75. -                    Texture hiRes = (zoomValue != 1.0f && i == 0 && item.getMediaType() != MediaItem.MEDIA_TYPE_VIDEO) ? displayItem
  76. +                    //添加是否是GifMode判断条件
  77. +                    Texture hiRes = (!playGifMode && zoomValue != 1.0f && i == 0 && item.getMediaType() != MediaItem.MEDIA_TYPE_VIDEO) ? displayItem
  78. .getHiResImage(view.getContext())
  79. : null;
  80. if (App.PIXEL_DENSITY > 1.0f) {
  81. }
  82. }
  83. texture = thumbnailTexture;
  84. -                        if (i == 0) {
  85. +                        //添加是否是GifMode判断条件
  86. +                        if (i == 0 && !playGifMode) {
  87. mSelectedMixRatio.setValue(0f);
  88. mSelectedMixRatio.animateValue(1f, 0.75f, view.getFrameTime());
  89. -                        if (i == 0) {
  90. +                        //添加是否是GifMode判断条件
  91. +                        if (i == 0 && !playGifMode) {
  92. mSelectedMixRatio.setValue(0f);
  93. -                        if (i == 0) {
  94. -                        if (i == 0) {
  95. +                        //添加是否是GifMode判断条件
  96. +                        if (i == 0 && !playGifMode) {
  97. mSelectedMixRatio.setValue(0f);
  98. mSelectedMixRatio.animateValue(1f, 0.75f, view.getFrameTime());
  99. +                        //判断纹理是否已经加载,如果是则预先解析第二祯图片
  100. +                        }
  101. +                    } else if (texture.isLoaded()) {
  102. +                        if (playGifMode && nextFrameTexture == null) {
  103. +                            mPreTime = SystemClock.uptimeMillis();
  104. +                            displayItem.preDecodeNextFrame(view);
  105. }
  106. }
  107. if (mCamera.isAnimating() || slideshowMode) {
  108. continue;
  109. }
  110. }
  111. -                    int theta = (int) displayItem.getImageTheta();
  112. +                    //取绝对值
  113. +                    int theta = Math.abs((int) displayItem.getImageTheta());
  114. // If it is in slideshow mode, we draw the previous item in
  115. // the next item's position.
  116. if (slideshowMode && timeElapsedSinceView < 1.0f && timeElapsedSinceView != 0) {
  117. int vboIndex = i + 1;
  118. float alpha = view.getAlpha();
  119. float selectedMixRatio = mSelectedMixRatio.getValue(view.getFrameTime());
  120. -                        if (selectedMixRatio != 1f) {
  121. +                        //添加是否是GifMode判断条件
  122. +                        if (selectedMixRatio != 1f && !playGifMode) {
  123. texture = thumbnailTexture;
  124. view.setAlpha(alpha * (1.0f - selectedMixRatio));
  125. }
  126. view.unbindMixed();
  127. }
  128. }

这就是图2中所表达的gif图片显示逻辑,但整个APP层需要修改的地方有很多,如图3所示:

图3
(注:这些修改并不是一次完成的,因此有的方法可能并没有使用,但也没有删除,比如MediaItem.java中的decodeNextFrame、startPlay等等,这些方法都是可以删掉的)
虽然看起来那么复杂,但是我们一定要记清楚原始思路“Gallery3D如何显示一张图片”。通过前面的分析我们知道了大致需要4步,这4步的内容正好添加在MediaItem.java中,我们来看看这几步吧,代码如下:
[java] view plaincopy
  1. +    //判断是否是gif图片
  2. +    public boolean isGifImage() {
  3. +        return ("image/gif".equals(mMimeType));
  4. +    }
  5. +    //播放gif
  6. +    public boolean playGif() {
  7. +        return mPlayGif;
  8. +    }
  9. +    //获取播放索引
  10. +    public int getDisplayIndex() {
  11. +        return mDisplayIndex;
  12. +    }
  13. +    //获取解码索引
  14. +    public int getDecodeIndex() {
  15. +        return mDecodeIndex;
  16. +    }
  17. +    //获取下一帧图片
  18. +    public void displayNextFrame() {
  19. +        if (mGifDecoder != null) {
  20. +            int count = mGifDecoder.getTotalFrameCount() - 1;
  21. +            if (mDisplayIndex < count) {
  22. +                mDisplayIndex++;
  23. +            } else if (mDisplayIndex == count) {
  24. +                mDisplayIndex = 0;
  25. +            }
  26. +            mTextureNeedChange = true;
  27. +        }
  28. +    }
  29. +    //纹理需要改变
  30. +    public boolean textureNeedChange() {
  31. +        return mTextureNeedChange;
  32. +    }
  33. +    //纹理已经改变
  34. +    public void textureChanged() {
  35. +        mTextureNeedChange = false;
  36. +    }
  37. +    //获取gif解码器,通过底层必经之路
  38. +    public synchronized GifDecoder getGifDecoder(Context context) {
  39. +        if (mGifDecoder == null) {
  40. +            InputStream inputStream = null;
  41. +            ContentResolver cr = context.getContentResolver();
  42. +            try {
  43. +                inputStream = cr.openInputStream(Uri.parse(mContentUri));
  44. +                mGifDecoder = new GifDecoder(inputStream);
  45. +                inputStream.close();
  46. +            } catch (FileNotFoundException e) {
  47. +                // TODO Auto-generated catch block
  48. +                e.printStackTrace();
  49. +            } catch (IOException e) {
  50. +                e.printStackTrace();
  51. +            }
  52. +        }
  53. +        return mGifDecoder;
  54. +    }
  55. +    //获取单张图片显示时间
  56. +    public int getFrameDuration() {
  57. +        if (mGifDecoder != null) {
  58. +            return mGifDecoder.getFrameDuration(mDisplayIndex);
  59. +        }
  60. +        return Shared.INFINITY;
  61. +    }
  62. +    //解析一张图片
  63. +    public synchronized Bitmap decodeOneFrame() {
  64. +        Bitmap bitmap = null;
  65. +        if (mGifDecoder != null) {
  66. +            int count = mGifDecoder.getTotalFrameCount() - 1;
  67. +            bitmap = mGifDecoder.getFrameBitmap(mDecodeIndex);
  68. +            // draw the bitmap to a white canvas
  69. +            bitmap = Util.whiteBackground(bitmap);
  70. +            if (mDecodeIndex < count) {
  71. +                mDecodeIndex++;
  72. +            } else if (mDecodeIndex == count) {
  73. +                mDecodeIndex = 0;
  74. +            }
  75. +        }
  76. +        return bitmap;
  77. +    }
  78. +    停止播放
  79. +    public void stopGif() {
  80. +        if (mGifDecoder != null) {
  81. +            mGifDecoder.close();
  82. +            mGifDecoder = null;
  83. +        }
  84. +        mPlayGif = false;
  85. +        mDisplayIndex = 0;
  86. +        mDecodeIndex = 0;
  87. +        mTextureNeedChange = false;
  88. +    }

Framework层

说到Framework层,这是里关键的接口,同时起到了穿针引线 承上启下的作用。也就是我们的GifDecoer.java,这是新添加的类,位于AndroidSourceframeworks/base/graphics/java/android/graphics/GifDecoder.java,代码如下:
[java] view plaincopy
  1. package android.graphics;
  2. import java.io.InputStream;
  3. import java.io.FileInputStream;
  4. import java.io.BufferedInputStream;
  5. import android.util.Log;
  6. import android.graphics.Bitmap;
  7. public class GifDecoder {
  8. /**
  9. * Specify the minimal frame duration in GIF file, unit is ms.
  10. * Set as 100, then gif animation hehaves mostly as other gif viewer.
  11. */
  12. private static final int MINIMAL_DURATION = 100;
  13. /**
  14. * Invalid returned value of some memeber function, such as getWidth()
  15. * getTotalFrameCount(), getFrameDuration()
  16. */
  17. public static final int INVALID_VALUE = 0;
  18. /**
  19. * Movie object maitained by GifDecoder, it contains raw GIF info
  20. * like graphic control informations, color table, pixel indexes,
  21. * called when application is no longer interested in gif info.
  22. * It is contains GIF frame bitmap, 8-bits per pixel,
  23. * using SkColorTable to specify the colors, which is much
  24. * memory efficient than ARGB_8888 config. This is why we
  25. * maintain a Movie object rather than a set of ARGB_8888 Bitmaps.
  26. */
  27. private Movie mMovie;
  28. /**
  29. * Constructor of GifDecoder, which receives InputStream as
  30. * parameter. Decode an InputStream into Movie object.
  31. * If the InputStream is null, no decoding will be performed
  32. *
  33. * @param is InputStream representing the file to be decoded.
  34. */
  35. public GifDecoder(InputStream is) {
  36. if (is == null)
  37. return;
  38. mMovie = Movie.decodeStream(is);
  39. }
  40. public GifDecoder(byte[] data, int offset,int length) {
  41. if (data == null)
  42. return;
  43. mMovie = Movie.decodeByteArray(data, offset, length);
  44. }
  45. /**
  46. * Constructor of GifDecoder, which receives file path name as
  47. * parameter. Decode a file path into Movie object.
  48. * If the specified file name is null, no decoding will be performed
  49. *
  50. * @param pathName complete path name for the file to be decoded.
  51. */
  52. public GifDecoder(String pathName) {
  53. if (pathName == null)
  54. return;
  55. mMovie = Movie.decodeFile(pathName);
  56. }
  57. /**
  58. * Close gif file, release all informations like frame count,
  59. * graphic control informations, color table, pixel indexes,
  60. * called when application is no longer interested in gif info.
  61. * It will release all the memory mMovie occupies. After close()
  62. * is call, GifDecoder should no longer been used.
  63. */
  64. public synchronized void close(){
  65. if (mMovie == null)
  66. return;
  67. mMovie.closeGif();
  68. mMovie = null;
  69. }
  70. /**
  71. * Get width of images in gif file.
  72. * if member mMovie is null, returns INVALID_VALUE
  73. *
  74. * @return The total frame count of gif file,
  75. *         or INVALID_VALUE if the mMovie is null
  76. */
  77. public synchronized int getWidth() {
  78. if (mMovie == null)
  79. return INVALID_VALUE;
  80. return mMovie.width();
  81. }
  82. /**
  83. * Get height of images in gif file.
  84. * if member mMovie is null, returns INVALID_VALUE
  85. *
  86. * @return The total frame count of gif file,
  87. *         or INVALID_VALUE if the mMovie is null
  88. */
  89. public synchronized int getHeight() {
  90. if (mMovie == null)
  91. return INVALID_VALUE;
  92. return mMovie.height();
  93. }
  94. /**
  95. * Get total duration of gif file.
  96. * if member mMovie is null, returns INVALID_VALUE
  97. *
  98. * @return The total duration of gif file,
  99. *         or INVALID_VALUE if the mMovie is null
  100. */
  101. public synchronized int getTotalDuration() {
  102. if (mMovie == null)
  103. return INVALID_VALUE;
  104. return mMovie.duration();
  105. }
  106. /**
  107. * Get total frame count of gif file.
  108. * if member mMovie is null, returns INVALID_VALUE
  109. *
  110. * @return The total frame count of gif file,
  111. *         or INVALID_VALUE if the mMovie is null
  112. */
  113. public synchronized int getTotalFrameCount() {
  114. if (mMovie == null)
  115. return INVALID_VALUE;
  116. return mMovie.gifTotalFrameCount();
  117. }
  118. /**
  119. * Get frame duration specified with frame index of gif file.
  120. * if member mMovie is null, returns INVALID_VALUE
  121. *
  122. * @param frameIndex index of frame interested.
  123. * @return The duration of the specified frame,
  124. *         or INVALID_VALUE if the mMovie is null
  125. */
  126. public synchronized int getFrameDuration(int frameIndex) {
  127. if (mMovie == null)
  128. return INVALID_VALUE;
  129. int duration = mMovie.gifFrameDuration(frameIndex);
  130. if (duration < MINIMAL_DURATION)
  131. duration = MINIMAL_DURATION;
  132. return duration;
  133. }
  134. /**
  135. * Get frame bitmap specified with frame index of gif file.
  136. * if member mMovie is null, returns null
  137. *
  138. * @param frameIndex index of frame interested.
  139. * @return The decoded bitmap, or null if the mMovie is null
  140. */
  141. public synchronized Bitmap getFrameBitmap(int frameIndex) {
  142. if (mMovie == null)
  143. return null;
  144. return mMovie.gifFrameBitmap(frameIndex);
  145. }
  146. }

其中都有注释,这里就不细说了。通过查看代码可以知道,实际上我们的GifDecoder调用的是Movie。也就是AndroidSource/frameworks/base/graphics/java/android/graphics/Movie.java。这里为什么调用Movie.java呢?仔细查看可以知道,我们的GifDecoder调用了Movie中的decodeStream方法,但是还有很多通往底层的接口没有打通,怎么办呢?与其另起炉灶不如就借Movie.java的地,直接声明引用Native方法。当然我们是可以自己写一个,不过有现成的干嘛不用呢?Movie.java代码如下:

[java] view plaincopy
  1. public class Movie {
  2. private final int mNativeMovie;
  3. public native boolean setTime(int relativeMilliseconds);
  4. public native void draw(Canvas canvas, float x, float y, Paint paint);
  5. +    //这里添加我们通往底层的接口
  6. +    //please see GifDecoder for information
  7. +    public native int gifFrameDuration(int frameIndex);
  8. +    public native int gifTotalFrameCount();
  9. +    public native Bitmap gifFrameBitmap(int frameIndex);
  10. +    public native void closeGif();
  11. public void draw(Canvas canvas, float x, float y) {
  12. draw(canvas, x, y, null);
  13. }
  14. -    public static native Movie decodeStream(InputStream is);
  15. +    //这里的修改是为了让解码器正常工作
  16. +
  17. +    public static Movie decodeStream(InputStream is) {
  18. +        // we need mark/reset to work properly
  19. +
  20. +        if (!is.markSupported()) {
  21. +            //the size of Buffer is aligned with BufferedInputStream
  22. +            // used in BitmapFactory of Android default version.
  23. +            is = new BufferedInputStream(is, 8*1024);
  24. +        }
  25. +        is.mark(1024);
  26. +
  27. +        return decodeMarkedStream(is);
  28. +    }
  29. +
  30. +//    public static native Movie decodeStream(InputStream is);
  31. +    private static native Movie decodeMarkedStream(InputStream is);

在Framework层中的Java修改就主要涉及到GifDecoder.java和Movie.java,如图4:

图4

Native层

在Native层中,实际上这里算不上什么层不层的,只是都是C/C++代码,姑且叫之Native层,首先看到AndroidSource/frameworks/base/core/jni/android/graphics/Movie.cpp中的修改,这里面的修改对应的就是Movie.java中的调用的native方法。修改如下:
[java] view plaincopy
  1. +//please see Movie.java for information
  2. +static int movie_gifFrameDuration(JNIEnv* env, jobject movie, int frameIndex) {
  3. +    NPE_CHECK_RETURN_ZERO(env, movie);
  4. +    SkMovie* m = J2Movie(env, movie);
  5. +//LOGE("Movie:movie_gifFrameDuration: frame number %d, duration is %d", frameIndex,m->getGifFrameDuration(frameIndex));
  6. +    return m->getGifFrameDuration(frameIndex);
  7. +}
  8. +
  9. +static jobject movie_gifFrameBitmap(JNIEnv* env, jobject movie, int frameIndex) {
  10. +    NPE_CHECK_RETURN_ZERO(env, movie);
  11. +    SkMovie* m = J2Movie(env, movie);
  12. +    int averTimePoint = 0;
  13. +    int frameDuration = 0;
  14. +    int frameCount = m->getGifTotalFrameCount();
  15. +    if (frameIndex < 0 && frameIndex >= frameCount )
  16. +        return NULL;
  17. +    m->setCurrFrame(frameIndex);
  18. +//then we get frameIndex Bitmap (the current frame of movie is frameIndex now)
  19. +    SkBitmap *createdBitmap = m->createGifFrameBitmap();
  20. +    SkBitmap *createdBitmap = m->createGifFrameBitmap();
  21. +    if (createdBitmap != NULL)
  22. +    {
  23. +        return GraphicsJNI::createBitmap(env, createdBitmap, false, NULL);
  24. +    }
  25. +    else
  26. +    {
  27. +        return NULL;
  28. +    }
  29. +}
  30. +
  31. +static int movie_gifTotalFrameCount(JNIEnv* env, jobject movie) {
  32. +    NPE_CHECK_RETURN_ZERO(env, movie);
  33. +    SkMovie* m = J2Movie(env, movie);
  34. +//LOGE("Movie:movie_gifTotalFrameCount: frame count %d", m->getGifTotalFrameCount());
  35. +    return m->getGifTotalFrameCount();
  36. +}
  37. +
  38. +static void movie_closeGif(JNIEnv* env, jobject movie) {
  39. +    NPE_CHECK_RETURN_VOID(env, movie);
  40. +    SkMovie* m = J2Movie(env, movie);
  41. +//LOGE("Movie:movie_closeGif()");
  42. +    delete m;
  43. +}
  44. +
  45. static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {
  46. NPE_CHECK_RETURN_ZERO(env, istream);
  47. {   "setTime",  "(I)Z", (void*)movie_setTime  },
  48. {   "draw",     "(Landroid/graphics/Canvas;FFLandroid/graphics/Paint;)V",
  49. (void*)movie_draw  },
  50. -    { "decodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
  51. +//please see Movie.java for information
  52. +    {   "gifFrameDuration",     "(I)I",
  53. +                            (void*)movie_gifFrameDuration  },
  54. +    {   "gifFrameBitmap",   "(I)Landroid/graphics/Bitmap;",
  55. +                            (void*)movie_gifFrameBitmap  },
  56. +    {   "gifTotalFrameCount",   "()I",
  57. +                            (void*)movie_gifTotalFrameCount  },
  58. +    {   "closeGif",   "()V",
  59. +                            (void*)movie_closeGif  },
  60. +    { "decodeMarkedStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
  61. (void*)movie_decodeStream },
  62. +//    { "decodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
  63. +//                            (void*)movie_decodeStream },
  64. { "decodeByteArray", "([BII)Landroid/graphics/Movie;",
  65. (void*)movie_decodeByteArray },
  66. };

通过以上代码我们可以知道,实际上我又调用到了AndroidSource/external/skia/src/images/SkMovie.cpp以及AndroidSource/external/skia/src/images/SkMovie_gif.cpp中,先来看下SkMovie.cpp中的修改:

[java] view plaincopy
  1. +int SkMovie::getGifFrameDuration(int frameIndex)
  2. +{
  3. +    return 0;
  4. +}
  5. +
  6. +int SkMovie::getGifTotalFrameCount()
  7. +{
  8. +    return 0;
  9. +}
  10. +
  11. +bool SkMovie::setCurrFrame(int frameIndex)
  12. +{
  13. +    return true;
  14. +}
  15. +
  16. +SkBitmap* SkMovie::createGifFrameBitmap()
  17. +{
  18. +    //get default bitmap, create a new bitmap and returns.
  19. +    SkBitmap *copyedBitmap = new SkBitmap();
  20. +    bool copyDone = false;
  21. +    if (fNeedBitmap)
  22. +    {
  23. +        if (!this->onGetBitmap(&fBitmap))   // failure
  24. +            fBitmap.reset();
  25. +{
  26. +    return 0;
  27. +}
  28. +
  29. +int SkMovie::getGifTotalFrameCount()
  30. +{
  31. +    return 0;
  32. +}
  33. +
  34. +bool SkMovie::setCurrFrame(int frameIndex)
  35. +{
  36. +    return true;
  37. +}
  38. +}
  39. +
  40. +SkBitmap* SkMovie::createGifFrameBitmap()
  41. +{
  42. +    //get default bitmap, create a new bitmap and returns.
  43. +    SkBitmap *copyedBitmap = new SkBitmap();
  44. +    bool copyDone = false;
  45. +    if (fNeedBitmap)
  46. +    {
  47. +        if (!this->onGetBitmap(&fBitmap))   // failure
  48. +            fBitmap.reset();
  49. +    }
  50. +    //now create a new bitmap from fBitmap
  51. +    if (fBitmap.canCopyTo(SkBitmap::kARGB_8888_Config) )
  52. +    {
  53. +//LOGE("SkMovie:createGifFrameBitmap:fBitmap can copy to 8888 config, then copy...");
  54. +        copyDone = fBitmap.copyTo(copyedBitmap, SkBitmap::kARGB_8888_Config);
  55. +    }
  56. +    else
  57. +    {
  58. +        copyDone = false;
  59. +//LOGE("SkMovie:createGifFrameBitmap:fBitmap can NOT copy to 8888 config");
  60. +    }
  61. +
  62. +    if (copyDone)
  63. +    {
  64. +        return copyedBitmap;
  65. +    }
  66. +    else
  67. +    {
  68. +        return NULL;
  69. +    }
  70. +}

接下来是针对SkMovie_gif.cpp的修改(因为平台原因,这里直接贴代码),代码如下:

[java] view plaincopy
  1. #include "SkMovie.h"
  2. #include "SkColor.h"
  3. #include "SkColorPriv.h"
  4. #include "SkStream.h"
  5. #include "SkTemplates.h"
  6. #include "SkUtils.h"
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include "gif_lib.h"
  11. #include "utils/Log.h"
  12. class SkGIFMovie : public SkMovie {
  13. public:
  14. SkGIFMovie(SkStream* stream);
  15. virtual ~SkGIFMovie();
  16. protected:
  17. virtual bool onGetInfo(Info*);
  18. virtual bool onSetTime(SkMSec);
  19. virtual bool onGetBitmap(SkBitmap*);
  20. //please see Movie.cpp for information
  21. virtual int getGifFrameDuration(int frameIndex);
  22. virtual int getGifTotalFrameCount();
  23. virtual bool setCurrFrame(int frameIndex);
  24. private:
  25. bool checkGifStream(SkStream* stream);
  26. bool getWordFromStream(SkStream* stream,int* word);
  27. bool getRecordType(SkStream* stream,GifRecordType* Type);
  28. bool checkImageDesc(SkStream* stream,char* buf);
  29. bool skipExtension(SkStream* stream,char* buf);
  30. bool skipComment(SkStream* stream,char* buf);
  31. bool skipGraphics(SkStream* stream,char* buf);
  32. bool skipPlaintext(SkStream* stream,char* buf);
  33. bool skipApplication(SkStream* stream,char* buf);
  34. bool skipSubblocksWithTerminator(SkStream* stream,char* buf);
  35. private:
  36. GifFileType* fGIF;
  37. int fCurrIndex;
  38. int fLastDrawIndex;
  39. SkBitmap fBackup;
  40. };
  41. static int Decode(GifFileType* fileType, GifByteType* out, int size) {
  42. SkStream* stream = (SkStream*) fileType->UserData;
  43. return (int) stream->read(out, size);
  44. }
  45. SkGIFMovie::SkGIFMovie(SkStream* stream)
  46. {
  47. int streamLength = stream->getLength();
  48. //if length of SkStream is below zero, no need for further parsing
  49. if (streamLength <= 0) {
  50. LOGE("SkGIFMovie:SkGIFMovie: GIF source file length is below 0");
  51. return;
  52. }
  53. //allocate a buffer to hold content of SkStream
  54. void * streamBuffer = malloc(streamLength+1);
  55. if (streamBuffer == 0) {
  56. LOGE("SkGIFMovie:SkGIFMovie: malloc Memory stream buffer failed");
  57. return;
  58. }
  59. //Fetch SkStream content into buffer
  60. if (streamLength != stream->read(streamBuffer, stream->getLength())) {
  61. LOGE("SkGIFMovie:SkGIFMovie: read GIF source to Memory Buffer failed");
  62. free(streamBuffer);
  63. return;
  64. }
  65. //we wrap stream with SkmemoryStream, cause
  66. //its rewind does not make mark on InputStream be
  67. //invalid.
  68. SkStream* memStream = new SkMemoryStream(streamBuffer,streamLength);
  69. bool bRewindable = memStream->rewind();
  70. if (bRewindable)
  71. {
  72. //check if GIF file is valid to decode
  73. bool bGifValid = checkGifStream(memStream);
  74. if (! bGifValid)
  75. {
  76. free(streamBuffer);
  77. fGIF = NULL;
  78. return;
  79. }
  80. //GIF file stream seems to be OK,
  81. // rewind stream for gif decoding
  82. memStream->rewind();
  83. }
  84. fGIF = DGifOpen( memStream, Decode );
  85. if (NULL == fGIF) {
  86. free(streamBuffer);
  87. return;
  88. }
  89. if (DGifSlurp(fGIF) != GIF_OK)
  90. {
  91. DGifCloseFile(fGIF);
  92. fGIF = NULL;
  93. }
  94. fCurrIndex = -1;
  95. fLastDrawIndex = -1;
  96. //release stream buffer when decoding is done.
  97. free(streamBuffer);
  98. }
  99. SkGIFMovie::~SkGIFMovie()
  100. {
  101. if (fGIF)
  102. DGifCloseFile(fGIF);
  103. }
  104. static SkMSec savedimage_duration(const SavedImage* image)
  105. {
  106. for (int j = 0; j < image->ExtensionBlockCount; j++)
  107. {
  108. if (image->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE)
  109. {
  110. int size = image->ExtensionBlocks[j].ByteCount;
  111. SkASSERT(size >= 4);
  112. const uint8_t* b = (const uint8_t*)image->ExtensionBlocks[j].Bytes;
  113. return ((b[2] << 8) | b[1]) * 10;
  114. }
  115. }
  116. return 0;
  117. }
  118. int SkGIFMovie::getGifFrameDuration(int frameIndex)
  119. {
  120. //for wrong frame index, return 0
  121. if (frameIndex < 0 || NULL == fGIF || frameIndex >= fGIF->ImageCount)
  122. return 0;
  123. return savedimage_duration(&fGIF->SavedImages[frameIndex]);
  124. }
  125. int SkGIFMovie::getGifTotalFrameCount()
  126. {
  127. //if fGIF is not valid, return 0
  128. if (NULL == fGIF)
  129. return 0;
  130. return fGIF->ImageCount < 0 ? 0 : fGIF->ImageCount;
  131. }
  132. bool SkGIFMovie::setCurrFrame(int frameIndex)
  133. {
  134. if (NULL == fGIF)
  135. return false;
  136. if (frameIndex >= 0 && frameIndex < fGIF->ImageCount)
  137. fCurrIndex = frameIndex;
  138. else
  139. fCurrIndex = 0;
  140. return true;
  141. }
  142. bool SkGIFMovie::getWordFromStream(SkStream* stream,int* word)
  143. {
  144. unsigned char buf[2];
  145. if (stream->read(buf, 2) != 2) {
  146. LOGE("SkGIFMovie:getWordFromStream: read from stream failed");
  147. return false;
  148. }
  149. *word = (((unsigned int)buf[1]) << 8) + buf[0];
  150. return true;
  151. }
  152. bool SkGIFMovie::getRecordType(SkStream* stream,GifRecordType* Type)
  153. {
  154. unsigned char buf;
  155. //read a record type to buffer
  156. if (stream->read(&buf, 1) != 1) {
  157. LOGE("SkGIFMovie:getRecordType: read from stream failed");
  158. return false;
  159. }
  160. //identify record type
  161. switch (buf) {
  162. case ',':
  163. *Type = IMAGE_DESC_RECORD_TYPE;
  164. break;
  165. case '!':
  166. *Type = EXTENSION_RECORD_TYPE;
  167. break;
  168. case ';':
  169. *Type = TERMINATE_RECORD_TYPE;
  170. break;
  171. default:
  172. *Type = UNDEFINED_RECORD_TYPE;
  173. LOGE("SkGIFMovie:getRecordType: wrong gif record type");
  174. return false;
  175. }
  176. return true;
  177. }
  178. /******************************************************************************
  179. * calculate all image frame count without decode image frame.
  180. * SkStream associated with GifFile and GifFile state is not
  181. * affected
  182. *****************************************************************************/
  183. bool SkGIFMovie::checkGifStream(SkStream* stream)
  184. {
  185. char buf[16];
  186. int screenWidth, screenHeight;
  187. int BitsPerPixel = 0;
  188. int frameCount = 0;
  189. GifRecordType RecordType;
  190. //maximum stream length is set to be 10M, if the stream is
  191. //larger than that, no further action is needed
  192. size_t length = stream->getLength();
  193. if (length > 10*1024*1024) {
  194. LOGE("SkGIFMovie:checkGifStream: stream length larger than 10M");
  195. return false;
  196. }
  197. if (GIF_STAMP_LEN != stream->read(buf, GIF_STAMP_LEN)) {
  198. LOGE("SkGIFMovie:checkGifStream: read GIF STAMP failed");
  199. return false;
  200. }
  201. //Check whether the first three charactar is "GIF", version
  202. // number is ignored.
  203. buf[GIF_STAMP_LEN] = 0;
  204. if (strncmp(GIF_STAMP, buf, GIF_VERSION_POS) != 0) {
  205. LOGE("SkGIFMovie:checkGifStream: check GIF stamp failed");
  206. return false;
  207. }
  208. //read screen width and height from stream
  209. screenWidth = 0;
  210. screenHeight = 0;
  211. if (! getWordFromStream(stream,&screenWidth) ||
  212. ! getWordFromStream(stream,&screenHeight)) {
  213. LOGE("SkGIFMovie:checkGifStream: get screen dimension failed");
  214. return false;
  215. }
  216. //check whether screen dimension is too large
  217. //maximum pixels in a single frame is constrained to 1.5M
  218. //which is aligned withSkImageDecoder_libgif.cpp
  219. if (screenWidth*screenHeight > 1536*1024) {
  220. LOGE("SkGIFMovie:checkGifStream: screen dimension is larger than 1.5M");
  221. return false;
  222. }
  223. //read screen color resolution and color map information
  224. if (3 != stream->read(buf, 3)) {
  225. LOGE("SkGIFMovie:checkGifStream: read color info failed");
  226. return false;
  227. }
  228. BitsPerPixel = (buf[0] & 0x07) + 1;
  229. if (buf[0] & 0x80) {
  230. //If we have global color table, skip it
  231. unsigned int colorTableBytes = (unsigned)(1 << BitsPerPixel) * 3;
  232. if (colorTableBytes != stream->skip(colorTableBytes)) {
  233. LOGE("SkGIFMovie:checkGifStream: skip global color table failed");
  234. return false;
  235. }
  236. } else {
  237. }
  238. //DGifOpen is over, now for DGifSlurp
  239. do {
  240. if (getRecordType(stream, &RecordType) == false)
  241. return false;
  242. switch (RecordType) {
  243. case IMAGE_DESC_RECORD_TYPE:
  244. if (checkImageDesc(stream,buf) == false)
  245. return false;
  246. frameCount ++;
  247. if (1 != stream->skip(1)) {
  248. LOGE("SkGIFMovie:checkGifStream: skip code size failed");
  249. return false;
  250. }
  251. if (skipSubblocksWithTerminator(stream,buf) == false) {
  252. LOGE("SkGIFMovie:checkGifStream: skip compressed image data failed");
  253. return false;
  254. }
  255. break;
  256. case EXTENSION_RECORD_TYPE:
  257. if (skipExtension(stream,buf) == false) {
  258. LOGE("SkGIFMovie:checkGifStream: skip extensions failed");
  259. return false;
  260. }
  261. break;
  262. case TERMINATE_RECORD_TYPE:
  263. break;
  264. default:    /* Should be trapped by DGifGetRecordType */
  265. break;
  266. }
  267. } while (RecordType != TERMINATE_RECORD_TYPE);
  268. //maximum pixels in all gif frames is constrained to 5M
  269. //although each frame has its own dimension, we estimate the total
  270. //pixels which the decoded gif file had be screen dimension multiply
  271. //total image count, this should be the worst case
  272. if (screenWidth * screenHeight * frameCount > 1024*1024*5) {
  273. LOGE("SkGIFMovie:checkGifStream: total pixels is larger than 5M");
  274. return false;
  275. }
  276. return true;
  277. }
  278. bool SkGIFMovie::checkImageDesc(SkStream* stream,char* buf)
  279. {
  280. int imageWidth,imageHeight;
  281. int BitsPerPixel;
  282. if (4 != stream->skip(4)) {
  283. LOGE("SkGIFMovie:getImageDesc: skip image left-top position");
  284. return false;
  285. }
  286. if (! getWordFromStream(stream,&imageWidth)||
  287. ! getWordFromStream(stream,&imageHeight)) {
  288. LOGE("SkGIFMovie:getImageDesc: read image width & height");
  289. return false;
  290. }
  291. if (1 != stream->read(buf, 1)) {
  292. LOGE("SkGIFMovie:getImageDesc: read image info failed");
  293. return false;
  294. }
  295. BitsPerPixel = (buf[0] & 0x07) + 1;
  296. if (buf[0] & 0x80) {
  297. //If this image have local color map, skip it
  298. unsigned int colorTableBytes = (unsigned)(1 << BitsPerPixel) * 3;
  299. if (colorTableBytes != stream->skip(colorTableBytes)) {
  300. LOGE("SkGIFMovie:getImageDesc: skip global color table failed");
  301. return false;
  302. }
  303. } else {
  304. }
  305. return true;
  306. }
  307. bool SkGIFMovie::skipExtension(SkStream* stream,char* buf)
  308. {
  309. int imageWidth,imageHeight;
  310. int BitsPerPixel;
  311. if (1 != stream->read(buf, 1)) {
  312. LOGE("SkGIFMovie:skipExtension: read extension type failed");
  313. return false;
  314. }
  315. switch (buf[0]) {
  316. case COMMENT_EXT_FUNC_CODE:
  317. if (skipComment(stream,buf)==false) {
  318. LOGE("SkGIFMovie:skipExtension: skip comment failed");
  319. return false;
  320. }
  321. break;
  322. case GRAPHICS_EXT_FUNC_CODE:
  323. if (skipGraphics(stream,buf)==false) {
  324. LOGE("SkGIFMovie:skipExtension: skip graphics failed");
  325. return false;
  326. }
  327. break;
  328. case PLAINTEXT_EXT_FUNC_CODE:
  329. if (skipPlaintext(stream,buf)==false) {
  330. LOGE("SkGIFMovie:skipExtension: skip plaintext failed");
  331. return false;
  332. }
  333. break;
  334. case APPLICATION_EXT_FUNC_CODE:
  335. if (skipApplication(stream,buf)==false) {
  336. LOGE("SkGIFMovie:skipExtension: skip application failed");
  337. return false;
  338. }
  339. break;
  340. default:
  341. LOGE("SkGIFMovie:skipExtension: wrong gif extension type");
  342. return false;
  343. }
  344. return true;
  345. }
  346. bool SkGIFMovie::skipComment(SkStream* stream,char* buf)
  347. {
  348. return skipSubblocksWithTerminator(stream,buf);
  349. }
  350. bool SkGIFMovie::skipGraphics(SkStream* stream,char* buf)
  351. {
  352. return skipSubblocksWithTerminator(stream,buf);
  353. }
  354. bool SkGIFMovie::skipPlaintext(SkStream* stream,char* buf)
  355. {
  356. return skipSubblocksWithTerminator(stream,buf);
  357. }
  358. bool SkGIFMovie::skipApplication(SkStream* stream,char* buf)
  359. {
  360. return skipSubblocksWithTerminator(stream,buf);
  361. }
  362. bool SkGIFMovie::skipSubblocksWithTerminator(SkStream* stream,char* buf)
  363. {
  364. do {//skip the whole compressed image data.
  365. //read sub-block size
  366. if (1 != stream->read(buf,1)) {
  367. LOGE("SkGIFMovie:skipSubblocksWithTerminator: read sub block size failed");
  368. return false;
  369. }
  370. if (buf[0] > 0) {
  371. if (buf[0] != stream->skip(buf[0])) {
  372. LOGE("SkGIFMovie:skipSubblocksWithTerminator: skip sub block failed");
  373. return false;
  374. }
  375. }
  376. } while(buf[0]!=0);
  377. return true;
  378. }
  379. bool SkGIFMovie::onGetInfo(Info* info)
  380. {
  381. if (NULL == fGIF)
  382. return false;
  383. SkMSec dur = 0;
  384. for (int i = 0; i < fGIF->ImageCount; i++)
  385. dur += savedimage_duration(&fGIF->SavedImages[i]);
  386. info->fDuration = dur;
  387. info->fWidth = fGIF->SWidth;
  388. info->fHeight = fGIF->SHeight;
  389. info->fIsOpaque = false;
  390. return true;
  391. }
  392. bool SkGIFMovie::onSetTime(SkMSec time)
  393. {
  394. if (NULL == fGIF)
  395. return false;
  396. SkMSec dur = 0;
  397. for (int i = 0; i < fGIF->ImageCount; i++)
  398. {
  399. dur += savedimage_duration(&fGIF->SavedImages[i]);
  400. if (dur >= time)
  401. {
  402. fCurrIndex = i;
  403. return fLastDrawIndex != fCurrIndex;
  404. }
  405. }
  406. fCurrIndex = fGIF->ImageCount - 1;
  407. return true;
  408. }
  409. static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObject* cmap,
  410. int transparent, int width)
  411. {
  412. for (; width > 0; width--, src++, dst++) {
  413. if (*src != transparent) {
  414. const GifColorType& col = cmap->Colors[*src];
  415. *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
  416. }
  417. }
  418. }
  419. static void copyInterlaceGroup(SkBitmap* bm, const unsigned char*& src,
  420. const ColorMapObject* cmap, int transparent, int copyWidth,
  421. int copyHeight, const GifImageDesc& imageDesc, int rowStep,
  422. int startRow)
  423. {
  424. int row;
  425. // every 'rowStep'th row, starting with row 'startRow'
  426. for (row = startRow; row < copyHeight; row += rowStep) {
  427. uint32_t* dst = bm->getAddr32(imageDesc.Left, imageDesc.Top + row);
  428. copyLine(dst, src, cmap, transparent, copyWidth);
  429. src += imageDesc.Width;
  430. }
  431. // pad for rest height
  432. src += imageDesc.Width * ((imageDesc.Height - row + rowStep - 1) / rowStep);
  433. }
  434. static void blitInterlace(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
  435. int transparent)
  436. {
  437. int width = bm->width();
  438. int height = bm->height();
  439. GifWord copyWidth = frame->ImageDesc.Width;
  440. if (frame->ImageDesc.Left + copyWidth > width) {
  441. copyWidth = width - frame->ImageDesc.Left;
  442. }
  443. GifWord copyHeight = frame->ImageDesc.Height;
  444. if (frame->ImageDesc.Top + copyHeight > height) {
  445. copyHeight = height - frame->ImageDesc.Top;
  446. }
  447. // deinterlace
  448. const unsigned char* src = (unsigned char*)frame->RasterBits;
  449. // group 1 - every 8th row, starting with row 0
  450. copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 0);
  451. // group 2 - every 8th row, starting with row 4
  452. copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 8, 4);
  453. // group 3 - every 4th row, starting with row 2
  454. copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 4, 2);
  455. copyInterlaceGroup(bm, src, cmap, transparent, copyWidth, copyHeight, frame->ImageDesc, 2, 1);
  456. }
  457. static void blitNormal(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap,
  458. int transparent)
  459. {
  460. int width = bm->width();
  461. int height = bm->height();
  462. const unsigned char* src = (unsigned char*)frame->RasterBits;
  463. uint32_t* dst = bm->getAddr32(frame->ImageDesc.Left, frame->ImageDesc.Top);
  464. GifWord copyWidth = frame->ImageDesc.Width;
  465. if (frame->ImageDesc.Left + copyWidth > width) {
  466. copyWidth = width - frame->ImageDesc.Left;
  467. }
  468. GifWord copyHeight = frame->ImageDesc.Height;
  469. if (frame->ImageDesc.Top + copyHeight > height) {
  470. copyHeight = height - frame->ImageDesc.Top;
  471. }
  472. int srcPad, dstPad;
  473. dstPad = width - copyWidth;
  474. srcPad = frame->ImageDesc.Width - copyWidth;
  475. for (; copyHeight > 0; copyHeight--) {
  476. copyLine(dst, src, cmap, transparent, copyWidth);
  477. src += frame->ImageDesc.Width;
  478. dst += width;
  479. }
  480. }
  481. static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, GifWord height,
  482. uint32_t col)
  483. {
  484. int bmWidth = bm->width();
  485. int bmHeight = bm->height();
  486. uint32_t* dst = bm->getAddr32(left, top);
  487. GifWord copyWidth = width;
  488. if (left + copyWidth > bmWidth) {
  489. copyWidth = bmWidth - left;
  490. }
  491. GifWord copyHeight = height;
  492. if (top + copyHeight > bmHeight) {
  493. copyHeight = bmHeight - top;
  494. }
  495. for (; copyHeight > 0; copyHeight--) {
  496. sk_memset32(dst, col, copyWidth);
  497. dst += bmWidth;
  498. }
  499. }
  500. static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObject* cmap)
  501. {
  502. int transparent = -1;
  503. for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
  504. ExtensionBlock* eb = frame->ExtensionBlocks + i;
  505. if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
  506. eb->ByteCount == 4) {
  507. bool has_transparency = ((eb->Bytes[0] & 1) == 1);
  508. if (has_transparency) {
  509. transparent = (unsigned char)eb->Bytes[3];
  510. }
  511. }
  512. }
  513. if (frame->ImageDesc.ColorMap != NULL) {
  514. // use local color table
  515. cmap = frame->ImageDesc.ColorMap;
  516. }
  517. if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
  518. SkASSERT(!"bad colortable setup");
  519. return;
  520. }
  521. if (frame->ImageDesc.Interlace) {
  522. blitInterlace(bm, frame, cmap, transparent);
  523. } else {
  524. blitNormal(bm, frame, cmap, transparent);
  525. }
  526. }
  527. static bool checkIfWillBeCleared(const SavedImage* frame)
  528. {
  529. for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
  530. ExtensionBlock* eb = frame->ExtensionBlocks + i;
  531. if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
  532. eb->ByteCount == 4) {
  533. // check disposal method
  534. int disposal = ((eb->Bytes[0] >> 2) & 7);
  535. if (disposal == 2 || disposal == 3) {
  536. return true;
  537. }
  538. }
  539. }
  540. return false;
  541. }
  542. static void getTransparencyAndDisposalMethod(const SavedImage* frame, bool* trans, int* disposal)
  543. {
  544. *trans = false;
  545. *disposal = 0;
  546. for (int i = 0; i < frame->ExtensionBlockCount; ++i) {
  547. ExtensionBlock* eb = frame->ExtensionBlocks + i;
  548. if (eb->Function == GRAPHICS_EXT_FUNC_CODE &&
  549. eb->ByteCount == 4) {
  550. *trans = ((eb->Bytes[0] & 1) == 1);
  551. *disposal = ((eb->Bytes[0] >> 2) & 7);
  552. }
  553. }
  554. }
  555. // return true if area of 'target' is completely covers area of 'covered'
  556. static bool checkIfCover(const SavedImage* target, const SavedImage* covered)
  557. {
  558. if (target->ImageDesc.Left <= covered->ImageDesc.Left
  559. && covered->ImageDesc.Left + covered->ImageDesc.Width <=
  560. target->ImageDesc.Left + target->ImageDesc.Width
  561. && target->ImageDesc.Top <= covered->ImageDesc.Top
  562. && covered->ImageDesc.Top + covered->ImageDesc.Height <=
  563. target->ImageDesc.Top + target->ImageDesc.Height) {
  564. return true;
  565. }
  566. return false;
  567. }
  568. static void disposeFrameIfNeeded(SkBitmap* bm, const SavedImage* cur, const SavedImage* next,
  569. SkBitmap* backup, SkColor color)
  570. {
  571. // We can skip disposal process if next frame is not transparent
  572. // and completely covers current area
  573. bool curTrans;
  574. int curDisposal;
  575. getTransparencyAndDisposalMethod(cur, &curTrans, &curDisposal);
  576. bool nextTrans;
  577. int nextDisposal;
  578. getTransparencyAndDisposalMethod(next, &nextTrans, &nextDisposal);
  579. if ((curDisposal == 2 || curDisposal == 3)
  580. && (nextTrans || !checkIfCover(next, cur))) {
  581. switch (curDisposal) {
  582. // restore to background color
  583. // -> 'background' means background under this image.
  584. case 2:
  585. fillRect(bm, cur->ImageDesc.Left, cur->ImageDesc.Top,
  586. cur->ImageDesc.Width, cur->ImageDesc.Height,
  587. color);
  588. break;
  589. // restore to previous
  590. case 3:
  591. bm->swap(*backup);
  592. break;
  593. }
  594. }
  595. // Save current image if next frame's disposal method == 3
  596. if (nextDisposal == 3) {
  597. const uint32_t* src = bm->getAddr32(0, 0);
  598. uint32_t* dst = backup->getAddr32(0, 0);
  599. int cnt = bm->width() * bm->height();
  600. memcpy(dst, src, cnt*sizeof(uint32_t));
  601. }
  602. }
  603. bool SkGIFMovie::onGetBitmap(SkBitmap* bm)
  604. {
  605. const GifFileType* gif = fGIF;
  606. if (NULL == gif)
  607. return false;
  608. if (gif->ImageCount < 1) {
  609. return false;
  610. }
  611. const int width = gif->SWidth;
  612. const int height = gif->SHeight;
  613. if (width <= 0 || height <= 0) {
  614. return false;
  615. }
  616. // no need to draw
  617. if (fLastDrawIndex >= 0 && fLastDrawIndex == fCurrIndex) {
  618. return true;
  619. }
  620. int startIndex = fLastDrawIndex + 1;
  621. if (fLastDrawIndex < 0 || !bm->readyToDraw()) {
  622. // first time
  623. startIndex = 0;
  624. // create bitmap
  625. bm->setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
  626. if (!bm->allocPixels(NULL)) {
  627. return false;
  628. }
  629. // create bitmap for backup
  630. fBackup.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
  631. if (!fBackup.allocPixels(NULL)) {
  632. return false;
  633. }
  634. } else if (startIndex > fCurrIndex) {
  635. // rewind to 1st frame for repeat
  636. startIndex = 0;
  637. }
  638. int lastIndex = fCurrIndex;
  639. if (lastIndex < 0) {
  640. // first time
  641. lastIndex = 0;
  642. } else if (lastIndex > fGIF->ImageCount - 1) {
  643. // this block must not be reached.
  644. lastIndex = fGIF->ImageCount - 1;
  645. }
  646. SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
  647. if (gif->SColorMap != NULL) {
  648. const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
  649. bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
  650. }
  651. static SkColor paintingColor = SkPackARGB32(0, 0, 0, 0);
  652. // draw each frames - not intelligent way
  653. for (int i = startIndex; i <= lastIndex; i++) {
  654. const SavedImage* cur = &fGIF->SavedImages[i];
  655. if (i == 0) {
  656. bool trans;
  657. int disposal;
  658. getTransparencyAndDisposalMethod(cur, &trans, &disposal);
  659. if (!trans && gif->SColorMap != NULL) {
  660. paintingColor = bgColor;
  661. } else {
  662. paintingColor = SkColorSetARGB(0, 0, 0, 0);
  663. }
  664. bm->eraseColor(paintingColor);
  665. fBackup.eraseColor(paintingColor);
  666. } else {
  667. // Dispose previous frame before move to next frame.
  668. const SavedImage* prev = &fGIF->SavedImages[i-1];
  669. disposeFrameIfNeeded(bm, prev, cur, &fBackup, paintingColor);
  670. }
  671. // Draw frame
  672. // We can skip this process if this index is not last and disposal
  673. // method == 2 or method == 3
  674. if (i == lastIndex || !checkIfWillBeCleared(cur)) {
  675. drawFrame(bm, cur, gif->SColorMap);
  676. }
  677. }
  678. // save index
  679. fLastDrawIndex = lastIndex;
  680. return true;
  681. }
  682. ///
  683. #include "SkTRegistry.h"
  684. SkMovie* Factory(SkStream* stream) {
  685. char buf[GIF_STAMP_LEN];
  686. if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
  687. if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
  688. memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
  689. memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
  690. // must rewind here, since our construct wants to re-read the data
  691. stream->rewind();
  692. return SkNEW_ARGS(SkGIFMovie, (stream));
  693. }
  694. }
  695. return NULL;
  696. }
  697. static SkTRegistry<SkMovie*, SkStream*> gReg(Factory);

完成以上Native层代码的修改,可别忘了修改AndroidSourceCode/external/skia/include/images/SkMovie.h哦,代码修改如下:

[cpp] view plaincopy
  1. bool setTime(SkMSec);
  2. +    virtual int getGifFrameDuration(int frameIndex);
  3. +    virtual int getGifTotalFrameCount();
  4. +    SkBitmap* createGifFrameBitmap();
  5. +    virtual bool setCurrFrame(int frameIndex);
  6. const SkBitmap& bitmap();

接下来就是调用具体的解码器来进行解码了,这部分内容因为系统已经做好了,因此到这里,gif的支持添加基本完成。

修改总览

我们来看看总的修改吧,如图5:
图5

代码时序图

图6是修改完成后,gif显示代码执行时序图:
图6

总结

因为自己从来没有接触过这些东西,起初看得真的是头大啊!当然,这么复杂的东西并不是我一个人搞定的,在多位朋友的帮助下,一点点的看,一点点的啃,当时记忆犹新的一句话就是 " 看这该死的代码看得都想吐了!!" " 吐完了回来继续看!!"针对这些不懂的东西,每个人一开始都是有畏惧感的,通过这件事情,让我知道,不管什么事情,一步一步来,稳扎稳打,总会守得云开见月明的。加油!!
(PS:修改记录在这里,希望大家能共同讨论与学习。在此注明,修改中依然有很多“坑”,如果有像我这种小白的,请多多跟踪源码!!)

Android 2.3 Gallery3D添加gif支持——修改代码(三)相关推荐

  1. Android Studio工程中添加Admob广告条代码详解

    前言: 纵观国内的广告平台,比如多盟,万普,百度,腾讯,或者别的一些广告平台,真是应接不暇,但是具体接入那就自己判断了,经过一番折腾,最终还是选择了谷歌平台,理应是稳定,可靠,安全,收入颇高,但是登录 ...

  2. Android系统移植与调试之-------如何修改Android设备添加重启、飞行模式、静音模式等功能(一)...

    1.首先先来看一下修改前后的效果对比图 修改之后的图片 确认重启界面 具体的修改内容在下一篇中具体介绍. Android系统移植与调试之------->如何修改Android设备添加重启.飞行模 ...

  3. android init.d脚本,◇添加init.d脚本支持教程贴◇

    ☆修改系统有风险,本人不承担任何后果!!☆ [安卓2.3及以上系统通用] 研究了好长时间终于找到了自己手动添加的方法,一种方法就是厨房,但没有boot.img是无法添加内核支持,第二种方法就是软件添加 ...

  4. Android源码中添加 修改应用

    第一部分:添加一个新的应用 1. 在和系统相同版本的SDK目录下开发自己的android应用 2. 把开发的android工程放到源码的packages/apps/目录下 3. 在工程目录下添加And ...

  5. IIS 添加mime 支持 apk,exe,.woff,IIS MIME设置 ,Android apk下载的MIME 设置 苹果ISO .ipa下载mime 设置...

    IIS 添加mime 支持 apk,exe,.woff,IIS MIME设置 ,Android apk下载的MIME 设置 苹果ISO .ipa下载mime 设置 原文:IIS 添加mime 支持 a ...

  6. 为Android设备添加A2SD支持

          相信很多用Android设备的用户都有这个问题,内部存储太小导致应用只能装那么几个,虽然rom也有提供移动到sd卡的选项,但是仅仅是移动程序文件到sd卡,并不能解决多少问题,多装几个还是会 ...

  7. android改微信号码,安卓版微信更新,已支持修改微信号

    人类的悲欢并不相通,除了曾经想改却不能改的微信号. 现在,安卓最新版的微信支持修改微信号了! 那些过往时刻,你觉得微信号非改不可的时刻,还记得吗?以下场景可能过于真实,很容易引起不适. 别人的微信号里 ...

  8. 【Android 安装包优化】WebP 应用 ( libwebp 源码下载 | Android.mk 和 Application.mk 构建脚本修改 | libwebp 函数库编译 )

    文章目录 一. libwebp 源码下载 二. libwebp 源码编译脚本修改 三. libwebp 函数库编译 四.参考资料 一. libwebp 源码下载 Google 提供了一系列的 WebP ...

  9. android udp rtp播放器,IjkPlayer For Android(3)-RTP直播硬解码支持

    IjkPlayer播放器其实是支持RTP直播的,但是只能用ffmpeg软解码不能使用硬解码. 这里将会从以下几个方面配置使用 1.UDP RTP/RTMP区别 2.FFMpeg推流 3.IjkPlay ...

  10. Android P HAL层添加HIDL实例(详细实现步骤)

    Android P HAL层添加HIDL实例 本文是参照 https://www.jianshu.com/p/b80865c61d8e 教程介绍实现,原理请参考原作者. 本文将介绍如何在P OS上添加 ...

最新文章

  1. js中修改this的指向方法整理
  2. 变量的高级主题(六)
  3. flex使用FlexPrintJob打印问题
  4. 第四章:条件语句(if)和循环结构(while)
  5. crontab环境变量
  6. E2: A Framework for NFV Applications, SOSP' 15
  7. JavaScript基础语法快速入门
  8. Android IntentService的使用和源代码分析
  9. EasyUI常用控件禁用方法
  10. 《白帽子讲web安全》学习笔记——web安全概述
  11. 思科交换机配置命令(详细命令总结归纳)
  12. 计算机无线网卡连接网络,台式机怎么连接无线网络?台式电脑不用网卡怎么连接网络?...
  13. 有没有测试牙齿需不需要修正的软件,测一测,你的牙齿需要矫正吗?
  14. UDP网络基础知识简介
  15. 参考文献正确格式 如何直接得到
  16. Python——变量和简单类型(下篇)
  17. 2021年互联网大厂Java面经总结,保准看明白!
  18. TCP拥塞控制算法纵横谈-Illinois和YeAH
  19. 攻击法国海军病毒Conficker在中国网络同步蔓延
  20. 【Python实战】如果没有音乐,生活就是一个错误 :n首回味无穷的歌,总有一曲深得你心哦~

热门文章

  1. lzg_ad:在CF卡上实现EWF功能
  2. 2020MPAcc,管理类联考网课,书籍资源推荐!
  3. 影牛社区短视频影视APP源码
  4. ABAP新手基础入门知识
  5. IDEA中集成使用SVN
  6. 海康威视硬盘录像机怎么连接萤石云
  7. 【海康威视】2022届超新星-AI算法工程师-萤石-暑假实习面经
  8. Zuul 上传大文件服务报错的问题
  9. Python黑帽子_hack与渗透测试编程之道 第三章代码
  10. ubuntu通过命令行清除内存