小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

GDI+ 在Delphi程序的應(yīng)用-可調(diào)節(jié)的文字陰影特效

 勤奮不止 2014-05-08

        利用GDI+輸出文字陰影效果有多種方法,最簡單的就是第一次輸出有偏移的灰色文字,第二次輸出正常文字。下面是仿C#文字輸出例子里的代碼片斷,輸出了這種帶陰影的文字:

  serifFontFamily := TGpFontFamily.GenericSerif;
  //Load the fonts we want to use
  titleFont := TGpFont.Create(serifFontFamily, 60);
  //Load the image to be used for the textured text from the exe's resource fork
  textImage := TGpBitmap.Create('....mediamarble.jpg');
  textTextureBrush := TGpTextureBrush.Create(textImage);
  //Set up shadow brush - make it translucent
  titleShadowBrush := TGpSolidBrush.Create(ARGB(70, kcBlack));

 //Draw a textured string
  s := 'Graphics  Samples';
  g.DrawString(s, titleFont, titleShadowBrush, 15, 25);
  g.DrawString(s, titleFont, textTextureBrush, 10, 20);

效果圖如下(作了適當(dāng)縮?。?/p>

        顯然,這種文字陰影效果不太令人滿意,沒有那種半陰影的效果。

        網(wǎng)上介紹了一種“借助GDI+的反走樣能力生成透明的陰影與半陰影”的文字陰影方法,其原理是將要輸出的文字輸出按一定比例縮小,以某種半灰調(diào)輸出到一個按同樣比例縮小的內(nèi)存位圖中,然后設(shè)置畫布插值模式為高質(zhì)量雙三次插值法,再將位圖放大到實際畫布大小輸出,因為雙三次插值放大使文本的邊緣產(chǎn)生Alpha模糊,這樣就出現(xiàn)陰影與半影效果,下面是這種方法的Delphi代碼:

procedure PaintText(g: TGpGraphics);
var
  brush: TGpLinearGradientBrush;
  font: TGpFont;
  fontFamily: TGpFontFamily;
  r: TGpRect;
  bmp: TGpBitmap;
  bg: TGpGraphics;
  m: TGpMatrix;
begin
  fontFamily := TGpFontFamily.Create('Times New Roman');
  font := TGpFont.Create(fontFamily, 50, [fsBold], utPixel);
  r := GpRect(Form1.PaintBox1.ClientRect);
  brush := TGpLinearGradientBrush.Create(r, kcBlue, kcAliceBlue, 90);
  // 填充漸變背景
  g.FillRectangle(brush, r);
  // 建立內(nèi)存位圖,其大小是畫布的1/4
  bmp := TGpBitmap.Create(r.Width shr 2, r.Height shr 2, g);
  bg := TGpGraphics.Create(bmp);
  bg.TextRenderingHint := thAntiAlias;
  // 按1/4縮放輸出陰影文字,并平移(3, 3)
  m := TGpMatrix.Create(0.25, 0, 0, 0.25, 3, 3);
  bg.SetTransform(m);
  bg.DrawString('文字陰影特效', font, Brushs[ARGB(128, 0, 0, 0)], 10, r.Height / 3);
  // 設(shè)置插值模式為高質(zhì)量雙三次插值法
  g.InterpolationMode := imHighQualityBicubic;
  g.TextRenderingHint := thAntiAlias;
  // 放大輸出陰影位圖到畫布
  g.DrawImage(bmp, r, 0, 0, bmp.Width, bmp.Height, utPixel);
  // 輸出正常文字
  g.DrawString('文字陰影特效', font, Brushs.White, 10, r.Height / 3);
  m.Free;
  bg.Free;
  bmp.Free;
  brush.Free;
  font.Free;
  fontFamily.Free;
end;

代碼中已經(jīng)作了注釋,就不再詳細(xì)講解,下面是運行效果截圖,圖的上部分是帶陰影的文字輸出截圖,下部分是單獨的陰影輸出截圖:

        從效果圖,特別是從上半部分截圖看,文字的陰影效果要比前面簡單的文字陰影效果好,其陰影邊緣有一定的半影效果;但是從下半部分單獨的文字陰影輸出圖看,還是覺得不盡人意。首先是邊緣模糊效果不太明顯,其次是陰影半影部分,也就是邊緣模糊部分有太強的放大痕跡,再次就是此方法無多大調(diào)節(jié)余地,想得到更明顯的半影效果的辦法就是進(jìn)一步縮小陰影文字比例,但由此帶來的鋸齒狀顯然更突出??磥恚胍玫胶玫奈淖株幱靶Ч?,如PhotoShop式樣中的投影效果,得另辟途徑。

        經(jīng)過研究,PhotoShop式樣中的投影效果其實就是一種高斯模糊效果。一般的圖像高斯模糊是對圖像各像素的RGB用高斯卷積矩陣進(jìn)行卷積處理(關(guān)于高斯模糊請看我的文章《GDI+ 在Delphi程序的應(yīng)用 -- 圖像卷積操作及高斯模糊》),而要處理文字陰影效果只需要建立一個透明的32位ARGB格式內(nèi)存位圖,將文字用一定的陰影色調(diào)輸出到位圖,然后用高斯模糊矩陣對位圖的Alpha字節(jié)進(jìn)行卷積處理,就可達(dá)到很好的效果。下面是函數(shù)代碼:

// 卷積處理陰影效果。Data: GDI+位圖數(shù)據(jù),要求32位ARGB格式; Source: 復(fù)制的源
// ConvolMatrix: 卷積矩陣; MatrixSize:矩陣大小, Nuclear: 卷積核(必須大于0)
procedure MakeShadow(Data: TBitmapData; Source: Pointer;
    ConvolMatrix: array of Integer; MatrixSize, Nuclear: LongWord);
var
  Radius, mSize, rSize: LongWord;
  x, y: LongWord;
  Width, Height: Integer;
  Matrix: Pointer;
asm
    push    esi
    push    edi
    push    ebx

    mov     esi, edx              // esi = Source + 3  (Alpha byte)
    add     esi, 3
    mov     edi, [eax + 16]   // edi = Data.Scan0
    mov     Matrix, ecx       // Matrix = ConvolMatrix
    mov     ecx, MatrixSize
    mov     edx, ecx
    dec     ecx
    mov     ebx, [eax]
    sub     ebx, ecx
    mov     Width, ebx        // Width = Data.Width - (MatrixSize - 1)
    mov     ebx, [eax + 4]
    sub     ebx, ecx
    mov     Height, ebx       // Height = Data.Height - (MatrixSize - 1)
    shr     ecx, 1
    mov     Radius, ecx       // Radius = MatrixSize / 2
    mov     eax, [eax + 8]
    mov     mSize, eax
    shl     edx, 2
    sub     mSize, edx        // mSize = Data.Stride - MatrixSize * 4
    add     eax, 4
    imul    eax, ecx
    add     edi, eax          // edi = edi + (Data.Stride * Radius + Radius * 4)
    add     edi, 3            // edi += 3  (Alpha byte)
    shl     ecx, 3             
    mov     rSize, ecx        // rSize = Radius * 2 * 4
    mov     ebx, Nuclear      // ebx = Nuclear

    mov     y, 0              // for (y = 0; y < Height; y ++)
  @yLoop:                     // {
    mov     x, 0              //   for (x = 0; x < Width; x ++)
  @xLoop:                     //   {
    push    esi               //     Save(esi)
    push    edi               //     Save(edi)
    mov     edi, Matrix       //     edi = Matrix

    xor     eax, eax          //     eax = 0
    
//用卷積矩陣處理Alpha字節(jié)
    mov     ecx, MatrixSize   //      for (I = 0; I < MatrixSize; I ++)
  @Loop3:                     //      {
    push    ecx
    mov     ecx, MatrixSize   //        for (J = 0; J <= MatrixSize; J ++)
  @Loop4:                     //        {
    movzx   edx, [esi]        //           edx = *esi  (Alpha byte)
    imul    edx, [edi]
    add     eax, edx          //           eax += edx * *edi
    add     esi, 4            //           esi += 4
    add     edi, 4            //           edi ++
    loop    @Loop4            //        }
    add     esi, mSize        //        esi += mSize
    pop     ecx
    loop    @Loop3            //      }
    cdq
    idiv    ebx               //      eax /= ebx
    pop     edi               //      Result(edi)
    mov     [edi], al         //      *edi = al
    add     edi, 4            //      edi += 4
    pop     esi               //      Reset(esi)  esi += 4
    add     esi, 4

    inc     x
    mov     eax, x
    cmp     eax, Width
    jl      @xLoop            //   }
    add     esi, rSize
    add     edi, rSize
    inc     y
    mov     eax, y
    cmp     eax, Height
    jl      @yLoop            // }

    pop     ebx
    pop     edi
    pop     esi
end;

procedure GdipShadow(Bmp: TGpBitmap; Radius: LongWord);
var
  Data: TBitmapData;
  Gauss: array of Integer;
  Q: Double;
  x, y, n, z: Integer;
  p: PInteger;
  Buf: Pointer;
begin
  // 根據(jù)半徑計算高斯模糊矩陣
  Q := Radius / 2;
  if Q = 0 then Q := 0.1;
  n := Radius shl 1 + 1;
  SetLength(Gauss, n * n);
  p := @Gauss[0];
  z := 0;
  for x := -Radius to Radius do
    for y := -Radius to Radius do
    begin
      p^ := Round(Exp(-(x * x + y * y) / (2.0 * Q * Q)) / (2.0 * PI * Q * Q) * 1000.0);
      Inc(z, p^);
      Inc(p);
    end;
  Data := Bmp.LockBits(GpRect(0, 0, Bmp.Width, Bmp.Height), [imRead, imWrite], pf32bppARGB);
  GetMem(Buf, Data.Height * Data.Stride);
  try
    // 備份源數(shù)據(jù)
    Move(Data.Scan0^, Buf^, Data.Height * Data.Stride);
     // 高斯卷積處理陰影效果
    MakeShadow(Data, Buf, Gauss, n, z);
  finally
    FreeMem(Buf);
    Bmp.UnlockBits(Data);
  end;
end;

// 計算并輸出文字陰影效果
// g: 文字輸出的畫布; str要輸出的文字; font: 字體; layoutRect: 限定的文字輸出范圍
// ShadowSize: 陰影大小; Distance: 陰影距離;
// Angle: 陰影輸出角度(左邊平行處為0度。順時針方向)
// ShadowAlpha: 陰影文字的不透明度; format: 文字輸出格式
procedure DrawShadowString(const g: TGpGraphics; const str: WideString;
    const font: TGpFont; const layoutRect: TGpRectF;
    ShadowSize, Distance: LongWord; Angle: Single = 60;
    ShadowAlpha: Byte = 192; const format: TGpStringFormat = nil); overload;
var
  Bmp: TGpBitmap;
  Bg: TGpGraphics;
  dr, sr: TGpRectF;
begin
  sr := GpRect(ShadowSize shl 1, ShadowSize shl 1, layoutRect.Width, layoutRect.Height);
  // 建立透明的32位ARGB陰影位圖,其大小為layoutRect長、寬度 + ShadowSize * 4
  Bmp := TGpBitmap.Create(Round(sr.Width) + ShadowSize shl 2,
                          Round(sr.Height) + ShadowSize shl 2, pf32bppARGB);
  Bg := TGpGraphics.Create(Bmp);
  try
    Bg.TextRenderingHint := thAntiAlias;
    // 以不透明度為ShadowAlpha的黑色畫刷,
    
// 在2倍ShadowSize偏移處輸出文字到位圖畫布,
    Bg.DrawString(str, font, Brushs[ARGB(ShadowAlpha, kcBlack)], sr, format);
    // 處理文字陰影效果
    GdipShadow(Bmp, ShadowSize);
    dr := layoutRect;
    // 根據(jù)角度計算陰影位圖在目標(biāo)畫布的偏移量
    Offset(dr, Cos(pi * Angle / 180) * Distance,
              Sin(pi * Angle / 180) * Distance);
    // 擴大源和目標(biāo)矩形,以輸出邊緣半影部分
    Inflate(dr, ShadowSize, ShadowSize);
    Inflate(sr, ShadowSize, ShadowSize);
    // 輸出陰影位圖到目標(biāo)畫布
    g.DrawImage(Bmp, dr, sr.X, sr.Y, sr.Width, sr.Height, utPixel);
  finally
    Bg.Free;
    Bmp.Free;
  end;
end;
// 計算并輸出文字陰影效果,除以輸出點origin替代上面布局矩形外,其他參數(shù)同上
procedure DrawShadowString(const g: TGpGraphics; const str: WideString;
    const font: TGpFont; const origin: TGpPointF;
    ShadowSize, Distance: LongWord; Angle: Single = 60;
    ShadowAlpha: Byte = 192; const format: TGpStringFormat = nil); overload;
begin
  DrawShadowString(g, str, font, g.MeasureString(str, font, origin, format),
                   ShadowSize, Distance, Angle, ShadowAlpha, format);
end;

        代碼中已經(jīng)含比較詳細(xì)的注釋,就不再講解了,本文介紹的函數(shù)最大的特點就是陰影的大小、間隔距離、輸出角度以及不透明度可根據(jù)需要調(diào)整;陰影效果也很好,邊緣模糊均勻,線條圓潤平滑,可與一般的PhotoShop文字陰影效果相媲美;由于核心函數(shù)MakeShadow代碼采用BASM,且只處理了像素的Alpha字節(jié),邊界處理也省略了,因此處理速度還是較滿意的。為了照顧需要pascal代碼的朋友,下面給出該函數(shù)的pascal版本,速度比BASM版本慢很多,不過,通過其中的代碼和注釋可以加深了解該函數(shù)的原理:

// 卷積處理陰影效果。Data: GDI+位圖數(shù)據(jù),要求32位ARGB格式; Source: 復(fù)制的源
// ConvolMatrix: 卷積矩陣; MatrixSize:矩陣大小, Nuclear: 卷積核(必須大于0)
procedure MakeShadow(Data: TBitmapData; Source: Pointer;
    ConvolMatrix: array of Integer; MatrixSize, Nuclear: LongWord);
var
  x, y, I, J: Integer;
  Width, Height, mSize, rSize: Integer;
  v, Radius, Count: Integer;
  pd, ps, ps1: PByte;
begin
  Radius := MatrixSize shr 1;
  Width := Data.Width - Radius shl 1;
  Height := Data.Height - Radius shl 1;
  mSize := Data.Stride - MatrixSize shl 2;
  rSize := Radius shl 3;
  Count := MatrixSize * MatrixSize;
  // pd 指向目標(biāo)偏移地址為卷積半徑后像素的Alpha字節(jié)
  
// 為簡化過程,不處理以Radius為半徑的邊界像素
  pd := Data.Scan0;
  Inc(pd, Radius * Data.Stride + Radius * 4 + 3);
  // ps 指向源首像素地址的Alpha字節(jié),也就是目標(biāo)首像素第一個卷積乘數(shù)的像素點
  ps := Source;
  Inc(ps, 3);
  for y := 1 to Height do
  begin
    for x := 1 to Width do
    begin
      ps1 := ps;
      v := 0;
      for I := 0 to count - 1 do
      begin
        if (I <> 0) and (I mod MatrixSize = 0) then
          Inc(ps1, mSize);
        Inc(v, ConvolMatrix[I] * ps1^);  // Alpha字節(jié)卷積求和
        Inc(ps1, 4);
      end;
      v := v div Nuclear;                // 卷積和 / 卷積核
      pd^ := v;
      inc(pd, 4);
      Inc(ps, 4);
    end;
    Inc(ps, rSize);
    Inc(pd, rSize);
  end;
end;

 

下面給出演示代碼和效果圖:

procedure TextPaint(g: TGpGraphics);
var
  brush: TGpLinearGradientBrush;
  font: TGpFont;
  fontFamily: TGpFontFamily;
  r: TGpRect;
begin
  fontFamily := TGpFontFamily.Create('Times New Roman'{'華文行楷'});
  font := TGpFont.Create(fontFamily, 50, [fsBold], utPixel);
  r := GpRect(Form1.PaintBox1.ClientRect);
  brush := TGpLinearGradientBrush.Create(r, kcBlue, kcAliceBlue, 90);
  g.FillRectangle(Brush, r);
  DrawShadowString(g, '文字陰影特效', font, GpPoint(10, r.Height / 3), 5, 10);
  g.TextRenderingHint := thAntiAlias;
  g.DrawString('文字陰影特效', font, Brushs.White, 10, r.Height / 3);
  brush.Free;
  font.Free;
  fontFamily.Free;
end;

       效果圖也和上面一樣分上下兩部分,以便效果比較,并給出了2種字體的文字輸出,其中下圖為華文行楷字體。

下面是華文彩云字體,五色漸變畫刷文字:

        由于本文代碼沒有做更多條件下的測試,可能存在BUG,而且算法也有待提出改進(jìn)意見,請朋友們不吝指教,來信請寄maozefa@hotmail.com。

        本例子中的GDI+版本系本人自己改寫的,與網(wǎng)上流通的版本不完全兼容,如需使用本版本,請參照《GDI+ for VCL基礎(chǔ) -- GDI+ 與 VCL 》一文的下載地址,并請留意后面的修改說明。

        后記1(2007.13.30):剛才發(fā)現(xiàn)函數(shù)中果然存在一點BUG,原因是把數(shù)據(jù)源備份地址通過GDI+的TBitmapData結(jié)構(gòu)的保留字段作為參數(shù)傳遞給MakeShadow函數(shù),原以為該保留字段可以使用的,沒想到GDI+ DLL內(nèi)部可能使用了該字段(看來,保留字段還是不要使用的好,呵呵),導(dǎo)致設(shè)置某些字體,或者字體大小,或者字體風(fēng)格時隨機出現(xiàn)陰影位圖清零,而無文字陰影輸出的BUG,現(xiàn)已經(jīng)修改本文代碼,請朋友們諒解并提出寶貴意見。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多