C++实现Photoshop色相/饱和度/明度功能

本文用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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值