AIR客户端-高效处理图片缩略图的解决思路1

7 篇文章 0 订阅

故事的背景:2016年,广州的冬天下起了鹅毛大雪。。。所以决定启动修改之前项目中的一个硬伤。如果各位程序员身边,有这么一位产品经理,用这么充分的理由来说服你,你会吐血三尺么?

硬伤:客户端软件处理图片速度很慢,显然不适应这个冬天的寒冷。

疗伤方案:既然中了寒毒,就需要对症下药,找到病根,AIR效率不高的事实又被端出了台面。

之前软件处理图片的逻辑都是托付给AIR端出里,当客户导入大量的高分辨率的大图时候,处理效果很慢,电脑配置差的,有时候会造成假死状态。项目软件是一个类似于电子相册的制作工具,可以可以上传大图,再DIY生产电子相册。抽离之前的代码逻辑,发现:

位图加载-------->AIR端----------->根据需求,处理bitmapdata数据---------->生产对应两个小尺寸的位图 ------->再用于DIY编辑。

上述就是程序基本逻辑,如果在flex内部优化的话,也没有很大的优化方案了,所以就有了下面提出的方案。

1.将上述处理逻辑给C++处理

C++的高效,不容置疑,这个方案肯定是行的通的,所以陷入了C++的研究中。

一.这里插播一个片段:

为了验证上述方案可研究型,用C#临时写了一个demo,实测确实效率高了很多,放出关键代码:

flash-AIR端代码

import flash.filesystem.File;
import flash.events.MouseEvent;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.BitmapData;
import flash.display.Bitmap;
import fl.motion.easing.Back;
import flash.utils.Timer;
import flash.events.TimerEvent;

var np:NativeProcess = new NativeProcess();
var npi:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var args:Vector.<String> = new Vector.<String>();
var picsArr:Array = [];
var picNum :uint   = 45;
for(var i:uint=1;i<=picNum;i++){
	picsArr.push("Pic/"+i+".jpg");
}
//这里模拟了一个数组的图片,在软件同目录里有一个Pic目录
picsArr.push("Pic/45.jpeg");
picsArr.push("Pic/bg.png");
picsArr.push("Pic/logo.png");
picsArr.push("Pic/bg.png");
   args.push("startImageProcessing");
   args.push("Pic/temp/");
   args.push("700");
   args.push("300");
   args.push("80");
   args.push(picsArr);
   npi.arguments = args;
   npi.executable = File.applicationDirectory.resolvePath("MyNativeExe.exe");
   np.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onMyData);
   
var firstRun:Boolean = true;

 function onMyData(e:ProgressEvent):void {
   while(np.standardOutput.bytesAvailable != 0) {
     text.appendText(String.fromCharCode(np.standardOutput.readByte()));
   }
   text.appendText("\n");
   var myData:Date = new Date();
   var endTime:Number = myData.time;
   text.appendText("结束时间:"+endTime+"\n");
   text.appendText("消耗时间:"+(endTime-startTime)+"\n");
   
   var curPicPath:String  = picsArr[startID];
	 var tempArr:Array      = curPicPath.split("/");
	 var curPicName :String = tempArr[tempArr.length-1];
	 var  cArr              = curPicName.split(".");
     var  midFileName:String= cArr[0] + "-mid." + cArr[1];
	 needLoadArr.push("Pic/temp/"+midFileName);
	 
	 if(firstRun){
		 loadPic();
		 firstRun = false;
	 }
	 
 }
 var startID:uint ;
 var totalID:uint;
 var loader:Loader;
 var needLoadArr:Array ;
 
 function loadPic(){
	 //检查needLoadArr数组里是否有数据需要加载
	 text1.appendText("needLoadArr数组里个数:"+needLoadArr.length);
	 if(needLoadArr.length>0){
		  text1.appendText("\n");
	      text1.appendText("当前ID1:"+startID+"\n");
		 var curPath:String = needLoadArr.shift();
	 }else{
		 checkTimer();
		 return;
	 }
	
	 if(!loader){
		 loader = new Loader();
		 loader.contentLoaderInfo.addEventListener(Event.COMPLETE,picLoaded);
	 }
	 
	 loader.load(new URLRequest(curPath));
 }
 var myTimer:Timer;
 function checkTimer(){
	 if(!myTimer){
		 myTimer = new Timer(100);
		 myTimer.addEventListener(TimerEvent.TIMER,onTimerFun);
	 }
	 myTimer.start();
 }
 
 function onTimerFun(e:TimerEvent):void{
	 if(needLoadArr.length>0){
		 loadPic();
		 myTimer.stop();
	 }
 }
 
 function picLoaded(e:Event):void{
	 startID++;
	 text1.appendText("图片加载完成-当前ID2:"+startID+"\n");
	
	 var bitd:BitmapData = loader.content as BitmapData;
	 loader.content.x = 20;
	 loader.content.y = 120;
	 addChild(loader.content);
	 if(startID<picsArr.length){
		 loadPic();
	 }else{
		 if(myTimer){
			 if(myTimer.running){
				 myTimer.stop();
				 myTimer.removeEventListener(TimerEvent.TIMER,onTimerFun);
				 myTimer = null;
			 }
		 }
	 }
	 
 }
 
 startBtn.addEventListener(MouseEvent.CLICK,onBtnClick);
 closeBtn.addEventListener(MouseEvent.CLICK,onBtnClick);
 
 var startTime:Number;
 function onBtnClick(e:MouseEvent):void{
	 var targetName:String = e.currentTarget.name;
	 if(targetName=="startBtn"){
	     var myData:Date = new Date();
	     startTime   =myData.time;
	     text.appendText("开始时间:"+startTime+"\n");
	     np.start(npi);
		 
		 startID = 0;
		 totalID = picsArr.length;
		 needLoadArr =[];
	 }else{
		 np.exit();
	 }
 }

