Flash在线拍摄用户头象
很多网站在上传用户头象时,除了传统方式上传外,都支持在线摄像头拍照并做简单编辑,完成之后再将图象数据提交到服务端(比如ASP.Net),这几天正好需要这个功能,研究了下,思路如下:
1、先获取摄像头视频
2、利用BitmapData.draw来对视频截图
3、在截图上,放一个方块允许用户手动调整位置,同时允许截图做缩放
4、用户调整完成后,对指定区域的BitmapData做copyPixes处理(即拷贝指定区域的像素)
5、将上一步得到的新BitmapData进行Jpeg编码压缩,然后再转到base64字符串
6、将字符串发送到ASP.Net,然后还原成图片保存即可(即base64的解码)
flash端代码:(第6步省略了)
import flash.media.Camera; import flash.text.TextField; import flash.media.Video; import flash.text.TextFormat; import fl.controls.Button; import flash.utils.Timer; import flash.events.MouseEvent; import flash.display.BitmapData; import flash.display.Bitmap; import flash.display.Sprite; import flash.geom.Rectangle; import flash.events.Event; var _cam:Camera; var _txtInfo:TextField = txtInfo; var _txtMsg:TextField = txtMsg; var _btnGet:Button = btnGet; var _btnSave:Button = btnSave; var _video:Video = myVideo; var _timer:Timer; var _videoIsWorked:Boolean = false; var _bmp:Bitmap; var _bmpTest:Bitmap; var _mask:Sprite; var _slider:SimpleSlider; init(); function init():void { this._txtMsg.visible = false; this._btnGet.label = "拍 照"; this._btnSave.label = "保 存"; this._txtInfo.selectable = false; var style:TextFormat = new TextFormat(); style.font = "宋体"; style.size = 12;//字体大小 style.color = 0x000000;//RGB色 style.leading = 7;//行间距 this._btnGet.setStyle("textFormat",style); this._btnSave.setStyle("textFormat",style); getCamera(); } function getCamera():void { this._btnGet.enabled = false; this._btnSave.enabled = false; this._cam = Camera.getCamera(); if (_cam == null) { this._txtInfo.text = "未检测到摄像头设备!"; return; } _cam.addEventListener(StatusEvent.STATUS, cameraStatusHandler); _cam.addEventListener(ActivityEvent.ACTIVITY, cameraActivityHandler); _cam.setQuality(0,0); this._video.attachCamera(this._cam); } //用户选择是否摄像头时触发 ; function cameraStatusHandler(e:StatusEvent):void { if (e.code == "Camera.Muted") { this._txtInfo.text = "您不允许使用摄像头!"; } else if (e.code == "Camera.Unmuted") { this._txtInfo.text = "摄像头视频获取中..."; _timer = new Timer(100,20);//每隔100ms检测摄像头状态,一共检测20次 cameraActivityHandler(null); } } //摄像头有活动时被触发 function cameraActivityHandler(e:ActivityEvent):void { if (! _videoIsWorked) { if (_timer != null) { _timer.addEventListener(TimerEvent.TIMER, checkCamera); _timer.addEventListener(TimerEvent.TIMER_COMPLETE, checkCameraComplete); _timer.start(); } } } //timer回调函数,用于检测摄像头设备是否正确 function checkCamera(e:TimerEvent):void { this._txtInfo.text = "摄像头视频获取中..."; if (this._cam.currentFPS > 0) { _timer.stop(); _videoIsWorked = true; this._txtInfo.text = "";//摄像头工作正常 this._txtInfo.text = "摄像头正常,请点击拍照"; //trace("_cam.width=",_cam.width,",_cam.height=",_cam.height); this._btnGet.addEventListener(MouseEvent.CLICK,btnGet_Click); this._btnGet.enabled = true; this._video.width = 240; this._video.height = 180; //trace(this._video.width,this._video.height); } } function btnGet_Click(e:MouseEvent):void { var bmd:BitmapData = new BitmapData(_cam.width,_cam.height); bmd.draw(_video); //trace(bmd.width,bmd.height); if (this._bmp == null) { this._bmp = new Bitmap(bmd); addChild(_bmp); _bmp.x = 260; _bmp.y = 10; _bmp.width = _video.width; _bmp.height = _video.height; if (this._mask == null) { this._mask = new Sprite(); this._mask.graphics.beginFill(0xffffff,0.3); this._mask.graphics.drawRect(0,0,125,125); this._mask.graphics.endFill(); this._mask.graphics.lineStyle(1,0xff0000,1); this._mask.graphics.lineTo(1,124); this._mask.graphics.lineTo(124,124); this._mask.graphics.lineTo(124,1); this._mask.graphics.lineTo(1,1); addChild(this._mask); this._mask.x = 260 + 57; this._mask.y = 10 + 27; this._mask.buttonMode = true; this._mask.addEventListener(MouseEvent.MOUSE_DOWN,mask_startDrag); this.stage.addEventListener(MouseEvent.MOUSE_UP,stage_endDrag); if (_slider == null) { _slider = new SimpleSlider(125,180,180); _slider.backWidth = 120; _slider.backHeight = 6; _slider.handleHeight = 16; _slider.addEventListener(Event.CHANGE,slider_Change); addChild(_slider); _slider.x = 320; _slider.y = 205; this._txtMsg.visible = true; } } } else { this._bmp.bitmapData = bmd; } this._btnSave.enabled = true; this._txtInfo.text = "拖动左侧方块可调整位置" ; this._btnSave.addEventListener(MouseEvent.CLICK,btnSave_Click); } function btnSave_Click(e:Event):void { //因为视频的大小跟摄像头原始像素的尺寸并不一致(即视频可能缩放过了,所以要用比例调整回最初始的状态) var ratio:Number = this._cam.width / this._bmp.width; var imgWidth:int = this._mask.width * ratio; var imgHeight:int = this._mask.height * ratio; //trace(ratio); var bmd:BitmapData = new BitmapData(imgWidth,imgHeight,false,0xffff99); bmd.copyPixels(this._bmp.bitmapData,new Rectangle((this._mask.x-260)*ratio,(this._mask.y - 10)*ratio,imgWidth,imgHeight),new Point()); if (this._bmpTest == null) { this._bmpTest = new Bitmap(bmd); addChild(_bmpTest); _bmpTest.x = 375; _bmpTest.y = 230; } else { this._bmpTest.bitmapData = bmd; } _bmpTest.width = this._mask.width; _bmpTest.height = this._mask.height; trace("_bmpTest.width=",this._mask.width,",_bmpTest.height=",this._mask.height); var jpegEncoder:JPGEncoder = new JPGEncoder(); var jpegArr:ByteArray = jpegEncoder.encode(bmd);//压缩成jpeg trace(jpegArr.length); var base64string:String = Base64.encode(jpegArr); txtBase64.text = base64string; trace(base64string.length); } function slider_Change(e:Event):void { //trace(e); this._bmp.height = this._slider.value; this._bmp.width = this._bmp.height * 4 / 3; } function mask_startDrag(e:MouseEvent):void { this._mask.startDrag(false,new Rectangle(260,10,240-125,180-125)); } function stage_endDrag(e:MouseEvent):void { this._mask.stopDrag(); //trace("x=",_mask.x); //trace("y=",_mask.y); } function checkCameraComplete(e:TimerEvent):void { this._txtInfo.text = "设备无法使用!(有可能被占用)"; _timer.removeEventListener(TimerEvent.TIMER, checkCamera); _timer.removeEventListener(TimerEvent.TIMER_COMPLETE, checkCameraComplete); _timer = null; }
Flash在线演示(需要电脑上连接好摄像头)
C#端的处理示例:
using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Web; namespace OnlinePhotoCapture { /// <summary> /// 将图片base64字符串原还原为图片并调整尺寸后保存(by 菩提树下的杨过http://yjmyzz.cnblogs.com/) /// </summary> public class SavePhoto : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string base64String = "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"; Image bmp = null; MemoryStream ms = null; try { byte[] b = Convert.FromBase64String(base64String); ms = new MemoryStream(b); bmp = ImageResize(new Bitmap(ms),125,125); bmp.Save(context.Request.MapPath("logo.jpg"), ImageFormat.Jpeg); context.Response.Write("success"); } catch (Exception ex) { context.Response.Write("failure:" + ex.Message.ToString()); } finally { bmp.Dispose(); ms.Dispose(); } } /// <summary> /// 修改图片尺寸 /// </summary> /// <param name="imgSrc"></param> /// <returns></returns> private Image ImageResize(Image imgSrc,int width,int height) { Image newImage = imgSrc.GetThumbnailImage(125, 125, null, new IntPtr()); Graphics g = Graphics.FromImage(newImage); g.DrawImage(newImage, 0, 0, newImage.Width, newImage.Height); g.Dispose(); return newImage; } public bool IsReusable { get { return false; } } } }
作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。