图形缩放的连带管理,极大图片的显示
分类:计算机编程

图形缩放的相干管理

点不清安卓开辟者都有图片加载的拍卖经历,比方通过减少节省图片加载中对内部存款和储蓄器的花销。
咱俩日常做的是把一张1280等等大小的图样以适应荧屏大小的尺码表现出来,同期能够透过缩放来察看。
只是那是雷同水平,通过压缩来管理的话平时会促成在最大尺寸放大后看不清细节,比方得到一张苍老师...哦不,获得一张小寒上河图,或许一张世(Zhang Shi卡塔尔国界地图,那时我们要保证在最大限度的扩充后仍是可以够看驾驭各种人物各样城市,平日的压缩的方案就不安妥了。

新近和好做的点东西上用到了图片的管理,可是还没找到适当的图形管理有关算法,倒是找到了多少个拿到缩略图的算法。代码非常短,轻便看了看规律也比较轻巧(其实不是简约是C#的卷入的太多了,所以显得比较容易)。既然没有有关的统意气风发调用的代码,索性,笔者来收拾豆蔻梢头套相关的管理代码然后公开出来吧。

此处大家要研究的是怎么着用一些剖析(BitmapRegionDecoder卡塔尔(قطر‎来完结在不占用过多内部存储器的情状下达成相当大图的缩放。

图表管理的相干剖析

常规贴源码:XPhotoView Demo

事实上小编要用的图形管理特别简单,就是轻便的裁减一下图形。然后把图纸设置到相应大小而已。然则对于设置的措施,C#幼功的函数中应用的Rect范围设置。这么些节制设置计算起来可能相对脑仁疼一些。所以本身在伪造要不要让调用者在忽视Rect这几个参数的场所欢悦的调用接口呢?于是乎,小编定义了多少个图片缩放类型(Full、Zoom、Overflow、Original)。然后暗中同意的让原有图片的中央点与输出图片的中坚点对齐。然后遵照缩放类型来对图片举行差异的缩放格局。

XPhotoView世袭ImageView达成了一点都非常的大图加载,德姆o中示范了什么样在Pager加载静态图片和动图,同期也支撑种种手势操作。
自己在百货店的成品上自定义了XPhotoView,在包含谈心列表,动图播放,还会有高清大图查看的功效上大器晚成度注脚了它的平安定协调高速,平日的开支中得以直接使用。

缩放格局

十分的大图片加载和有个别深入剖析

对此不足为道的图样,大家加载的笔触很简单就是减削大小,用Options来得到大小然后和脚下显示器尺寸举行相比,然后以自然的值压缩。可是那样推动的主题素材是,压缩后的图片会丢弃细节,倘使是小泽...呸,假设是晴天上河图压缩到显示屏尺寸,放大后怕是人都看不见。而整张图片加载肯定是没用的,内部存款和储蓄器绝争持马爆。
缓和方案很简短,大家只加载图片的片段区域,那部分区域适配显示器大小,合作手势移动的时候更新显示相应的区域就能够了。
Android提供了BitmapRegionDecoder来开展图纸的有的深入分析,这是XPhotoView的重大思路。
剩下的正是何等飞快的加载的标题了,怎么样兼备代码逻辑,让它能够急迅的响应手势动作吗。

  1. Full:让图片摈弃原本的比重,直接遵照指标数据开展拉伸
  2. Zoom:让图片保持原本比例,何况保障原始图片在对象图片的正宗旨,没有图片的岗位保留米雾灰
  3. Original:让图片保持原本的大大小小,输出图片相同于裁剪中间地方有些范围的图形
  4. Overflow:让图片保持原本的比例,而且保障输指标是被图片填满的不留空白

部分解析的代码逻辑

调用的范例

代码构造

XPhotoView的代码概要如下所示,

--|--|--XPhotoView
  |  |
  |  |--PhotoViewAttacher
  |--GestureManager

粗粗能够分为两有的,XPhotoView和PhotoViewAttacher担任图片的加载和深入分析,GestureManager肩负手势的判断和响应。整个库对外暴光的只是XPhotoView的多少个public方法用来setImage和有关的Listener,还会有是还是不是是Gif的参数。
Attacher本人只担当图片的拆分深入解析和渲染进程,同临时候Bitmap也是保存在Attacher中。Attacher和XPhotoView之间通过Interface互相调用,以此隔开分离。

ImageHelper.GetInstance().ImageCompress(InputFile, Path.Combine(OutPutPath, item_file), new System.Drawing.Size(500, 300), ImageZoomType.Full, 50);

Attacher 的拆解深入分析进度

大家临时忽视Gif的有的,先描述一下Attacher的思绪。
Attacher有叁个里头子类BitmapUnit和BitmapGridStrategy,初阶图片会被BitmapRegionDecoder切割为 N*M 的网格,然后存款和储蓄在BitmapUnit[N][M]二维数组 mGrids 中。

图片 1

image.png

以晴天上河图为例,图中高亮的线条把图纸分割为三有的,就是说我们用 BitmapUnit[1][3] 来存款和储蓄那张图片。这么做的缘故是,当我们加大图片来查看的时候,只须要深入分析单个格子以至它周围格子里的图纸。
理当如此在我们以适配显示屏的标准下查看全图时,是通过mSampleSize比例收缩的,也正是说在mGrids中的Bitmap是减削过后的占小内部存款和储蓄器的位图,不用忧郁OOM的难点。

    /** 当前图片的的采样率 */
    private int mSampleSize = 0;
    /***
     * View Rect
     * View 坐标系*/
    private Rect mViewRect = new Rect();

    /** 原图 Rect
     *  Bitmap 坐标系 */
    private Rect mImageRect = new Rect();

    /**
     * 实际展示的 Bitmap 大小
     * Bitmap 坐标系 */
    private RectF mShowBitmapRect = new RectF();
    /**
     * view 相对 Show Bitmap 的坐标
     * Bitmap 坐标系 */
    private Rect mViewBitmapRect = new Rect();

以上是Attacher中的关键变量,整个深入深入分析和渲染的经过基于那四个Rect的坐标。

近些日子大家初阶一切工艺流程。

样本突显

初始化
/**
 * @param bitmap 设置 bitmap
 * @param cache 是否cache
 */
void setBitmap(Bitmap bitmap, boolean cache);

/**
 * @param is 设置输入流
 * @param config config
 */
void setInputStream(InputStream is, Bitmap.Config config);

那七个是Attacher定义的对外接口,它只同意二种办法来设置图片,不管是哪个格局,都会转变为InputStream对象mDecodeInputStream,来作为BitmapRegionDecoder的发源。
若以setBitmap(卡塔尔(قطر‎方法起先化的话,会多安装叁个mSrcBitmap,当进行一些深入分析时就不会因此BitmapRegionDecoder来解析,而是会间接从mSrcBitmap中createBitmap对应的区域出来。这种方式的前提是暗许不相会世OOM,究竟已经能够整个Bitmap作为参数字传送进来了,可是不能确认保障在后头createBitmap时不会OOM,所以不提倡用这么些措施来初步化。

在调用那三个办法任何三个过后,都会调用initialize(卡塔尔(قطر‎来初叶化要求的线程和Handler,

/** 初始化所需参数和线程*/
private synchronized void initialize(Bitmap.Config config)

接下来我们过来setBitmapDecoder(final InputStream is卡塔尔方法,当时大家起先确实的拆图和剖判。那一个法子是具备的源点,何况只会也只应该走三回。
它会把mInstanceDecoderRunnable丢给handler然后开首运营,在解析完结后通过回调告知上层深入解析实现,能够实行停业进度条之类的操作。

原有图片

赢得图片开端显示参数

那会儿我们会调用那么些方法

private void initiateViewRect(int viewWidth, int viewHeight)

唯独首先次调用的时候是在onDraw在此之前,在setImage之后,当时大家并不知道具体的Canvas的抑扬顿挫,由此没办法明确缩放比例,还恐怕有任何的Rect所急需的早先化的具体值。因而那时mViewRect的值都还是0,作为参数字传送进来后通过校验是不行值,则会脱离本次的点子。
那时候大家来探视draw(卡塔尔(英语:State of Qatar)方法,

@Override
public boolean draw(@NonNull Canvas canvas, int width, int height) {
    if (isNotAvailable()) {
        return false;
    }

    if (mSrcBitmap != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        int mw = canvas.getMaximumBitmapWidth();
        int mh = canvas.getMaximumBitmapHeight();

        /**
         * 如果图片太大,直接使用bitmap 会占用很大内存,所以建议缓存为文件再显示
         */
        if (mSrcBitmap.getHeight() > mh || mSrcBitmap.getWidth() > mw) {
            //TODO
        }
    }

    /**
     * 更新视图或者画出图片
     */
    return !checkOrUpdateViewRect(width, height) && mBitmapGrid.drawVisibleGrid(canvas);
}

在XPhotoView调用draw(卡塔尔(英语:State of Qatar)方法后,最终博览会开实用检查,正是checkOrUpdateViewRect(卡塔尔(قطر‎,那时会把真正的视图的大小作为参数字传送给 initiateViewRect(卡塔尔(قطر‎,然后再真正的开展参数的最初化。
科学,这里大家用延迟的艺术来取拿到实在的视图大小,即便代码不轻巧通晓,不过稳固性进步了。

接下去我们要开端化多少个参数,

mShowBitmapRect, mViewBitmapRect, mSampleSize

图片的开首化呈现格局有两种,按Android的概念有巴博斯 SL级_CENTER,CENTER_CROP等,这里大家私下认可用 CENTE帕杰罗_CROP 的方法,而且呈现图片的胚胎部分,横图从左侧开始,竖图从最上边初叶。
这里须求关心的重点代码是,

    private void initiateViewRect(int viewWidth, int viewHeight) {
      ····
      /** 以 view 宽/长比例和 image 宽/长比例做比较
         *  iW/iH < vW/vH : 左右留空,取高比值
         *  iW/iH > vW/vH : 上下留空,取宽比值 */
        float ratio = (imgWidth/imgHeight < viewWidth/viewHeight) ? (imgHeight * 1.0f / viewHeight) : (imgWidth * 1.0f / viewWidth);

        mShowBitmapRect.set(0, 0, (int) (imgWidth / ratio), (int) (imgHeight / ratio));

        /** 保存初始大小 */
        mShowBitmapRect.round(mInitiatedShowBitmapRect);

        /** 取缩小到适配view时的bitmap的起始位置 */
        int left = (int) ((mShowBitmapRect.width() - mViewRect.width()) / 2);
        int top = (int) ((mShowBitmapRect.height() - mViewRect.height()) / 2);

        left = mShowBitmapRect.width() < mViewRect.width() ? left : 0;
        int right = left   mViewRect.width();
        top = mShowBitmapRect.height() < mViewRect.height() ? top : 0;
        int bottom = top   mViewRect.height();

        mViewBitmapRect.set(left, top, right, bottom);
      ····
    }

我们经过比较View和Image的高/宽比例,显明图片是横图仍旧竖图,
以横图为例子,那时咱们必要将它上下撑大到刚刚铺满显示屏,那么就能够View的可观和Image的可观来算出压缩值 ratio,用它来算出减弱后的图片的莫过于尺寸,保存在 mShowBitmapRect中。
完了这一步后,接下去总计mViewBitmapRect。还记得大家的设定是展示图片的开首部位么,

/** 取缩小到适配view时的bitmap的起始位置 */
int left = (int) ((mShowBitmapRect.width() - mViewRect.width()) / 2);
int top = (int) ((mShowBitmapRect.height() - mViewRect.height()) / 2);

left = mShowBitmapRect.width() < mViewRect.width() ? left : 0;
int right = left   mViewRect.width();
top = mShowBitmapRect.height() < mViewRect.height() ? top : 0;
int bottom = top   mViewRect.height();

mViewBitmapRect.set(left, top, right, bottom);

那部分代码,既保证了不小图在缩放后从开第四地点上马,也确定保障了普通图片缩放后不满屏的场合下居中突显,大家能够探究商量。

要在意initiateViewRect这一个措施,它不光在初步化的时候调用,前面每趟的缩放,都急需调用它来更新mShowBitmapRect,因为老是的缩放都会让实际显示的图片大小发生改换。

图片 2

绘制流程

关心 draw(卡塔尔(قطر‎方法,这里是绘制流程的非常重要部分,
老是绘制前都会先更新当前的顺序Rect对象,以获得对应的体现中的Grid,大家只绘制展现出来的片段Grid单元,
下边包车型客车图大约陈诉在绘制时的状态,

*  -- -- -- -- -- 
* |XX|XX|11|11|XX|
*  -- -- -- -- -- 
* |XX|XX|11|11|XX|
*  -- -- -- -- -- 
* |XX|XX|XX|XX|XX|
*  -- -- -- -- -- 

标识为11的八个格子,表示前段时间可以看到的区域,xx的意味不可以知道区域。
对此可知区域,会组成当前的缩放值,从mBitmapGrid中抽取,然后经过XPhotoView传进来的Canvas对象绘制,
对此不可知区域,会回笼掉对应的bitmap对象,以节外省部存款和储蓄器。

Full 300*500 

手势响应

关于手势响应,是比较容易的四个部分,
我们定义了GestureManager,把XPhotoView的风浪交给GestureManager的onTouchEvent(卡塔尔国管理,
那有个别代码相对简便易行,不做过多解释。

图片 3

合作动图

动图的展现情势有二种方案,

  • 用Movie类来突显
  • 托管给Glide的GifDrawable去渲染

 

Movie的方式

这种方法相对简单,
在大家不知晓对应的文书恐怕图片是或不是是动图的情况下,以常常逻辑设置即可,
设置之后会用Movie类来决断是还是不是是八个可行的GIF图,
自此在draw时,在Gif的动静下会用Movie类来开展渲染

Full 500 * 300

Glide的方式

多多体系会用Glide来做图片的下载和浮现,
Glide本人会剖断图片是还是不是为Gif,当是Gif时会布局贰个 GifDrawable 对象,
我们直接把那个GifDrawable对象用 setImageDrawable 的办法设置到XPhotoView,
GifDrawable会接管动图的绘图流程。
专心如若这种景色下动图不动的话,供给在 setImageDrawable 之后调用 GifDrawable 的 start(卡塔尔(قطر‎方法,

if(glideDrawable instanceof GifDrawable) {
  holder.photoView.setGif(true);
  holder.photoView.setImageDrawable(glideDrawable);
  ((GifDrawable) glideDrawable).start();
}

图片 4

 

Original 300 * 500

图片 5

 

Original 500 * 300

图片 6

 

Overflow 300 * 500

图片 7

 

Overflow 500 * 300

图片 8

 

Zoom 300 * 500

图片 9

 

Zoom 500 * 300

图片 10

存在的贫乏

  1. 减削未有独到的本领
  2. 当前只扶助JPEG格式的图纸
  3. 有的图纸实行缩放之后会留给多个1像素浅浅的边

大浪涛沙的从头到尾的经过

图表管理不光是缩放,还会有其余的重重事物相比实用(截图、压缩、按高缩放、按宽缩放、增添水印、增添隐讳水印),小编计划把图片常用的相干管理。收拾成二个增加援助类,方便迁入到各种项目中去。

因而本身创设了贰个特别的图纸管理的开源项目便利后续效应步向,地址如下:

 

压缩相关的代码

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ImageCompress
{
    public struct AnchorPoint
    {
        //public AnchorPoint() : this(0, 0) { }
        public AnchorPoint(double x, double y)
        {
            X = x;
            Y = y;
        }

        public double X, Y;
    }

    public enum ImageZoomType
    {
        Full,
        Zoom,
        Overflow,
        Original,
    }

    public delegate void ImageHandle(Image image, Bitmap bitmap);

    public class ImageHelper
    {
        private ImageHelper() { }

        private static ImageHelper _image_helper = null;
        private Dictionary<ImageZoomType, ImageHandle> _name_to_handle = new Dictionary<ImageZoomType, ImageHandle>();

        public static ImageHelper GetInstance()
        {
            if (_image_helper == null)
            {
                _image_helper = new ImageHelper();

                //填满
                _image_helper._name_to_handle[ImageZoomType.Full] = (image, bitmap) =>
                {
                    _image_helper.DrawImage(image, bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
                };

                //原始
                _image_helper._name_to_handle[ImageZoomType.Original] = (image, bitmap) =>
                {
                    _image_helper.DrawImage(image, bitmap, 1, new AnchorPoint(0.5, 0.5), new AnchorPoint(0.5, 0.5));
                };

                //溢出
                _image_helper._name_to_handle[ImageZoomType.Overflow] = (image, bitmap) =>
                {
                    float proportion_x = (float)bitmap.Width / image.Width;
                    float proportion_y = (float)bitmap.Height / image.Height;
                    _image_helper.DrawImage(image, bitmap, proportion_x > proportion_y ? proportion_x : proportion_y, new AnchorPoint(0.5, 0.5), new AnchorPoint(0.5, 0.5));
                };

                //缩放
                _image_helper._name_to_handle[ImageZoomType.Zoom] = (image, bitmap) =>
                {
                    float proportion_x = (float)bitmap.Width / image.Width;
                    float proportion_y = (float)bitmap.Height / image.Height;
                    _image_helper.DrawImage(image, bitmap, proportion_x < proportion_y ? proportion_x : proportion_y, new AnchorPoint(0.5, 0.5), new AnchorPoint(0.5, 0.5));
                };
            }

            return _image_helper;
        }

        /// <summary>
        /// 压缩图片
        /// </summary>
        /// <param name="source_path">源数据位置</param>
        /// <param name="save_path">保存数据位置</param>
        /// <param name="save_size">保存图片大小</param>
        /// <param name="ztype">缩放模式</param>
        /// <param name="flag">图片保存品质</param>
        /// <returns>是否完成压缩</returns>
        public bool ImageCompress(string source_path, string save_path, Size save_size, ImageZoomType ztype, int flag)
        {
            bool success = false;

            Image source = null;

            while (true)
            {
                source = LoadImage(source_path);
                if (source == null)
                    break;

                Bitmap bitmap = new Bitmap(save_size.Width, save_size.Height);

                if (_name_to_handle.ContainsKey(ztype))
                {
                    _name_to_handle[ztype](source, bitmap);
                }
                else
                {
                    break;
                }

                success = SaveImage(bitmap, save_path, source.RawFormat, flag);
                break;
            }

            if (source != null)
                source.Dispose();

            return success;
        }

        public Image LoadImage(string source_path)
        {
            Image image = null;
            while (true)
            {
                if (!File.Exists(source_path))
                    break;

                try
                {
                    image = Image.FromFile(source_path);
                }
                catch
                {
                    break;
                }

                break;
            }

            return image;
        }

        /// <summary>
        /// 将BitMap保存到磁盘上
        /// </summary>
        /// <param name="image">需要保存的图片</param>
        /// <param name="save_path">保存的路径</param>
        /// <param name="format">保存格式</param>
        /// <param name="flag">保存质量</param>
        /// <returns></returns>
        public bool SaveImage(Bitmap image, string save_path, ImageFormat format, int flag)
        {
            //以下代码为保存图片时,设置压缩质量  
            EncoderParameters ep = new EncoderParameters();
            long[] qy = new long[1];
            qy[0] = flag;//设置压缩的比例1-100  
            EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
            ep.Param[0] = eParam;
            try
            {
                ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
                ImageCodecInfo jpegICIinfo = null;
                for (int x = 0; x < arrayICI.Length; x  )
                {
                    if (arrayICI[x].FormatDescription.Equals("JPEG"))
                    {
                        jpegICIinfo = arrayICI[x];
                        break;
                    }
                }
                if (jpegICIinfo != null)
                {
                    image.Save(save_path, jpegICIinfo, ep);//dFile是压缩后的新路径  
                }
                else
                {
                    image.Save(save_path, format);
                }
                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                image.Dispose();
            }
        }

        /// <summary>
        /// 画图
        /// </summary>
        /// <param name="source">原始图像</param>
        /// <param name="output">输出图像</param>
        /// <param name="souce_scale">原始图像的缩放值</param>
        /// <param name="souce_anchor"></param>
        /// <param name="graphics_anchor"></param>
        public void DrawImage(Image source, Bitmap output, float souce_scale, AnchorPoint souce_anchor, AnchorPoint graphics_anchor)
        {
            DrawImage(source, output, souce_scale, souce_anchor, new Point((int)(output.Width * graphics_anchor.X), (int)(output.Height * graphics_anchor.Y)));
        }

        /// <summary>
        /// 画图
        /// </summary>
        /// <param name="source">原始图像</param>
        /// <param name="output">输出凸显</param>
        /// <param name="source_scale">图像的所放值</param>
        /// <param name="source_anchor">源图像锚点</param>
        /// <param name="souce_point">图像位置</param>
        public void DrawImage(Image source, Bitmap output, float source_scale, AnchorPoint source_anchor, Point souce_point)
        {
            var pic_po = new Point((int)(souce_point.X - source.Size.Width * source_scale * source_anchor.X), (int)(souce_point.Y - source.Size.Height * source_scale * source_anchor.Y));
            DrawImage(source, output, new Rectangle(pic_po, new Size((int)(source.Width * source_scale), (int)(source.Height * source_scale))));
        }

        /// <summary>
        /// 画图
        /// </summary>
        /// <param name="source"></param>
        /// <param name="output"></param>
        /// <param name="rect"></param>
        public void DrawImage(Image source, Bitmap output, Rectangle rect)
        {
            Graphics g = Graphics.FromImage(output);
            g.Clear(Color.WhiteSmoke);
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(source, rect, 0, 0, source.Width, source.Height, GraphicsUnit.Pixel);
            g.Dispose();
        }
    }
}

  

 

PS:为了雅观本文中的原始图片并非原本图片,因为本来图片太大了。会引致页面滑动,所以小编上传了一张十分的小的图片。Git的图片则是固有的图纸

本文由pc28.am发布于计算机编程,转载请注明出处:图形缩放的连带管理,极大图片的显示

上一篇:pc28.am:python的turtle库真好玩,Python实现七彩蟒蛇 下一篇:没有了
猜你喜欢
热门排行
精彩图文