精华内容
下载资源
问答
  • 主要介绍了 Android canvas drawBitmap方法详解及实例的相关资料,需要的朋友可以参考下
  • drawbitmap用法

    2014-12-04 09:01:50
    drawbitmap从创建到怎么使用,用于新手阶段,很容易理解与上手。
  • DrawBitmap-20200222.rar

    2020-02-22 18:00:24
    BitBlt()函数使用方法。 介绍了如何GDI技术在MFC对话框上显示bitmap文件。 介绍了如何GDI技术在MFC对话框上显示bitmap文件。
  • // canvas.drawBitmap(bitMapDisplay, (float) m_posX, (float) m_posY, mPaint); invalidate(); } } } bitmap相关: <原文:https://blog.csdn.net/wanliguodu/article/details/84973846> 一、Bitmap...
    package com.example.webgettest;
    
    import android.support.constraint.ConstraintLayout;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    
    import android.widget.Toast;
    
    public class imagechangesize extends AppCompatActivity {
        ImageView imageView = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            imageView = new ImageView(this);
    
    
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_imagechangesize);
            ConstraintLayout ll =  findViewById(R.id.con);
            ll.addView(imageView);
    
            // 向左移动
            Button botton0 = (Button) findViewById(R.id.button);
            botton0.setOnClickListener(new OnClickListener() {
                public void onClick(View arg0) {
                    imageView.setPosLeft();
    
                }
            });
             //向右移动
            Button botton1 = (Button) findViewById(R.id.button2);
            botton1.setOnClickListener(new OnClickListener() {
                public void onClick(View arg0) {
                    imageView.setPosRight();
                }
            });
            // 左旋转
            Button botton2 = (Button) findViewById(R.id.button3);
            botton2.setOnClickListener(new OnClickListener() {
                public void onClick(View arg0) {
                    imageView.setRotationLeft();
                }
            });
            // 右旋转
            Button botton3 = (Button) findViewById(R.id.button4);
            botton3.setOnClickListener(new OnClickListener() {
                public void onClick(View arg0) {
                    imageView.setRotationRight();
                }
            });
            // 缩小
            Button botton4 = (Button) findViewById(R.id.button5);
            botton4.setOnClickListener(new OnClickListener() {
                public void onClick(View arg0) {
                    imageView.setNarrow();
                }
            });
            // 放大
            Button botton5 = (Button) findViewById(R.id.button6);
            botton5.setOnClickListener(new OnClickListener() {
                public void onClick(View arg0) {
                    imageView.setEnlarge();
                }
            });
            super.onCreate(savedInstanceState);
        }
        class ImageView extends View {
            Paint mPaint = null;
            Bitmap bitMap = null;
            Bitmap bitMapDisplay = null;
            int m_posX = 120;
            int m_posY = 50;
            int m_bitMapWidth = 0;
            int m_bitMapHeight = 0;
            Matrix mMatrix = null;
            float mAngle = 0.0f;
            float mScale = 1f;//1为原图的大小
    
            public ImageView(Context context) {
                super(context);
                mPaint = new Paint();
                mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
                bitMap = BitmapFactory.decodeResource(this.getResources(),
                        R.drawable.qqq);
                bitMapDisplay = bitMap;
                mMatrix = new Matrix();
                // 获取图片宽高
                if(bitMap==null)
                {
                    Toast.makeText(imagechangesize.this,"错误",Toast.LENGTH_SHORT).show();
                    return;
                }else {
                    m_bitMapWidth = bitMap.getWidth();
    //                m_bitMapWidth=50;
    //                m_bitMapHeight=50;
                    m_bitMapHeight = bitMap.getHeight();
                }
            }
            // 向左移动
            public void setPosLeft() {
                m_posX -= 10;
            }
            // 向右移动
            public void setPosRight() {
                m_posX += 10;
            }
            // 向左旋转
            public void setRotationLeft() {
                mAngle-=5;
                setAngle();
            }
            // 向右旋转
            public void setRotationRight() {
                mAngle+=5;
                setAngle();
            }
            // 缩小图片
            public void setNarrow() {
                if (mScale > 0.5) {
                    mScale -= 0.1;
                    setScale();
                }
            }
            // 放大图片
            public void setEnlarge() {
                if (mScale < 2) {
                    mScale += 0.1;
                    setScale();
                }
            }
            // 设置缩放比例
            public void setAngle() {
                mMatrix.reset();
                mMatrix.setRotate(mAngle);
                bitMapDisplay = Bitmap.createBitmap(bitMap, 0, 0, m_bitMapWidth,
                        m_bitMapHeight, mMatrix, true);
            }
            // 设置旋转比例
            public void setScale() {
                mMatrix.reset();
                //float sx X轴缩放
                //float sy Y轴缩放
                mMatrix.postScale(mScale, mScale);
                bitMapDisplay = Bitmap.createBitmap(bitMap, 0, 0, m_bitMapWidth,
                        m_bitMapHeight, mMatrix, true);
            }
            @Override
            protected void onDraw(Canvas canvas) {
                super.onDraw(canvas);
               // canvas.drawBitmap(bitMapDisplay, (float) m_posX, (float) m_posY, mPaint);
                invalidate();
            }
        }
    }
    

    bitmap相关:

    <原文:https://blog.csdn.net/wanliguodu/article/details/84973846>

    一、Bitmap
    Bitmap 代表一个位图,BitmapDrawable 里封装的图片就是一个 Bitmap 对象。开发者为了把一个 Bitmap 对象包装成 BitmapDrawable 对象,可以调用 BitmapDrawable 的构造器:
    BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap);
    1
    如果需要获取 BitmapDrawable 所包装的 Bitmap 对象,则可以用 BitmapDrawable 的 getBitmap() 的方法:
    Bitmap bitmap = bitmapDrawable.getBitmap();
    1
    1.1 Bitmap的创建
    在 Bitmap 类中, Bitmap 构造方法默认权限,而且这个类也是 final 的,所以我们无法 new 一个 Bitmap 对象。可以根据静态方法 createBitmap 来创建 Bitmap 类。这些重载的方法有13个,他们分别是:

    这些方法大致可以分为三类(本文所用的源码是 android - 26):
    1.1.1 根据已有的Bitmap来创建新Bitmap
    /**
    * 通过矩阵的方式,返回原始 Bitmap 中的一个不可变子集。新 Bitmap 可能返回的就是原始的 Bitmap,也可能还是复制出来的。
    * 新 Bitmap 与原始 Bitmap 具有相同的密度(density)和颜色空间;
    *
    * @param source   原始 Bitmap
    * @param x        在原始 Bitmap 中 x方向的其起始坐标(你可能只需要原始 Bitmap x方向上的一部分)
    * @param y        在原始 Bitmap 中 y方向的其起始坐标(你可能只需要原始 Bitmap y方向上的一部分)
    * @param width    需要返回 Bitmap 的宽度(px)(如果超过原始Bitmap宽度会报错)
    * @param height   需要返回 Bitmap 的高度(px)(如果超过原始Bitmap高度会报错)
    * @param m        Matrix类型,表示需要做的变换操作
    * @param filter   是否需要过滤,只有 matrix 变换不只有平移操作才有效
    */
    public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
                @Nullable Matrix m, boolean filter)
    1234567891011121314
    1.1.2 通过像素点数组创建空的Bitmap
    /**
         *
         * 返回具有指定宽度和高度的不可变位图,每个像素值设置为colors数组中的对应值。
         * 其初始密度由给定的确定DisplayMetrics。新创建的位图位于sRGB 颜色空间中。
         * @param display  显示将显示此位图的显示的度量标准
         * @param colors   用于初始化像素的sRGB数组
         * @param offset   颜色数组中第一个颜色之前要跳过的值的数量
         * @param stride   行之间数组中的颜色数(必须> = width或<= -width)
         * @param width    位图的宽度
         * @param height   位图的高度
         * @param config   要创建的位图配置。如果配置不支持每像素alpha(例如RGB_565),
         * 那么colors []中的alpha字节将被忽略(假设为FF)
         */
        public static Bitmap createBitmap(@NonNull DisplayMetrics display,
                @NonNull @ColorInt int[] colors, int offset, int stride,
                int width, int height, @NonNull Config config)
    12345678910111213141516
    1.1.3 创建缩放的Bitmap
    /**
    * 对Bitmap进行缩放,缩放成宽 dstWidth、高 dstHeight 的新Bitmap
    */
    public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,boolean filter)
    1234
    二、BitmapFactory
    BitmapFactory 是一个工具类,它提供了大量的方法,这个方法可用于不同的数据源来解析、创建 Bitmap 对象。大概有如下方法:

    2.1 创建Bitmap的方法

    decodeByteArray(byte[] data, int offset, int length, Options opts):从指定字节数组的 offset 位置开始,将长度 length 的字节数据解析成 Bitmap 对象。
    decodeFile(String pathName, Options opts):从 pathName 指定的文件中解析、创建 Bitmap 对象。
    decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts):用于从 FileDescriptor 对应的文件中解析、创建 Bitmap 对象。
    decodeResource(Resources res, int id, Options opts):用于给定的资源 ID 从指定资源中解析、创建 Bitmap 对象。
    decodeStream(InputStream is, Rect outPadding, Options opts):用于从指定输入流中解析、创建 Bitmap 对象。

    decodeFile 和 decodeResource 其实最终都会调用 decodeStream 方法来解析 Bitmap 。有一个特别有意思的事情是,在 decodeResource 调用 decodeStream 之前还会调用 decodeResourceStream 这个方法,接下来让我们看看 decodeResourceStream 方法的源码:
    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
                InputStream is, Rect pad, Options opts) {
            validate(opts);
            if (opts == null) {
                opts = new Options();
            }

            if (opts.inDensity == 0 && value != null) {
                final int density = value.density;
                if (density == TypedValue.DENSITY_DEFAULT) {
                    opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
                } else if (density != TypedValue.DENSITY_NONE) {
                      //这里 density 的值如果对应资源目录为 hdpi 的话,就是 240;如果是 xhdpi 则是 320;如果是 xxdpi 则是 480
                    opts.inDensity = density;
                }
            }
            
            if (opts.inTargetDensity == 0 && res != null) {
                opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
            }
            
            return decodeStream(is, pad, opts);
        }
    1234567891011121314151617181920212223
    这个方法主要对 Options (详解的属性值在下面)进行处理,在得到 opts.inDensity 的属性前提下,如果没有对该属性的设定值,那么 opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; 这个值默认为标准dpi的基值:160。如果没有设定 opts.inTargetDensity 的值时,opts.inTargetDensity = res.getDisplayMetrics().densityDpi;  该值为当前设备的 densityDpi,这个值是根据你放置在 drawable 下的文件不同而不同的(具体参考  Android 屏幕各种参数的介绍和学习)。
    所以说 decodeResourceStream 这个方法主要对 opts.inDensity 和 opts.inTargetDensity进行赋值。那什么时候使用这个 opts 属性呢?在将参数传入 decodeStream方法,该方法在调用 native 方法进行解析 Bitmap 后会调用 setDensityFromOptions 这个方法:
    /**
         * Set the newly decoded bitmap's density based on the Options.
         */
        private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
            if (outputBitmap == null || opts == null) return;
             //opts.inDensity 这个值会因为你放置在 drawable 下不同分辨率的文件夹下而不同
            final int density = opts.inDensity;
            if (density != 0) {
                outputBitmap.setDensity(density);
                final int targetDensity = opts.inTargetDensity;
                if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
                    return;
                }

                byte[] np = outputBitmap.getNinePatchChunk();
                final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
                if (opts.inScaled || isNinePatch) {
                    outputBitmap.setDensity(targetDensity);
                }
            } else if (opts.inBitmap != null) {
                // bitmap was reused, ensure density is reset
                outputBitmap.setDensity(Bitmap.getDefaultDensity());
            }
        }
    123456789101112131415161718192021222324
    这个方法就是把刚刚赋值过的两个属性 inDensity 和 inTargetDensity 给 Bitmap 进行赋值,不过并不是直接赋值给 Bitmap 而是判断 inDensity 的值与 inTargetDensity 或 该设备屏幕 Density 不相等 ,而且 opts.inScaled = true 时,条件才成立。具体的计算见:
    从上面的源码可以得出一个重要的结论:


    decodeResource 在解析时会对 Bitmap 根据当前设备屏幕密度 densityDpi 的值进行缩放适配操作,使得解析出来的 Bitmap 与当前设备分辨率匹配,并且一般来说,这时 Bitmap 的大小将比原始的 Bitmap 大。
    decodeFile、decodeStream 在解析时不会对 Bitmap 进行一系列的屏幕适配,解析出来的将是原始大小的图。


    2.2 BitmapFactory.Options的属性解析
    在使用 BitmapFactory 时 Options 这个静态内部类经常用到,里面有很多经常使用的属性,让我们来看一些比较重要的:

    inJustDecodeBounds:如果这个值为 true ,那么在解码的时候将不会返回 Bitmap ,只会返回这个 Bitmap 的尺寸。这个属性的目的是,如果你只想知道一个 Bitmap 的尺寸,但又不想将其加载到内存中时,是一个非常好用的属性。
    outWidth和outHeight:表示这个 Bitmap 的宽和高,一般和 inJustDecodeBounds 一起使用来获得 Bitmap的宽高,但是不加载到内存。
    inSampleSize:压缩图片时采样率的值,如果这个值大于1,那么就会按照比例(1 / inSampleSize)来缩小 Bitmap 的宽和高。如果这个值为 2,那么 Bitmap 的宽为原来的1/2,高为原来的1/2,那么这个 Bitmap 是所占内存像素值会缩小为原来的 1/4。
    inDensity:表示这个 Bitmap 的像素密度,对应的是 DisplayMetrics 中的 densityDpi,不是 density。(如果不明白它俩之间的异同,可以看我的 Android 屏幕各种参数的介绍和学习 )
    inTargetDensity:表示要被新 Bitmap 的目标像素密度,对应的是 DisplayMetrics 中的 densityDpi。
    inScreenDensity:表示实际设备的像素密度,对应的是 DisplayMetrics 中的 densityDpi。
    inPreferredConfig:这个值是设置色彩模式,默认值是 ARGB_8888,这个模式下,一个像素点占用 4Byte 。RGB_565 占用 2Byte,ARGB_4444 占用 4Byte(以废弃)。
    inPremultiplied:这个值和透明度通道有关,默认值是 true,如果设置为 true,则返回的 Bitmap 的颜色通道上会预先附加上透明度通道。
    inDither:这个值和抖动解码有关,默认值为 false,表示不采用抖动解码。
    inScaled:设置这个Bitmap 是否可以被缩放,默认值是 true,表示可以被缩放。
    inPreferQualityOverSpeed:这个值表示是否在解码时图片有更高的品质,仅用于 JPEG 格式。如果设置为 true,则图片会有更高的品质,但是会解码速度会很慢。
    inBitmap:这个参数用来实现 Bitmap 内存的复用,但复用存在一些限制,具体体现在:在 Android 4.4 之前只能重用相同大小的 Bitmap 的内存,而 Android 4.4 及以后版本则只要后来的 Bitmap 比之前的小即可。使用 inBitmap 参数前,每创建一个  Bitmap 对象都会分配一块内存供其使用,而使用了 inBitmap 参数后,多个 Bitmap 可以复用一块内存,这样可以提高性能。

    三、计算Bitmap的大小
    3.1 Android API 的方法
    在使用 Bitmap 时经常会出现 OOM 的现象(这是多么让人心痛的错误啊),那么一张图片究竟是有多大呢?在 Bitmap 中提供了一个供我们查看 Bitmap 大小的 getByteCount() 方法:
    public final int getByteCount() {
            if (mRecycled) {
                Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap! "
                        + "This is undefined behavior!");
                return 0;
            }
            // int result permits bitmaps up to 46,340 x 46,340
            return getRowBytes() * getHeight();
        }
    123456789
    这个是 Android API所给的方法,当我们想进一步看看它是怎么实现的时候却发现 getRowBytes 调用的是 native 方法。那么我们能不能给一张在某个手机上的图片,你就知道 Bitmap 所在内存的大小呢?为了探究 Bitmap 的奥秘,我们去手动计算一张 Bitmap 的大小。
    3.2 手动计算
    下载了 Android framework 源码,Bitmap 相关的源码在文件下:frameworks\base\core\jni\android\graphics,找到 Bitmap.cpp, getRowBytes() 对应的函数为:
    static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
        LocalScopedBitmap bitmap(bitmapHandle);
        return static_cast<jint>(bitmap->rowBytes());
    }
    1234
    SkBitmap.cpp:
    size_t SkBitmap::ComputeRowBytes(Config c, int width) {
        return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
    }
    123
    SkImageInfo.h:
    static int  SkColorTypeBytesPerPixel(SkColorType ct) {
       static const  uint8_t gSize[] = {
        0,  // Unknown

        1,  // Alpha_8

        2,  // RGB_565

        2,  // ARGB_4444

        4,  // RGBA_8888

        4,  // BGRA_8888

        1,  // kIndex_8

      };
      SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
                    size_mismatch_with_SkColorType_enum);
       SkASSERT((size_t)ct < SK_ARRAY_COUNT(gSize));
       return gSize[ct];
    }
    static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) {
        return width * SkColorTypeBytesPerPixel(ct);

    }
    1234567891011121314151617181920212223242526
    顺便提一下,Bitmap 本质上是一个 SKBitmap ,而这个 SKBitmap 也是大有来头,它来自 Skia 。这个是 Android 2D 图像引擎,而且也是 flutter 的图像引擎。我们发现 ARGB_8888(也就是我们最常用的 Bitmap 的格式)的一个像素占用 4byte,那么 rowBytes 实际上就是 4*width bytes。那么一行图片所占的内存计算公式:
    图片的占用内存 = 图片的长度(像素单位) * 图片的宽度(像素单位) * 单位像素所占字节数
    但需要注意的是, 在使用decodeResource 获得的 Bitmap 的时候,上面的计算公式并不准确。让我们来看看原因。decodeStream 会调用 native 方法 nativeDecodeStream 最终会调用BitmapFactory.cpp 的 doDecode函数:
    static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
            // .....省略
            if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
                  //对应不同 dpi 的值不同,这个值跟这张图片的放置的目录有关,如 hdpi 是240;xdpi 是320;xxdpi 是480。
                const int density = env->GetIntField(options, gOptions_densityFieldID);
                //特定手机的屏幕像素密度不同,如华为p20 pro targetDensity是480
                const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
                const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
                if (density != 0 && targetDensity != 0 && density != screenDensity) {
                      //缩放比例(这个是重点)
                    scale = (float) targetDensity / density;
                }
            }
        }
        //这里这个deodingBitmap就是解码出来的bitmap,大小是图片原始的大小
        int scaledWidth = size.width();
        int scaledHeight = size.height();
        bool willScale = false;

        // Apply a fine scaling step if necessary.
        if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) {
            willScale = true;
            scaledWidth = codec->getInfo().width() / sampleSize;
            scaledHeight = codec->getInfo().height() / sampleSize;
        }
        // Scale is necessary due to density differences.
        //进行缩放后的高度和宽度
        if (scale != 1.0f) {
            willScale = true;
            scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);//①
            scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
        }
        //.......省略
        if (willScale) {
            // This is weird so let me explain: we could use the scale parameter
            // directly, but for historical reasons this is how the corresponding
            // Dalvik code has always behaved. We simply recreate the behavior here.
            // The result is slightly different from simply using scale because of
            // the 0.5f rounding bias applied when computing the target image size
            //sx 和 sy 实际上约等于 scale ,因为在①出可以看出scaledWidth 和 scaledHeight 是由 width 和 height 乘以 scale 得到的。
            const float sx = scaledWidth / float(decodingBitmap.width());
            const float sy = scaledHeight / float(decodingBitmap.height());

            // Set the allocator for the outputBitmap.
            SkBitmap::Allocator* outputAllocator;
            if (javaBitmap != nullptr) {
                outputAllocator = &recyclingAllocator;
            } else {
                outputAllocator = &defaultAllocator;
            }

            SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType());
            // FIXME: If the alphaType is kUnpremul and the image has alpha, the
            // colors may not be correct, since Skia does not yet support drawing
            // to/from unpremultiplied bitmaps.
            outputBitmap.setInfo(
                    bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType));
            if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
                // This should only fail on OOM.  The recyclingAllocator should have
                // enough memory since we check this before decoding using the
                // scaleCheckingAllocator.
                return nullObjectReturn("allocation failed for scaled bitmap");
            }

            SkPaint paint;
            // kSrc_Mode instructs us to overwrite the uninitialized pixels in
            // outputBitmap.  Otherwise we would blend by default, which is not
            // what we want.
            paint.setBlendMode(SkBlendMode::kSrc);
            paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering

            SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy);
            canvas.scale(sx, sy);//canvas进行缩放
            canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
        } else {
            outputBitmap.swap(decodingBitmap);
        }
        //.......省略
    }
    12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
    所以,sx 和 sy 约等于 scale 的值,而 scale = (float) targetDensity / density;,那么我们来计算,在一张4000 * 3000 的jpg 图片,我们把它放在 drawable - xhdpi 目录下,在华为 P20 Pro 上加载,这个手机上densityDpi为 480,用 getByteCount 算出占用内存为 108000000。
    我们来手动计算:
    sx = 480/320,
    sy = 480/320,
    4000 * sx *3000 * sy  * 4 = 108000000
    如果你自己算你手机上的可能和 getByteCount 不一致,那是因为精度不一样,大家可以看到上面源码 ①处,scaledWidth = static_cast(scaledWidth * scale + 0.5f); 它是这样算的,所以我们可以用:
    scaledWidth  = int (480/320 *4000 + 0.5)
    scaledHeight  =  int (480/320 *3000 + 0.5)
    Bitmap所占内存空间为:scaledWidth  * scaledHeight  * 4
    所以这时Bitmap所占内存空间的方式为:
    图片的占用内存 = 缩放后图片的长度(像素单位) * 缩放后图片的宽度(像素单位) * 单位像素所占字节数
    总结:这个方法主要是让我们知道为什么同一张图片放在不同分辨率的文件下,Bitmap 所占内存空间的不同。而且这个计算方式是用 decodeResource 来得到Bitmap 的大小时,才有效。而用 decodeFile、decodeStream直接计算就行,没有缩放宽和高的整个过程。
    四、Bitmap的缩放
    4.1 质量压缩
    质量压缩不会改变图片的像素点,即我们使用完质量压缩后,在转换 Bitmap 时占用内存依旧不会减小。但是可以减少我们存储在本地文件的大小,即放到 disk上的大小。如果减少 Bitmap 加载到内存的大小,可以用采样压缩。下面是质量压缩的代码:
     /**
         * 质量压缩方法,并不能减小加载到内存时所占用内存的空间,应该是减小的所占用磁盘的空间
         * @param image
         * @param compressFormat
         * @return
         */
        public static Bitmap compressbyQuality(Bitmap image, Bitmap.CompressFormat compressFormat) {

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            image.compress(compressFormat, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
            int quality = 100;

            while ( baos.toByteArray().length / 1024 > 100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
                baos.reset();//重置baos即清空baos
                if(quality > 10){
                    quality -= 20;//每次都减少20
                }else {
                    break;
                }
                image.compress(Bitmap.CompressFormat.JPEG, quality, baos);//这里压缩options%,把压缩后的数据存放到baos中

            }
            ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            Bitmap bmp = BitmapFactory.decodeStream(isBm, null, options);//把ByteArrayInputStream数据生成图片

            return bmp;
        }
    123456789101112131415161718192021222324252627282930
    4.2 采样压缩
    这个方法主要用在图片资源本身较大,或者适当地采样并不会影响视觉效果的条件下,这时候我们输出的目标可能相对的较小,对图片的大小和分辨率都减小。采样压缩最典型的代码如下:
    BitmapFactory.Options options = new Options();
    options.inSampleSize = 2;
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);
    123
    4.3 使用矩阵
    前面我们采用了采样压缩,Bitmap 所占用的内存是小了,可是图的尺寸也小了。当我们需要尺寸较大时该怎么办?我们要用用 Canvas 绘制怎么办?当然可以用矩阵(Matrix):
    /**
         * 矩阵缩放图片
         * @param sourceBitmap
         * @param width 要缩放到的宽度
         * @param height 要缩放到的长度
         * @return
         */
        private Bitmap getScaleBitmap(Bitmap sourceBitmap,float width,float height){
            Bitmap scaleBitmap;
            //定义矩阵对象
            Matrix matrix = new Matrix();
            float scale_x = width/sourceBitmap.getWidth();
            float scale_y = height/sourceBitmap.getHeight();
            matrix.postScale(scale_x,scale_y);

            try {
                scaleBitmap = Bitmap.createBitmap(sourceBitmap,0,0,sourceBitmap.getWidth(),sourceBitmap.getHeight(),matrix,true);
            }catch (OutOfMemoryError e){
                scaleBitmap = null;
                System.gc();
            }
            return scaleBitmap;
        }
    1234567891011121314151617181920212223
    4.4 合理选择Bitmap的像素格式
    ARG_B8888 格式的图片,每像素占用 4 Byte,而  RGB_565 则是 2 Byte。显而易见,不同的像素格式其Bitmap 的大小也就不同。

     


    格式
    单位像素所占字节数
    描述

     


    ALPHA_8
    1
    只有一个alpha通道


    ARGB_4444
    2
    这个从API 13开始不建议使用,因为质量太差


    ARGB_8888
    4
    ARGB四个通道,每个通道8bit


    RGB_565
    2
    每个像素占2Byte,其中红色占5bit,绿色占6bit,蓝色占5bit


    4.5 综合优化
    下面的例子主要用到了 Bitmap 的采样压缩(这个采样率是根据需求来进行生成的),使用到了inBitmap内存复用和 inJustDecodeBounds (这两个字段上面都有介绍)
    下面介绍获取采样的流程:

    将 BitmapFactory.Options 的 inJustDecodeBounds 参数设置为 true 并加装图片。
    从 BitmapFactory.Options 中取出图片的原始宽和高,它们对应于 outWidth 和 outHeight 参数。
    根据采样率的规则并结合目标 View 的所需要大小计算出采样率 inSampleSize 。
    将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 false ,然后重新加装图片。

    /**
         * 采样率压缩,这个和矩阵来实现缩放有点类似,但是有一个原则是“大图小用用采样,小图大用用矩阵”。
         * 也可以先用采样来压缩图片,这样内存小了,可是图的尺寸也小。如果要是用 Canvas 来绘制这张图时,再用矩阵放大
         * @param image
         * @param compressFormat
         * @param requestWidth 要求的宽度
         * @param requestHeight 要求的长度
         * @return
         */
        public static Bitmap compressbySample(Bitmap image, Bitmap.CompressFormat compressFormat, int requestWidth, int requestHeight){
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            image.compress(compressFormat, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
            ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            options.inPurgeable = true;
            options.inJustDecodeBounds = true;//只读取图片的头信息,不去解析真是的位图
            BitmapFactory.decodeStream(isBm,null,options);
            options.inSampleSize = calculateInSampleSize(options,requestWidth,requestHeight);
            //-------------inBitmap------------------
            options.inMutable = true;
            try{
                Bitmap inBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.RGB_565);
                if (inBitmap != null && canUseForInBitmap(inBitmap, options)) {
                    options.inBitmap = inBitmap;
                }
            }catch (OutOfMemoryError e){
                options.inBitmap = null;
                System.gc();
            }

            //---------------------------------------

            options.inJustDecodeBounds = false;//真正的解析位图
            isBm.reset();
            Bitmap compressBitmap;
            try{
                compressBitmap =  BitmapFactory.decodeStream(isBm, null, options);//把ByteArrayInputStream数据生成图片
            }catch (OutOfMemoryError e){
                compressBitmap = null;
                System.gc();
            }

            return compressBitmap;
        }

        /**
         * 采样压缩比例
         * @param options
         * @param reqWidth 要求的宽度
         * @param reqHeight 要求的长度
         * @return
         */
        private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

            int originalWidth = options.outWidth;
            int originalHeight = options.outHeight;
            
            int inSampleSize = 1;

            if (originalHeight > reqHeight || originalWidth > reqHeight){
                // 计算出实际宽高和目标宽高的比率
                final int heightRatio = Math.round((float) originalHeight / (float) reqHeight);
                final int widthRatio = Math.round((float) originalWidth / (float) reqWidth);
                // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
                // 一定都会大于等于目标的宽和高。
                inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;

            }
            return inSampleSize;
        }

    </原文:https://blog.csdn.net/wanliguodu/article/details/84973846>

    android matrix 最全方法详解与进阶(完整篇)

    https://blog.csdn.net/cquwentao/article/details/51445269

    展开全文
  • Android canvas.drawBitmap讲解

    千次阅读 2019-12-24 17:47:47
    drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 参数://Bitmap:图片对象,left:偏移左边的位置,top: 偏移顶部的位置 2, drawBitmap( Bitmap bitmap, Rect src, Rect dst, Paint paint); ...

    1,基本的绘制图片方法

    drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

    参数://Bitmap:图片对象,left:偏移左边的位置,top: 偏移顶部的位置

    2, drawBitmap( Bitmap bitmap, Rect src,  Rect dst,  Paint paint);

    这里由2个Rect,第一个Rect --src 代表要裁剪的bitmap的区域,如传null,表示需要绘制整个图片,

    第二个Rect ---det表示需要将bitmap,绘制在屏幕上的位置,不可为空,并且大于src则把src的裁截区放大,小于src则把src的裁截区缩小。

     Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.ic_logo);
            //绘制方法1:---原图,制作偏移
            canvas.drawBitmap(bitmap,100,100,mPaint);//将图片从(0,0)位置向左偏移100,向右偏移100
    
            Rect srcRect = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);//截取图片左上1/4的区域
    
            Rect dstRect = new Rect(500,500,800,800);//图片需要绘制的矩形区域
            //绘制方法2:--先裁剪再展示
            canvas.drawBitmap(bitmap,srcRect,dstRect, mPaint);

    效果图:

     

    展开全文
  • Canvas drawBitmap()方法的详细说明

    千次阅读 2019-08-16 14:54:29
    1、基本的绘制图片方法 ...drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 2、对图片剪接和限定显示区域 drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint); Rect src: ...

    1、基本的绘制图片方法
    //Bitmap:图片对象,left:偏移左边的位置,top: 偏移顶部的位置
    drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
    2、对图片剪接和限定显示区域
    drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint);
    Rect src: 是对图片进行裁截,若是空null则显示整个图片
    RectF dst:是图片在Canvas画布中显示的区域,大于src则把src的裁截区放大,小于src则把src的裁截区缩小

    关于drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)方法
    这个方法我看了很久,并做了一些测试,终于弄明白了。
    这个方法可以用来剪辑一张图片的一部分,即当我们把一组图片做成一张时,我们可以用此方法来剪辑出单个图片。
    bitmap的默认坐标是0,0.我们可以在此基础上剪图片。
    矩形src为我们所剪辑的图片的包围框,即你所剪的图片,如果为空,就是整张图片。
    矩形dst容纳你所剪的图片,然后根据此矩形的位置设置图片的位置。此参数不能为空。
    当你剪的图片大小大于dst时,多余的部分将不会显示。
    也就是说src是裁减区,对原始图的裁减区域,而dst是代表图片显示位置.

    展开全文
  • drawBitmap基本用法

    2019-09-30 12:43:51
    Bitmap:图片对象,left:偏移左边的位置,top: 偏移顶部的位置drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 2、对图片剪接和限定显示区域 drawBitmap(Bitmap bitmap, Rect src, RectF dst, ...

    1、基本的绘制图片方法

    Bitmap:图片对象,left:偏移左边的位置,top: 偏移顶部的位置
    drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

    2、对图片剪接和限定显示区域

    drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint);
    Rect src: 是对图片进行裁截,若是空null则显示整个图片
    RectF dst:是图片在Canvas画布中显示的区域,大于src则把src的裁截区放大,小于src则把src的裁截区缩小

     

    关于drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)方法
    这个方法我看了很久,并做了一些测试,终于弄明白了。
    这个方法可以用来剪辑一张图片的一部分,即当我们把一组图片做成一张时,我们可以用此方法来剪辑出单个图片。
    bitmap的默认坐标是0,0.我们可以在此基础上剪图片。
    矩形src为我们所剪辑的图片的包围框,即你所剪的图片,如果为空,就是整张图片。
    矩形dst容纳你所剪的图片,然后根据此矩形的位置设置图片的位置。此参数不能为空。
    当你剪的图片大小大于dst时,多余的部分将不会显示。
    也就是说src是裁减区,对原始图的裁减区域,而dst是代表图片显示位置.

    转载于:https://www.cnblogs.com/myxiaoQ/articles/3729957.html

    展开全文
  • 关于Android 自定义View中的onDraw里的drawBitmap 有一个半圆的图片,是一个统计图(就是油箱表的那样),还有一个指针的图片要把指针添加到图片里,然后传值让那个指针根据而移动。 找不到什么方法,所以求教下...
  • 本篇博客继续学习 Paint 和 Canvas 的基础用法,上一篇博客学习了基础API使用( 基础几何图形,Path 路径 ),接下来学习 绘制文本 和 绘制图片 上一篇文章,没看的有必要先了解一下: ...drawBitmap void drawB...
  • Canvas开篇之drawBitmap方法讲解

    万次阅读 2018-05-08 11:20:14
    所以canvas 我们也就可以分上面三块逐个击破,今天咱们主要看 drawXXX里的drawBitmap,看完之后一起做一个漂浮星空的小栗子; 在Canvas 里 drawBitmap 有如下方法可用 : 而咱们也主要讲其中的 drawBitmap(Bitmap,Rect...
  • canvas.drawBitmap(mBitmap,mSrcRect,mDestRect,mBitPaint); //canvas.drawBitmap(mBitmap,0,0,mBitPaint); } private void initPaint() { mBitPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBitPaint....
  • Android之drawBitmap方法

    千次阅读 2019-04-01 19:27:01
    canvas.drawBitmap(Bitmap bitmap, Rect src,Rect dst, Paint paint) 这几个参数有什么含义? bitmap是要进行处理的位图 src是对bitmap裁剪 如果是整张,那么: Rect src=new Rect(0,0,bitmap.getWidth(),bitmap....
  • 转载请注明出处:http://blog.csdn.net/binbinqq86/article/details/78327834项目中经常会用到Canvas来绘图,制作一些自定义view等,其实绘图相关的东西...首先来说一下drawBitmap这个方法:public void drawBitmap(@No
  • void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) Draw the bitmap using the specified matrix. 使用指定的矩阵绘制位图。也就是可以通过matrix的属性来控制如何绘制位图。该方法有三个参数,分别是:...
  • canvas.drawBitmap(mBitmap,mSrcRect,mDestRect,mBitPaint); Log.d("TAG","图片已打印"); setWillNotDraw(false); invalidate(); } ``` 这是log显示的结果 ``` 05-04 22:04:06.443 15938-15938/...
  • android Canvas.drawBitmap 方法的理解

    千次阅读 2019-04-18 14:50:22
    在自定义view中需要绘制出画笔的图片,并且在当前按下的位置实时绘制, 我的步骤是:(1)获取资源文件的... Bitmap bitmapPaint = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.paint); ...
  • Android draw bitmap 图片不显示的问题

    千次阅读 2019-07-13 23:26:02
    之前遇到一个绘制bitmap 不显示的问题。 问题是,第一次进去,会显示,能够绘制出来,第二次再进就是黑屏。 后来去看代码,该走的draw 的地方都走了。看了两天。 只怪自己对Paint 这个类不熟悉。不知道看什么。怀疑...
  • void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) Draw the specified bitmap, with its top/left corner at (x,y), using the specified paint, transformed by the current matrix. 这个方法...
  • 1.http://www.gcssloop.com/customview/Canvas_PictureText GcsSloop本篇文章讲的是,canvas绘制bitmap中的drawBitmap函数的一个比较有意思的使用方法,我们以后也许会用到也说不定的哦。我们先来浏览一下这个函数...
  • android drawBitmap 透明区域 变黑

    千次阅读 2019-08-13 18:57:13
    使用PorterDuffXfermode 处理重叠区域时,Bitmap 非透明区域 变黑,改了几个小时。终于解决了。 @Override public void onDraw(){ canvas.drawRect(0, 0,getWidth(),getHeight(), mPaint); mPaint....
  • 自定义view-drawBitmap巧妙使用

    千次阅读 2017-05-27 10:15:39
    自定义view-drawBitmap巧妙使用
  • Canvas.drawBitmap()方法绘图空白

    千次阅读 2018-08-29 14:40:47
    问题出现的场景: 将一个ImageView上原有的Bitmap进行放缩操作后,重新设置到该ImageView上。放缩部分代码如下: ... Bitmap newBitmap = Bitmap.createBitmap((int) (mBitmap.getWidth() * x),(int) (mBitmap...
  • void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle. 绘制指定的位图,自动缩放/平移以填充目标...
  • //調用ondraw } } Rect类是一个很好用的方法,它指定了一个图片上的矩形范围, 在drawBitmap中传入source和dest的rect后,canvas会自动帮我们进行图片的缩放转换。 在XML中声明我们自己定义的这个CustomView.然后在...
  • private void setHome_Logo() { ImageView logo = (ImageView) view.findViewById(R.id.img)... Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.logo); Bitmap front = BitmapFactory.d...
  • android canvas drawBitmap方法详解

    千次阅读 2016-01-12 16:35:13
    之前自己在自定义view,用到canvas.drawBitmap(Bitmap, SrcRect, DesRect, Paint)的时候,对其中的第2和3个参数的含义含糊不清。看源码函数也没理解,然后看了一些其他的博客加上自己的理解,整理如下。首先,我们看...
  • 使用drawBitmap绘制图片

    2017-07-31 17:58:08
    drawBitmap写了个会动的小僵尸
  • 造成这个问题的原因就在于安卓系统会根据bitmap的density和当前运行设备的density进行比较,不同会进行缩放。 项目里的图片叫 ic_launcher.png,大小是72*72,只有一张,放到了drawable-xhdpi的文件夹里了 代码是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,478
精华内容 21,791
关键字:

drawbitmap