gpt4 book ai didi

winapi - 如何在 Windows 10 上设置玻璃混合颜色?

转载 作者:行者123 更新时间:2023-12-02 11:31:47 25 4
gpt4 key购买 nike

使用undocumented SetWindowCompositionAttribute API在 Windows 10 上,可以为 window 启用玻璃。玻璃是白色或透明的,如以下屏幕截图所示:

enter image description here

但是,Windows 10 开始菜单和通知中心也使用玻璃,都与强调色融合在一起,如下所示:

enter image description here

它是如何做到的?

调查

以下示例中的强调色是浅紫色 - 这是“设置”应用程序的屏幕截图:

enter image description here

AccentPolicy structure defined in this example code具有重音状态、标志和渐变颜色字段:

  AccentPolicy = packed record
AccentState: Integer;
AccentFlags: Integer;
GradientColor: Integer;
AnimationId: Integer;
end;

并且状态可以具有以下任何值:

  ACCENT_ENABLE_GRADIENT = 1;
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2;
ACCENT_ENABLE_BLURBEHIND = 3;

请注意,前两个是在 this github gist 上找到的。 .

第三个效果很好——可以使用玻璃。另外两个中,

  • ACCENT_ENABLE_GRADIENT 会导致窗口完全变成灰色,无论其后面是什么。没有透明度或玻璃效果,但正在绘制的窗口颜色是由 DWM 绘制的,而不是由应用程序绘制的。

enter image description here

  • ACCENT_ENABLE_TRANSPARENTGRADIENT 会导致窗口完全用强调色绘制,无论其后面是什么。没有透明度或玻璃效果,但正在绘制的窗口颜色是由 DWM 绘制的,而不是由应用程序绘制的。

enter image description here

所以这已经很接近了,它似乎是一些弹出窗口(如音量控制小程序)所使用的。

这些值不能一起进行或运算,并且 GradientColor 字段的值除了必须为非零之外没有任何作用。

直接在支持玻璃的窗口上绘图会导致非常奇怪的混合。这里用红色填充客户区(ABGR 格式为 0x000000FF):

enter image description here

任何非零 alpha,例如 0xAA0000FF,都不会产生任何颜色:

enter image description here

都不符合“开始”菜单或通知区域的外观。

这些窗口是如何做到的?

最佳答案

由于Delphi上的GDI窗体不支持alpha channel (除非使用alpha分层窗口,这可能不合适),通常黑色将被视为透明颜色,除非组件支持alpha channel 。

tl;dr 只需使用您的 TTransparentCanvas.Rectangle(0,0,Width+1,Height+1,222),使用 DwmGetColorizationColor 获得的颜色你可以blend颜色较深。

下面将使用 TImage 组件代替。

我将使用 TImage 和 TImage32 (Graphics32) 来显示 Alpha channel 的差异。这是无边框形式,因为边框不接受我们的着色。

enter image description here

如您所见,左侧使用 TImage1 并受 Aero Glass 影响,右侧使用 TGraphics32,它允许叠加不透明颜色(无半透明)。

现在,我们将使用带有半透明 PNG 的 TImage1,我们可以使用以下代码创建它:

procedure SetAlphaColorPicture(
const Col: TColor;
const Alpha: Integer;
Picture: TPicture;
const _width: Integer;
const _height: Integer
);
var
png: TPngImage;
x,y: integer;
sl: pByteArray;
begin

png := TPngImage.CreateBlank(COLOR_RGBALPHA, 8, _width, _height);
try

png.Canvas.Brush.Color := Col;
png.Canvas.FillRect(Rect(0,0,_width,_height));
for y := 0 to png.Height - 1 do
begin
sl := png.AlphaScanline[y];
FillChar(sl^, png.Width, Alpha);
end;

Picture.Assign(png);

finally
png.Free;
end;
end;

我们需要向表单添加另一个 TImage 组件并将其发送回来,这样其他组件就不会位于它下面。

SetAlphaColorPicture(clblack, 200, Image1.Picture, 10,10  );
Image1.Align := alClient;
Image1.Stretch := True;
Image1.Visible := True;

enter image description here

这就是我们的表单与“开始”菜单的样子。

现在,要获得强调色,请使用 DwmGetColorizationColor ,已在 DwmAPI.pas 中定义

function TForm1.GetAccentColor:TColor;
var
col: cardinal;
opaque: longbool;
newcolor: TColor;
a,r,g,b: byte;
begin
DwmGetColorizationColor(col, opaque);
a := Byte(col shr 24);
r := Byte(col shr 16);
g := Byte(col shr 8);
b := Byte(col);

newcolor := RGB(
round(r*(a/255)+255-a),
round(g*(a/255)+255-a),
round(b*(a/255)+255-a)
);

Result := newcolor;

end;

但是,如“开始”菜单所示,该颜色不够暗。

所以我们需要将强调色与深色混合:

