1. 程式人生 > >C++介面庫:在GDI上新增完全的alpha混合支援

C++介面庫:在GDI上新增完全的alpha混合支援

    做完了小型WCF之後,就是小型的WPF了。之前那個完全避免使用者大部分的錯誤使用導致的死鎖的方案還有一點點的問題,所以先休息一下,做做別的。為了在C++上重現一套類似WPF的工具,首先要解決繪圖部分。

    繪圖的裝置當然是需要可切換的,於是用bridge模式定義了大量的介面,這些介面用來建立畫筆、畫刷、字型和圖形,然後圖形用樹的形式組織起來,最後放到一塊跟視窗連結的畫板上面,有需要的時候自動繪製。為了最快速地開始工作,我實現了一個GDI的繪圖裝置,以後有空再做DirectX的。但是我們知道GDI對alpha的支援是很弱的,只有一個叫alphablend的API用來貼bitmap,因此為了讓畫刷和畫筆都能夠支援alpha漸變、alpha點陣圖和alpha顏色,做了很多的工作。下面是截圖:



    之所以不用GDI+僅僅是因為我不喜歡。下面是利用GDI組合出上面的效果的那部分程式碼:
   1
 #include "VL_GDIRenderer.h"   2    3 namespace vl
   4 {
   5 namespace interaction
   6     {
   7 namespace renderer
   8         {
   9   10 enum IndirectFillReason
  11             {
  12                 ifrDirectFill,
  13                 ifrLinearGradient,
  14                 ifrAlphaLinearGradient,
  15
                 ifrAlphaBitmap,
  16             };
  17   18             IndirectFillReason GetIndirectFillReason(const VL_IrBrushRec& Brush)
  19             {
  20 switch(Brush.BrushKind)
  21                 {
  22 case VL_IrBrushRec::bkSolid:
  23 if(Brush.MainColor.A!=255)
  24                     {
  25 return ifrAlphaBitmap;
  26                     }
  27 else  28                     {
  29 return ifrDirectFill;
  30                     }
  31 case VL_IrBrushRec::bkLinearGradient:
  32 if(Brush.MainColor.A!=255|| Brush.GradientColor.A!=255)
  33                     {
  34 return ifrAlphaLinearGradient;
  35                     }
  36 else  37                     {
  38 return ifrLinearGradient;
  39                     }
  40 case VL_IrBrushRec::bkBitmap:
  41 if(Brush.Bitmap->IsAlphaChannelBuilt())
  42                     {
  43 return ifrAlphaBitmap;
  44                     }
  45 else  46                     {
  47 return ifrDirectFill;
  48                     }
  49 default:
  50 return ifrDirectFill;
  51                 }
  52             }
  53   54             VBool IsBrushAlpha(const VL_IrBrushRec& Brush)
  55             {
  56 switch(GetIndirectFillReason(Brush))
  57                 {
  58 case ifrAlphaBitmap:
  59 returntrue;
  60 case ifrAlphaLinearGradient:
  61 returntrue;
  62 default:
  63 returnfalse;
  64                 }
  65             }
  66   67             VL_IrColor MixColor(const VL_IrColor& ColorA , const VL_IrColor& ColorB , VDouble Ratio)
  68             {
  69 return VL_IrColor(
  70                     ColorA.R+Round(Ratio*(ColorB.R-ColorA.R)),
  71                     ColorA.G+Round(Ratio*(ColorB.G-ColorA.G)),
  72                     ColorA.B+Round(Ratio*(ColorB.B-ColorA.B)),
  73                     ColorA.A+Round(Ratio*(ColorB.A-ColorA.A))
  74                     );
  75             }
  76   77 void SetColor(TRIVERTEX& Vertex , const VL_IrColor& Color)
  78             {
  79                 Vertex.Red=Color.R<<8;
  80                 Vertex.Green=Color.G<<8;
  81                 Vertex.Blue=Color.G<<8;
  82                 Vertex.Alpha=Color.A<<8;
  83             }
  84   85 /*********************************************************************************************************
  86 VL_GDIBrush
  87 *********************************************************************************************************/  88   89             VL_GDIBrush::VL_GDIBrush(VL_GDIFactory* Factory , const VL_IrBrushRec& Brush)
  90             {
  91                 FFactory=Factory;
  92                 FInternalData=Brush;
  93 switch(FInternalData.BrushKind)
  94                 {
  95 case VL_IrBrushRec::bkSolid:
  96                     FBrush=new VL_WinBrush(RGB(FInternalData.MainColor.R,FInternalData.MainColor.G,FInternalData.MainColor.B));
  97 if(FInternalData.MainColor.A!=255)
  98                     {
  99                         VL_WinBitmap::Ptr Bitmap=new VL_WinBitmap(32,32,VL_WinBitmap::vbb32Bits,true);
 100                         Bitmap->GetWinDC()->SetBrush(FBrush);
 101                         Bitmap->GetWinDC()->FillRect(0,0,32,32);
 102                         Bitmap->GenerateAlpha(FInternalData.MainColor.A);
 103                         Bitmap->BuildAlphaChannel();
 104                         FInternalData.Bitmap=Bitmap;
 105                     }
 106 break;
 107 case VL_IrBrushRec::bkLinearGradient:
 108 break;
 109 case VL_IrBrushRec::bkBitmap:
 110                     FBrush=new VL_WinBrush(FInternalData.Bitmap);
 111 break;
 112 default:
 113 throw L"內部錯誤";
 114                 }
 115             }
 116  117 const VL_IrBrushRec& VL_GDIBrush::GetInternalData()
 118             {
 119 return FInternalData;
 120             }
 121  122             IVL_IrFactory* VL_GDIBrush::GetFactory()
 123             {
 124 return FFactory;
 125             }
 126  127             VL_WinBrush::Ptr VL_GDIBrush::GetGDIBrush()
 128             {
 129 return FBrush;
 130             }
 131  132 /*********************************************************************************************************
 133 VL_GDIPen
 134 *********************************************************************************************************/ 135  136             VL_GDIPen::VL_GDIPen(VL_GDIFactory* Factory , const VL_IrBrushRec& Brush , const VL_IrPenRec& Pen)
 137             {
 138                 FFactory=Factory;
 139                 FInternalBrush=Brush;
 140                 FInternalPen=Pen;
 141  142                 VInt EndCap=0;
 143                 VInt Join=0;
 144 switch(FInternalPen.EndCap)
 145                 {
 146 case VL_IrPenRec::ecFlat:
 147                     EndCap=PS_ENDCAP_FLAT;
 148 break;
 149 case VL_IrPenRec::ecRound:
 150                     EndCap=PS_ENDCAP_ROUND;
 151 break;
 152 case VL_IrPenRec::ecSquare:
 153                     EndCap=PS_ENDCAP_SQUARE;
 154 break;
 155 default:
 156 throw L"內部錯誤";
 157                 }
 158 switch(FInternalPen.Join)
 159                 {
 160 case VL_IrPenRec::jBevel:
 161                     Join=PS_JOIN_BEVEL;
 162 break;
 163 case VL_IrPenRec::jMiter:
 164                     Join=PS_JOIN_MITER;
 165 break;
 166 case VL_IrPenRec::jRound:
 167                     Join=PS_JOIN_ROUND;
 168 break;
 169 default:
 170 throw L"內部錯誤";
 171                 }
 172  173 switch(FInternalBrush.BrushKind)
 174                 {
 175 case VL_IrBrushRec::bkSolid:
 176                     FPen=new VL_WinPen(PS_SOLID,EndCap,Join,FInternalPen.Weight,RGB(FInternalBrush.MainColor.R,FInternalBrush.MainColor.G,FInternalBrush.MainColor.B));
 177 if(FInternalBrush.MainColor.A!=255)
 178                     {
 179                         VL_WinBrush::Ptr SolidBrush=new VL_WinBrush(RGB(FInternalBrush.MainColor.R,FInternalBrush.MainColor.G,FInternalBrush.MainColor.B));
 180                         VL_WinBitmap::Ptr Bitmap=new VL_WinBitmap(32,32,VL_WinBitmap::vbb32Bits,true);
 181                         Bitmap->GetWinDC()->SetBrush(SolidBrush);
 182                         Bitmap->GetWinDC()->FillRect(0,0,32,32);
 183                         Bitmap->GenerateAlpha(FInternalBrush.MainColor.A);
 184                         Bitmap->BuildAlphaChannel();
 185                         FInternalBrush.Bitmap=Bitmap;
 186                     }
 187 break;
 188 case VL_IrBrushRec::bkLinearGradient:
 189                     FPen=new VL_WinPen(PS_SOLID,EndCap,Join,FInternalPen.Weight,RGB(FInternalBrush.MainColor.R,FInternalBrush.MainColor.G,FInternalBrush.MainColor.B));
 190 break;
 191 case VL_IrBrushRec::bkBitmap:
 192                     FPen=new VL_WinPen(FInternalBrush.Bitmap,PS_SOLID,EndCap,Join,FInternalPen.Weight);
 193 break;
 194 default:
 195 throw L"內部錯誤";
 196                 }
 197             }
 198  199             VL_GDIPen::~VL_GDIPen()
 200             {
 201             }
 202  203 const VL_IrPenRec& VL_GDIPen::GetInternalPen()
 204             {
 205 return FInternalPen;
 206             }
 207  208 const VL_IrBrushRec& VL_GDIPen::GetInternalBrush()
 209             {
 210 return FInternalBrush;
 211             }
 212  213             IVL_IrFactory* VL_GDIPen::GetFactory()
 214             {
 215 return FFactory;
 216             }
 217  218             VL_WinPen::Ptr VL_GDIPen::GetGDIPen()
 219             {
 220 return FPen;
 221             }
 222  223 /*********************************************************************************************************
 224 VL_GDIFont
 225 *********************************************************************************************************/ 226  227             VL_GDIFont::VL_GDIFont(VL_GDIFactory* Factory , const VL_IrFontRec& Font)
 228             {
 229                 FFactory=Factory;
 230                 FInternalData=Font;
 231                 FFont=new VL_WinFont(FInternalData.Name,FInternalData.Height,FInternalData.Width,0,0,FInternalData.Bold?900:400,FInternalData.Italic,FInternalData.Underline,FInternalData.StrikeOut,true);
 232             }
 233  234             VL_GDIFont::~VL_GDIFont()
 235             {
 236             }
 237  238 const VL_IrFontRec& VL_GDIFont::GetInternalData()
 239             {
 240 return FInternalData;
 241             }
 242  243             IVL_IrFactory* VL_GDIFont::GetFactory()
 244             {
 245 return FFactory;
 246             }
 247  248             VL_WinFont::Ptr VL_GDIFont::GetGDIFont()
 249             {
 250 return FFont;
 251             }
 252  253 /*********************************************************************************************************
 254 VL_GDIElement
 255 *********************************************************************************************************/ 256  257             VL_WinDC* VL_GDIElement::GetDC()
 258             {
 259 return FCanvas?FCanvas->GetDC():0;
 260             }
 261  262 void VL_GDIElement::CreatePath()
 263             {
 264                 FCanvas->GetDC()->BeginPath();
 265                 DrawForPath();
 266                 FCanvas->GetDC()->EndPath();
 267             }
 268  269 void VL_GDIElement::EnsureRegion()
 270             {
 271 if(FEnvironmentModified)
 272                 {
 273                     FEnvironmentModified=false;
 274 if(FCanvas)
 275                     {
 276 if(FPen)
 277                         {
 278                             FCanvas->GetDC()->SetPen(FPen->GetGDIPen());
 279                             CreatePath();
 280                             FCanvas->GetDC()->WidenPath();
 281                             FBorderRegion=FCanvas->GetDC()->RegionFromPath();
 282                             CreatePath();
 283                             FContentRegion=new VL_WinRegion(FCanvas->GetDC()->RegionFromPath(),FBorderRegion,RGN_DIFF);
 284                         }
 285 else 286                         {
 287                             CreatePath();
 288                             FContentRegion=FCanvas->GetDC()->RegionFromPath();
 289                         }
 290                         {
 291                             RECT Rect={0,0,0,0};
 292 if(FBorderRegion)
 293                             {
 294                                 Rect=FBorderRegion->GetBoundRect();
 295                             }
 296 elseif(FContentRegion)
 297                             {
 298                                 Rect=FContentRegion->GetBoundRect();
 299                             }
 300                             FElementRectangle=VL_IrRect(VL_IrPoint(Rect.left,Rect.top),VL_IrPoint(Rect.right-Rect.left,Rect.bottom-Rect.top));
 301                         }
 302                         {
 303                             RECT Rect={0,0,0,0};
 304 if(FContentRegion)
 305                             {
 306                                 Rect=FContentRegion->GetBoundRect();
 307                             }
 308                             FClientRectangle=VL_IrRect(VL_IrPoint(Rect.left,Rect.top),VL_IrPoint(Rect.right-Rect.left,Rect.bottom-Rect.top));
 309                         }
 310                     }
 311                 }
 312             }
 313  314 void VL_GDIElement::EnvironmentModified(VL_GDICanvas* Canvas)
 315             {
 316 if(FCanvas || Canvas)
 317                 {
 318                     FCanvas=Canvas;
 319                     FBorderRegion=0;
 320                     FContentRegion=0;
 321                     FElementRectangle=VL_IrRect();
 322                     FClientRectangle=VL_IrRect();
 323                     FEnvironmentModified=true;
 324 for(VInt i=0;i<FChildren.GetCount();i++)
 325                     {
 326                         FChildren[i]->EnvironmentModified(Canvas);
 327                     }
 328                 }
 329             }
 330  331             VL_WinRegion::Ptr VL_GDIElement::EnterClip(VL_WinRegion::Ptr CurrentClip , VL_WinRegion::Ptr Region)
 332             {
 333                 VL_WinRegion::Ptr Clip=CurrentClip?new VL_WinRegion(CurrentClip,Region,RGN_AND):Region;
 334                 FCanvas->GetDC()->ClipRegion(Clip);
 335 return Clip;
 336             }
 337  338 void VL_GDIElement::LeaveClip(VL_WinRegion::Ptr CurrentClip)
 339             {
 340 if(CurrentClip)
 341                 {
 342                     FCanvas->GetDC()->ClipRegion(CurrentClip);
 343                 }
 344 else 345                 {
 346                     FCanvas->GetDC()->RemoveClip();
 347                 }
 348             }
 349  350 void VL_GDIElement::FillLinearGradient(VL_WinRegion::Ptr CurrentClip , VL_WinRegion::Ptr Region , const VL_IrBrushRec& Brush)
 351             {
 352                 RECT BoundRect=EnterClip(CurrentClip,Region)->GetBoundRect();
 353                 VDouble Sin=sin(Brush.GradientAngle);
 354                 VDouble Cos=cos(Brush.GradientAngle);
 355 if(Sin<0)
 356                 {
 357                     Cos=-Cos;
 358                     Sin=-Sin;
 359                 }
 360  361                 TRIVERTEX Vertices[4];
 362                 Vertices[0].x=BoundRect.left;
 363                 Vertices[0].y=BoundRect.top;
 364                 Vertices[1].x