GDI+ 在Delphi程式的應用 — 仿Photoshop的明度調整

來源:互聯網
上載者:User

        這幾天研究了一下Photoshop的色相/飽和度命令,也就是所謂的HSB顏色模式,沒完全搞明白,網上搜尋也沒一點結果,看了一些介紹HSB演算法的文章,其實講的就是HSV或者HSL的演算法。

        關於PS色相/飽和度中的色相,就不用研究了,原理和HSV或者HSL的H都是一樣的。

        而飽和度在-100,0,+100這三點上的效果與HSL完全一樣,其它範圍就有區別了,特別是在0 -- +100範圍,調整時比HSL的H調整要平坦,所以有效調整幅度較大,有些圖片調整到+50%以上還不覺很大失真(這裡的“失真”是針對顏色中難看的斑點來說的,並不是說整個圖片不覺失真),而HSL的H的正向調整10%以上就很難看了;與HSV則沒一點是相同的,可見PS的色相/飽和度演算法應該是在HSL基礎上改進的。

        最令人困惑的是PS的明度調整,好像是“獨立”於色相飽和度的。我們知道,要在程式中利用HSV或HSL模式調整V或者L,往往要先將RGB轉換為HSV或HSL,或者至少要在其中將V或者L部分分離出來,修改後再轉換為RGB模式(可參見我的文章《GDI+ 在Delphi程式的應用 -- 線性調整映像亮度 》分離HSL的L部分調整亮度),而PS的明度調整則不一樣,完全不用轉換RGB到所謂的HSB進行調整,直接寫個函數就可以了,請看下面的Delphi過程及測試代碼(分別用GDI+的TGpBitmap和Delphi的TBitmap測試),用於模仿PS明度調整(嚴格的說不叫模仿,而是實實在在的PS明度調整過程):

 

   說明:為了統一《GDI+ 在Delphi程式的應用》系列文章所用資料類型和影像處理格式,本文代碼已作了修訂,代碼中所用Gdiplus單元及BUG更正見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。(2008.8.18記)

     資料類型:

  1. type
  2.   // 與GDI+ TBitmapData結構相容的映像資料結構
  3.   TImageData = packed record
  4.     Width: LongWord;         // 映像寬度
  5.     Height: LongWord;        // 映像高度
  6.     Stride: LongWord;        // 映像掃描線位元組長度
  7.     PixelFormat: LongWord;   // 未使用
  8.     Scan0: Pointer;          // 映像資料地址
  9.     Reserved: LongWord;      // 保留
  10.   end;
  11.   PImageData = ^TImageData;
  12. // 擷取TBitmap映像的TImageData資料結構,便於處理TBitmap映像
  13. function GetImageData(Bmp: TBitmap): TImageData;
  14. begin
  15.   Bmp.PixelFormat := pf32bit;
  16.   Result.Width := Bmp.Width;
  17.   Result.Height := Bmp.Height;
  18.   Result.Scan0 := Bmp.ScanLine[Bmp.Height - 1];
  19.   Result.Stride := Result.Width shl 2;
  20. //  Result.Stride := (((32 * Bmp.Width) + 31) and $ffffffe0) shr 3;
  21. end;

   

過程代碼:

  1. // 調整圖象明度
  2. procedure PSBrightness(Data: TImageData; Value: Integer);
  3. asm
  4.     push    ebp
  5.     push    esi
  6.     push    edi
  7.     push    ebx
  8.     mov     edi, [eax + 16] // edi = Data.Scan0
  9.     mov     ebp, [eax + 4]  // edp = Data.Height * Data.Width
  10.     imul    ebp, [eax]
  11.     mov     esi, edx        // esi = Value
  12.     mov     ebx, 255        // ebx = 255
  13.     cld
  14.   @PixelLoop:               // for (i = ebp; i > 0; i --)
  15.     mov     ecx, 3          // {
  16.   @vLoop:                   //   for (j = 3; j > 0; j --)
  17.     movzx   eax, [edi]      //   {
  18.     push    eax
  19.     test    esi, esi
  20.     js      @@1
  21.     neg     eax             //     if (Value > 0)
  22.     add     eax, ebx        //       rgb = rgb + (255 - rgb) * Value / 255
  23.   @@1:
  24.     imul    eax, esi        //     else
  25.     cdq                     //       rgb = rgb + rgb * Value / 255
  26.     idiv    ebx
  27.     pop     edx
  28.     add     eax, edx
  29.     jns     @@2             //     rgb = max(0, min(255, rgb))
  30.     xor     eax, eax
  31.     jmp     @@3
  32.   @@2:
  33.     cmp     eax, ebx
  34.     jle     @@3
  35.     mov     eax, ebx
  36.   @@3:
  37.     stosb                   //     *edi ++ = rgb
  38.     loop    @vLoop          //   }
  39.     inc     edi             //   edi ++
  40.     dec     ebp
  41.     jnz     @PixelLoop      // }
  42.     pop     ebx
  43.     pop     edi
  44.     pop     esi
  45.     pop     ebp
  46. end;
  47. // 調整GDI+圖象明度
  48. procedure GdipPSBrightness(Bmp: TGpBitmap; Value: Integer);
  49. var
  50.   Data: TBitmapData;
  51. begin
  52.   if Value = 0 then Exit;
  53.   Data := Bmp.LockBits(GpRect(0, 0, Bmp.Width, Bmp.Height), [imRead, imWrite], pf32bppARGB);
  54.   try
  55.     PSBrightness(TImageData(Data), Value);
  56.   finally
  57.     Bmp.UnlockBits(Data);
  58.   end;
  59. end;
  60. // 調整TBitmap圖象明度
  61. procedure BitmapPSBrightness(Bmp: TBitmap; Value: Integer);
  62. begin
  63.   if Value <> 0 then
  64.     HSLBrightness(GetImageData(Bmp), Value);
  65. end;

        可以看出,上面的PSBrightness過程沒有依賴任何顏色模式轉換,而是採用了下面這個虛擬碼公式:

