{
     Apophysis Copyright (C) 2001-2004 Mark Townsend

     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}
unit Gradient;

interface


uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, ComCtrls, Registry, cmap, Menus, ToolWin, Buttons,
  AppEvnts;
const
  PixelCountMax = 32768;

type
  pRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..PixelCountMax - 1] of TRGBTriple;

type
  TGradientForm = class(TForm)
    pnlPalette: TPanel;
    pnlControls: TPanel;
    cmbPalette: TComboBox;
    GradientImage: TImage;
    PopupMenu: TPopupMenu;
    mnuReverse: TMenuItem;
    mnuInvert: TMenuItem;
    btnMenu: TSpeedButton;
    Popup: TPopupMenu;
    mnuHue: TMenuItem;
    mnuRotate: TMenuItem;
    N1: TMenuItem;
    mnuSaturation: TMenuItem;
    mnuBrightness: TMenuItem;
    N2: TMenuItem;
    ScrollBar: TScrollBar;
    lblVal: TLabel;
    mnuBlur: TMenuItem;
    btnOpen: TSpeedButton;
    N3: TMenuItem;
    mnuGradientBrowser: TMenuItem;
    mnuSmoothPalette: TMenuItem;
    btnSmoothPalette: TSpeedButton;
    N4: TMenuItem;
    SaveGradient1: TMenuItem;
    SaveasMapfile1: TMenuItem;
    SaveDialog: TSaveDialog;
    Label1: TLabel;
    btnPaste: TSpeedButton;
    btnCopy: TSpeedButton;
    N5: TMenuItem;
    mnuCopy: TMenuItem;
    mnuPaste: TMenuItem;
    ApplicationEvents: TApplicationEvents;
    mnuSaveasDefault: TMenuItem;
    N6: TMenuItem;
    mnuRandomize: TMenuItem;
    N7: TMenuItem;
    mnuFrequency: TMenuItem;
    Contrast1: TMenuItem;
    procedure cmbPaletteChange(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnApplyClick(Sender: TObject);
    procedure DrawPalette;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure mnuReverseClick(Sender: TObject);
    procedure mnuInvertClick(Sender: TObject);
    procedure btnMenuClick(Sender: TObject);
    procedure mnuRotateClick(Sender: TObject);
    procedure mnuHueClick(Sender: TObject);
    procedure mnuSaturationClick(Sender: TObject);
    procedure ScrollBarChange(Sender: TObject);
    procedure mnuBrightnessClick(Sender: TObject);
    procedure mnuBlurClick(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure mnuSmoothPaletteClick(Sender: TObject);
    procedure SaveGradient1Click(Sender: TObject);
    procedure SaveasMapfile1Click(Sender: TObject);
    procedure cmbPaletteDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure ScrollBarScroll(Sender: TObject; ScrollCode: TScrollCode;
      var ScrollPos: Integer);
    procedure btnCopyClick(Sender: TObject);
    procedure btnPasteClick(Sender: TObject);
    procedure ApplicationEventsActivate(Sender: TObject);
    procedure mnuSaveasDefaultClick(Sender: TObject);
    procedure mnuRandomizeClick(Sender: TObject);
    procedure mnuFrequencyClick(Sender: TObject);
    procedure Contrast1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    procedure Apply;
    function Blur(const radius: integer; const pal: TColorMap): TColorMap;
    function Frequency(const times: Integer; const pal: TColorMap): TColorMap;
    procedure SaveMap(FileName: string);
  public
    Palette: TColorMap;
    BackupPal: TColorMap;
    procedure UpdateGradient(Pal: TColorMap);
    function RandomGradient: TColorMap;
  end;

var
  GradientForm: TGradientForm;
  pCmap: integer;

function GradientInClipboard: boolean;
procedure RGBToHSV(R, G, B: byte; var H, S, V: real);
procedure HSVToRGB(H, S, V: real; var Rb, Gb, Bb: integer);

implementation

uses Main, cmapdata, Math, Browser, Editor, Global, Save, Adjust, Mutate, ClipBrd;

{$R *.DFM}

procedure TGradientForm.Apply;
begin
  MainForm.StopThread;
  MainForm.UpdateUndo;
  MainCp.CmapIndex := cmbPalette.ItemIndex;
  MainCp.cmap := Palette;
  if EditForm.visible then EditForm.UpdateDisplay;
//  if AdjustForm.visible then AdjustForm.UpdateDisplay;
  if MutateForm.Visible then MutateForm.UpdateDisplay;
  MainForm.RedrawTimer.enabled := true;
end;

procedure TGradientForm.SaveMap(FileName: string);
var
  i: Integer;
  l: string;
  MapFile: TextFile;
begin
{ Save a map file }
  AssignFile(MapFile, FileName);
  try
    ReWrite(MapFile);
    { first line with comment }
    l := Format(' %3d %3d %3d  Exported from Apophysis 2.0', [Palette[0][0], palette[0][1],
      palette[0][2]]);
    Writeln(MapFile, l);
    { now the rest }
    for i := 1 to 255 do
    begin
      l := Format(' %3d %3d %3d', [Palette[i][0], palette[i][1],
        palette[i][2]]);
      Writeln(MapFile, l);
    end;
    CloseFile(MapFile);
  except
    on EInOutError do Application.MessageBox(PChar('Cannot Open File: ' +
        FileName), 'Apophysis', 16);
  end;
end;

procedure TGradientForm.UpdateGradient(Pal: TColorMap);
begin
  Palette := Pal;
  BackupPal := Pal;
  DrawPalette;
  ScrollBar.Position := 0;
end;

procedure HSVToRGB(H, S, V: real; var Rb, Gb, Bb: integer);
var
  R, G, B, Sa, Va, Hue, i, f, p, q, t: real;
begin
  R := 0;
  G := 0;
  B := 0;
  Sa := S / 100;
  Va := V / 100;
  if S = 0 then
  begin
    R := Va;
    G := Va;
    B := Va;
  end
  else
  begin
    Hue := H / 60;
    if Hue = 6 then Hue := 0;
    i := Int(Hue);
    f := Hue - i;
    p := Va * (1 - Sa);
    q := Va * (1 - (Sa * f));
    t := Va * (1 - (Sa * (1 - f)));
    case Round(i) of
      0: begin
          R := Va;
          G := t;
          B := p;
        end;
      1: begin
          R := q;
          G := Va;
          B := p;
        end;
      2: begin
          R := p;
          G := Va;
          B := t;
        end;
      3: begin
          R := p;
          G := q;
          B := Va;
        end;
      4: begin
          R := t;
          G := p;
          B := Va;
        end;
      5: begin
          R := Va;
          G := p;
          B := q;
        end;
    end;
  end;
  Rb := Round(Int(255.9999 * R));
  Gb := Round(Int(255.9999 * G));
  Bb := Round(Int(255.9999 * B));
end;

procedure RGBToHSV(R, G, B: byte; var H, S, V: real);
var
  vRed, vGreen, vBlue, Mx, Mn, Va, Sa, rc, gc, bc: real;
begin
  vRed := R / 255;
  vGreen := G / 255;
  vBlue := B / 255;
  Mx := vRed;
  if vGreen > Mx then Mx := vGreen;
  if vBlue > Mx then Mx := vBlue;
  Mn := vRed;
  if vGreen < Mn then Mn := vGreen;
  if vBlue < Mn then Mn := vBlue;
  Va := Mx;
  if Mx <> 0 then
    Sa := (Mx - Mn) / Mx
  else
    Sa := 0;
  if Sa = 0 then
    H := 0
  else
  begin
    rc := (Mx - vRed) / (Mx - Mn);
    gc := (Mx - vGreen) / (Mx - Mn);
    bc := (Mx - vBlue) / (Mx - Mn);
    if Mx = vRed then
      H := bc - gc
    else if Mx = vGreen then
      H := 2 + rc - bc
    else if Mx = vBlue then
      H := 4 + gc - rc;
    H := H * 60;
    if H < 0 then H := H + 360;
  end;
  S := Sa * 100;
  V := Va * 100;
end;

function TGradientForm.Blur(const Radius: Integer; const pal: TColorMap): TColorMap;
var
  r, g, b, n, i, j, k: Integer;
begin
  Result := Pal;
  if Radius <> 0 then
    for i := 0 to 255 do
    begin
      n := -1;
      r := 0;
      g := 0;
      b := 0;
      for j := i - radius to i + radius do
      begin
        inc(n);
        k := (256 + j) mod 256;
        if k <> i then begin
          r := r + Pal[k][0];
          g := g + Pal[k][1];
          b := b + Pal[k][2];
        end;
      end;
      if n <> 0 then begin
        Result[i][0] := r div n;
        Result[i][1] := g div n;
        Result[i][2] := b div n;
      end;
    end;
end;

function TGradientForm.Frequency(const times: Integer; const pal: TColorMap): TColorMap;
{ This can be improved }
var
  n, i, j: Integer;
begin
  Result := Pal;
  if times <> 1 then
  begin
    n := 256 div times;
    for j := 0 to times do
      for i := 0 to n do
      begin
        if (i + j * n) < 256 then
        begin
          Result[i + j * n][0] := pal[i * times][0];
          Result[i + j * n][1] := pal[i * times][1];
          Result[i + j * n][2] := pal[i * times][2];
        end;
      end;
  end;
end;

procedure TGradientForm.DrawPalette;
var
  i, j: integer;
  Row: pRGBTripleArray;
  BitMap: TBitMap;
begin
  BitMap := TBitMap.Create;
  try
    Bitmap.PixelFormat := pf24bit;
    BitMap.Width := 256;
    BitMap.Height := 1;
    for j := 0 to Bitmap.Height - 1 do
    begin
      Row := Bitmap.Scanline[j];
      for i := 0 to Bitmap.Width - 1 do
      begin
        with Row[i] do
        begin
          rgbtRed := Palette[i][0];
          rgbtGreen := Palette[i][1];
          rgbtBlue := Palette[i][2];
        end
      end
    end;
    GradientImage.Picture.Graphic := Bitmap;
    GradientImage.Refresh;
  finally
    BitMap.Free;
  end;
end;

procedure TGradientForm.cmbPaletteChange(Sender: TObject);
var
  i: integer;
begin
  i := cmbPalette.ItemIndex;
  GetCmap(i, 1, Palette);
  BackupPal := Palette;
  ScrollBar.Position := 0;
  DrawPalette;
  Apply;
end;

procedure TGradientForm.FormShow(Sender: TObject);
var
  Registry: TRegistry;
begin
  { Read posution from registry }
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    if Registry.OpenKey('Software\' + APP_NAME + '\Forms\Gradient', False) then
    begin
      if Registry.ValueExists('Left') then
        GradientForm.Left := Registry.ReadInteger('Left');
      if Registry.ValueExists('Top') then
        GradientForm.Top := Registry.ReadInteger('Top');
    end;
    Registry.CloseKey;
  finally
    Registry.Free;
  end;
  DrawPalette;
end;

procedure TGradientForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
var
  Registry: TRegistry;
begin
  { Write position to registry }
  Registry := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CURRENT_USER;
    { Defaults }
    if Registry.OpenKey('\Software\' + APP_NAME + '\Forms\Gradient', True) then
    begin
      Registry.WriteInteger('Top', GradientForm.Top);
      Registry.WriteInteger('Left', GradientForm.Left);
    end;
  finally
    Registry.Free;
  end;
end;


procedure TGradientForm.btnApplyClick(Sender: TObject);
begin
  Apply;
end;

procedure TGradientForm.mnuReverseClick(Sender: TObject);
var
  i: integer;
  pal: TColorMap;
begin
  for i := 0 to 255 do begin
    pal[i][0] := Palette[255 - i][0];
    pal[i][1] := Palette[255 - i][1];
    pal[i][2] := Palette[255 - i][2];
  end;
  UpdateGradient(pal);
  Apply;
end;

procedure TGradientForm.mnuInvertClick(Sender: TObject);
var
  i: integer;
begin
  for i := 0 to 255 do
  begin
    Palette[i][0] := 255 - Palette[i][0];
    Palette[i][1] := 255 - Palette[i][1];
    Palette[i][2] := 255 - Palette[i][2];
  end;
  UpdateGradient(palette);
  Apply;
end;

procedure TGradientForm.btnMenuClick(Sender: TObject);
begin
  Popup.Popup(btnMenu.ClientOrigin.x, btnMenu.ClientOrigin.y + btnMenu.Height);
end;

procedure TGradientForm.ScrollBarChange(Sender: TObject);
var
  intens, i, r, g, b: integer;
  h, s, v: real;
begin
  lblVal.Caption := IntToStr(ScrollBar.Position);
  if btnMenu.Caption = 'Hue' then
  begin
    for i := 0 to 255 do
    begin
      RGBToHSV(BackupPal[i][0], BackupPal[i][1], BackupPal[i][2], h, s, v);
      h := Round(360 + h + ScrollBar.Position) mod 360;
      HSVToRGB(h, s, v, Palette[i][0], Palette[i][1], Palette[i][2]);
    end;
  end;
  if btnMenu.Caption = 'Saturation' then
  begin
    for i := 0 to 255 do
    begin
      RGBToHSV(BackupPal[i][0], BackupPal[i][1], BackupPal[i][2], h, s, v);
      s := s + ScrollBar.Position;
      if s > 100 then s := 100;
      if s < 0 then s := 0;
      HSVToRGB(h, s, v, Palette[i][0], Palette[i][1], Palette[i][2]);
    end;
  end;
  if btnMenu.Caption = 'Contrast' then
  begin
    intens := scrollBar.Position;
    if intens > 0 then intens := intens * 2;
    for i := 0 to 255 do
    begin
      r := BackupPal[i][0];
      g := BackupPal[i][1];
      b := BackupPal[i][2];
      r := round(r + intens / 100 * (r - 127));
      g := round(g + intens / 100 * (g - 127));
      b := round(b + intens / 100 * (b - 127));
      if R > 255 then R := 255 else if R < 0 then R := 0;
      if G > 255 then G := 255 else if G < 0 then G := 0;
      if B > 255 then B := 255 else if B < 0 then B := 0;
      Palette[i][0] := r;
      Palette[i][1] := g;
      Palette[i][2] := b;
    end;
  end;
  if btnMenu.Caption = 'Brightness' then
  begin
    for i := 0 to 255 do
    begin
      Palette[i][0] := BackupPal[i][0] + ScrollBar.Position;
      if Palette[i][0] > 255 then Palette[i][0] := 255;
      if Palette[i][0] < 0 then Palette[i][0] := 0;
      Palette[i][1] := BackupPal[i][1] + ScrollBar.Position;
      if Palette[i][1] > 255 then Palette[i][1] := 255;
      if Palette[i][1] < 0 then Palette[i][1] := 0;
      Palette[i][2] := BackupPal[i][2] + ScrollBar.Position;
      if Palette[i][2] > 255 then Palette[i][2] := 255;
      if Palette[i][2] < 0 then Palette[i][2] := 0;
    end;
  end;
  if btnMenu.Caption = 'Rotate' then
  begin
    for i := 0 to 255 do
    begin
      Palette[i][0] := BackupPal[(255 + i - ScrollBar.Position) mod 256][0];
      Palette[i][1] := BackupPal[(255 + i - ScrollBar.Position) mod 256][1];
      Palette[i][2] := BackupPal[(255 + i - ScrollBar.Position) mod 256][2];
    end;
  end;
  if btnMenu.Caption = 'Blur' then
  begin
    Palette := Blur(ScrollBar.Position, BackupPal);
  end;
  if btnMenu.Caption = 'Frequency' then
  begin
    Palette := Frequency(ScrollBar.Position, BackupPal);
  end;
  DrawPalette;
end;

{ ***************************** Adjust menu ********************************* }

procedure TGradientForm.mnuRotateClick(Sender: TObject);
begin
  btnMenu.Caption := 'Rotate';
  BackupPal := Palette;
  ScrollBar.Min := 0;
  ScrollBar.Max := 255;
  ScrollBar.LargeChange := 15;
  ScrollBar.Position := 0;
end;

procedure TGradientForm.mnuHueClick(Sender: TObject);
begin
  btnMenu.Caption := 'Hue';
  BackupPal := Palette;
  ScrollBar.Min := 0;
  ScrollBar.Max := 360;
  ScrollBar.LargeChange := 15;
  ScrollBar.Position := 0;
end;

procedure TGradientForm.mnuBrightnessClick(Sender: TObject);
begin
  btnMenu.Caption := 'Brightness';
  BackupPal := Palette;
  ScrollBar.Min := -255;
  ScrollBar.Max := 255;
  ScrollBar.LargeChange := 15;
  ScrollBar.Position := 0;
end;

procedure TGradientForm.mnuSaturationClick(Sender: TObject);
begin
  btnMenu.Caption := 'Saturation';
  BackupPal := Palette;
  ScrollBar.Min := -100;
  ScrollBar.Max := 100;
  ScrollBar.LargeChange := 15;
  ScrollBar.Position := 0;
end;

procedure TGradientForm.mnuBlurClick(Sender: TObject);
begin
  btnMenu.Caption := 'Blur';
  BackupPal := Palette;
  ScrollBar.Min := 0;
  ScrollBar.Max := 127;
  ScrollBar.LargeChange := 15;
  ScrollBar.Position := 0;
end;

procedure TGradientForm.mnuFrequencyClick(Sender: TObject);
begin
  btnMenu.Caption := 'Frequency';
  BackupPal := Palette;
  ScrollBar.Min := 1;
  ScrollBar.Max := 10;
  ScrollBar.LargeChange := 1;
  ScrollBar.Position := 1;
end;

procedure TGradientForm.btnOpenClick(Sender: TObject);
begin
  GradientBrowser.Filename := GradientFile;
  GradientBrowser.Show;
end;

procedure TGradientForm.mnuSmoothPaletteClick(Sender: TObject);
begin
  MainForm.SmoothPalette;
end;

procedure TGradientForm.SaveGradient1Click(Sender: TObject);
var
  gradstr: TStringList;
begin
  gradstr := TStringList.Create;
  try
    SaveForm.Caption := 'Save Gradient';
    SaveForm.Filename := GradientFile;
    SaveForm.Title := MainCp.name;
    if SaveForm.ShowModal = mrOK then
    begin
      gradstr.add(CleanIdentifier(SaveForm.Title) + ' {');
      gradstr.add(MainForm.GradientFromPalette(Palette, SaveForm.Title));
      gradstr.add('}');
      if MainForm.SaveGradient(gradstr.text, SaveForm.Title, SaveForm.Filename) then
        GradientFile := SaveForm.FileName;
    end;
  finally
    gradstr.free
  end;
end;

procedure TGradientForm.SaveasMapfile1Click(Sender: TObject);
begin
  SaveDialog.Filename := MainCp.name + '.map';
  if SaveDialog.execute then
    SaveMap(SaveDialog.Filename);
end;

procedure TGradientForm.cmbPaletteDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  i, j: integer;
  Row: pRGBTripleArray;
  Bitmap: TBitmap;
  pal: TColorMap;
  PalName: string;
begin
{ Draw the preset palettes on the combo box items }
  GetCMap(index, 1, pal);
  GetCmapName(index, PalName);

  BitMap := TBitMap.create;
  Bitmap.PixelFormat := pf24bit;
  BitMap.Width := 256;
  BitMap.Height := 100;

  for j := 0 to Bitmap.Height - 1 do
  begin
    Row := Bitmap.Scanline[j];
    for i := 0 to Bitmap.Width - 1 do
    begin
      with Row[i] do
      begin
        rgbtRed := Pal[i][0];
        rgbtGreen := Pal[i][1];
        rgbtBlue := Pal[i][2];
      end
    end
  end;
  with Control as TComboBox do
  begin
    Canvas.Rectangle(Rect);

    Canvas.TextOut(4, Rect.Top, PalName);
    Rect.Left := (Rect.Left + rect.Right) div 2;
    Canvas.StretchDraw(Rect, Bitmap);
  end;
  BitMap.Free;
end;

procedure TGradientForm.ScrollBarScroll(Sender: TObject;
  ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
  if ScrollCode = scEndScroll then Apply;
end;

procedure TGradientForm.btnCopyClick(Sender: TObject);
var
  gradstr: TStringList;
begin
  gradstr := TStringList.Create;
  try
    gradstr.add(CleanIdentifier(MainCp.name) + ' {');
    gradstr.add('gradient:');
    gradstr.add(' title="' + MainCp.name + '" smooth=no');
    gradstr.add(GradientString(Palette));
    gradstr.add('}');
    Clipboard.SetTextBuf(PChar(gradstr.text));
    btnPaste.enabled := true;
    mnuPaste.enabled := true;
    MainForm.btnPaste.enabled := False;
    MainForm.mnuPaste.enabled := False;
  finally
    gradstr.free
  end;
end;

procedure TGradientForm.btnPasteClick(Sender: TObject);
begin
  if Clipboard.HasFormat(CF_TEXT) then
  begin
    UpdateGradient(CreatePalette(Clipboard.AsText));
    Apply;
  end;
end;

function GradientInClipboard: boolean;
var
  gradstr: TStringList;
begin
  { returns true if gradient in clipboard - can be tricked }
  result := true;
  if Clipboard.HasFormat(CF_TEXT) then
  begin
    gradstr := TStringList.Create;
    try
      gradstr.text := Clipboard.AsText;
      if (Pos('}', gradstr.text) = 0) or (Pos('{', gradstr.text) = 0) or
        (Pos('gradient:', gradstr.text) = 0) or (Pos('fractal:', gradstr.text) <> 0) then
      begin
        result := false;
        exit;
      end;
    finally
      gradstr.free;
    end;
  end
  else
    result := false;
end;

procedure TGradientForm.ApplicationEventsActivate(Sender: TObject);
begin
  if GradientInClipboard then begin
    mnuPaste.enabled := true;
    btnPaste.enabled := true;
  end
  else
  begin
    mnuPaste.enabled := false;
    btnPaste.enabled := false;
  end;
end;

procedure TGradientForm.mnuSaveasDefaultClick(Sender: TObject);
begin
  MainForm.DefaultPalette := Palette;
  SaveMap(AppPath + 'default.map');
end;

procedure RGBBlend(a, b: integer; var Palette: TColorMap);
{ Linear blend between to indices of a palette }
var
  c, v: real;
  vrange, range: real;
  i: integer;
begin
  if a = b then
  begin
    Exit;
  end;
  range := b - a;
  vrange := Palette[b mod 256][0] - Palette[a mod 256][0];
  c := Palette[a mod 256][0];
  v := vrange / range;
  for i := (a + 1) to (b - 1) do
  begin
    c := c + v;
    Palette[i mod 256][0] := Round(c);
  end;
  vrange := Palette[b mod 256][1] - Palette[a mod 256][1];
  c := Palette[a mod 256][1];
  v := vrange / range;
  for i := a + 1 to b - 1 do
  begin
    c := c + v;
    Palette[i mod 256][1] := Round(c);
  end;
  vrange := Palette[b mod 256][2] - Palette[a mod 256][2];
  c := Palette[a mod 256][2];
  v := vrange / range;
  for i := a + 1 to b - 1 do
  begin
    c := c + v;
    Palette[i mod 256][2] := Round(c);
  end;
end;

function TGradientForm.RandomGradient: TColorMap;
var
  a, b, n, nodes: integer;
  rgb: array[0..2] of double;
  hsv: array[0..2] of double;
  pal: TColorMap;
begin
  inc(MainForm.Seed);
  RandSeed := MainForm.seed;
  nodes := random((MaxNodes - 1) - (MinNodes - 2)) + (MinNodes - 1);
  n := 256 div nodes;
  b := 0;
  hsv[0] := (random(MaxHue - (MinHue - 1)) + MinHue) / 100;
  hsv[1] := (random(MaxSat - (MinSat - 1)) + MinSat) / 100;
  hsv[2] := (random(MaxLum - (MinLum - 1)) + MinLum) / 100;
  hsv2rgb(hsv, rgb);
  Pal[0][0] := Round(rgb[0] * 255);
  Pal[0][1] := Round(rgb[1] * 255);
  Pal[0][2] := Round(rgb[2] * 255);
  repeat
    a := b;
    b := b + n;
    hsv[0] := (random(MaxHue - (MinHue - 1)) + MinHue) / 100;
    hsv[1] := (random(MaxSat - (MinSat - 1)) + MinSat) / 100;
    hsv[2] := (random(MaxLum - (MinLum - 1)) + MinLum) / 100;
    hsv2rgb(hsv, rgb);
    if b > 255 then b := 255;
    Pal[b][0] := Round(rgb[0] * 255);
    Pal[b][1] := Round(rgb[1] * 255);
    Pal[b][2] := Round(rgb[2] * 255);
    RGBBlend(a, b, pal);
  until b = 255;
  Result := Pal;
end;

procedure TGradientForm.mnuRandomizeClick(Sender: TObject);
begin
  GradientForm.UpdateGradient(RandomGradient);
  GradientForm.Apply;
end;

procedure TGradientForm.Contrast1Click(Sender: TObject);
begin
  btnMenu.Caption := 'Contrast';
  BackupPal := Palette;
  ScrollBar.Min := -100;
  ScrollBar.Max := 100;
  ScrollBar.LargeChange := 15;
  ScrollBar.Position := 0;
end;

procedure TGradientForm.FormCreate(Sender: TObject);
begin
  Sendmessage(cmbPalette.Handle, CB_SETDROPPEDWIDTH , cmbPalette.width * 2, 0);
end;

end.