//Credits to Roy M Klever http://rmklever.com/?p=116
function TForm1.BlendColors(Col1, Col2: TColor; A: Byte): TColor;
var
c1,c2: LongInt;
r,g,b,v1,v2: byte;
begin
A := Round(2.55 * A);
c1 := ColorToRGB(Col1);
c2 := ColorToRGB(Col2);
v1 := Byte(c1);
v2 := Byte(c2);
r := A * (v1 - v2) shr 8 + v2;
v1 := Byte(c1 shr 8);
v2 := Byte(c2 shr 8);
g := A * (v1 - v2) shr 8 + v2;
v1 := Byte(c1 shr 16);
v2 := Byte(c2 shr 16);
b := A * (v1 - v2) shr 8 + v2;
Result := (b shl 16) + (g shl 8) + r;
end;

...

SetAlphaColorPicture(BlendColors(GetAccentColor, clBlack, 50) , 222, Image1.Picture, 10, 10);

这是 clBlack 与强调色混合 50% 的结果: enter image description here

您可能还想添加其他内容,例如检测强调色何时发生变化并自动更新我们的应用程序颜色,例如:

procedure WndProc(var Message: TMessage);override;
...
procedure TForm1.WndProc(var Message: TMessage);
const
WM_DWMCOLORIZATIONCOLORCHANGED = $0320;
begin
if Message.Msg = WM_DWMCOLORIZATIONCOLORCHANGED then
begin
// here we update the TImage with the new color
end;
inherited WndProc(Message);
end;

为了与 Windows 10 开始菜单设置保持一致,您可以读取注册表以了解任务栏/开始菜单是否是半透明的(启用)以及开始菜单是否启用使用强调色或仅使用黑色背景,以执行以下操作:所以这个键会告诉我们:

'SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize'
ColorPrevalence = 1 or 0 (enabled / disabled)
EnableTransparency = 1 or 0

这是完整的代码,需要TImage1,TImage2,用于着色,其他的不是可选的。

unit Unit1;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, GR32_Image, DWMApi, GR32_Layers,
Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Imaging.pngimage, Registry;

type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Image3: TImage;
Image321: TImage32;
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
function TaskbarAccented:boolean;
function TaskbarTranslucent:boolean;
procedure EnableBlur;
function GetAccentColor:TColor;
function BlendColors(Col1, Col2: TColor; A: Byte): TColor;
procedure WndProc(var Message: TMessage);override;
procedure UpdateColorization;
public
{ Public declarations }
end;

AccentPolicy = packed record
AccentState: Integer;
AccentFlags: Integer;
GradientColor: Integer;
AnimationId: Integer;
end;

TWinCompAttrData = packed record
attribute: THandle;
pData: Pointer;
dataSize: ULONG;
end;


var
Form1: TForm1;

var
SetWindowCompositionAttribute: function (Wnd: HWND; const AttrData: TWinCompAttrData): BOOL; stdcall = Nil;

implementation

{$R *.dfm}

procedure SetAlphaColorPicture(
const Col: TColor;
const Alpha: Integer;
Picture: TPicture;
const _width: Integer;
const _height: Integer
);
var
png: TPngImage;
x,y: integer;
sl: pByteArray;
begin

png := TPngImage.CreateBlank(COLOR_RGBALPHA, 8, _width, _height);
try

png.Canvas.Brush.Color := Col;
png.Canvas.FillRect(Rect(0,0,_width,_height));
for y := 0 to png.Height - 1 do
begin
sl := png.AlphaScanline[y];
FillChar(sl^, png.Width, Alpha);
end;

Picture.Assign(png);

finally
png.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;

procedure TForm1.EnableBlur;
const
WCA_ACCENT_POLICY = 19;
ACCENT_ENABLE_BLURBEHIND = 3;
DrawLeftBorder = $20;
DrawTopBorder = $40;
DrawRightBorder = $80;
DrawBottomBorder = $100;
var
dwm10: THandle;
data : TWinCompAttrData;
accent: AccentPolicy;
begin

dwm10 := LoadLibrary('user32.dll');
try
@SetWindowCompositionAttribute := GetProcAddress(dwm10, 'SetWindowCompositionAttribute');
if @SetWindowCompositionAttribute <> nil then
begin
accent.AccentState := ACCENT_ENABLE_BLURBEHIND ;
accent.AccentFlags := DrawLeftBorder or DrawTopBorder or DrawRightBorder or DrawBottomBorder;

data.Attribute := WCA_ACCENT_POLICY;
data.dataSize := SizeOf(accent);
data.pData := @accent;
SetWindowCompositionAttribute(Handle, data);
end
else
begin
ShowMessage('Not found Windows 10 blur API');
end;
finally
FreeLibrary(dwm10);
end;

end;

procedure TForm1.FormCreate(Sender: TObject);
var
BlendFunc: TBlendFunction;
bmp: TBitmap;
begin
DoubleBuffered := True;
Color := clBlack;
BorderStyle := bsNone;
if TaskbarTranslucent then
EnableBlur;

