正在无聊地等待测试机执行完庞大的测试任务。为了避免睡着,随手写了一个画图的过程。 没多大用处,不过是指定图片的透明色然后画透明bitmap到DC,呃,可以一次性指定图片中最多8种颜色都作为透明色。速度还可以。和TBitmap.Draw比较了一下,画1280*1024*24bit图片各100次:指定一种透明色,我的过程耗时2375毫秒,TBitmap.Draw耗时4078毫秒。
指定8种透明色,我的过程耗时2279毫秒,TBitmap.Draw(还是一种透明色)耗时4031毫秒代码奉上:
var
TransMasks: array [0..2, 0..255] of Byte;procedure TransparentPaint(DC: HDC; x, y: Integer; Bmp: TBitmap; Invert: Boolean;
ClrCnt: Integer; const TransColors: array of Cardinal);
const
ROP_DstCopy = $00AA0029;var
MaskBits: Pointer;
mskLnW, srcLnW, srcPxW,
dln, sln, dpx, spx,
i, j, w, h: Integer;
pxMask, m, bk, fr: Byte;
hbmp: HBITMAP;begin
// only support 24/32 bits bitmap
case Bmp.PixelFormat of
pf24Bit:
begin
srcPxW := 3;
srcLnW := BytesPerScanLine(Bmp.Width, 24, 32);
end;
pf32Bit:
begin
srcPxW := 4;
srcLnW := BytesPerScanLine(Bmp.Width, 32, 32);
end;
else
Exit;
end; if Invert then
begin
bk := $FF;
fr := 0;
end
else begin
fr := $FF;
bk := 0;
end; w := Bmp.Width;
h := Bmp.Height; // setup transparent masks
if ClrCnt > 8 then ClrCnt := 8;
m := 1;
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := TransMasks[0, PByte(dpx+2)^] or m;
TransMasks[1, PByte(dpx+1)^] := TransMasks[1, PByte(dpx+1)^] or m;
TransMasks[2, PByte(dpx)^] := TransMasks[2, PByte(dpx)^] or m;
m := m shl 1;
end; // calc monochrome bitmap's line width
mskLnW := ((w+7) shr 3 + 1) and $FFFFFFFE;
// allocate monochrome bitmap's bits data
MaskBits := AllocMem(mskLnW * h); // dln, sln point to first scanline
dln := Integer(MaskBits);
sln := Integer(Bmp.ScanLine[0]);
// calculating mask bitmap
for i := 1 to h do
begin
dpx := dln;
spx := sln;
pxMask := $7F;
for j := 1 to w do
begin
m := TransMasks[0, PByte(spx)^] and TransMasks[1, PByte(spx+1)^] and TransMasks[2, PByte(spx+2)^];
if (m = 0) or ( m or (m-1) <> m + (m-1) ) then // not transparent color, set pixel bit as foreground bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and fr)
else // is transparent color, set pixel bit as transparent bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and bk); Inc(spx, srcPxW); // next source pixel // next dest pixel
asm
ROR pxMask, 1
JC @@1
INC dpx
@@1:
end; end;
// next scanline
Inc(dln, mskLnW);
Dec(sln, srcLnW); end;
// clear transparent masks for next time calling
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := 0;
TransMasks[1, PByte(dpx+1)^] := 0;
TransMasks[2, PByte(dpx)^] := 0;
end;
// generate monochrome bitmap
hbmp := CreateBitmap(w, h, 1, 1, MaskBits); // paint
MaskBlt(DC, x, y, w, h, Bmp.Canvas.Handle, 0, 0, hbmp, 0, 0,
MakeRop4(SRCCOPY, ROP_DstCopy)); // free memory
DeleteObject(hbmp);
FreeMem(MaskBits);end;
指定8种透明色,我的过程耗时2279毫秒,TBitmap.Draw(还是一种透明色)耗时4031毫秒代码奉上:
var
TransMasks: array [0..2, 0..255] of Byte;procedure TransparentPaint(DC: HDC; x, y: Integer; Bmp: TBitmap; Invert: Boolean;
ClrCnt: Integer; const TransColors: array of Cardinal);
const
ROP_DstCopy = $00AA0029;var
MaskBits: Pointer;
mskLnW, srcLnW, srcPxW,
dln, sln, dpx, spx,
i, j, w, h: Integer;
pxMask, m, bk, fr: Byte;
hbmp: HBITMAP;begin
// only support 24/32 bits bitmap
case Bmp.PixelFormat of
pf24Bit:
begin
srcPxW := 3;
srcLnW := BytesPerScanLine(Bmp.Width, 24, 32);
end;
pf32Bit:
begin
srcPxW := 4;
srcLnW := BytesPerScanLine(Bmp.Width, 32, 32);
end;
else
Exit;
end; if Invert then
begin
bk := $FF;
fr := 0;
end
else begin
fr := $FF;
bk := 0;
end; w := Bmp.Width;
h := Bmp.Height; // setup transparent masks
if ClrCnt > 8 then ClrCnt := 8;
m := 1;
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := TransMasks[0, PByte(dpx+2)^] or m;
TransMasks[1, PByte(dpx+1)^] := TransMasks[1, PByte(dpx+1)^] or m;
TransMasks[2, PByte(dpx)^] := TransMasks[2, PByte(dpx)^] or m;
m := m shl 1;
end; // calc monochrome bitmap's line width
mskLnW := ((w+7) shr 3 + 1) and $FFFFFFFE;
// allocate monochrome bitmap's bits data
MaskBits := AllocMem(mskLnW * h); // dln, sln point to first scanline
dln := Integer(MaskBits);
sln := Integer(Bmp.ScanLine[0]);
// calculating mask bitmap
for i := 1 to h do
begin
dpx := dln;
spx := sln;
pxMask := $7F;
for j := 1 to w do
begin
m := TransMasks[0, PByte(spx)^] and TransMasks[1, PByte(spx+1)^] and TransMasks[2, PByte(spx+2)^];
if (m = 0) or ( m or (m-1) <> m + (m-1) ) then // not transparent color, set pixel bit as foreground bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and fr)
else // is transparent color, set pixel bit as transparent bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and bk); Inc(spx, srcPxW); // next source pixel // next dest pixel
asm
ROR pxMask, 1
JC @@1
INC dpx
@@1:
end; end;
// next scanline
Inc(dln, mskLnW);
Dec(sln, srcLnW); end;
// clear transparent masks for next time calling
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := 0;
TransMasks[1, PByte(dpx+1)^] := 0;
TransMasks[2, PByte(dpx)^] := 0;
end;
// generate monochrome bitmap
hbmp := CreateBitmap(w, h, 1, 1, MaskBits); // paint
MaskBlt(DC, x, y, w, h, Bmp.Canvas.Handle, 0, 0, hbmp, 0, 0,
MakeRop4(SRCCOPY, ROP_DstCopy)); // free memory
DeleteObject(hbmp);
FreeMem(MaskBits);end;
DC := GetDC(Handle);
TransparentPaint(DC, 128, 176, Image1.Picture.Bitmap, False, 3, [clRed, clBlue, clYellow]);
ReleaseDC(Handle, DC);
之后效果:所有图片中红色,蓝色,黄色的地方都成为了背景
var
TransMasks: array [0..2, 0..255] of Byte;改成
var
TransMasks: array [0..2, 0..255] of Cardinal;那么最多就支持32种透明色了, 不过占用内存也增加到了3k
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;
var
TransMasks: array [0..2, 0..255] of Byte;implementation{$R *.dfm}
procedure TransparentPaint(DC: HDC; x, y: Integer; Bmp: TBitmap; Invert: Boolean;
ClrCnt: Integer; const TransColors: array of Cardinal);
const
ROP_DstCopy = $00AA0029;var
MaskBits: Pointer;
mskLnW, srcLnW, srcPxW,
dln, sln, dpx, spx,
i, j, w, h: Integer;
pxMask, m, bk, fr: Byte;
hbmp: HBITMAP;begin
// only support 24/32 bits bitmap
case Bmp.PixelFormat of
pf24Bit:
begin
srcPxW := 3;
srcLnW := BytesPerScanLine(Bmp.Width, 24, 32);
end;
pf32Bit:
begin
srcPxW := 4;
srcLnW := BytesPerScanLine(Bmp.Width, 32, 32);
end;
else
Exit;
end; if Invert then
begin
bk := $FF;
fr := 0;
end
else begin
fr := $FF;
bk := 0;
end; w := Bmp.Width;
h := Bmp.Height; // setup transparent masks
if ClrCnt > 8 then ClrCnt := 8;
m := 1;
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := TransMasks[0, PByte(dpx+2)^] or m;
TransMasks[1, PByte(dpx+1)^] := TransMasks[1, PByte(dpx+1)^] or m;
TransMasks[2, PByte(dpx)^] := TransMasks[2, PByte(dpx)^] or m;
m := m shl 1;
end; // calc monochrome bitmap's line width
mskLnW := ((w+7) shr 3 + 1) and $FFFFFFFE;
// allocate monochrome bitmap's bits data
MaskBits := AllocMem(mskLnW * h); // dln, sln point to first scanline
dln := Integer(MaskBits);
sln := Integer(Bmp.ScanLine[0]);
// calculating mask bitmap
for i := 1 to h do
begin
dpx := dln;
spx := sln;
pxMask := $7F;
for j := 1 to w do
begin
m := TransMasks[0, PByte(spx)^] and TransMasks[1, PByte(spx+1)^] and TransMasks[2, PByte(spx+2)^];
if (m = 0) or ( m or (m-1) <> m + (m-1) ) then // not transparent color, set pixel bit as foreground bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and fr)
else // is transparent color, set pixel bit as transparent bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and bk); Inc(spx, srcPxW); // next source pixel // next dest pixel
asm
ROR pxMask, 1
JC @@1
INC dpx
@@1:
end; end;
// next scanline
Inc(dln, mskLnW);
Dec(sln, srcLnW); end;
// clear transparent masks for next time calling
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := 0;
TransMasks[1, PByte(dpx+1)^] := 0;
TransMasks[2, PByte(dpx)^] := 0;
end;
// generate monochrome bitmap
hbmp := CreateBitmap(w, h, 1, 1, MaskBits); // paint
MaskBlt(DC, x, y, w, h, Bmp.Canvas.Handle, 0, 0, hbmp, 0, 0,
MakeRop4(SRCCOPY, ROP_DstCopy)); // free memory
DeleteObject(hbmp);
FreeMem(MaskBits);end;procedure TForm1.Button1Click(Sender: TObject);
var
dc:hdc;
begin
DC := GetDC(Handle);
TransparentPaint(DC, 128, 176, Image1.Picture.Bitmap, False, 3, [clRed, clBlue, clYellow]);
ReleaseDC(Handle, DC);end;end.
unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;
var
TransMasks: array [0..2, 0..255] of Byte;implementation{$R *.dfm}
procedure TransparentPaint(DC: HDC; x, y: Integer; Bmp: TBitmap; Invert: Boolean;
ClrCnt: Integer; const TransColors: array of Cardinal);
const
ROP_DstCopy = $00AA0029;var
MaskBits: Pointer;
mskLnW, srcLnW, srcPxW,
dln, sln, dpx, spx,
i, j, w, h: Integer;
pxMask, m, bk, fr: Byte;
hbmp: HBITMAP;begin
// only support 24/32 bits bitmap
case Bmp.PixelFormat of
pf24Bit:
begin
srcPxW := 3;
srcLnW := BytesPerScanLine(Bmp.Width, 24, 32);
end;
pf32Bit:
begin
srcPxW := 4;
srcLnW := BytesPerScanLine(Bmp.Width, 32, 32);
end;
else
Exit;
end; if Invert then
begin
bk := $FF;
fr := 0;
end
else begin
fr := $FF;
bk := 0;
end; w := Bmp.Width;
h := Bmp.Height; // setup transparent masks
if ClrCnt > 8 then ClrCnt := 8;
m := 1;
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := TransMasks[0, PByte(dpx+2)^] or m;
TransMasks[1, PByte(dpx+1)^] := TransMasks[1, PByte(dpx+1)^] or m;
TransMasks[2, PByte(dpx)^] := TransMasks[2, PByte(dpx)^] or m;
m := m shl 1;
end; // calc monochrome bitmap's line width
mskLnW := ((w+7) shr 3 + 1) and $FFFFFFFE;
// allocate monochrome bitmap's bits data
MaskBits := AllocMem(mskLnW * h); // dln, sln point to first scanline
dln := Integer(MaskBits);
sln := Integer(Bmp.ScanLine[0]);
// calculating mask bitmap
for i := 1 to h do
begin
dpx := dln;
spx := sln;
pxMask := $7F;
for j := 1 to w do
begin
m := TransMasks[0, PByte(spx)^] and TransMasks[1, PByte(spx+1)^] and TransMasks[2, PByte(spx+2)^];
if (m = 0) or ( m or (m-1) <> m + (m-1) ) then // not transparent color, set pixel bit as foreground bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and fr)
else // is transparent color, set pixel bit as transparent bit
PByte(dpx)^ := (PByte(dpx)^ and pxMask) or ((not pxMask) and bk); Inc(spx, srcPxW); // next source pixel // next dest pixel
asm
ROR pxMask, 1
JC @@1
INC dpx
@@1:
end; end;
// next scanline
Inc(dln, mskLnW);
Dec(sln, srcLnW); end;
// clear transparent masks for next time calling
for i := 0 to ClrCnt-1 do
begin
dpx := Integer(@TransColors[i]);
TransMasks[0, PByte(dpx+2)^] := 0;
TransMasks[1, PByte(dpx+1)^] := 0;
TransMasks[2, PByte(dpx)^] := 0;
end;
// generate monochrome bitmap
hbmp := CreateBitmap(w, h, 1, 1, MaskBits); // paint
MaskBlt(DC, x, y, w, h, Bmp.Canvas.Handle, 0, 0, hbmp, 0, 0,
MakeRop4(SRCCOPY, ROP_DstCopy)); // free memory
DeleteObject(hbmp);
FreeMem(MaskBits);end;procedure TForm1.Button1Click(Sender: TObject);
var
dc:hdc;
begin
DC := GetDC(Handle);
TransparentPaint(DC, 128, 176, Image1.Picture.Bitmap, False, 3, [clRed, clBlue, clYellow]);
ReleaseDC(Handle, DC);end;end.怎么没有透明呢,和楼主测试的不一样?
var
dc:hdc;
begin
DC := GetDC(Handle);
TransparentPaint(DC, 128, 176, Image1.Picture.Bitmap, false, 1, [clRed]);
ReleaseDC(Handle, DC);end;这么测试一下 ,结果显示的是原图,一点没变,红色还在?
抠相常用的做法都是采集左上或右上几个像素的RGB平均分量值,找出关键色,然后再根据分量偏差设定一个阈值来判断源像素点是否需要透明