本文用C++实现Photoshop色相/饱和度/明度功能,界面程序使用BCB6;图片操作采用GDI+。代码也可适用于其它C/C++编译器(可能要稍作修改)。
有关Photoshop饱和度调整原理可参见《GDI+ 在Delphi程序的应用 -- 图像饱和度调整》,明度调整原理可参见《GDI+ 在Delphi程序的应用 -- 仿Photoshop的明度调整》。
色相/饱和度/明度功能头文件:
- #ifndef RgbHsbH
- #define RgbHsbH
-
- #include <windows.h>
- #include <algorithm>
- using std::min;
- using std::max;
- #include <gdiplus.h>
- using namespace Gdiplus;
-
- void GdipHSBAdjustment(Bitmap *Bmp, int hValue, int sValue, int bValue);
-
- #endif
色相/饱和度/明度功能代码文件:
- #include "RgbHsb.h"
-
- inline void SwapRGB(int &a, int &b)
- {
- a += b;
- b = a - b;
- a -= b;
- }
-
- inline void CheckRGB(int &Value)
- {
- if (Value < 0) Value = 0;
- else if (Value > 255) Value = 255;
- }
-
- inline void AssignRGB(BYTE &R, BYTE &G, BYTE &B, int intR, int intG, int intB)
- {
- R = intR;
- G = intG;
- B = intB;
- }
-
- void SetBright(BYTE &R, BYTE &G, BYTE &B, int bValue)
- {
- int intR = R;
- int intG = G;
- int intB = B;
- if (bValue > 0)
- {
- intR = intR + (255 - intR) * bValue / 255;
- intG = intG + (255 - intG) * bValue / 255;
- intB = intB + (255 - intB) * bValue / 255;
- }
- else if (bValue < 0)
- {
- intR = intR + intR * bValue / 255;
- intG = intG + intG * bValue / 255;
- intB = intB + intB * bValue / 255;
- }
- CheckRGB(intR);
- CheckRGB(intG);
- CheckRGB(intB);
- AssignRGB(R, G, B, intR, intG, intB);
- }
-
- void SetHueAndSaturation(BYTE &R, BYTE &G, BYTE &B, int hValue, int sValue)
- {
- int intR = R;
- int intG = G;
- int intB = B;
-
- if (intR < intG)
- SwapRGB(intR, intG);
- if (intR < intB)
- SwapRGB(intR, intB);
- if (intB > intG)
- SwapRGB(intB, intG);
-
- int delta = intR - intB;
- if (!delta) return;
-
- int entire = intR + intB;
- int H, S, L = entire >> 1;
- if (L < 128)
- S = delta * 255 / entire;
- else
- S = delta * 255 / (510 - entire);
- if (hValue)
- {
- if (intR == R)
- H = (G - B) * 60 / delta;
- else if (intR == G)
- H = (B - R) * 60 / delta + 120;
- else
- H = (R - G) * 60 / delta + 240;
- H += hValue;
- if (H < 0) H += 360;
- else if (H > 360) H -= 360;
- int index = H / 60;
- int extra = H % 60;
- if (index & 1) extra = 60 - extra;
- extra = (extra * 255 + 30) / 60;
- intG = extra - (extra - 128) * (255 - S) / 255;
- int Lum = L - 128;
- if (Lum > 0)
- intG += (((255 - intG) * Lum + 64) / 128);
- else if (Lum < 0)
- intG += (intG * Lum / 128);
- CheckRGB(intG);
- switch (index)
- {
- case 1:
- SwapRGB(intR, intG);
- break;
- case 2:
- SwapRGB(intR, intB);
- SwapRGB(intG, intB);
- break;
- case 3:
- SwapRGB(intR, intB);
- break;
- case 4:
- SwapRGB(intR, intG);
- SwapRGB(intG, intB);
- break;
- case 5:
- SwapRGB(intG, intB);
- break;
- }
- }
- else
- {
- intR = R;
- intG = G;
- intB = B;
- }
- if (sValue)
- {
- if (sValue > 0)
- {
- sValue = sValue + S >= 255? S: 255 - sValue;
- sValue = 65025 / sValue - 255;
- }
- intR += ((intR - L) * sValue / 255);
- intG += ((intG - L) * sValue / 255);
- intB += ((intB - L) * sValue / 255);
- CheckRGB(intR);
- CheckRGB(intG);
- CheckRGB(intB);
- }
- AssignRGB(R, G, B, intR, intG, intB);
- }
-
- void GdipHSBAdjustment(Bitmap *Bmp, int hValue, int sValue, int bValue)
- {
- sValue = sValue * 255 / 100;
- bValue = bValue * 255 / 100;
- BitmapData data;
- Rect r(0, 0, Bmp->GetWidth(), Bmp->GetHeight());
- Bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);
- try
- {
- int offset = data.Stride - data.Width * 4;
- unsigned char *p = (unsigned char*)data.Scan0;
- for (int y = 0; y < data.Height; y ++, p += offset)
- for (int x = 0; x < data.Width; x ++, p += 4)
- {
- if (sValue > 0 && bValue)
- SetBright(p[2], p[1], p[0], bValue);
- SetHueAndSaturation(p[2], p[1], p[0], hValue, sValue);
- if (bValue && sValue <= 0)
- SetBright(p[2], p[1], p[0], bValue);
- }
-
- }
- __finally
- {
- Bmp->UnlockBits(&data);
- }
- }
BCB6界面头文件:
- //---------------------------------------------------------------------------
-
- #ifndef mainH
- #define mainH
- //---------------------------------------------------------------------------
- #include <Classes.hpp>
- #include <Controls.hpp>
- #include <StdCtrls.hpp>
- #include <Forms.hpp>
- #include <ComCtrls.hpp>
- #include <ExtCtrls.hpp>
- #include "RgbHsb.h"
- //---------------------------------------------------------------------------
- class TForm1 : public TForm
- {
- __published: // IDE-managed Components
- TButton *Button1;
- TLabel *Label1;
- TLabel *Label2;
- TLabel *Label3;
- TTrackBar *HBar;
- TTrackBar *SBar;
- TTrackBar *BBar;
- TEdit *HEdit;
- TEdit *SEdit;
- TEdit *BEdit;
- TPaintBox *PaintBox1;
- void __fastcall PaintBox1Paint(TObject *Sender);
- void __fastcall HEditKeyPress(TObject *Sender, char &Key);
- void __fastcall HEditChange(TObject *Sender);
- void __fastcall HBarChange(TObject *Sender);
- void __fastcall SBarChange(TObject *Sender);
- void __fastcall BBarChange(TObject *Sender);
- void __fastcall Button1Click(TObject *Sender);
- private: // User declarations
- public: // User declarations
- __fastcall TForm1(TComponent* Owner);
- __fastcall ~TForm1(void);
- };
- //---------------------------------------------------------------------------
- extern PACKAGE TForm1 *Form1;
- //---------------------------------------------------------------------------
- #endif
BCB6界面代码文件:
- //---------------------------------------------------------------------------
-
- #include <vcl.h>
- #pragma hdrstop
-
- #include "main.h"
- #include <stdlib.h>
- //---------------------------------------------------------------------------
- #pragma package(smart_init)
- #pragma resource "*.dfm"
- TForm1 *Form1;
-
- ULONG gdiplusToken;
- Bitmap *Bmp, *tmpBmp;
- Gdiplus::Rect r;
- bool lock;
-
- //---------------------------------------------------------------------------
- __fastcall TForm1::TForm1(TComponent* Owner)
- : TForm(Owner)
- {
- Gdiplus::GdiplusStartupInput gdiplusStartupInput;
- GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
- Bmp = new Bitmap(WideString("100_0349.jpg"/*"d://100_1.jpg"*/));
- r = Gdiplus::Rect(0, 0, Bmp->GetWidth(), Bmp->GetHeight());
- tmpBmp = Bmp->Clone(r, PixelFormat24bppRGB);
- DoubleBuffered = true;
- lock = false;
- }
- __fastcall TForm1::~TForm1(void)
- {
- delete tmpBmp;
- delete Bmp;
- GdiplusShutdown(gdiplusToken);
- }
- //---------------------------------------------------------------------------
-
-
- void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
- {
- Gdiplus::Graphics g(PaintBox1->Canvas->Handle);
- g.DrawImage(tmpBmp, r);
- g.TranslateTransform(0, r.Height);
- g.DrawImage(Bmp, r);
- }
- //---------------------------------------------------------------------------
-
- void __fastcall TForm1::HEditKeyPress(TObject *Sender, char &Key)
- {
- if (Key >= 32 && (Key < 48 || Key > 57))
- Key = 0;
- }
- //---------------------------------------------------------------------------
-
- void __fastcall TForm1::HEditChange(TObject *Sender)
- {
- lock = true;
- if (((TEdit*)Sender)->Text.Length() == 0)
- ((TEdit*)Sender)->Text = "0";
- switch (((TEdit*)Sender)->Tag)
- {
- case 0: HBar->Position = HEdit->Text.ToInt();
- break;
- case 1: SBar->Position = SEdit->Text.ToInt();
- break;
- case 2: BBar->Position = BEdit->Text.ToInt();
- break;
- }
- lock = false;
- delete tmpBmp;
- tmpBmp = Bmp->Clone(r, PixelFormat24bppRGB);
- if (HBar->Position || SBar->Position || BBar->Position)
- GdipHSBAdjustment(tmpBmp, HBar->Position, SBar->Position, BBar->Position);
- PaintBox1->Invalidate();
- }
- //---------------------------------------------------------------------------
-
- void __fastcall TForm1::HBarChange(TObject *Sender)
- {
- if (!lock)
- HEdit->Text = HBar->Position;
- }
- //---------------------------------------------------------------------------
-
- void __fastcall TForm1::SBarChange(TObject *Sender)
- {
- if (!lock)
- SEdit->Text = SBar->Position;
- }
- //---------------------------------------------------------------------------
-
- void __fastcall TForm1::BBarChange(TObject *Sender)
- {
- if (!lock)
- BEdit->Text = BBar->Position;
- }
- //---------------------------------------------------------------------------
-
- void __fastcall TForm1::Button1Click(TObject *Sender)
- {
- HBar->Position = 0;
- SBar->Position = 0;
- BBar->Position = 0;
- }
- //---------------------------------------------------------------------------
界面截图:
上面的代码没作多的优化,速度不是很理想,下面是一段插入汇编码,速度比前面的纯C++代码快很多倍:
- void _SetBrightness(BitmapData* data, long value)
- {
- __asm
- {
- // push esi
- // push edi
- // push ebx
-
- mov eax, data
- mov edi, [eax + 16]
- mov esi, [eax + 4]
- imul esi, [eax]
- mov edx, value
- cld
- PixelLoop:
- mov ecx, 3
- RGBLoop:
- movzx eax, [edi]
- mov ebx, eax
- test edx, edx
- js Label1
- neg eax
- add eax, 255
- Label1:
- imul eax, edx
- sar eax, 8
- add eax, ebx
- jns Label2
- xor eax, eax
- jmp Label3
- Label2:
- cmp eax, 255
- jle Label3
- mov eax, 255
- Label3:
- stosb
- loop RGBLoop
- inc edi
- dec esi
- jnz PixelLoop
-
- // pop ebx
- // pop edi
- // pop esi
- }
- }
-
- void _HueAndSaturation(BitmapData* data, long hValue, long sValue)
- {
- long I, S, delta;
-
- __asm
- {
- // push esi
- // push edi
- // push ebx
- mov eax, data
- mov edi, [eax + 16] // edi = Data.Scan0
- mov ecx, [eax + 4]
- imul ecx, [eax]
- mov I, ecx // I = Data.Width * Data.Height
- cld
- PixelLoop: // for (I --; I >= 0; I --)
- dec I // {
- js end
- movzx ecx, [edi + 2] // ecx = rgbMax
- movzx ebx, [edi + 1]
- movzx esi, [edi]
- cmp ecx, ebx // esi = rgbMin
- jge SumHsl1
- xchg ecx, ebx
- SumHsl1:
- cmp ecx, esi
- jge SumHsl2
- xchg ecx, esi
- SumHsl2:
- cmp esi, ebx
- jle SumHsl3
- mov esi, ebx
- SumHsl3:
- mov eax, ecx // delta = rgbMax - rgbMin
- sub eax, esi // if (delta == 0)
- jnz SumHsl4 // {
- add edi, 4 // edi += 4
- jmp PixelLoop // continue
- SumHsl4:
- mov delta, eax // }
- add esi, ecx
- mov ebx, esi // ebx = rgbMax + rgbMin
- shr esi, 1 // esi = L = (rgbMax + rgbMin) / 2
- cmp esi, 128
- jl SumHsl5
- neg ebx // if (L >= 128) ebx = 510 - ebx
- add ebx, 510
- SumHsl5:
- imul eax, 255 // eax = S = delta * 255 / ebx
- cdq
- div ebx
- cmp hValue, 0
- je Label1
-
- push esi // if (hValue != 0) Sum Hue
- push eax
- mov S, eax
- cmp cl, [edi + 2]
- jne SumHsl6
- movzx eax, [edi + 1] // if (R == rgbMax) eax = G - B; ebx = 0
- movzx edx, [edi]
- xor ebx, ebx
- jmp SumHsl8
- SumHsl6:
- cmp cl, [edi + 1]
- jne SumHsl7
- movzx eax, [edi] // if (G == rgbMax) eax = B - R; ebx = 120
- movzx edx, [edi + 2]
- mov ebx, 120
- jmp SumHsl8
- SumHsl7:
- movzx eax, [edi + 2] // if (B == rgbMax) eax = R - G; ebx = 240
- movzx edx, [edi + 1]
- mov ebx, 240
- SumHsl8:
- sub eax, edx
- imul eax, 60 // H = ebx + eax * 60 / delta
- cdq
- idiv dword ptr delta
- add eax, ebx
- add eax, hValue // newH = H + hValue
- jns SumHsl10 // 0 <= newH < 360
- add eax, 360
- SumHsl10:
- cmp eax, 360
- jl SumHsl11
- sub eax, 360
- SumHsl11:
- mov ebx, 60
- cdq // eax = newH / 60 (hue index)
- div ebx // edx = newH % 60 (hue extra)
- test eax, 1
- jz SumHsl12
- neg edx // if (eax & 1) edx = 60 - edx
- add edx, 60
- SumHsl12:
- push eax // Save hue index
- mov eax, edx
- imul eax, 255
- add eax, 30
- mov ebx, 60
- cdq
- idiv ebx
- mov edx, eax // extra = edx * 255 / 60
- mov ebx, 255
- sub ebx, S
- sub eax, 128
- imul eax, ebx
- sar eax, 8
- sub edx, eax // rgbCenter = extra -
- mov eax, edx // (rxtra - 128) * (255 - S) / 255
- sub esi, 128 // L -= 128
- js SumHsl13 // eax = L < 0? rgbCenter : 255 - rgbCenter
- neg eax
- add eax, 255
- SumHsl13:
- imul eax, esi
- js SumHsl14
- add eax, 64
- SumHsl14:
- sar eax, 7
- add edx, eax // rgbCenter = rgbCenter + eax * L / 128
- jns SumHsl15
- xor edx, edx
- jmp SumHsl16
- SumHsl15:
- cmp edx, 255
- jle SumHsl16
- mov edx, 255
- SumHsl16:
- pop eax // reset hue index
- mov ebx, ecx // ecx = rgbMax, edx = rgbCenter
- sub ebx, delta // ebx = rgbMin = rgbMax - delta
- test eax, eax // switch (hue index)
- jz H60 // {
- H120: // case 1(60 - 119):
- cmp eax, 1 // red = rgbCenter
- jne H180 // green = rgbMax
- xchg ecx, edx // blue = rgbMin
- jmp H60 // break
- H180: // case 2(120 - 179):
- cmp eax, 2 // red = rgbMin
- jne H240 // green = rgbMax
- xchg ecx, ebx // blue = rgbCenter
- xchg edx, ebx
- jmp H60 // break
- H240: // case 3(180 - 239):
- cmp eax, 3 // red = rgbMin
- jne H300 // green = rgbCenter
- xchg ecx, ebx // blue = rgbMax
- jmp H60 // break
- H300: // case 4(240 - 299):
- cmp eax, 4 // red = rgbCenter
- jne H360 // green = rgbMin
- xchg edx, ebx // blue = rgbMax
- xchg ecx, ebx
- jmp H60 // break
- H360: // case 5:(300 - 359)
- xchg edx, ebx // red=rgbMax,green=rgbMin,blue=rgbCenter
- H60: // break
- mov [edi], bl // default:
- mov [edi + 1], dl // red=rgbMax,green=rgbCenter,blue=rgbmin
- mov [edi + 2], cl // }
-
- pop eax
- pop esi
- cmp dword ptr sValue, 0
- jnz Label1 // if (sValue == 0){
- add edi, 4 // edi += 4, continue
- jmp PixelLoop // }
- Label1:
- mov ebx, sValue // ebx = sValue
- test ebx, ebx // if (ebx > 0)
- js Label10 // {
- add bl, al
- jnc Label6 // if (ebx + S >= 255)
- mov ebx, eax // ebx = S
- jmp Label7
- Label6:
- mov ebx, 255
- sub ebx, sValue // else ebx = 255 - sValue
- Label7:
- mov eax, 65025 // ebx = 65025 / ebx - 255
- cdq
- div ebx
- sub eax, 255
- mov ebx, eax // }
- Label10:
- mov ecx, 3
- RGBLoop: // for (J = 3; J > 0; J --)
- movzx eax, [edi] // {
- mov edx, eax
- sub eax, esi // rgb = rgb + (rgb - L) * ebx / 255
- imul eax, ebx
- sar eax, 8
- add eax, edx
- jns Label11
- xor eax, eax // if (rgb < 0) rgb = 0
- jmp Label12
- Label11:
- cmp eax, 255
- jle Label12
- mov eax, 255 // else if (rgb > 255) rgb = 255
- Label12:
- stosb // *edi ++ = rgb
- loop RGBLoop // }
- inc edi // edi ++
- jmp PixelLoop // }
- end:
- // pop ebx
- // pop edi
- // pop esi
- }
- }
修改前面的GdipHSBAdjustment函数:
- void GdipHSBAdjustment(Bitmap *Bmp, int hValue, int sValue, int bValue)
- {
- sValue = sValue * 255 / 100;
- bValue = bValue * 255 / 100;
- BitmapData data;
- Rect r(0, 0, Bmp->GetWidth(), Bmp->GetHeight());
- Bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);
- try
- {
- if (bValue && sValue > 0)
- _SetBrightness(&data, bValue);
- if (hValue || sValue)
- _HueAndSaturation(&data, hValue, sValue);
- if (bValue && sValue <= 0)
- _SetBrightness(&data, bValue);
- }
- __finally
- {
- Bmp->UnlockBits(&data);
- }
- }
如有错误或者建议,请来信指导:maozefa@hotmail.com
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。