lazarus-ccr/components/mbColorLib/HRingPicker.pas

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.