if (value >= 0)
  RGB = RGB + (255 - RGB) * value / 255;
else
  RGB = RGB + RGB * value / 255;

其中RGB分別表示顏色的R、G、B,value為明度值。那麼這個公式的含義是什麼的,其實就是HSL轉換為RGB的L部分的公式變形,我在《GDI+ 在Delphi程式的應用 -- 線性調整映像亮度 》中採用的公式和它形式是一樣的,只是計算基數不同 :

L = L - 128;
if (L >= 0)
  RGB = RGB + (255 - RGB) * L / 128;
else
  RGB = RGB + RGB * L / 128;

        前者使用的是value的全範圍255或者100%(公式後的/255改為/100),而後者採用的是L的1/2,也就是128或者50%(就這點區別,效果可就大相徑庭了),而且要利用L調整亮度,必須從HSL空間中擷取L再加上調整值,而PS明度調整則不需要從HSB的得到原來的B,這就意味著B在HSB中始終為“0”!雖然PS的明度調整是和色相/飽和度調整放在一起的,但完全不依賴於色相/飽和度,這就是我感覺其好像是“獨立”的原因,也是研究PS飽和度演算法不果的重要原因(一個B=0的HSB模式,光靠HS部分怎樣正確轉換為R、G、B?要知道HSV的V和HSL中的L在正確轉換為RGB模式中至關重要!)。

        假如我的感覺是正確的,那麼PS“獨立”的明度調整又是什麼原理呢?我們知道,一般的非線性RGB亮度調整隻是在原有R、G、B值基礎上增加和減少一定量來實現的,而PS的明度調整原理還得從前面那個公式上去找。我們將正向明度調整公式:RGB = RGB + (255 - RGB)  * value / 255轉換為RGB = (RGB * (255 - value) + 255 * value) / 255,如果value用1表示最大值255,則為RGB = RGB * (1 - value) + 255 * value,可以看出什麼呢?凡是知道映像合成的人都知道這個公式,其實PS的明度調整是採用Alpha合成方式,這裡的value就是Alpha,公式前面部分RGB * (1 - value)的是映像部分,後面的255 * value部分則是一個白色遮照層,明度越大,遮照層的Alpha越大,映像就越談,反之亦然。而明度的負調整則是以一個黑色遮照層來完成的。負100%就全黑了。只有遮照層Alpha=0,也就是明度值為0時,才是完完全全的圖片顯示。要驗證上面的說法很簡單,一是運行我的測試代碼,而是在PS中,用一個全白或全黑圖層覆蓋在一張圖片上,調整這個層的不透明度,可以看出和明度調整效果完全一樣!

        其實,我只是對PS的飽和度調整感興趣,原因前面已經說了,比HSV和HSL的飽和度調整效果要好,範圍要大,飽和度演算法沒研究出來,到搞了個明度調整過程。希望知道PS飽和度演算法的朋友不吝賜教,本人不甚感激!

 

  測試代碼:

  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   Image: TGpBitmap;
  4.   g: TGpGraphics;
  5. begin
  6.   Image := TGpBitmap.Create('D:/VclLib/GdiplusDemo/Media/20041001.jpg');
  7.   g := TGpGraphics.Create(Handle, False);
  8.   g.DrawImage(Image, 10, 10);
  9.   GdipPSBrightness(Image, 30);
  10.   g.DrawImage(Image, 10, 220);
  11.   Image.Free;
  12.   g.Free;
  13. end;
  14. procedure TForm1.Button2Click(Sender: TObject);
  15. var
  16.   Image: TBitmap;
  17. begin
  18.   Image := TBitmap.Create;
  19.   Image.LoadFromFile('D:/VclLib/GdiplusDemo/Media/20041001.bmp');
  20.   Canvas.Draw(10, 10, Image);
  21.   BitmapPSBrightness(Image, -30);
  22.   Canvas.Draw(10, 220, Image);
  23.   Image.Free;
  24. end;

  如有錯誤請來信指正:maozefa@hotmail.com

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.