
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5596 8e941d3f-bd1b-0410-a28a-d453659cc2b4
330 lines
7.3 KiB
ObjectPascal
330 lines
7.3 KiB
ObjectPascal
unit HRingPicker;
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE DELPHI}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
LCLIntf, LCLType, SysUtils, Classes, Controls, Graphics, Math, Forms,
|
|
HTMLColors, mbColorConv, mbColorPickerControl;
|
|
|
|
type
|
|
THRingPicker = class(TmbHSLVColorPickerControl)
|
|
private
|
|
FSelectedColor: TColor;
|
|
FHueLineColor: TColor;
|
|
FRadius: integer;
|
|
procedure SetRadius(r: integer);
|
|
procedure SetHueLineColor(c: TColor);
|
|
protected
|
|
procedure CreateGradient; override;
|
|
procedure DrawHueLine;
|
|
function GetGradientColor2D(X, Y: Integer): TColor; override;
|
|
function GetSelectedColor: TColor; override;
|
|
// function MouseOnPicker(X, Y: Integer): Boolean;
|
|
procedure Paint; override;
|
|
procedure Resize; override;
|
|
procedure SelectColor(x, y: integer); override;
|
|
procedure SetRelHue(H: Double); override;
|
|
procedure SetSelectedColor(c: TColor); override;
|
|
procedure UpdateCoords;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
function GetColorAtPoint(x, y: integer): TColor; override;
|
|
property ColorUnderCursor;
|
|
published
|
|
property Hue default 0;
|
|
property Luminance default 127;
|
|
property Saturation default 255;
|
|
property Value default 255;
|
|
property MaxHue default 360;
|
|
property MaxLuminance default 255;
|
|
property MaxSaturation default 255;
|
|
property MaxValue default 255;
|
|
property HueLineColor: TColor read FHueLineColor write SetHueLineColor default clGray;
|
|
property Radius: integer read FRadius write SetRadius default 40;
|
|
property SelectedColor default clRed; //clNone;
|
|
property OnChange;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
mbUtils;
|
|
|
|
{ THRingPicker }
|
|
|
|
constructor THRingPicker.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
SetInitialBounds(0, 0, 204, 204);
|
|
FHue := 0.0;
|
|
FVal := 1.0;
|
|
FLum := 0.5;
|
|
FSat := 1.0;
|
|
SetSelectedColor(clRed);
|
|
FRadius := 40;
|
|
FHueLineColor := clGray;
|
|
HintFormat := 'Hue: %h (selected)';
|
|
TabStop := true;
|
|
end;
|
|
|
|
procedure THRingPicker.CreateGradient;
|
|
begin
|
|
FGradientWidth := Min(Width, Height);
|
|
FGradientHeight := FGradientWidth;
|
|
inherited;
|
|
end;
|
|
|
|
procedure THRingPicker.DrawHueLine;
|
|
var
|
|
angle: double;
|
|
sinAngle, cosAngle: Double;
|
|
radius: integer;
|
|
begin
|
|
radius := Min(Width, Height) div 2;
|
|
if (FHue >= 0) and (FHue <= 1.0) then
|
|
begin
|
|
angle := -FHue * TWO_PI;
|
|
SinCos(angle, sinAngle, cosAngle);
|
|
Canvas.Pen.Color := FHueLineColor;
|
|
Canvas.MoveTo(radius, radius);
|
|
Canvas.LineTo(radius + round(radius*cosAngle), radius + round(radius*sinAngle));
|
|
end;
|
|
end;
|
|
|
|
function THRingPicker.GetColorAtPoint(x, y: integer): TColor;
|
|
var
|
|
angle: Double;
|
|
dx, dy, radius: integer;
|
|
h: Double;
|
|
begin
|
|
radius := Min(Width, Height) div 2;
|
|
|
|
if PointInCircle(Point(x, y), Min(Width, Height)) then
|
|
begin
|
|
dx := x - Radius;
|
|
dy := y - Radius;
|
|
angle := 360 + 180 * arctan2(-dy, dx) / pi;
|
|
if angle < 0 then
|
|
angle := angle + 360
|
|
else if angle > 360 then
|
|
angle := angle - 360;
|
|
h := angle / 360;
|
|
Result := HSLVtoColor(h, FSat, FLum, FVal);
|
|
end
|
|
else
|
|
Result := clNone;
|
|
end;
|
|
|
|
{ Outer loop: Y, Inner loop: X }
|
|
function THRingPicker.GetGradientColor2D(X, Y: Integer): TColor;
|
|
var
|
|
dx, dy: Integer;
|
|
dSq, rSq: Integer;
|
|
radius, size: Integer;
|
|
H: Double;
|
|
begin
|
|
size := FGradientWidth; // or Height, they are the same...
|
|
radius := size div 2;
|
|
rSq := sqr(radius);
|
|
dx := X - radius;
|
|
dy := Y - radius;
|
|
dSq := sqr(dx) + sqr(dy);
|
|
if dSq <= rSq then
|
|
begin
|
|
H := 180 * (1 + arctan2(dx, dy) / pi); // wp: order (x,y) is correct!
|
|
H := H + 90;
|
|
if H > 360 then H := H - 360;
|
|
Result := HSLVtoColor(H/360, FSat, FLum, FVal);
|
|
if WebSafe then
|
|
Result := GetWebSafe(Result);
|
|
end else
|
|
Result := GetDefaultColor(dctBrush);
|
|
end;
|
|
|
|
function THRingPicker.GetSelectedColor: TColor;
|
|
begin
|
|
if FSelectedColor <> clNone then
|
|
Result := HSLVtoColor(FHue, FSat, FLum, FVal)
|
|
else
|
|
Result := clNone;
|
|
end;
|
|
{
|
|
function THRingPicker.MouseOnPicker(X, Y: Integer): Boolean;
|
|
var
|
|
diameter, r: Integer;
|
|
P, ctr: TPoint;
|
|
begin
|
|
diameter := Min(Width, Height);
|
|
r := diameter div 2; // outer radius
|
|
P := Point(x, y);
|
|
ctr := Point(r, r);
|
|
Result := PtInCircle(P, ctr, r) and not PtInCircle(P, ctr, Radius);
|
|
end; }
|
|
|
|
procedure THRingPicker.Paint;
|
|
var
|
|
rgn, r1, r2: HRGN;
|
|
r: TRect;
|
|
size: Integer;
|
|
ringwidth: Integer;
|
|
begin
|
|
PaintParentBack(Canvas);
|
|
size := Min(Width, Height); // diameter of circle
|
|
ringwidth := size div 2 - FRadius; // FRadius is inner radius
|
|
r := ClientRect;
|
|
r.Right := R.Left + size;
|
|
R.Bottom := R.Top + size;
|
|
InflateRect(R, -1, -1); // Remove spurious black pixels at the border
|
|
r1 := CreateEllipticRgnIndirect(R);
|
|
if ringwidth > 0 then
|
|
begin
|
|
rgn := r1;
|
|
InflateRect(R, -ringwidth, - ringwidth);
|
|
r2 := CreateEllipticRgnIndirect(R);
|
|
CombineRgn(rgn, r1, r2, RGN_DIFF);
|
|
end;
|
|
SelectClipRgn(Canvas.Handle, rgn);
|
|
Canvas.Draw(0, 0, FBufferBmp);
|
|
DeleteObject(rgn);
|
|
DrawHueLine;
|
|
DoChange;
|
|
end;
|
|
|
|
procedure THRingPicker.Resize;
|
|
begin
|
|
inherited;
|
|
if Min(Width, Height) <> FGradientWidth then
|
|
CreateGradient;
|
|
UpdateCoords;
|
|
end;
|
|
|
|
procedure THRingPicker.SelectColor(x, y: integer);
|
|
var
|
|
angle, dx, dy, Radius: integer;
|
|
begin
|
|
mx := y;
|
|
my := y;
|
|
FSelectedColor := clWhite;
|
|
radius := Min(Width, Height) div 2;
|
|
dx := x - radius;
|
|
dy := y - radius;
|
|
angle := round(360 + 180*arctan2(-dy, dx) / pi);
|
|
SetRelHue(angle/360);
|
|
end;
|
|
|
|
procedure THRingPicker.SetHueLineColor(c: TColor);
|
|
begin
|
|
if FHueLineColor <> c then
|
|
begin
|
|
FHueLineColor := c;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure THRingPicker.SetRadius(r: integer);
|
|
begin
|
|
if FRadius <> r then
|
|
begin
|
|
FRadius := r;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure THRingPicker.SetRelHue(H: Double);
|
|
begin
|
|
if H > 1 then H := H - 1;
|
|
if H < 0 then H := H + 1;
|
|
if FHue <> h then
|
|
begin
|
|
FHue := h;
|
|
UpdateCoords;
|
|
Invalidate;
|
|
DoChange;
|
|
end;
|
|
end;
|
|
|
|
(*
|
|
procedure THRingPicker.SetSat(s: integer);
|
|
begin
|
|
Clamp(s, 0, FMaxSat);
|
|
if Saturation <> s then
|
|
begin
|
|
FSat := s / FMaxSat;
|
|
UpdateCoords;
|
|
Invalidate;
|
|
DoChange;
|
|
end;
|
|
end;
|
|
*)
|
|
procedure THRingPicker.SetSelectedColor(c: TColor);
|
|
var
|
|
H: Double = 0;
|
|
S: Double = 0;
|
|
L: Double = 0;
|
|
V: Double = 0;
|
|
needNewGradient: Boolean;
|
|
begin
|
|
if WebSafe then
|
|
c := GetWebSafe(c);
|
|
if c = GetSelectedColor then
|
|
Exit;
|
|
|
|
ColorToHSLV(c, H, S, L, V);
|
|
case BrightnessMode of
|
|
bmLuminance:
|
|
begin
|
|
needNewGradient := (S <> FSat) or (L <> FLum);
|
|
FLum := L;
|
|
end;
|
|
bmValue:
|
|
begin
|
|
needNewGradient := (S <> FSat) or (V <> FVal);
|
|
FVal := V;
|
|
end;
|
|
end;
|
|
FHue := h;
|
|
FSat := s;
|
|
UpdateCoords;
|
|
if needNewGradient then
|
|
CreateGradient;
|
|
Invalidate;
|
|
DoChange;
|
|
end;
|
|
|
|
(*
|
|
procedure THRingPicker.SetVal(v: integer);
|
|
begin
|
|
Clamp(v, 0, FMaxVal);
|
|
if Value <> V then
|
|
begin
|
|
FVal := V / FMaxVal;
|
|
if BrightnessMode = bmValue then
|
|
begin
|
|
CreateGradient;
|
|
Invalidate;
|
|
end;
|
|
DoChange;
|
|
end;
|
|
end;
|
|
*)
|
|
procedure THRingPicker.UpdateCoords;
|
|
var
|
|
r, angle: double;
|
|
radius: integer;
|
|
sinAngle, cosAngle: Double;
|
|
begin
|
|
radius := Min(Width, Height) div 2;
|
|
r := -radius * FSat;
|
|
angle := -(FHue * 2 + 1) * pi;
|
|
SinCos(angle, sinAngle, cosAngle);
|
|
mx := round(cosAngle * r) + radius;
|
|
my := round(sinAngle * r) + radius;
|
|
end;
|
|
|
|
end.
|