收藏 分销(赏)

Bitmap解码与创建.docx

上传人:仙人****88 文档编号:11739772 上传时间:2025-08-11 格式:DOCX 页数:13 大小:33.49KB 下载积分:10 金币
下载 相关 举报
Bitmap解码与创建.docx_第1页
第1页 / 共13页
Bitmap解码与创建.docx_第2页
第2页 / 共13页


点击查看更多>>
资源描述
· Title:Bitmap解码使用范例-ImageView · Date:2011.11.23 · Author:Tianyang Lu · Content: · 知道了Bitmap的工作原理,那实际是怎样使用的呢?一个图片资源是怎样显示在屏幕上的呢? 拿ImageView做例子,看看ImageView.java,这里有四种装载资源的方法,先看前两种: /** * Sets a drawable as the content of this ImageView. * * This does Bitmap reading and decoding on the UI * thread, which can cause a latency hiccup. If that's a concern, * consider using {@link #setImageDrawable} or * {@link #setImageBitmap} and * {@link android.graphics.BitmapFactory} instead. * * @param resId the resource identifier of the the drawable * * @attr ref android.R.styleable#ImageView_src */ @android.view.RemotableViewMethod public void setImageResource(int resId) { if (mUri != null || mResource != resId) { updateDrawable(null); mResource = resId; mUri = null; resolveUri(); //意思是处理Uri,就在这个方法里载入资源的 requestLayout(); invalidate(); } } /** * Sets the content of this ImageView to the specified Uri. * * This does Bitmap reading and decoding on the UI * thread, which can cause a latency hiccup. If that's a concern, * consider using {@link #setImageDrawable} or * {@link #setImageBitmap} and * {@link android.graphics.BitmapFactory} instead. * * @param uri The Uri of an image */ @android.view.RemotableViewMethod public void setImageURI(Uri uri) { if (mResource != 0 || (mUri != uri && (uri == null || mUri == null || !uri.equals(mUri)))) { updateDrawable(null); mResource = 0; mUri = uri; resolveUri(); requestLayout(); invalidate(); } } 这两个方法其实是差不多的,都是调用resolveUri()方法载入的,这个方法明显可以 通过int resID和Uri两个任意一个来载入资源,跳到这个方法看看: private void resolveUri() { if (mDrawable != null) { return; } Resources rsrc = getResources(); if (rsrc == null) { return; } Drawable d = null; if (mResource != 0) { //如果int mResouce 资源ID不为0 try { d = rsrc.getDrawable(mResource); //那么该ID的Drawable就赋给Drawable d } catch (Exception e) { Log.w("ImageView", "Unable to find resource: " + mResource, e); // Don't try again. mUri = null; } } else if (mUri != null) //否则,当mUri不为null String scheme = mUri.getScheme(); if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { //如果是资源类型的,SCHEME_ANDROID_RESOURCE = "android.resource" try { // Load drawable through Resources, to get the source density information ContentResolver.OpenResourceIdResult r = mContext.getContentResolver().getResourceId(mUri); d = r.r.getDrawable(r.id); //用读Resouce的方法载入 } catch (Exception e) { Log.w("ImageView", "Unable to open content: " + mUri, e); } } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) //如果是Content或文件类型的,SCHEME_CONTENT = "content" || ContentResolver.SCHEME_FILE.equals(scheme)) { //SCHEME_FILE = "file" try { d = Drawable.createFromStream( //以流的方式创建一个Drawable mContext.getContentResolver().openInputStream(mUri), null); } catch (Exception e) { Log.w("ImageView", "Unable to open content: " + mUri, e); } } else { //其他类型 d = Drawable.createFromPath(mUri.toString()); } if (d == null) { System.out.println("resolveUri failed on bad bitmap uri: " + mUri); // Don't try again. mUri = null; } } else { return; } updateDrawable(d); } 我们先只看和图片相关的,用到了Drawable.createFromStream和Drawable.createFromPath这两个方法 而这两个方法,前者调用了BitmapFactory.decodeResourceStream(),后者调用了BitmapFactory.decodeFile() 创建了Bitmap对象实例,最后都调用了Drawable.drawableFromBitmap()返回了一个BitmapDrawable实例,见下: private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np, Rect pad, String srcName) { if (np != null) { return new NinePatchDrawable(res, bm, np, pad, srcName); } return new BitmapDrawable(res, bm); //返回了一个BitmapDrawable } } 其实到这里就知道了ImageView载入图片时实际是怎么使用解码器创建Bitmap对象的了,然后接下来ImageView 的onDraw方法调用Drawable.draw(Canvas canvas)的抽象方法(Drawable的子类必须实现draw方法)将成员变量 mDrawable画出来。 · Title:Bitmap解码过程 · Date:2011.11.22 · Author:Tianyang Lu · Content: · 我们先来看BitmapFactory.java,主要的解码方法decodeStream()和decodeByteArray()、decodeFileDescriptor(), · 其他的都是调用这两个方法实现的。而这三个方法的实现分别基于这三个native方法,nativeDecodeStream()和 · nativeDecodeByteArray()、nativeDecodeFileDescriptor(),我们在BitmapFactory.cpp里找到这三个方法, · 发现三个方法其实都是调用doDecode()实现的。static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, jobject options, bool allowPurgeable, bool forcePurgeable = false) 在这里有几句关键代码 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); //创建解码器 ... if (!decoder->decode(stream, bitmap, prefConfig, decodeMode)) { //解码 return nullObjectReturn("decoder->decode returned false"); } ... return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk); //返回java Bitmap对象实例,在上一节分析过 那我们再去看看创建SkImageDecoder对象的方法SkImageDecoder::Factory,在SkImageDecoder_Factory.cpp中看到: typedef SkTRegistry<SkImageDecoder*, SkStream*> DecodeReg; //解码器,链表形式 template DecodeReg* DecodeReg::gHead; SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { const DecodeReg* curr = DecodeReg::Head(); while (curr) { SkImageDecoder* codec = curr->factory()(stream); //创建解码器,没找到curr->factort()(stream)这个?? // we rewind here, because we promise later when we call "decode", that // the stream will be at its beginning. stream->rewind(); if (codec) { return codec; } curr = curr->next(); } return NULL; } 看看SkTRegistry.h,这里不太懂,先贴着 /** Template class that registers itself (in the constructor) into a linked-list and provides a function-pointer. This can be used to auto-register a set of services, e.g. a set of image codecs. */ template <typename T, typename P> class SkTRegistry : SkNoncopyable { public: typedef T (*Factory)(P); SkTRegistry(Factory fact) { #ifdef ANDROID // work-around for double-initialization bug { SkTRegistry* reg = gHead; while (reg) { if (reg == this) { return; } reg = reg->fChain; } } #endif fFact = fact; fChain = gHead; gHead = this; } static const SkTRegistry* Head() { return gHead; } const SkTRegistry* next() const { return fChain; } Factory factory() const { return fFact; } private: Factory fFact; SkTRegistry* fChain; static SkTRegistry* gHead; }; 而这些解码器的注册过程是怎样的呢?去SkImageDecoder_libbmp.cpp看看,先看看SkBMPImageDecoder类定义: class SkBMPImageDecoder : public SkImageDecoder { //继承SkImageDecoder public: SkBMPImageDecoder() {} virtual Format getFormat() const { return kBMP_Format; } protected: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode); }; 然后下面有个static SkImageDecoder* Factory(SkStream* stream)方法(见下),哦~~应该就是上面调用 curr->factort()(stream)的具体实现,引用湛哥的话:(当然这里是BMP解码器,但是原理一样的) DFactory就是jpeg解码器需要注册的入口函数,这个函数接受的流参数,首先会检查是否符合文件规范, 是就创建一个SkJPEGImageDecoder,那么在SkImageDecoder::Factory就会有个非Null的codec,否则, 继续下一个解码器查询。static SkTRegistry gDReg(DFactory);是注册函数。 很明显,这是一个静态的变量,将会在libskia.so装载的时候首先执行。 static SkImageDecoder* Factory(SkStream* stream) { static const char kBmpMagic[] = { 'B', 'M' }; size_t len = stream->getLength(); char buffer[sizeof(kBmpMagic)]; if (len > sizeof(kBmpMagic) && stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) && !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) { return SkNEW(SkBMPImageDecoder); //创建并返回SkBMPImageDecoder对象实例 } return NULL; } static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory); 好吧,有了解码器,那解码器是怎么工作的呢?回到第一个代码块, decoder->decode(stream, bitmap, prefConfig, decodeMode),看看SkImageDecoder.cpp: bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkBitmap::Config pref, Mode mode) { // pass a temporary bitmap, so that if we return false, we are assured of // leaving the caller's bitmap untouched. SkBitmap tmp; // we reset this to false before calling onDecode fShouldCancelDecode = false; // assign this, for use by getPrefConfig(), in case fUsePrefTable is false fDefaultPref = pref; if (!this->onDecode(stream, &tmp, mode)) { return false; } bm->swap(tmp); return true; } 恩,调用了onDecode(),有没有很熟悉?看上面的SkBMPImageDecoder类的例子,里面就有 protected: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode); 看看onDecode的具体实现,比如jpeg里,可以看到调用了jpeglib里解码函数,至此,已经到最底层了 · Title:Bitmap(Java对象)构造过程 · Date:2011.11.22 · Author:Tianyang Lu · Content: 从Bitmap.java这个类来看,有多个createBitmap方法,例如: /** * Returns a mutable bitmap with the specified width and height. Its * initial density is as per {@link #getDensity}. * * @param width The width of the bitmap * @param height The height of the bitmap * @param config The bitmap config to create. * @throws IllegalArgumentException if the width or height are <= 0 */ public static Bitmap createBitmap(int width, int height, Config config) { Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true); bm.eraseColor(0); // start with black/transparent pixels return bm; } 这里调用了nativeCreate方法,这是个native mothod,在Bitmap.cpp中看到: static JNINativeMethod gBitmapMethods[] = { { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", (void*)Bitmap_creator}, ... } static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, int offset, int stride, int width, int height, SkBitmap::Config config, jboolean isMutable) { if (width <= 0 || height <= 0) { doThrowIAE(env, "width and height must be > 0"); return NULL; } if (NULL != jColors) { size_t n = env->GetArrayLength(jColors); if (n < SkAbs32(stride) * (size_t)height) { doThrowAIOOBE(env); return NULL; } } SkBitmap bitmap; bitmap.setConfig(config, width, height); if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) { return NULL; } if (jColors != NULL) { GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap); } return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable, NULL); } 这里new了一个SkBitmap,返回了GraphicsJNI::createBitmap的返回值,继续往下找:(Graphics.cpp) jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, jbyteArray ninepatch, int density) { SkASSERT(bitmap != NULL); SkASSERT(NULL != bitmap->pixelRef()); jobject obj = env->AllocObject(gBitmap_class); //obj转化成了Bitmap类 if (obj) { env->CallVoidMethod(obj, gBitmap_constructorMethodID, (jint)bitmap, isMutable, ninepatch, density);//构造方法 if (hasException(env)) { obj = NULL; } } return obj; } ... gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(IZ[BI)V"); env->CallVoidMethod(obj, gBitmap_constructorMethodID,(jint)bitmap, isMutable, ninepatch, density); 这一句将转换成jint型的SkBitmap的指针变量bitmap传递给了Bitmap的构造方法,回看Bitmap.java中该方法: /** * @noinspection UnusedDeclaration */ /* Private constructor that must received an already allocated native bitmap int (pointer). This can be called from JNI code. */ private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) { if (nativeBitmap == 0) { throw new RuntimeException("internal error: native bitmap is 0"); } // we delete this in our finalizer mNativeBitmap = nativeBitmap; mIsMutable = isMutable; mNinePatchChunk = ninePatchChunk; if (density >= 0) { mDensity = density; } } 总的来看,GraphicsJNI::createBitmap()新建一个jobject型变量obj,关联了Bitmap.java类, 用Bitmap构造方法创建了一个实例并返回。实际上是用SkBitmap*类型传入来构造Bitmap对象的, 通过调用GraphicsJNI::createBitmap(),Bitmap_creator()(也就是Bitmap.nativeCreate()方法), Bitmap.createBitmap()一层一层返回Bitmap实例,完成了创建Bitmap实例的方法
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服