标签:hwnd version while 播放 结束 box effect UI oba
写在前面
本系列博客URL:
http://www.cnblogs.com/drgraph
配套软件下载地址:
http://www.czwenwu.com/YeeVingSetup.exe
配套软件含三个可执行文件:YeeVingDriver.exe,YeeVingPlayer.exe,WatchDog.exe
其中,YeeVingDriver.exe是双目触控屏的驱动程序,内含键盘鼠标钩子,安装或运行的时候有可能会当成病毒。
WatchDog.exe是无人值守软件
YeeVingPlayer.exe是广告播放软件客户端。
本系列博客是在上述三个软件研发过程中的片面记录,基本上是属于想到哪写到哪的,不系统。主要目的是自己整理归纳一下,并期望与更多朋友交流。
QQ/微信:282397369
EMail: drgraph@qq.com
需求
广告播放,一个主要需求是要以多种效果来展示素材。
闭门造车不是一个好选项,可以参照成熟专业软件的实现方式。PPT里的动画就是一个好的可参考对象。
之前已经实现了各种PPT的动画,今天稍微总结一下。
Ribbon界面
首先做PPT的动画界面,这个用Ribbon风格控件,就是花点时间的事,没太大的技术含量。
最花时间的是找图标,不过现在别的不好找,代码、图标却是大把。
花了半天时间,虽谈不上是神似,也能算得上是形似的了。具体功能将逐个实现。
再看下PPT中的动画属性,其界面最终所围绕的也就是这两个
分解一下,设计几个数据结构:
typedefstruct tagEnhanceInfo { // 增强选项 UnicodeStringSoundFileName; // 声音文件 TEffect_Enhance_AfterPlayAfterPlayMode; // 动画播放后处理方式 TColorAfterAnimationColor; // 变换为某种颜色,FAfterPlayMode为teapToColor有效 UnicodeStringAnimationText; // 动画文本 intDelayPercent; // 字母之间延迟百分比 }ENHANCE_INFO; typedefstruct tagTimerInfo { // 计时选项 TEffect_Time_StartStartMode; // 开始选项 intDelay; // 延迟秒数 TEffect_Time_PeriodPeriodMode; // 期间选项 TEffect_Time_RepeatRepeatMode; // 重复选项 boolQuickBackAfterPlay; // 播完后快退 }TIMER_INFO; enum TEffect_Enhance_AfterPlay { // 增强选项-动画后播放选项 teapToColor,// 变换为某种颜色 teapNoDarking,// 不变暗 teapHide,// 隐藏 teapHideAfterClick// 下次单击后隐藏 }; enum TEffect_Time_Start { // 计时选项-开始选项 ttsClick,// 单击时 ttsSameTimeAsPrevOne,// 与上一动画同时 ttsAfterPrevOne// 上一动画完成之后 }; enum TEffect_Time_Period { // 计时选项-期间 ttpVerySlow,// 非常慢 ttpSlow,// 慢速 ttpNormal,// 中速 ttpFast,// 快速 ttpVeryFast// 非常快 }; enum TEffect_Time_Repeat { // 计时选项-重复 ttrNone,// 无 ttr2,// 2 ttr3,// 3 ttr4,// 4 ttr5,// 5 ttr10,// 10 ttrTillNextClick,// 直到下一次单击 ttrTillEnd// 直到结束 }; enum CbwEffectType { // 效果类型枚举量 cetBase= 0, // TCbwAnimationEffect cetAppear= 1, // TCbwAnimationEffect_Appear cetFadeOut= 2, // TCbwAnimationEffect_FadeOut cetFlyIn= 3, // TCbwAnimationEffect_FlyIn cetEnd }; enum CbwEffectDirection { // 动画方向 cedFromBottom= 0, // 自底部 cedFromLeftBottom= 1, // 自左下部 cedFromLeft= 2, // 自左侧 cedFromLeftTop= 3, // 自左上部 cedFromTop= 4, // 自顶部 cedFromRightTop= 5, // 自右上部 cedFromRight= 6, // 自右侧 cedFromRightBottom= 7 // 自右下部 };
为了更好地模块化,把对象相关的动画属性提取出来
typedefstruct tagObjectMat { TPointLeftTopPosition; TCbwObject* Object; cv::MatMat; cv::MatMask; void__fastcall BuildMask(int height, int width); }OBJECTMAT;
绝大部分动画,是控制过程的显示。由OpenCV技术来看,这个过程分为三块:显示区域、显示内容、屏蔽内容。
这样,可以设计动画基类为:
/** *@class TCbwAnimationEffect *@brief 动画基类 * * 处理动画基本内容 *@author DrGraph *@version 1.0 *@date 2017-10-07 *@QQ: 282397369 */ class TCbwAnimationEffect { bool__fastcall IsAtEnd(); CbwObjectsFAnimationObjects; // 相关对象 cv::Mat__fastcall Object2Mat(TCbwObject * object, double ratio = 1); int__fastcall GetRepeateTime(); protected: vector<OBJECTMAT>FDestMats; intFCurrentIndex; // 当前帧索引 intFPeriodLength; // 周期长度,指动画一个周期内的帧数 intFPosition; // 当前位置,指动画累加索引位置 intFWidth, FHeight; // 长宽尺寸,指显示限制 public: CbwEffectTypeEffectType; ENHANCE_INFOEnhanceOption; // 增强选项 TIMER_INFOTimerOption; // 计时选项 __fastcallTCbwAnimationEffect(); staticTCbwAnimationEffect * Build(); void__fastcall Assign(TCbwAnimationEffect * other); void__fastcall AddToXmlNode(CbwXmlNode * node); void__fastcall GetFromXmlNode(CbwXmlNode * node); void__fastcall First(); void__fastcall Next(); void__fastcall SetRelativeObject(CbwObjects relativeObjects, TPaintBox* pb, TScrollBox * scrollBox); void__fastcall SetBounds(int width, int height); void__fastcall Draw(HWND wnd, BYTE * backData, int width, int height); cv::MatCurrentMat; __propertybool Eof = {read = IsAtEnd}; protected: virtualTRect __fastcall BuildDisplayRect(OBJECTMAT * m); virtualvoid __fastcall BuildDisplayMat(cv::Mat& destMat, cv::Mat& srcMat); virtualvoid __fastcall BuildMaskMat(cv::Mat& destMat, cv::Mat& srcMat); }; 核心实现代码为: __fastcallTCbwAnimationEffect::TCbwAnimationEffect() { TimerOption.StartMode= ttsClick; TimerOption.Delay= 0; TimerOption.PeriodMode= ttpNormal; TimerOption.RepeatMode= ttrNone; TimerOption.QuickBackAfterPlay= false; EffectType= cetBase; } TCbwAnimationEffect *TCbwAnimationEffect::Build() { returnnew TCbwAnimationEffect; } TRect __fastcallTCbwAnimationEffect::BuildDisplayRect(OBJECTMAT * m) { TRectresult(m->LeftTopPosition.x, m->LeftTopPosition.y, m->LeftTopPosition.x+ m->Mat.cols, m->LeftTopPosition.y + m->Mat.rows); returnresult; } void __fastcall TCbwAnimationEffect::BuildDisplayMat(cv::Mat&destMat, cv::Mat&srcMat) { destMat= srcMat.clone(); } void __fastcallTCbwAnimationEffect::BuildMaskMat(cv::Mat& destMat, cv::Mat&srcMat) { destMat= srcMat.clone(); } void __fastcallTCbwAnimationEffect::Draw(HWND wnd, BYTE * backData, int width, intheight) { cv::MatbackGndMat(height, width, CV_8UC3); // 背景 GlobalOpenCVObject->CopyRGBDatasToMat(backGndMat,backData, width, height,true); CBW_ITERATOR(vector<OBJECTMAT>,FDestMats) { OBJECTMAT* animationObject = &(*it); //以下取得待显示的区域、内容 TRectanimationDisplayRect = BuildDisplayRect(animationObject); // 目标区域 cv::MatanimationDisplayMat = cv::Mat::zeros(animationDisplayRect.Height(), animationDisplayRect.Width(),animationObject->Mat.type()); BuildDisplayMat(animationDisplayMat,animationObject->Mat); // 待显示内容 cv::MatmaskMat = cv::Mat::zeros(animationObject->Mask.rows, animationObject->Mask.cols,animationObject->Mask.type()); BuildMaskMat(maskMat,animationObject->Mask); // Mask内容 TRectsuitableDisplayRect; // 真正的目标显示区域 suitableDisplayRect.left= max(0, int(animationDisplayRect.left)); suitableDisplayRect.top= max(0, int(animationDisplayRect.top)); suitableDisplayRect.right= min(int(animationDisplayRect.right), width); suitableDisplayRect.bottom= min(int(animationDisplayRect.bottom), height); TRectsuitableRectInMat(0, 0, suitableDisplayRect.Width(), suitableDisplayRect.Height()); intdeltaL = max(0,int(suitableDisplayRect.left - animationDisplayRect.left)); if(suitableDisplayRect.left != animationDisplayRect.left) { suitableRectInMat.left+= deltaL; suitableRectInMat.right+= deltaL; } intdeltaT = max(0,int(suitableDisplayRect.top - animationDisplayRect.top)); if(suitableDisplayRect.top != animationDisplayRect.top) { suitableRectInMat.top+= deltaT; suitableRectInMat.bottom+= deltaT; } cv::MatdestPartMat = animationDisplayMat(cv::Rect(suitableRectInMat.left, suitableRectInMat.top,suitableRectInMat.Width(), suitableRectInMat.Height())); cv::MatbackgndPartMat = // 目标背景区域相应矩阵 backGndMat(cv::Rect(suitableDisplayRect.left, suitableDisplayRect.top,suitableDisplayRect.Width(), suitableDisplayRect.Height())); cv::MatmaskPartMat = maskMat(cv::Rect(deltaL,deltaT, suitableDisplayRect.Width(), suitableDisplayRect.Height())); destPartMat.copyTo(backgndPartMat,maskPartMat); } GlobalOpenCVObject->PreviewMat(wnd,backGndMat, width, height); } void __fastcallTCbwAnimationEffect::Assign(TCbwAnimationEffect * other) { TimerOption.StartMode= other->TimerOption.StartMode; TimerOption.Delay= other->TimerOption.Delay; TimerOption.PeriodMode= other->TimerOption.PeriodMode; TimerOption.RepeatMode= other->TimerOption.RepeatMode; TimerOption.QuickBackAfterPlay= other->TimerOption.QuickBackAfterPlay; } bool __fastcallTCbwAnimationEffect::IsAtEnd() { boolresult = (FPosition >= FPeriodLength * GetRepeateTime()); returnresult; } void __fastcall TCbwAnimationEffect::First(){ FPosition= 0; FCurrentIndex= 0; } void __fastcall TCbwAnimationEffect::Next(){ ++FPosition; FCurrentIndex= FPosition % FPeriodLength; } cv::Mat __fastcallTCbwAnimationEffect::Object2Mat(TCbwObject * object, doubleratio) { TCanvas* oldCanvas = object->Canvas; Graphics::TBitmap* bitmap = new Graphics::TBitmap; bitmap->PixelFormat= pf24bit; bitmap->Width= object->Width; bitmap->Height= object->Height; TCanvas* canvas = bitmap->Canvas; boolallowDraw = object->AllowDraw; boololdDrawBorderFlag = object->DrawBorderFlag; object->AllowDraw= false; { TRestoreleft(object, "Left", 0); TRestoretop(object, "Top", 0); object->Canvas= canvas; object->AllowDraw= allowDraw; object->DrawBorderFlag= false; object->Draw(); object->AllowDraw= false; }object->Canvas = oldCanvas; object->AllowDraw= allowDraw; object->DrawBorderFlag= oldDrawBorderFlag; BYTE* backData = THelper::Graphics::GetBitmapData(bitmap); cv::Matresult; GlobalOpenCVObject->CopyRGBDatasToMat(result,backData, bitmap->Width, bitmap->Height,true); deletebitmap; deletebackData; returnresult; } void __fastcallTCbwAnimationEffect::OBJECTMAT::BuildMask(int height, int width) { Mask= cv::Mat(height, width, CV_8UC1); BYTE* pSrc = Mat.data; BYTE* pDst = Mask.data; for(int i = height * width - 1; i >= 0; --i) { BYTEB = *pSrc++; BYTEG = *pSrc++; BYTER = *pSrc++; *pDst++= (B == 0xFF && G == 0xFF && R == 0xFF) ? 0 : 255; } } void __fastcallTCbwAnimationEffect::SetRelativeObject (CbwObjectsrelativeObjects, TPaintBox * pb, TScrollBox * scrollBox) { CBW_ITERATOR(CbwObjects,relativeObjects) { cv::Matmat = Object2Mat(*it); TPointlt = TPoint((*it)->Left, (*it)->Top); lt= pb->ClientToScreen(lt); lt= scrollBox->ScreenToClient(lt); OBJECTMATm; m.Mat= mat; m.LeftTopPosition= lt; m.Object= *it; m.BuildMask((*it)->Height,(*it)->Width); FDestMats.push_back(m); } FPeriodLength= 50; FCurrentIndex= 0; FPosition= 0; } void __fastcallTCbwAnimationEffect::SetBounds(int width, int height) { FWidth= width; FHeight= height; }
为方便调用处逻辑简单,可采用:
typedef TCbwAnimationEffect *(*BuildEffectObject)(); // 智能构造函数 typedef std::map<int,BuildEffectObject>EffectObjectMap; // 构造各图元对象映射 #define CREATEEFFECTOBJECTif(!CbwEffectObjectMap) CbwEffectObjectMap = new EffectObjectMap; (*CbwEffectObjectMap) … CREATEEFFECTOBJECT[cetBase]= TCbwAnimationEffect::Build; CREATEEFFECTOBJECT[cetAppear]= TCbwAnimationEffect_Appear::Build; CREATEEFFECTOBJECT[cetFadeOut]= TCbwAnimationEffect_FadeOut::Build; CREATEEFFECTOBJECT[cetFlyIn]= TCbwAnimationEffect_FlyIn::Build;
这样,调用时,简单处理即可:
if(!(*CbwEffectObjectMap)[effectType]) { THelper::Util::MessageBox(L"本动画效果尚未实现,请稍候!", false); return; } TCbwAnimationEffect* effectItem = (*CbwEffectObjectMap)[effectType](); // 根据类型创建动画对象
调用,先处理用户选中某个对象后的动画类型逻辑
void __fastcall TForm::AddEffect(TdxRibbonGalleryGroupItem*AItem) { if(cSelectedObjects->MetaNumber== 0) return; inteffectType = AItem->ImageIndex + 1; if(!(*CbwEffectObjectMap)[effectType]) { THelper::Util::MessageBox(L"本动画效果尚未实现,请稍候!", false); return; } TCbwAnimationEffect* effectItem = (*CbwEffectObjectMap)[effectType](); // 根据类型创建动画对象 effectItem->SetRelativeObject(cSelectedObjects->SubObjects,PaintBox, ScrollBox); effectItem->SetBounds(ScrollBox->Width,ScrollBox->Height); Graphics::TBitmap* bitmap = new Graphics::TBitmap; bitmap->PixelFormat= pf24bit; bitmap->Width= ScrollBox->Width; bitmap->Height= ScrollBox->Height; RECTdisplayRect = Rect(ScrollBox->HorzScrollBar->Position, ScrollBox->VertScrollBar->Position,ScrollBox->HorzScrollBar->Position + ScrollBox->Width,ScrollBox->VertScrollBar->Position + ScrollBox->Height); Graphics::TBitmap* FPreviewBitmap = new Graphics::TBitmap; FPreviewBitmap->PixelFormat= pf24bit; FPreviewBitmap->Width= PaintBox->Width; FPreviewBitmap->Height= PaintBox->Height; TCanvas* canvas = FPreviewBitmap->Canvas; canvas->Rectangle(0,0, 10000, 10000); CBW_ITERATOR(CbwObjects,Objects)(*it)->Canvas = canvas; CBW_ITERATOR(CbwObjects,Objects) { TCbwObject* object = *it; if(!CanObjectBeVisible(object) || !object->CanContinueWithRect (displayRect,CBW_CONTINUE_DRAW) || object->Selected) continue; object->Draw(); } PostPaint(canvas); bitmap->Canvas->CopyRect(Rect(0,0, bitmap->Width, bitmap->Height), canvas, displayRect); CBW_ITERATOR(CbwObjects,Objects)(*it)->Canvas = PaintBox->Canvas; TRestoreApplicationCurrentStatus(TGraphApp::CurrentStatus, cfsAnimation); BYTE* backData = THelper::Graphics::GetBitmapData(bitmap); effectItem->First(); while(!effectItem->Eof){ effectItem->Draw(ScrollBox->Handle,backData, bitmap->Width, bitmap->Height); effectItem->Next(); Sleep(10); } deletebackData; deleteFPreviewBitmap; deletebitmap; deleteeffectItem; }
剩下的事情就是针对各动画类型进行细化。
标签:hwnd version while 播放 结束 box effect UI oba
原文地址:http://www.cnblogs.com/drgraph/p/7635575.html