UpdateColorization;
(*BlendFunc.BlendOp := AC_SRC_OVER;
BlendFunc.BlendFlags := 0;
BlendFunc.SourceConstantAlpha := 96;
BlendFunc.AlphaFormat := AC_SRC_ALPHA;
bmp := TBitmap.Create;
try
bmp.SetSize(Width, Height);
bmp.Canvas.Brush.Color := clRed;
bmp.Canvas.FillRect(Rect(0,0,Width,Height));
Winapi.Windows.AlphaBlend(Canvas.Handle, 50,50,Width, Height,
bmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height, BlendFunc);
finally
bmp.Free;
end;*)
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture;
Perform(WM_SYSCOMMAND, $F012, 0);
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin

ReleaseCapture;
Perform(WM_SYSCOMMAND, $F012, 0);
end;


function TForm1.TaskbarAccented: boolean;
var
reg: TRegistry;
begin
Result := False;
reg := TRegistry.Create;
try
reg.RootKey := HKEY_CURRENT_USER;
reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize');
try
if reg.ReadInteger('ColorPrevalence') = 1 then
Result := True;
except
Result := False;
end;
reg.CloseKey;

finally
reg.Free;
end;
end;

function TForm1.TaskbarTranslucent: boolean;
var
reg: TRegistry;
begin
Result := False;
reg := TRegistry.Create;
try
reg.RootKey := HKEY_CURRENT_USER;
reg.OpenKeyReadOnly('SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize');
try
if reg.ReadInteger('EnableTransparency') = 1 then
Result := True;
except
Result := False;
end;
reg.CloseKey;

finally
reg.Free;
end;
end;

procedure TForm1.UpdateColorization;
begin
if TaskbarTranslucent then
begin
if TaskbarAccented then
SetAlphaColorPicture(BlendColors(GetAccentColor, clBlack, 50) , 222, Image1.Picture, 10, 10)
else
SetAlphaColorPicture(clblack, 222, Image1.Picture, 10,10 );
Image1.Align := alClient;
Image1.Stretch := True;
Image1.Visible := True;
end
else
Image1.Visible := False;

end;

function TForm1.GetAccentColor:TColor;
var
col: cardinal;
opaque: longbool;
newcolor: TColor;
a,r,g,b: byte;
begin
DwmGetColorizationColor(col, opaque);
a := Byte(col shr 24);
r := Byte(col shr 16);
g := Byte(col shr 8);
b := Byte(col);


newcolor := RGB(
round(r*(a/255)+255-a),
round(g*(a/255)+255-a),
round(b*(a/255)+255-a)
);

Result := newcolor;


end;

//Credits to Roy M Klever http://rmklever.com/?p=116
function TForm1.BlendColors(Col1, Col2: TColor; A: Byte): TColor;
var
c1,c2: LongInt;
r,g,b,v1,v2: byte;
begin
A := Round(2.55 * A);
c1 := ColorToRGB(Col1);
c2 := ColorToRGB(Col2);
v1 := Byte(c1);
v2 := Byte(c2);
r := A * (v1 - v2) shr 8 + v2;
v1 := Byte(c1 shr 8);
v2 := Byte(c2 shr 8);
g := A * (v1 - v2) shr 8 + v2;
v1 := Byte(c1 shr 16);
v2 := Byte(c2 shr 16);
b := A * (v1 - v2) shr 8 + v2;
Result := (b shl 16) + (g shl 8) + r;
end;

procedure TForm1.WndProc(var Message: TMessage);
//const
// WM_DWMCOLORIZATIONCOLORCHANGED = $0320;
begin
if Message.Msg = WM_DWMCOLORIZATIONCOLORCHANGED then
begin
UpdateColorization;
end;
inherited WndProc(Message);

end;

initialization
SetWindowCompositionAttribute := GetProcAddress(GetModuleHandle(user32), 'SetWindowCompositionAttribute');
end.

这是source code and demo binary希望有帮助。

希望有更好的方法,如果有,请告诉我们。

顺便说一句,在 C# 和 WPF 上更容易,但这些应用程序在冷启动时非常慢。

[奖励更新]或者,在 Windows 10 April 2018 Update 或更高版本(可能适用于 Fall Creators Update)上,您可以使用背后的丙烯酸模糊,其使用方式如下:

const ACCENT_ENABLE_ACRYLICBLURBEHIND = 4;
...
accent.AccentState := ACCENT_ENABLE_ACRYLICBLURBEHIND;
// $AABBGGRR
accent.GradientColor := (opacity SHL 24) or (clRed);

但是如果执行 WM_NCCALCSIZE,这可能不起作用,即仅适用于 bsNone 边框样式或避免 WM_NCALCSIZE。请注意,包括着色,无需手动绘制。

关于winapi - 如何在 Windows 10 上设置玻璃混合颜色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32724187/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com