windows – 为什么自定义光标图像显示不正确?
|
背景 我编写了一个函数,它根据与给定设备上下文关联的位图创建自定义光标.我使用它来创建拖拽光标,显示为“撕下” – 有点像它们在“Trello”中使用. 我一直在使用该函数一段时间没有问题,但当我使用它与新的树组件我正在努力它开始创建部分空白游标. 我已经验证了Delphi 2010和Delphi Berlin都出现了这个问题,并且我也验证了它在Windows 7和Windows 10中都被破坏了. 这是一张显示光标应该是什么样子的照片(对不起 – 找不到快速浏览光标的方法): 而且当它部分空白时它看起来像是什么(好吧,它不仅仅是部分空白 – 它实际上是不可见的): 故障排除 在我发现故障排除之后,如果在调用GetDragCursor之前将PNG图像写入与DC关联的位图,则光标会混乱. 这是我能想到的最简单的代码,用于演示问题: 包含两个TPaintBox组件的表单:MyPaintBoxWorks和MyPaintBoxBroken. >当您单击MyPaintBoxWorks时,您将获得预期的光标. 为了使其易于阅读(我希望),我已经排除了所有错误和资源处理.这对问题没有影响. uses
Types,pngimage;
//////////////////////////////////////////////////////////////////////
procedure TMyForm.FormPaint(Sender: TObject);
begin
MyPaintBoxWorks.Canvas.Brush.Color := clGreen;
MyPaintBoxWorks.Canvas.Rectangle( 0,MyPaintBoxWorks.Width,MyPaintBoxWorks.Height );
MyPaintBoxBroken.Canvas.Brush.Color := clRed;
MyPaintBoxBroken.Canvas.Rectangle( 0,MyPaintBoxBroken.Width,MyPaintBoxBroken.Height );
end;
function GetDragCursor( Handle: HDC;
Width,Height: integer;
CursorX,CursorY: integer ): TCursor; forward;
//////////////////////////////////////////////////////////////////////
procedure TMyForm.MyPaintBoxWorksMouseDown( Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X,Y: Integer);
begin
Screen.Cursor := GetDragCursor( MyPaintBoxWorks.Canvas.Handle,MyPaintBoxWorks.Height,X,Y );
end;
//////////////////////////////////////////////////////////////////////
procedure TMyForm.MyPaintBoxBrokenMouseDown( Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X,Y: Integer );
var
Img: TPngImage;
begin
Img := TPngImage.Create;
Img.LoadFromFile( 'D:TestImage.png' );
Img.Draw( MyPaintBoxBroken.Canvas,Rect( 20,20,40,40 ) );
Screen.Cursor := GetDragCursor( MyPaintBoxBroken.Canvas.Handle,MyPaintBoxBroken.Height,Y );
end;
//////////////////////////////////////////////////////////////////////
function GetDragCursor( Handle: HDC;
Width,CursorY: integer ): TCursor;
var
MaskDC : HDC;
OrgMaskBmp : HBITMAP;
MaskBmp : HBITMAP;
ColourDC : HDC;
OrgColourBmp : HBITMAP;
ColourBmp : HBITMAP;
IconInfo : TIconInfo;
Brush : HBRUSH;
begin
// Create Colour bitmap
// ====================
ColourDC := CreateCompatibleDC( Handle );
ColourBmp := CreateCompatibleBitmap( Handle,Width,Height );
OrgColourBmp := SelectObject( ColourDC,ColourBmp );
BitBlt( ColourDC,Height,Handle,SRCCOPY );
SelectObject( ColourDC,OrgColourBmp );
// Create Mask bitmap
// ==================
MaskDC := CreateCompatibleDC( Handle );
MaskBmp := CreateCompatibleBitmap( Handle,Height );
OrgMaskBmp := SelectObject( MaskDC,MaskBmp );
// Fill with white
Brush := CreateSolidBrush( $FFFFFF );
FillRect( MaskDC,Rect( 0,Height ),Brush );
DeleteObject( Brush );
// Fill masked area with black
Brush := CreateSolidBrush( $000000 );
FillRect( MaskDC,Brush );
DeleteObject( Brush );
SelectObject( MaskDC,OrgMaskBmp );
// Create and set cursor
// =====================
with iconInfo do
begin
fIcon := FALSE;
xHotspot := CursorX;
yHotspot := CursorY;
hbmMask := MaskBmp;
hbmColor := ColourBmp;
end;
Screen.Cursors[1] := CreateIconIndirect( iconInfo );
Result := 1;
end;
我已经详细研究了函数和微软文档,我发现函数没有任何问题. 我也研究过TPngImage.Draw,看不出任何明显的错误(我不应该这么想).功能: >依次调用TPngImage.DrawPartialTrans (我在问题的最后包含了函数的代码以供参考) 如果我:总是正确生成游标: >注释掉写入像素缓冲区的代码,或 这看起来像缓冲区溢出,但代码中没有任何东西似乎支持这一点.而且,我的代码更有可能是错误的. 题 我的函数GetDragCursor或DrawPartialTrans中有什么错误或看起来可疑吗? procedure TPngImage.DrawPartialTrans(DC: HDC; Rect: TRect);
{Adjust the rectangle structure}
procedure AdjustRect(var Rect: TRect);
var
t: Integer;
begin
if Rect.Right < Rect.Left then
begin
t := Rect.Right;
Rect.Right := Rect.Left;
Rect.Left := t;
end;
if Rect.Bottom < Rect.Top then
begin
t := Rect.Bottom;
Rect.Bottom := Rect.Top;
Rect.Top := t;
end
end;
type
{Access to pixels}
TPixelLine = Array[Word] of TRGBQuad;
pPixelLine = ^TPixelLine;
const
{Structure used to create the bitmap}
BitmapInfoHeader: TBitmapInfoHeader =
(biSize: sizeof(TBitmapInfoHeader);
biWidth: 100;
biHeight: 100;
biPlanes: 1;
biBitCount: 32;
biCompression: BI_RGB;
biSizeImage: 0;
biXPelsPerMeter: 0;
biYPelsPerMeter: 0;
biClrUsed: 0;
biClrImportant: 0);
var
{Buffer bitmap creation}
BitmapInfo : TBitmapInfo;
BufferDC : HDC;
BufferBits : Pointer;
OldBitmap,BufferBitmap: HBitmap;
Header: TChunkIHDR;
{Transparency/palette chunks}
TransparencyChunk: TChunktRNS;
PaletteChunk: TChunkPLTE;
TransValue,PaletteIndex: Byte;
CurBit: Integer;
Data: PByte;
{Buffer bitmap modification}
BytesPerRowDest,BytesPerRowSrc,BytesPerRowAlpha: Integer;
ImageSource,ImageSourceOrg,AlphaSource : pByteArray;
ImageData : pPixelLine;
i,j,i2,j2 : Integer;
{For bitmap stretching}
W,H : Cardinal;
Stretch : Boolean;
FactorX,FactorY: Double;
begin
{Prepares the rectangle structure to stretch draw}
if (Rect.Right = Rect.Left) or (Rect.Bottom = Rect.Top) then exit;
AdjustRect(Rect);
{Gets the width and height}
W := Rect.Right - Rect.Left;
H := Rect.Bottom - Rect.Top;
Header := Self.Header; {Fast access to header}
Stretch := (W <> Header.Width) or (H <> Header.Height);
if Stretch then FactorX := W / Header.Width else FactorX := 1;
if Stretch then FactorY := H / Header.Height else FactorY := 1;
{Prepare to create the bitmap}
Fillchar(BitmapInfo,sizeof(BitmapInfo),#0);
BitmapInfoHeader.biWidth := W;
BitmapInfoHeader.biHeight := -Integer(H);
BitmapInfo.bmiHeader := BitmapInfoHeader;
{Create the bitmap which will receive the background,the applied}
{alpha blending and then will be painted on the background}
BufferDC := CreateCompatibleDC(0);
{In case BufferDC could not be created}
if (BufferDC = 0) then RaiseError(EPNGOutMemory,EPNGOutMemoryText);
BufferBitmap := CreateDIBSection(BufferDC,BitmapInfo,DIB_RGB_COLORS,BufferBits,0);
{In case buffer bitmap could not be created}
if (BufferBitmap = 0) or (BufferBits = Nil) then
begin
if BufferBitmap <> 0 then DeleteObject(BufferBitmap);
DeleteDC(BufferDC);
RaiseError(EPNGOutMemory,EPNGOutMemoryText);
end;
{Selects new bitmap and release old bitmap}
OldBitmap := SelectObject(BufferDC,BufferBitmap);
{Draws the background on the buffer image}
BitBlt(BufferDC,W,H,DC,Rect.Left,Rect.Top,SRCCOPY);
{Obtain number of bytes for each row}
BytesPerRowAlpha := Header.Width;
BytesPerRowDest := (((BitmapInfo.bmiHeader.biBitCount * W) + 31)
and not 31) div 8; {Number of bytes for each image row in destination}
BytesPerRowSrc := (((Header.BitmapInfo.bmiHeader.biBitCount * Header.Width) +
31) and not 31) div 8; {Number of bytes for each image row in source}
{Obtains image pointers}
ImageData := BufferBits;
AlphaSource := Header.ImageAlpha;
Longint(ImageSource) := Longint(Header.ImageData) +
Header.BytesPerRow * Longint(Header.Height - 1);
ImageSourceOrg := ImageSource;
case Header.BitmapInfo.bmiHeader.biBitCount of
{R,G,B images}
24:
FOR j := 1 TO H DO
begin
{Process all the pixels in this line}
FOR i := 0 TO W - 1 DO
begin
if Stretch then i2 := trunc(i / FactorX) else i2 := i;
{Optmize when we dont have transparency}
if (AlphaSource[i2] <> 0) then
if (AlphaSource[i2] = 255) then
begin
pRGBTriple(@ImageData[i])^ := pRGBTriple(@ImageSource[i2 * 3])^;
ImageData[i].rgbReserved := 255;
end
else
with ImageData[i] do
begin
rgbRed := ($7F + ImageSource[2+i2*3] * AlphaSource[i2] + rgbRed *
(not AlphaSource[i2])) div $FF;
rgbGreen := ($7F + ImageSource[1+i2*3] * AlphaSource[i2] +
rgbGreen * (not AlphaSource[i2])) div $FF;
rgbBlue := ($7F + ImageSource[i2*3] * AlphaSource[i2] + rgbBlue *
(not AlphaSource[i2])) div $FF;
rgbReserved := not (($7F + (not rgbReserved) * (not AlphaSource[i2])) div $FF);
end;
end;
{Move pointers}
inc(Longint(ImageData),BytesPerRowDest);
if Stretch then j2 := trunc(j / FactorY) else j2 := j;
Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
Longint(AlphaSource) := Longint(Header.ImageAlpha) +
BytesPerRowAlpha * j2;
end;
{Palette images with 1 byte for each pixel}
1,4,8: if Header.ColorType = COLOR_GRAYSCALEALPHA then
FOR j := 1 TO H DO
begin
{Process all the pixels in this line}
FOR i := 0 TO W - 1 DO
with ImageData[i],Header.BitmapInfo do begin
if Stretch then i2 := trunc(i / FactorX) else i2 := i;
rgbRed := ($7F + ImageSource[i2] * AlphaSource[i2] +
rgbRed * (not AlphaSource[i2])) div $FF;
rgbGreen := ($7F + ImageSource[i2] * AlphaSource[i2] +
rgbGreen * (not AlphaSource[i2])) div $FF;
rgbBlue := ($7F + ImageSource[i2] * AlphaSource[i2] +
rgbBlue * (not AlphaSource[i2])) div $FF;
rgbReserved := not (($7F + (not rgbReserved) * (not AlphaSource[i2])) div $FF);
end;
{Move pointers}
Longint(ImageData) := Longint(ImageData) + BytesPerRowDest;
if Stretch then j2 := trunc(j / FactorY) else j2 := j;
Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
Longint(AlphaSource) := Longint(Header.ImageAlpha) +
BytesPerRowAlpha * j2;
end
else {Palette images}
begin
{Obtain pointer to the transparency chunk}
TransparencyChunk := TChunktRNS(Chunks.ItemFromClass(TChunktRNS));
PaletteChunk := TChunkPLTE(Chunks.ItemFromClass(TChunkPLTE));
FOR j := 1 TO H DO
begin
{Process all the pixels in this line}
i := 0;
repeat
CurBit := 0;
if Stretch then i2 := trunc(i / FactorX) else i2 := i;
Data := @ImageSource[i2];
repeat
{Obtains the palette index}
case Header.BitDepth of
1: PaletteIndex := (Data^ shr (7-(I Mod 8))) and 1;
2,4: PaletteIndex := (Data^ shr ((1-(I Mod 2))*4)) and $0F;
else PaletteIndex := Data^;
end;
{Updates the image with the new pixel}
with ImageData[i] do
begin
TransValue := TransparencyChunk.PaletteValues[PaletteIndex];
rgbRed := (255 + PaletteChunk.Item[PaletteIndex].rgbRed *
TransValue + rgbRed * (255 - TransValue)) shr 8;
rgbGreen := (255 + PaletteChunk.Item[PaletteIndex].rgbGreen *
TransValue + rgbGreen * (255 - TransValue)) shr 8;
rgbBlue := (255 + PaletteChunk.Item[PaletteIndex].rgbBlue *
TransValue + rgbBlue * (255 - TransValue)) shr 8;
end;
{Move to next data}
inc(i); inc(CurBit,Header.BitmapInfo.bmiHeader.biBitCount);
until CurBit >= 8;
{Move to next source data}
//inc(Data);
until i >= Integer(W);
{Move pointers}
Longint(ImageData) := Longint(ImageData) + BytesPerRowDest;
if Stretch then j2 := trunc(j / FactorY) else j2 := j;
Longint(ImageSource) := Longint(ImageSourceOrg) - BytesPerRowSrc * j2;
end
end {Palette images}
end {case Header.BitmapInfo.bmiHeader.biBitCount};
{Draws the new bitmap on the foreground}
BitBlt(DC,BufferDC,SRCCOPY);
{Free bitmap}
SelectObject(BufferDC,OldBitmap);
DeleteObject(BufferBitmap);
DeleteDC(BufferDC);
end;
我能够使它与GDI一起工作.
好像Delphi png draw在透明的32位Bitmap上绘制得不好. (*见编辑) 你的GetDragCursor对我很有用. (编辑:鄂州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- windows – 批处理 – 将命令输出存储到变量(多行)
- win10连接远程桌面,出现身份验证错误,要求的函数不正确,
- windows-nginx-https-本地配置
- 树莓派teamviewer远程 windows远程桌面
- Windows Phone 7 – 如何在WIndows Phone应用程序中访问Nav
- npm ERR!注册表错误解析json – 尝试在Windows 8中安装Cor
- 使用免费工具开发轻量级(无运行时)基于Windows的GUI应用程序
- 用windows下编译过的eAccelerator for PHP 5.1.6实现php加速
- 在Windows XP上从压缩标准库中嵌入Python 3.3
- Windows Phone 7 – 您可以在Windows 7手机上安装自己的应用
