概述
阅读提示:
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
尽可能保持二者内容一致,可相互对照。
本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。
本文是基于《GDI+在Delphi程序的应用 – Photoshop色相/饱和度/明度功能》一文的BASM实用性过程,有关实现原理可参见《GDI+ 在Delphi程序的应用 -- 图像饱和度调整》和《GDI+ 在Delphi程序的应用 -- 仿Photoshop的明度调整》,纯PAS实现代码和测试例子代码见《GDI+在Delphi程序的应用 – Photoshop色相/饱和度/明度功能》。
procedure GetBrightTable(Bright: Integer; var Table: TGrayTable);
asm
push
ebx
cmp
eax, -255
jge
@@1
mov
eax, -255
jmp
@@2
@@1:
cmp
eax, 255
jle
@@2
mov
eax, 255
@@2:
push
eax
mov
ebx, 255
fild
dword ptr[esp]
fwait
mov
[esp], ebx
fidiv
dword ptr[esp]// Bright / 255
fwait
xor
ecx, ecx
test
eax, eax
jg
@@Loop
xor
ebx, ebx
// mask = Bright > 0? 255 : 0
@@Loop:
mov
[esp], ecx
xor
[esp], ebx
fild
dword ptr[esp]
fmul
st(0), st(1)
fistp
dword ptr[esp]
fwait
mov
eax, [esp]
add
eax, ecx
mov
[edx], al
// Table[i] = (i ^ mask) * Bright / 255
inc
edx
inc
ecx
cmp
ecx, 256
jb
@@Loop
ffree
st
pop
eax
pop
ebx
end;
procedure HSBSetBright(var Data: TImageData; const Table: TGrayTable);
asm
push
ebp
push
esi
push
edi
push
ebx
mov
esi, edx
call
_SetDataRegs
mov
ebp, edx
@@yLoop:
push
ecx
@@xLoop:
movzx
eax, [edi].TARGBQuad.Blue
movzx
edx, [edi].TARGBQuad.Green
mov
al, [esi+eax]
mov
dl, [esi+edx]
mov
[edi].TARGBQuad.Blue, al
mov
[edi].TARGBQuad.Green, dl
movzx
eax, [edi].TARGBQuad.Red
mov
al, [esi+eax]
mov
[edi].TARGBQuad.Red, al
add
edi, 4
loop
@@xLoop
pop
ecx
add
edi, ebx
dec
ebp
jnz
@@yLoop
pop
ebx
pop
edi
pop
esi
pop
ebp
end;
procedure HSBSetHueAndSaturation(var Data: TImageData; Hv, Sv: Integer);
const
_fcd5: Single = 0.5;
_fc1: Single = 1.0;
_fc2: Single = 2.0;
_fc4: Single = 4.0;
_fc6: Single = 6.0;
_fc255: Single = 255.0;
_fc510: Single = 510.0;
var
fHv, fSv: Single;
width, height, datOffset: Integer;
asm
push
esi
push
edi
push
ebx
push
edx
push
ecx
call
_SetDataRegs
mov
width, ecx
mov
height, edx
mov
datOffset, ebx
pop
ebx
// Sv
pop
esi
// Hv
pxor
xmm7, xmm7
mov
eax, 60
cvtsi2ss
xmm6, esi
cvtsi2ss
xmm0, eax
divss
xmm6, xmm0
movss
fHv, xmm6
// fSv = Hv / 60
cvtsi2ss
xmm6, ebx
divss
xmm6, _fc255
movss
fSv, xmm6
// xmm6 = fSv = Sv / 255
test
ebx, ebx
jle
@@1
mov
eax, 255
xor
eax, ebx
cvtsi2ss
xmm0, eax
movss
xmm6, _fc255
divss
xmm6, xmm0
// if (Sv > 0)
subss
xmm6, _fc1
//
xmm6 = 255 / (255 - Sv) - 1
@@1:
pshufd
xmm6, xmm6, 0
@@yLoop:
push
width
@@xLoop:
movzx
ecx, [edi].TARGBQuad.Blue
movzx
edx, [edi].TARGBQuad.Green
movzx
eax, [edi].TARGBQuad.Red
cmp
ecx, edx
// ecx = rgbMax
jge
@@3
// edx = rgbMin
xchg
ecx, edx
@@3:
cmp
ecx, eax
jge
@@4
xchg
ecx, eax
@@4:
cmp
edx, eax
cmova
edx, eax
mov
eax, ecx
sub
eax, edx
// delta = rgbMax - rgbmin
jz
@@next
// if (delta == 0) continue
@@5:
movd
xmm0, edx
// rgbMin
pinsrw
xmm0, ecx, 2
// rgbMax
add
ecx, edx
// ecx = rgbMar + rgbMin
cvtsi2ss
xmm3, ecx
divss
xmm3, _fc510
// xmm3 = L = ecx / 510
comiss
xmm3, _fcd5
jb
@@6
neg
ecx
add
ecx, 510
// if (L >= 0.5) ecx = 510 - ecx
@@6:
cvtsi2ss
xmm2, eax
cvtsi2ss
xmm4, ecx
divss
xmm2, xmm4
// xmm2 = S = delta / ecx
test
esi, esi
jnz
@@7
// if (Hv == 0) goto @@20
movd
xmm0, [edi]
punpcklbw xmm0, xmm7
jmp
@@20
@@7:
cvtsi2ss
xmm4, eax
// delta
movss
xmm5, fHv
// add = fHv
pextrw
eax, xmm0, 2
// rgbMax
cmp
al, [edi].TARGBQuad.Red
jne
@@8
// if (R == rgbMax) eax = G - B
movzx
eax, [edi].TARGBQuad.Green
movzx
edx, [edi].TARGBQuad.Blue
jmp
@@10
@@8:
cmp
al, [edi].TARGBQuad.Green
jne
@@9
movzx
eax, [edi].TARGBQuad.Blue
movzx
edx, [edi].TARGBQuad.Red
addss
xmm5, _fc2
// if (G == rgbMax) eax = B - R; add += 2
jmp
@@10
@@9:
movzx
eax, [edi].TARGBQuad.Red
movzx
edx, [edi].TARGBQuad.Green
addss
xmm5, _fc4
// if (B == rgbMax) eax = R - G; add += 4
@@10:
sub
eax, edx
cvtsi2ss
xmm1, eax
divss
xmm1, xmm4
addss
xmm1, xmm5
// H = eax / delta + add
comiss
xmm1, xmm7
jae
@@11
addss
xmm1, _fc6
// if (H < 0) H += 6
jmp
@@12
@@11:
comiss
xmm1, _fc6
jb
@@12
subss
xmm1, _fc6
// else if (H >= 6) H -= 6
@@12:
cvtss2si
edx, xmm1
// index = Round(H)
cvtsi2ss
xmm4, edx
subss
xmm1, xmm4
// extra = H - index
comiss
xmm1, xmm7
// if (extra < 0) // 如果index发生五入
jae
@@13
// {
dec
edx
//
index --
addss
xmm1, _fc1
//
extra ++
@@13:
// }
test
edx, 1
jz
@@14
movaps
xmm4, xmm1
movss
xmm1, _fc1
subss
xmm1, xmm4
// if (index & 1) extra = 1 - extra
@@14:
movaps
xmm4, xmm1
movss
xmm5, _fc1
subss
xmm4, _fcd5
subss
xmm5, xmm2
mulss
xmm4, xmm5
subss
xmm1, xmm4
// extra = extra - (extra - 0.5) * (1.0 - S);
movaps
xmm4, xmm1
// extra0 = extra
movaps
xmm5, xmm3
subss
xmm5, _fcd5
// L0 = L - 0.5
comiss
xmm5, xmm7
jb
@@15
movss
xmm4, _fc1
subss
xmm4, xmm1
// if (L0 >= 0) extra0 = 1 - extra
@@15:
mulss
xmm4, xmm5
mulss
xmm4, _fc2
addss
xmm1, xmm4
mulss
xmm1, _fc255
cvtss2si
eax, xmm1
// rgbMid = (extra + extra0 * L0 * 2) * 255
pinsrw
xmm0, eax, 1
// xmm0 = 0000 MAX MEDIAN MIN
jmp
@@jmpTable[edx*4].Pointer
@@jmpTable:
dd
offset
@@H60
dd
offset
@@H120
dd
offset
@@H180
dd
offset
@@H240
dd
offset
@@H300
dd
offset
@@H360
dd
offset
@@18// 当H=6.0时,SSE判断误差导致index=6,实际应为0
@@H120:
// 60 - 119
pshuflw
xmm0, xmm0, 216
jmp
@@18
@@H180:
// 120 - 179
pshuflw
xmm0, xmm0, 201
jmp
@@18
@@H240:
// 180 - 239
pshuflw
xmm0, xmm0, 198
jmp
@@18
@@H300:
// 240 - 299
pshuflw
xmm0, xmm0, 210
jmp
@@18
@@H360:
// 300 - 359
pshuflw
xmm0, xmm0, 225
@@H60:
// 0 - 59
@@18:
test
ebx, ebx
// if (Sv == 0) continue
jz
@@25
@@20:
punpcklwd xmm0, xmm7
cvtdq2ps
xmm0, xmm0
movaps
xmm1, xmm0
mulss
xmm3, _fc255
pshufd
xmm3, xmm3, 0
subps
xmm0, xmm3
// rgb0 = rgb - L
test
ebx, ebx
jle
@@21
movaps
xmm3, xmm2
// if (Sv > 0)
addss
xmm3, fSv
// {
comiss
xmm3, _fc1
jb
@@21
rcpss
xmm2, xmm2
//
if ((fSv + S) >= 1)
subss
xmm2, _fc1
//
rgb0 = rgb0 * (1 / S - 1)
pshufd
xmm2, xmm2, 0
//
else
mulps
xmm0, xmm2
//
rgb0 = rgb0 * (1 / (1 - fSv) - 1)
jmp
@@22
// }
@@21:
// else
mulps
xmm0, xmm6
//
rgb0 = rgb0 * fSv
@@22:
addps
xmm0, xmm1
// rgb += rgb0
cvtps2dq
xmm0, xmm0
packssdw
xmm0, xmm7
@@25:
packuswb
xmm0, xmm7
mov
al, [edi].TARGBQuad.Alpha
movd
[edi], xmm0
mov
[edi].TARGBQuad.Alpha, al
@@next:
add
edi, 4
dec
width
jnz
@@xLoop
add
edi, datOffset
pop
width
dec
height
jnz
@@yLoop
pop
ebx
pop
edi
pop
esi
end;
procedure ImageHSBAdjustment(var Data: TImageData; hValue, sValue, bValue: Integer);
var
BrightTab: TGrayTable;
begin
if hValue > 180 then hValue := 180
else if hValue < -180 then hValue := -180;
if sValue > 255 then sValue := 255
else if sValue < -255 then sValue := -255;
if bValue <> 0 then GetBrightTable(bValue, BrightTab);
if (sValue > 0) and (bValue <> 0) then
HSBSetBright(Data, BrightTab);
if (hValue <> 0) or (sValue <> 0) then
begin
HSBSetHueAndSaturation(Data, hValue, sValue);
end;
if (sValue <= 0) and (bValue <> 0) then
HSBSetBright(Data, BrightTab);
end;
这次重新修订,将明度部分独立出来,采用一个256元素大小的明度表直接进行替换,大大加快了速度;色相和饱和度调整部分也进行了修改,全部采用SSE代码,使计算精确度得以提高。
下面是个简单的HSB演示程序:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gdiplus, ImageData, StdCtrls, ComCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
PaintBox1: TPaintBox;
Button1: TButton;
HBar: TTrackBar;
SBar: TTrackBar;
BBar: TTrackBar;
HEdit: TEdit;
SEdit: TEdit;
BEdit: TEdit;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
procedure HBarChange(Sender: TObject);
procedure SBarChange(Sender: TObject);
procedure BBarChange(Sender: TObject);
procedure HEditChange(Sender: TObject);
procedure HEditKeyPress(Sender: TObject; var Key: Char);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
Bmp: TGpBitmap;
tmpBmp: TGpBitmap;
r: TGpRect;
Lock: Boolean;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.BBarChange(Sender: TObject);
begin
if not Lock then
BEdit.Text := IntToStr(BBar.Position);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
HBar.Position := 0;
SBar.Position := 0;
BBar.Position := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Bmp := TGpBitmap.Create('....media100_0349.jpg');
r := GpRect(0, 0, Bmp.Width, Bmp.Height);
tmpBmp := Bmp.Clone(r, pf32bppARGB);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
tmpBmp.Free;
Bmp.Free;
end;
procedure TForm1.HBarChange(Sender: TObject);
begin
if not Lock then
HEdit.Text := IntToStr(HBar.Position);
end;
procedure TForm1.HEditChange(Sender: TObject);
var
Data: TImageData;
begin
Lock := True;
with TEdit(Sender) do
begin
if Length(Text) = 0 then Text := '0';
case Tag of
0: HBar.Position := StrToInt(Text);
1: SBar.Position := StrToInt(Text);
2: BBar.Position := StrToInt(Text);
end;
Lock := False;
tmpBmp.Free;
tmpBmp := Bmp.Clone(r, pf32bppARGB);
if (HBar.Position <> 0) or (SBar.Position <> 0) or (BBar.Position <> 0) then
begin
Data := LockGpBitmap(tmpBmp);
ImageHSBAdjustment(Data, HBar.Position, Round(SBar.Position * 255.0 / 100),
Round(BBar.Position * 255.0 / 100));
UnlockGpBitmap(tmpBmp, Data);
end;
PaintBox1Paint(nil);
end;
end;
procedure TForm1.HEditKeyPress(Sender: TObject; var Key: Char);
begin
if (Key >= #32) and not (Key in ['0'..'9']) then
Key := #0;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
g: TGpGraphics;
begin
g := TGpGraphics.Create(PaintBox1.Canvas.Handle);
try
g.DrawImage(tmpBmp, r);
g.TranslateTransform(0, r.Height);
g.DrawImage(Bmp, r);
finally
g.Free;
end;
end;
procedure TForm1.SBarChange(Sender: TObject);
begin
if not Lock then
SEdit.Text := IntToStr(SBar.Position);
end;
end.
运行界面如下:
《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com
这里可访问《Delphi图像处理 -- 文章索引》。
最后
以上就是沉默大白为你收集整理的Delphi图像处理 -- 色相/饱和度调整的全部内容,希望文章能够帮你解决Delphi图像处理 -- 色相/饱和度调整所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复