C#端:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Drawing;

namespace WindowsFormsApplication1
{
    
    static class Program
    {
        static private string saveUrl;
        static private int maxWidth;
        static private Double maxHeight;
        static private Double quality;
    
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static int Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Form1 myForm = new Form1();
 
            int i = args.Length;
            myForm.label1.Text = "hello world";
            myForm.label1.Text += "\n";
            myForm.label1.Text += "参数:"+i+"个数";
            for (int t = 0; t < i; t++)
            {
                myForm.label1.Text += "\n";
                myForm.label1.Text += args[t] is string+"   :";
                if (t==5)
                {
                    string[] sArr = args[t].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                    myForm.label1.Text += "图片加载的个数:"+sArr.Length;
                    doImageProcessing(sArr);
                }
                else
                {
                    myForm.label1.Text += args[t] + "   :"+t;
                    switch (t)
                    {
                        case 1:
                            saveUrl  = args[t];
                            break;
                        case 2:
                            maxWidth = Convert.ToInt32(args[t]);
                            break;
                        case 3:
                            maxHeight = Convert.ToInt32(args[t]);
                            break;
                        case 4:
                            quality = Convert.ToInt32(args[t]);                           
                            break;

                    }
                }                
            }
            Application.Run(myForm);
            return 1;
        }

        static void doImageProcessing(string[] args)
        {
            int i = args.Length;
            for (var t = 0; t < i; t++)
            {
                //CutForCustom(args[t]);
                CutForCustom1(args[t]);
            }
            System.Console.Write("complete");
        }

        static void CutForCustom(string fileUrl)
        {
            System.IO.FileStream fs = new System.IO.FileStream(fileUrl, FileMode.Open, FileAccess.Read);
            string[] sArr = fileUrl.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            string fileName = sArr[sArr.Length - 1];
            string[] cArr = fileName.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);
            string midFileName = cArr[0] + "-mid." + cArr[1];
            //Images.ZoomAuto(fs, saveUrl + midFileName, maxWidth, maxHeight, "", "");
        }

        static void CutForCustom1(string fileUrl)
        {
            Image image = Image.FromFile(fileUrl);
            Image thumbnail = image.GetThumbnailImage(150,
                100, null, IntPtr.Zero);
            string[] sArr = fileUrl.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
            string fileName = sArr[sArr.Length - 1];
            string[] cArr = fileName.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);
            string midFileName = cArr[0] + "-mid." + cArr[1];
            thumbnail.Save(saveUrl + midFileName);
        }
    }
}

测试项目的结构:

因为都很简单,所以没有过多的注释了,不明白的话,可以和我交流。 

实测的话,从大图处理到小图生成,再到图片加载到AIR端,49张大图,230M的体积,大致用了12秒上下的样子,全部显示完毕。平均一张大图从处理到显示到客户端,用时12/50 近似于0.24s,完全符合客户使用的习惯。

如果这个话题就这么结束了,肯定不符合这次本文的要求,话说广州100年来第一次飘雪,能这么快就结束这篇技术讨论么?C++ 大哥都还没出场呢?

好了,给C++ 大哥一个机会。

二.C++大哥出来了

C++本人不熟,所以各种搜索资料,后来发现大部分人推荐C++类库:CXimage,Cimg,FreeImage等,我也是按这个顺序,依次研究了一遍。因为C++不熟悉,编译IDE也不熟悉,所以配置这个花费不少心思,各种报错一步步解决。最后还是。。。

先说CXimage ,这个大部分极力推荐,所以第一个那他开刀做研究,结果发现自己被他开刀了,也有可能不是他的问题,但问题就是在我电脑上跑不通。我还是大致讲述下这个:

(1) 下载这个类库,我下的是cximage600_full这个版本,ps 后来下cximage701_full也一样

(2)参考网上的教材,解压文件后,打开对应的项目文件,执行编译批生成,然后找到对应的h文件和lib文件,放到一个文件夹下,后面要用上

  DLL这个目录事后发现可放可不放,大家可以先忽略


(3) 就是配置VS软件里,项目的库文件指引,就要把上述目录的地址,添加进去,一个是头文件指引,header ,一个是静态库 lib。

(4)然后可以运行提供的实例,或者自己写一些简单的测试。

可惜我一直没有能正常跑通,碰到过字符提示编码问题,碰到过提示电脑缺少某某dll,等等问题,我都一一参考网络资源搞定,最后还是死在程序运行后,提示一个0x000007b上,百度和google说是direct问题,用过修复工具DirectX_Repair-v3.3 不行,删除重装也不行。最后无奈放弃。。也许大家可以行的通,那就可以少走很多弯路了。

Cimg 这个类库呢,貌似跑的通,但是对文件格式支持,需要依赖一个cover.exe文件,测试效果不是太理想,感觉不是我需要的那种原生C++处理,所以放弃

FreeImage这个和CXimage 有点类似,也是提示错误,程序无法启动,所以心哇凉哇凉的。

这里各种恨广州的雪。。。。。。

三.继续研究缩略图

心凉也不能放弃,之前用第三方类库挖的坑,就是哭也要把自己埋进去了。公司没有C++的人,哥有人啊,所以就打电话给朋友:“喂,小明么? 此处省略500字”。朋友研究它的,我也不能闲着,再换换思路。

大家用win系统都很熟悉了,是不是发现系统自带的文件缩略图预览功能很方便?我靠,我是不是把大家心里想说的话给说出来了?

是啊,聪明你的,肯定开始各种脑补了,哈哈,是的,我也觉得有希望,所以又开始研究了。

资料有很多,我提供官方的吧:

Microsoft Office Thumbnails in SharePoint

点击打开链接

Shell (including Windows 7 Libraries)

http://blogs.msdn.com/b/windowssdk/archive/2009/06/12/windows-api-code-pack-for-microsoft-net-framework.aspx

原来系统有一个C++API

GetThumbnailImage
官方解释
提供,可以直接获取文件的缩略图对象


win系统会给所有的文件配备一个对应的缩略图示,系统自带生成后保存在用户电脑的目录里:

红色涂抹部分,大家用自己的电脑用户名替换


这里有一个db,其实这部分就是用户在平时操作后,文件关联的预览缩略图数据库,为什么我可以这么肯定?因为我也好奇,在好奇心下,分析了这个db结构和文件。

下面这个就是我的db的结构图:

随便选取一列,会显示对应的文件缩略图。 这也是为什么在系统预览图片,这么快捷方便的原因。

好了,既然都验证了,我们就用这个系统的GDI API去实现上述功能了。

实测效果确实高效,200多张图,2个G的量,现在属于秒级的处理速度。稍后再补充更新。未完待续。。。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

完结篇

C++的语法不是很熟,自己也是网上找到资料。

我只贴出关键的几个语法贴图,这几个语法,我上面也做过重点介绍的:

图片读取的逻辑:

img 对象是定义好了的:

//这里是一个循环,主要是处理一组批量的图片,下面的主要是图片路径的转换,最后指向img这个对象
strPath.Format( TEXT("%s\\%s"), m_strImageDir, *iter );
filenamenew.Format( TEXT("%s\\%s"),m_strImageDirnew,  *iter );
USES_CONVERSION;
Bitmap img( A2W(strPath) );

GetThumbnailImage 这个上面详细讲过了,大家可以回顾


还有一个保存的逻辑:

都是内置的API接口,所以没有好说明的了

最后用AIR扩展,测试得到的效果:

文件和上述测试的文件一样:256M的体积,48张大图,从开始处理到结束,一共耗时667毫秒,体积增加的话,耗时量增加的也不算明显,所以项目的需求中,2G的体量,200张大图的情况,压力也不大,也是秒级的业务。


这个项目研究到此结束了,总的来说,坎坷之路,从不同的领域到不同的领域,思路换了又换,还好顺利完成调研需要,大家有类似问题,欢迎探讨。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值