Advertisement
Stella_209

AL_ToneCurve.pas

May 21st, 2018
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 25.86 KB | None | 0 0
  1. (*
  2.  
  3.   AL_ToneCurve : Interactive grafical Delphi component for modify a bitmap
  4.   ------------   caracteristic in R-G-B chanels (like PhotoShop)
  5.  
  6.   Original idea and source by: Roy Magne Klever
  7.                                rmklever@gmail.com
  8.                                http://www.rmklever.com
  9.                                Curve Tool : http://rmklever.com/?p=467
  10.  
  11.   LineStyle      New property the style of the lines between the cureve
  12.                  points.
  13.                  TToneCurveType = (cuvLinear, cuvSpline);
  14.  
  15.   imgView        A bitmap, where do the tone effects and get a histogram.
  16.  
  17.   ApplyCurve     The most important procedure: if íou non declered az
  18.                  imgView bitmap, you can gíve an outhern bitmap.
  19.  
  20.   This component has fix dimensions (width/height) = 272.
  21.  
  22.   Agócs László Hungary 2016
  23.   StellaSOFT
  24.   WEB       : http://stella.kojot.co.hu/
  25.   Email     : lagocsstella@gmail.com
  26.  
  27. *)
  28.  
  29. unit AL_ToneCurve;
  30.  
  31. interface
  32.  
  33. uses
  34.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  35.   Math, StdCtrls, ExtCtrls, ComCtrls, FileCtrl,
  36.   Szamok;
  37.  
  38. type
  39.  
  40.   PRGB_24 = ^TRGB_24;
  41.   TRGB_24 = record B, G, R: Byte; end;
  42.   PRGBArray = ^TRGBArray;
  43.   TRGBArray = array [Word] of TRGB_24;
  44.  
  45.   TToneCurveType = (cuvLinear, cuvSpline);
  46.  
  47. (*
  48.   TALCustomToneCurve = class(TCustomControl)
  49.   private
  50.   protected
  51.   public
  52.     constructor Create(AOwner: TComponent); override;
  53.     destructor Destroy; override;
  54.     procedure Paint; override;
  55.   published
  56.   end;
  57. *)
  58.  
  59.   TALToneCurve = class(TCustomControl)
  60.   private
  61.     FPresetPath: String;
  62.     FimgView: TBitmap;
  63.     FRepaint: TNotifyEvent;
  64.     FFileName: String;
  65.     FChannel: integer;
  66.     FLineType: TToneCurveType;
  67.     FColor: TColor;
  68.     FHistogram: boolean;
  69.     FPresetName: String;
  70.     procedure WMEraseBkGnd(var Message:TWMEraseBkGnd); message WM_ERASEBKGND;
  71.     procedure WMSize(var Msg: TWMSize); message WM_SIZE;
  72.     procedure SetFileName(const Value: String);
  73.     procedure SetChannel(const Value: integer);
  74.     procedure SetPresetPath(const Value: String);
  75.     procedure setLineType(const Value: TToneCurveType);
  76.     procedure SetColor(const Value: TColor);
  77.     procedure SetHistogram(const Value: boolean);
  78.   protected
  79.     BackBMP : TBitmap;          // The memory bitmap for drawing
  80.     nPts: Array[0..3] of Integer;
  81.     ptX, ptY: Array[0..3, 1..32] of Integer;
  82.     ptP, ptU: Array[0..3, 1..32] of Single;
  83.     nHist: Array[0..3, Byte] of Integer;
  84.     maxHist: Array[0..3] of Integer;
  85.     aPt, cIdx: Integer;
  86.     oldHistogram: boolean;
  87.     ImgLoaded: Boolean;
  88.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  89.       X, Y: Integer); override;
  90.     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  91.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  92.       X, Y: Integer); override;
  93.     function CompareNatural(s1, s2: string): Integer;
  94.     function SortMe(List: TStringList; i1, i2: Integer): Integer;
  95.     function Blend(Color1, Color2: TColor; A: Byte): TColor;
  96.     procedure BilinearRescale(Src, Dest: TBitmap);
  97.     function PtInCircle(cx, cy, x, y, radius: Integer): Boolean;
  98.     procedure WuLine(x1, y1, x2, y2: integer; Color: TColor);
  99.     procedure SetPandU;
  100.     function GetCurvePoint(i: Integer; v: Single): Single;
  101.     procedure DrawCurve;
  102.     procedure AddPoint(pt: TPoint);
  103.     procedure DelPoint(idx: Integer);
  104.     procedure GetHist;
  105.     function IsPoint(x, y: integer): integer;
  106.   public
  107.     LUT: Array[0..3, 0..255] of Byte;
  108.     Presets     : TStrings;
  109.     constructor Create(AOwner: TComponent);override;
  110.     destructor Destroy; override;
  111.     procedure Paint; override;
  112.     procedure ApplyCurve(Img: TBitmap);
  113.     procedure Reset;
  114.     procedure Invers;
  115.     procedure GetCurvesPreset(Files: TStrings);
  116.     procedure LoadPreset(Idx: Integer);
  117.     procedure SavePreset(fName: string);
  118.     function  AddPreset: string;
  119.   published
  120.     property Channel    : integer read FChannel write SetChannel;            // 0:RGB, 1-2-3: r-g-b
  121.     property Color      : TColor read FColor write SetColor default clWhite; // 0:RGB, 1-2-3: r-g-b
  122.     property PresetPath : String read FPresetPath write SetPresetPath;       // Preset directory
  123.     property Histogram  : boolean read FHistogram write SetHistogram;
  124.     property imgView    : TBitmap read FimgView write FimgView;              // Bitmap for tone curves
  125.     property FileName   : String read FFileName write SetFileName;
  126.     property LineType   : TToneCurveType read FLineType write setLineType;   // Linear/Spline
  127.     property PresetName : String read FPresetName write FPresetName;
  128.     property OnRepaint  : TNotifyEvent read FRepaint write FRepaint;
  129.   end;
  130.  
  131.   procedure Register;
  132.  
  133. implementation
  134.  
  135. constructor TALToneCurve.Create(AOwner: TComponent);
  136. var
  137.   bmp: TBitmap;
  138.   i, j: Integer;
  139. begin
  140.   inherited;
  141.   Presets := TStringList.Create;
  142.   BackBMP := TBitmap.Create;
  143.   BackBMP.Width := 1;
  144.   BackBMP.Height := 1;
  145.   BackBMP.Canvas.Pixels[0, 0] := $00C4C4C4;
  146.   bmp:= TBitmap.Create;
  147.   bmp.PixelFormat:= pf24bit;
  148.   bmp.Width:= 276;
  149.   bmp.Height:= 276;
  150.   BackBMP.Assign(bmp);
  151.   bmp.Free;
  152.   FLineType := cuvSpline;
  153.   FHistogram := True;
  154.   FChannel := 0;
  155.   cIdx:= 0;
  156.   aPt:= -1;
  157.   // Init start values
  158.   for i:= 0 to 3 do begin
  159.     nPts[i]:= 2;
  160.     ptX[i, 1]:= 0;
  161.     ptX[i, 2]:= 255;
  162.     ptY[i, 1]:= 0;
  163.     ptY[i, 2]:= 255;
  164.     for j:= 0 to 255 do LUT[i, j]:= j;
  165.   end;
  166.   FPresetPath:= ExtractFileDir(Application.ExeName) + '\Curves\';
  167.   GetCurvesPreset(Presets);
  168.   FPresetName := '';
  169.   Color := clWhite;
  170.   width := 272;
  171.   height := 272;
  172. end;
  173.  
  174. destructor TALToneCurve.Destroy;
  175. begin
  176.   BackBMP.Free;
  177.   Presets.Free;
  178.   inherited;
  179. end;
  180.  
  181. function TALToneCurve.CompareNatural(s1, s2: string): Integer;
  182.   function ExtractNr(n: Integer; var Txt: string): Int64;
  183.   begin
  184.     while (n <= Length(Txt)) and (Txt[n] >= '0') and (Txt[n] <= '9') do n := n + 1;
  185.     Result := StrToInt64Def(Copy(Txt, 1, n - 1), 0);
  186.     Delete(Txt, 1, (n - 1));
  187.   end;
  188. var
  189.   b: Boolean;
  190. begin
  191.   Result := 0;
  192.   s1 := LowerCase(s1);
  193.   s2 := LowerCase(s2);
  194.   if (s1 <> s2) and (s1 <> '') and (s2 <> '') then begin
  195.     b := False;
  196.     while (not b) do begin
  197.       if ((s1[1] >= '0') and (s1[1] <= '9')) and ((s2[1] >= '0') and (s2[1] <= '9')) then
  198.         Result := Sgn(ExtractNr(1, s1) - ExtractNr(1, s2))
  199.       else
  200.         Result := Sgn(Integer(s1[1]) - Integer(s2[1]));
  201.       b := (Result <> 0) or (Min(Length(s1), Length(s2)) < 2);
  202.       if not b then begin
  203.         Delete(s1, 1, 1);
  204.         Delete(s2, 1, 1);
  205.       end;
  206.     end;
  207.   end;
  208.   if Result = 0 then begin
  209.     if (Length(s1) = 1) and (Length(s2) = 1) then
  210.       Result := Sgn(Integer(s1[1]) - Integer(s2[1]))
  211.     else
  212.       Result := Sgn(Length(s1) - Length(s2));
  213.   end;
  214. end;
  215.  
  216. function TALToneCurve.SortMe(List: TStringList; i1, i2: Integer): Integer;
  217. begin
  218.   Result := CompareNatural(List[i1], List[i2]);
  219. end;
  220.  
  221. function TALToneCurve.Blend(Color1, Color2: TColor; A: Byte): TColor;
  222. var
  223.   c1, c2: LongInt;
  224.   r, g, b, v1, v2: byte;
  225. begin
  226.   A := Round(2.56 * A);
  227.   c1 := ColorToRGB(Color1);
  228.   c2 := ColorToRGB(Color2);
  229.   v1 := Byte(c1);
  230.   v2 := Byte(c2);
  231.   r := A * (v1 - v2) shr 8 + v2;
  232.   v1 := Byte(c1 shr 8);
  233.   v2 := Byte(c2 shr 8);
  234.   g := A * (v1 - v2) shr 8 + v2;
  235.   v1 := Byte(c1 shr 16);
  236.   v2 := Byte(c2 shr 16);
  237.   b := A * (v1 - v2) shr 8 + v2;
  238.   Result := (b shl 16) + (g shl 8) + r;
  239. end;
  240.  
  241. procedure TALToneCurve.BilinearRescale(Src, Dest: TBitmap);
  242. var
  243.   x, y, px, py: Integer;
  244.   i, x1, x2, z, z2, iz2: Integer;
  245.   w1, w2, w3, w4: Integer;
  246.   Ratio: Integer;
  247.   sDst, sDstOff: Integer;
  248.   PScanLine: array of PRGBArray;
  249.   Src1, Src2: PRGBArray;
  250.   C, C1, C2: TRGB_24;
  251. begin
  252.   if (Dest.Width < 2) or (Dest.Height < 2) then begin
  253.     Dest.Assign(Src);
  254.     Exit;
  255.   end;
  256.   SetLength(PScanLine, Src.Height);
  257.   PScanLine[0]:= (Src.Scanline[0]);
  258.   i := Integer(Src.Scanline[1]) - Integer(PScanLine[0]);
  259.   for y := 1 to Src.Height - 1 do PScanLine[y]:= PRGBArray(Integer(PScanLine[y - 1]) + i);
  260.   sDst := Integer(Dest.ScanLine[0]);
  261.   sDstOff := Integer(Dest.ScanLine[1]) - sDst;
  262.   Ratio := ((Src.Width - 1) shl 15) div Dest.Width;
  263.   py := 0;
  264.   for y := 0 to Dest.Height - 1 do begin
  265.     i := py shr 15;
  266.     if i > src.Height - 1 then i := src.Height - 1;
  267.     Src1 := PScanline[i];
  268.     if i < src.Height - 1 then Src2 := PScanline[i + 1] else Src2 := Src1;
  269.     z2 := py and $7FFF;
  270.     iz2 := $8000 - z2;
  271.     px := 0;
  272.     for x := 0 to Dest.Width - 1 do begin
  273.       x1 := px shr 15;
  274.       x2 := x1 + 1;
  275.       C1 := Src1[x1];
  276.       C2 := Src2[x1];
  277.       z := px and $7FFF;
  278.       w2 := (z * iz2) shr 15;
  279.       w1 := iz2 - w2;
  280.       w4 := (z * z2) shr 15;
  281.       w3 := z2 - w4;
  282.       C.R := (C1.R * w1 + Src1[x2].R * w2 + C2.R * w3 + Src2[x2].R * w4) shr 15;
  283.       C.G := (C1.G * w1 + Src1[x2].G * w2 + C2.G * w3 + Src2[x2].G * w4) shr 15;
  284.       C.B := (C1.B * w1 + Src2[x2].B * w2 + C2.B * w3 + Src2[x2].B * w4) shr 15;
  285.       PRGBArray(sDst)[x] := C;
  286.       Inc(px, Ratio);
  287.     end;
  288.     sDst := sDst + SDstOff;
  289.     Inc(py, Ratio);
  290.   end;
  291.   SetLength(PScanline, 0);
  292. end;
  293.  
  294. function TALToneCurve.PtInCircle(cx, cy, x, y, radius: Integer): Boolean;
  295. begin
  296.   Result:= ((cx - x) * (cx - x)) + ((cy - y) * (cy - y)) <= radius * radius;
  297. end;
  298.  
  299.  
  300. procedure TALToneCurve.WuLine( x1, y1, x2, y2: integer; Color: TColor);
  301. var
  302.   Src: TBitmap;
  303.   c: Cardinal;
  304.   r, g, b: Byte;
  305.   rgb: TRGB_24;
  306.   i, dx, dy, x, y, w, h, a1, a2 : integer;
  307.   dxi, dyi, gradient : integer;
  308.   Line: array of PRGBArray;
  309.  
  310.   function BlendPixel(x, y, a: Integer): TRGB_24;
  311.   begin
  312.     Result.R:= a * (r - Line[y][x].R) shr 8 + Line[y][x].R;
  313.     Result.G:= a * (g - Line[y][x].G) shr 8 + Line[y][x].G;
  314.     Result.B:= a * (b - Line[y][x].B) shr 8 + Line[y][x].B;
  315.   end;
  316.  
  317. begin
  318.   c:= ColorToRGB(Color);
  319.   r:= c and 255;
  320.   g:= (c shr 8) and 255;
  321.   b:= (c shr 16) and 255;
  322.   w:= BackBMP.Width;
  323.   h:= BackBMP.Height;
  324.   if (x1 = x2) or (y1 = y2) then begin
  325.     BackBMP.Canvas.Pen.Color:= Color;
  326.     BackBMP.Canvas.MoveTo(x1, y1);
  327.     BackBMP.Canvas.LineTo(x2, y2);
  328.     Exit;
  329.   end;
  330.   // make an array of source scanlines to speed up the rendering
  331.   SetLength(Line, BackBMP.Height);
  332.   Line[0]:= (BackBMP.Scanline[0]);
  333.   i:= Integer(BackBMP.Scanline[1]) - Integer(Line[0]);
  334.   for y:= 1 to BackBMP.Height - 1 do Line[y]:= PRGBArray(Integer(Line[y - 1]) + i);
  335.   dx:= abs(x2 - x1);
  336.   dy:= abs(y2 - y1);
  337.   if dx > dy then begin // horizontal or vertical
  338.     if y2 > y1 then dy:= -dy;
  339.     gradient:= dy shl 8 div dx;
  340.     if x2 < x1 then begin
  341.       i:= x1; x1:= x2; x2:= i;
  342.       dyi:= y2 shl 8;
  343.     end else begin
  344.       dyi:= y1 shl 8;
  345.       gradient:= -gradient;
  346.     end;
  347.     if x1 >= W then x2:= W - 1;
  348.     for x := x1 to x2 do begin
  349.       Y:= dyi shr 8;
  350.       if (x < 0) or (y < 0) or (y > h - 2) then Inc(dyi, gradient) else begin
  351.         a1 := dyi - y shl 8;
  352.         a2 := 256 - a1;
  353.         Line[y][x]:= BlendPixel(x, y, a1);
  354.         Line[y + 1][x]:= BlendPixel(x, y + 1, a2);
  355.         Inc(dyi, gradient);
  356.       end;
  357.     end;
  358.   end else begin
  359.     if x2 > x1 then dx:= -dx;
  360.     gradient:= dx shl 8 div dy;
  361.     if y2 < y1 then begin
  362.       i:= y1; y1:= y2; y2:= i;
  363.       dxi:= x2 shl 8;
  364.     end else begin
  365.       dxi:= x1 shl 8;
  366.       gradient:= -gradient;
  367.     end;
  368.     if y2 >= h then y2:= h - 1;
  369.     for y := y1 to y2 do begin
  370.       x:= dxi shr 8;
  371.       if (y < 0) or (x < 0) or (x > w - 2) then Inc(dxi, gradient) else begin
  372.         a1 := dxi - x shl 8;
  373.         a2 := 256 - a1;
  374.         Line[y][x]:= BlendPixel(x, y, a2);
  375.         Line[y][x + 1]:= BlendPixel(x + 1, y, a1);
  376.         Inc(dxi, gradient);
  377.       end;
  378.     end;
  379.   end;
  380. end;
  381.  
  382. procedure TALToneCurve.AddPoint(pt: TPoint);
  383. var
  384.   i, x: Integer;
  385. begin
  386.   i:= 1;
  387.   while (i <= nPts[cIdx]) and (pt.X > ptX[cIdx, i]) do i:= i + 1;
  388.   if i <= nPts[cIdx] + 1 then begin
  389.     Caption:= IntToStr(i);
  390.  
  391.     for x:= 31 downto i do begin
  392.       ptX[cIdx, x + 1]:= ptX[cIdx, x];
  393.       ptY[cIdx, x + 1]:= ptY[cIdx, x];
  394.     end;
  395.     ptX[cIdx, i]:= pt.X;
  396.     ptY[cIdx, i]:= pt.Y;
  397.     apt:= i;
  398.     nPts[cIdx]:= nPts[cIdx] + 1;
  399.   end;
  400. end;
  401.  
  402. procedure TALToneCurve.ApplyCurve(Img: TBitmap);
  403. var
  404.   SRow: PRGBArray;
  405.   SFill, X, Y: Integer;
  406. begin
  407. Try
  408.   if ImgLoaded then Exit;
  409.   SRow:= PRGBArray(Img.ScanLine[0]);
  410.   SFill := Integer(Img.ScanLine[1]) - Integer(SRow);
  411.   for Y := 0 to Img.Height - 1 do begin
  412.     for X := 0 to Img.Width - 1 do begin
  413.       SRow[X].R:= LUT[0, LUT[1, SRow[X].R]];
  414.       SRow[X].G:= LUT[0, LUT[2, SRow[X].G]];
  415.       SRow[X].B:= LUT[0, LUT[3, SRow[X].B]];
  416.     end;
  417.     Inc(Integer(SRow), SFill);
  418.   end;
  419. except
  420. end;
  421. end;
  422.  
  423. procedure TALToneCurve.DelPoint(idx: Integer);
  424. var
  425.   x: Integer;
  426. begin
  427.   if nPts[cIdx] = 2 then begin
  428.     ShowMessage('At least two points must exist.');
  429.     Exit;
  430.   end;
  431.   if (idx > 0) and (idx <= nPts[cIdx]) then begin
  432.     for x:= idx to 31 do begin
  433.       ptX[cIdx, x]:= ptX[cIdx, x + 1];
  434.       ptY[cIdx, x]:= ptY[cIdx, x + 1];
  435.     end;
  436.     apt:= -1;
  437.     nPts[cIdx]:= nPts[cIdx] - 1;
  438.     DrawCurve;
  439.   end;
  440. end;
  441.  
  442. procedure TALToneCurve.DrawCurve;
  443. var
  444.   n, i, j, k, x, y, x1, x2, xpos, ypos: Integer;
  445.   c, c1: TColor;
  446.   f: Single;
  447.   tga: Single;
  448. const lColors : array[0..3] of TColor = (clBlack,clRed,clGreen,clBlue);
  449. begin
  450. if BackBMP<>nil then begin
  451. Try
  452.   if imgView<>nil then
  453.      if Assigned(FRepaint) then FRepaint(Self);
  454.   if FHistogram then
  455.      GetHist;
  456.   SetPandU;
  457.   With BackBMP.Canvas do
  458.   begin
  459.        Brush.Color:= Color;
  460.        FillRect(ClipRect);
  461.  
  462.   // Paint histogram
  463.   case cIdx of
  464.     0: c:= RGB(192, 192, 192);
  465.     1: c:= RGB(255, 190, 190);
  466.     2: c:= RGB(190, 220, 190);
  467.     3: c:= RGB(190, 190, 255);
  468.     else c:= RGB(190, 190, 255);
  469.   end;
  470.  
  471.   c1:= Blend(c, clWhite, 30);
  472.   Pen.Color:= c;
  473.   Pen.Width:= 1;
  474.   j:= MulDiv(nHist[cIdx, 0], 230, maxHist[cIdx]);
  475.   MoveTo(8, 255 + 8);
  476.   LineTo(8, (255 + 8) - j);
  477.   k:= (255 + 8) - j;
  478.   for i := 1 to 255 do begin
  479.     j:= (255 + 8) - MulDiv(nHist[cIdx, i], 230, maxHist[cIdx]);
  480.     Pen.Color:= c1;
  481.     MoveTo(i + 8, 255 + 8);
  482.     LineTo(i + 8, j);
  483.     WuLine(8 + (i - 1), k, 8 + i, j, c);
  484.     k:= j;
  485.   end;
  486.   // Histogram done...
  487.   // Paint guidelines
  488.   Pen.Color:= clGray;
  489.   Brush.Style:= bsClear;
  490.   Rectangle(Rect(8, 8, 264, 264));
  491.   Pen.Color:= clSilver;
  492.   Pen.Style:= psDot;
  493.   for i:= 1 to 5 do begin
  494.     j:= 8 + (i * 50);
  495.     MoveTo(8, 272-j); LineTo(264, 272-j);
  496.     MoveTo(j, 8); LineTo(j, 264);
  497.   end;
  498.   Pen.Style:= psSolid;
  499.   Pen.Color:= clSilver;
  500.   MoveTo(8, 263);
  501.   LineTo(263, 8);
  502.   // Guidelines done...
  503.  
  504.   // Paint points and curve
  505.   Pen.Color:= clBlack;
  506.   Brush.Color:= clBlack;
  507.   Brush.Style:= bsClear;
  508.   for i:= 1 to nPts[cIdx] do
  509.     Rectangle(8 + ptX[cIdx, i] - 3, 263 - ptY[cIdx, i] - 3, 8 + ptX[cIdx, i] + 4, 263 - ptY[cIdx, i] + 4);
  510.  
  511.  
  512.   xpos:= 0; ypos:= 0;
  513.   x:= 8 + ptX[cIdx, 1];
  514.   y:= 263 - ptY[cIdx, 1];
  515.   MoveTo(8, y);
  516.   if ptX[cIdx, 1] > 0 then LineTo(x, y);
  517.   for i:= 1 to nPts[cIdx] - 1 do begin
  518.     x1:= ptX[cIdx, i];
  519.     x2:= ptX[cIdx, i + 1];
  520.  
  521.     Case LineType of
  522.     cuvLinear:
  523.     begin
  524.       LineTo(x2 + 8, 263 - ptY[cIdx, i + 1]);
  525.       tga := (ptY[cIdx, i + 1]-ptY[cIdx, i]) / (x2-x1);
  526.       for j:=x1 to x2 do begin
  527.           ypos := Round(ptY[cIdx, i] + (j-x1)*tga);
  528.           LUT[cIdx, j]:= ypos;
  529.       end;
  530.     end;
  531.     cuvSpline:
  532.     for j:=  x1 to x2 do begin
  533.       xpos:= j;
  534.       ypos:= Trunc(GetCurvePoint(i, xpos));
  535.       if ypos < 0 then ypos:= 0 else if ypos > 255 then ypos:= 255;
  536.       WuLine(x, y, 8 + xpos, 263 - ypos, lColors[cIdx]);
  537.       LUT[cIdx, xpos]:= ypos;
  538.       x:= 8 + xpos;
  539.       y:= 263 - ypos;
  540.     end;
  541.     end;
  542.   end;
  543.  
  544.   MoveTo(8 + xpos, 263 - ypos);
  545.   if ptX[cIdx, nPts[cIdx]] < 255 then LineTo(263, 263 - ptY[cIdx, nPts[cIdx]]);
  546.   if ptX[cIdx, 1] > 0 then for i:= 0 to ptx[cIdx, 1] - 1 do Lut[cIdx, i]:= ptY[cIdx, 1];
  547.   if ptX[cIdx, nPts[cIdx]] < 255 then for i:= ptx[cIdx, nPts[cIdx]] + 1 to 255 do Lut[cIdx, i]:= ypos;
  548.   // Curve and points done...
  549.  
  550.   end;
  551.  
  552. finally
  553.   Canvas.Draw(0,0,BackBMP);
  554. end;
  555. end;
  556. end;
  557.  
  558. function TALToneCurve.GetCurvePoint(i: Integer; v: Single): Single;
  559. var
  560.   t0, t1: Single;
  561. begin
  562.   t0:= (v - ptX[cIdx, i]) / ptU[cIdx,i];
  563.   t1:= 1 - t0;
  564.   Result:= t0 * ptY[cIdx, i + 1] + t1 * ptY[cIdx, i] + ptU[cIdx, i] *
  565.     ptU[cIdx, i] * ((t0*t0*t0-t0) * ptP[cIdx, i + 1] + (t1*t1*t1-t1) * ptP[cIdx, i]) / 6;
  566. end;
  567.  
  568. procedure TALToneCurve.GetHist;
  569. var
  570.   SRow: PRGBArray;
  571.   i, x, y, SFill: Integer;
  572.   Src: TBitmap;
  573.   RGB: TRGB_24;
  574.   r, g, b, l: Byte;
  575. begin
  576. Try
  577.   if ImgLoaded then Exit;
  578.   if imgView<>nil then
  579.   if not imgView.Empty then begin
  580.   for y:= 0 to 3 do begin
  581.     maxHist[y]:= 0;
  582.     for x := 0 to 255 do nHist[y, x]:= 0;
  583.   end;
  584.   Src:= imgView;
  585.   SRow:= PRGBArray(Src.ScanLine[0]);
  586.   SFill := Integer(Src.ScanLine[1]) - Integer(SRow);
  587.   for Y := 0 to Src.Height - 1 do begin
  588.     for X := 0 to Src.Width - 1 do begin
  589.       rgb:= SRow[X];
  590.       r:= RGB.R; g:= RGB.G;  b:= RGB.B; l:= (r + g + b) div 3;
  591.       nHist[0, l]:= nHist[0, l] + 1;
  592.       nHist[1, r]:= nHist[1, r] + 1;
  593.       nHist[2, g]:= nHist[2, g] + 1;
  594.       nHist[3, b]:= nHist[3, b] + 1;
  595.     end;
  596.     Inc(Integer(SRow), SFill);
  597.   end;
  598.   for y := 0 to 3 do for x := 0 to 255 do if nHist[y, x] > maxHist[y] then maxHist[y]:= nHist[y, x];
  599.   end;
  600. except
  601. end;
  602. end;
  603.  
  604. function TALToneCurve.IsPoint(x, y: integer): integer;
  605. var
  606.   i: Integer;
  607.   p: TPoint;
  608. begin
  609.   x:= x - 8;
  610.   y:= y - 8;
  611.   p.X:= x;
  612.   p.Y:= 255 - y;
  613.   if p.X < 0 then p.X:= 0 else if p.X > 255 then p.X:= 255;
  614.   if p.Y < 0 then p.Y:= 0 else if p.Y > 255 then p.Y:= 255;
  615.   apt:= -1;
  616.   i:= 1;
  617.   while (i <= nPts[cIdx]) and (not PtInCircle(ptX[cIdx, i], ptY[cIdx, i], p.X, p.Y, 5)) do inc(i);
  618.   if i <= nPts[cIdx] then apt:= i else apt:=-1;
  619.   Result := apt;
  620. end;
  621.  
  622. procedure TALToneCurve.SetChannel(const Value: integer);
  623. begin
  624.   if FChannel<>Value then begin
  625.      FChannel := Value;
  626.      cIdx:= Value;
  627.      DrawCurve;
  628.   end;
  629. end;
  630.  
  631. procedure TALToneCurve.Reset;
  632. var
  633.   j: Integer;
  634. begin
  635.   aPt:= -1;
  636.   // Reset values
  637.   nPts[cIdx]:= 2;
  638.   ptX[cIdx, 1]:= 0;
  639.   ptX[cIdx, 2]:= 255;
  640.   ptY[cIdx, 1]:= 0;
  641.   ptY[cIdx, 2]:= 255;
  642.   for j:= 0 to 255 do LUT[cIdx, j]:= j;
  643.   DrawCurve;
  644. end;
  645.  
  646. procedure TALToneCurve.Invers;
  647. var
  648.   i: Integer;
  649. begin
  650.   For i:=1 to nPts[cIdx] do ptY[cIdx, i]:= 255-ptY[cIdx, i];
  651.   GetHist;
  652.   DrawCurve;
  653. end;
  654.  
  655.  
  656. procedure TALToneCurve.LoadPreset(Idx: Integer);
  657. var
  658.   x, y, i, j, k, m, n, p, q, cnt: Integer;
  659.   FileArr, Simple: Array[0..255] of Byte;
  660.   CurvePts: Array[0..3] of String;
  661.   s: String;
  662.   Stream: TFileStream;
  663.   ACVFile, Curve: String;
  664. begin
  665.   q:= cIdx;
  666.   ACVFile:= PresetPath + Presets.Strings[Idx] + '.acv';
  667.   Stream:= TFileStream.Create(ACVFile, fmOpenRead or fmShareDenyWrite);
  668.   try
  669.     i:= Stream.Size;
  670.     Stream.Read(FLineType,1);
  671.     Stream.ReadBuffer(FileArr, i-1);
  672.   finally
  673.     Stream.Free;
  674.   end;
  675.   i:= i div 2;
  676.   n:= 1;
  677.   for j:= 0 to i - 1 do begin
  678.     Simple[j]:= FileArr[n];
  679.     n:= n + 2;
  680.   end;
  681.   if (Simple[0] <> 4) then begin
  682.     ShowMessage('This file version is not supported. Sorry!');
  683.     Exit;
  684.   end;
  685.   // Clear old values
  686.   for i := 0 to 3 do begin
  687.     for n := 1 to 32 do begin
  688.       ptP[i, n]:= 0;
  689.       ptU[i, n]:= 0;
  690.     end;
  691.   end;
  692.   k:= 2;
  693.   for m := 0 to 3 do begin
  694.     cnt:= Simple[k];
  695.     i:= Simple[k] * 2;
  696.     nPts[m]:= cnt;
  697.     n:= k + 1;
  698.     for j:= 1 to cnt do begin
  699.       ptX[m, j]:= Simple[n + 1];
  700.       ptY[m, j]:= Simple[n];
  701.       n:= n + 2;
  702.     end;
  703.     cIdx:= m;
  704.     SetPandU;
  705.     y:= 255;
  706.     for p:= 1 to cnt - 1 do begin
  707.       for x:=  ptX[m, p] to ptX[m, p + 1] do begin
  708.         y:= Trunc(GetCurvePoint(p, x));
  709.         if y < 0 then y:= 0 else if y > 255 then y:= 255;
  710.         LUT[m, x]:= y;
  711.       end;
  712.     end;
  713.     if ptX[m, 1] > 0 then for p:= 0 to ptx[m,  1] - 1 do Lut[m, p]:= ptY[m, 1];
  714.     if ptX[m, nPts[m]] < 255 then for p:= ptx[m, nPts[m]] + 1 to 255 do LUT[m, p]:= 255;
  715.     k:= k + i + 1;
  716.   end;
  717.   cIdx:= q;
  718.   DrawCurve;
  719.   FPresetName := Presets.Strings[Idx];
  720. end;
  721.  
  722. procedure TALToneCurve.SavePreset(fName: string);
  723. var
  724.   i, j, k: Integer;
  725.   txt: String;
  726.   acv: Array[0..255] of Byte;
  727.   Stream: TFileStream;
  728.   ACVFile: String;
  729.   typ: byte;
  730. begin
  731.   for i := 0 to 255 do acv[i]:= 0;
  732.   if fName <> '' then begin
  733.     j:= 1;
  734.     acv[j]:= 4; inc(j, 2);
  735.     acv[j]:= 5; inc(j, 2);
  736.     for i := 0 to 3 do begin
  737.       acv[j]:= nPts[i]; inc(j, 2);
  738.       for k:= 1 to nPts[i] do begin
  739.         acv[j]:= ptY[i, k]; inc(j, 2);
  740.         acv[j]:= ptX[i, k]; inc(j, 2);
  741.       end;
  742.     end;
  743.     acv[j]:= 2; inc(j, 2);
  744.     acv[j]:= 0; inc(j, 2);
  745.     acv[j]:= 0; inc(j, 2);
  746.     acv[j]:= 255; inc(j, 2);
  747.     acv[j]:= 255; inc(j, 2);
  748.     ACVFile:= PresetPath + fName + '.acv';
  749.     Stream:= TFileStream.Create(ACVFile, fmCreate or fmOpenWrite or fmShareDenyWrite);
  750.     try
  751.       typ := Ord(FLineType);
  752.       Stream.Write(typ,1);
  753.       Stream.WriteBuffer(acv, j - 1);
  754.     finally
  755.       Stream.Free;
  756.       GetCurvesPreset(Presets);
  757.     end;
  758.   end;
  759. end;
  760.  
  761.  
  762. procedure TALToneCurve.GetCurvesPreset(Files: TStrings);
  763. var
  764.   SR: TSearchRec;
  765.   i : integer;
  766. begin
  767.   if not DirectoryExists(PresetPath) then CreateDir(PresetPath) else begin
  768.     Files.Clear;
  769.       if FindFirst(PresetPath + '*.acv', faArchive, SR) = 0 then
  770.           begin
  771.             repeat
  772.                 Files.Add(SR.Name);
  773.             until FindNext(SR) <> 0;
  774.             FindClose(SR);
  775.           end;
  776.           if Files.Count > 0 then begin
  777. //            Files.CustomSort(Self.SortMe);
  778.             for i := 0 to Files.Count - 1 do Files.Strings[i]:=ChangeFileExt(Files[i], '');
  779.           end;
  780.   end;
  781. end;
  782.  
  783. procedure TALToneCurve.SetPresetPath(const Value: String);
  784. begin
  785.   FPresetPath := Value;
  786.   GetCurvesPreset(Presets);
  787. end;
  788.  
  789. procedure TALToneCurve.SetPandU;
  790. var
  791.   i: Integer;
  792.   d, w: array[1..32] of Single;
  793. begin
  794.   for i:= 2 to nPts[cIdx] - 1 do d[i]:= 2 * (ptX[cIdx, i + 1] - ptX[cIdx, i - 1]);
  795.   for i:= 1 to nPts[cIdx] - 1 do ptU[cIdx, i]:= ptX[cIdx, i + 1] - ptX[cIdx, i];
  796.   for i:= 2 to nPts[cIdx] - 1 do w[i]:= 6 * ((ptY[cIdx, i + 1] - ptY[cIdx, i]) /
  797.     ptU[cIdx, i] - (ptY[cIdx, i] - ptY[cIdx, i - 1]) / ptU[cIdx, i - 1]);
  798.   for i:= 2 to nPts[cIdx] - 2 do begin
  799.     w[i + 1]:= w[i + 1] - w[i] * ptU[cIdx, i] / d[i];
  800.     d[i + 1]:= d[i + 1] - ptU[cIdx, i] * ptU[cIdx, i] / d[i];
  801.   end;
  802.   ptP[cIdx, 1]:= 0;
  803.   for i:= nPts[cIdx] - 1 downto 2 do ptP[cIdx, i]:= (w[i] - ptU[cIdx, i] * ptP[cIdx, i + 1]) / d[i];
  804.   ptP[cIdx, nPts[cIdx]]:= 0;
  805. end;
  806.  
  807. procedure TALToneCurve.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  808.   Y: Integer);
  809. var
  810.   i: Integer;
  811.   p: TPoint;
  812. begin
  813.   x:= x - 8;
  814.   y:= y - 8;
  815.   p.X:= x;
  816.   p.Y:= 255 - y;
  817.   if p.X < 0 then p.X:= 0 else if p.X > 255 then p.X:= 255;
  818.   if p.Y < 0 then p.Y:= 0 else if p.Y > 255 then p.Y:= 255;
  819.   apt:= -1;
  820.   i:= 1;
  821.   while (i <= nPts[cIdx]) and (not PtInCircle(ptX[cIdx, i], ptY[cIdx, i], p.X, p.Y, 5)) do inc(i);
  822.   if i <= nPts[cIdx] then apt:= i;
  823.   if (ssLeft in Shift) then begin
  824.     if (apt <> -1) then begin
  825.       if (apt > 0) and (apt < nPts[cIdx]) then begin
  826.         if (p.X > ptX[cIdx, apt - 1]) and (p.X < ptX[cIdx, apt + 1]) then ptX[cIdx, apt]:= p.X;
  827.         ptY[cIdx, apt]:= p.Y;
  828.       end;
  829.     end else if nPts[cIdx] < 31 then AddPoint(p);
  830.   end else if (ssRight in Shift) then DelPoint(apt);
  831.   oldHistogram := FHistogram;
  832.   FHistogram   := False;
  833.   DrawCurve;
  834. end;
  835.  
  836. procedure TALToneCurve.MouseMove(Shift: TShiftState; X, Y: Integer);
  837. var
  838.   i: Integer;
  839.   p: TPoint;
  840. begin
  841.   if not (ssLeft in Shift) then begin
  842.      apt := IsPoint(x,y);
  843.   if apt <> -1 then
  844.       Cursor := crHandPoint
  845.   else
  846.       Cursor := crDefault;
  847.       Exit;
  848.   end;
  849.   x:= x - 8;
  850.   y:= y - 8;
  851.   p.X:= x;
  852.   p.Y:= 255 - y;
  853.   if p.X < 0 then p.X:= 0 else if p.X > 255 then p.X:= 255;
  854.   if p.Y < 0 then p.Y:= 0 else if p.Y > 255 then p.Y:= 255;
  855.   if apt = -1 then begin
  856.     i:= 1;
  857.     while (i <= nPts[cIdx]) and (not PtInCircle(ptX[cIdx, i], ptY[cIdx, i], p.X, p.Y, 5)) do inc(i);
  858.     if i <= nPts[cIdx] then apt:= i;
  859.   end;
  860.   if (ssLeft in Shift) and (apt <> -1) then begin
  861.     if (apt > 1) and (apt < nPts[cIdx]) then begin
  862.       if (p.X > ptX[cIdx, apt - 1]) and (p.X < ptX[cIdx, apt + 1]) then ptX[cIdx, apt]:= p.X;
  863.     end else begin
  864.       if (apt = 1) and (p.X < ptX[cIdx, apt + 1]) then ptX[cIdx, apt]:= p.X;
  865.       if (apt = nPts[cIdx]) and (p.X > ptX[cIdx, apt - 1]) then ptX[cIdx, apt]:= p.X;
  866.     end;
  867.     ptY[cIdx, apt]:= p.Y;
  868.   end;
  869.   FHistogram   := False;
  870.   DrawCurve;
  871. end;
  872.  
  873. procedure TALToneCurve.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  874.   Y: Integer);
  875. begin
  876.   apt:= -1;
  877.   FHistogram   := oldHistogram;
  878.   DrawCurve;
  879.   if imgView<>nil then
  880.      if Assigned(FRepaint) then FRepaint(Self);
  881. end;
  882.  
  883. procedure TALToneCurve.SetFileName(const Value: String);
  884. begin
  885.   // New image file loading in the imgView
  886.   FFileName := Value;
  887.   if FimgView<>nil then begin
  888.   end;
  889. end;
  890.  
  891. procedure TALToneCurve.setLineType(const Value: TToneCurveType);
  892. begin
  893.   FLineType := Value;
  894.   DrawCurve;
  895.   if imgView<>nil then
  896.      if Assigned(FRepaint) then FRepaint(Self);
  897. end;
  898.  
  899. procedure TALToneCurve.SetColor(const Value: TColor);
  900. begin
  901.   FColor := Value;
  902.   invalidate;
  903. end;
  904.  
  905. procedure TALToneCurve.WMEraseBkGnd(var Message: TWMEraseBkGnd);
  906. begin
  907.   Message.Result := -1
  908. end;
  909.  
  910. procedure TALToneCurve.WMSize(var Msg: TWMSize);
  911. begin
  912.   Msg.width := 272;
  913.   Msg.height := 272;
  914. end;
  915.  
  916. procedure TALToneCurve.Paint;
  917. begin
  918.   DrawCurve;
  919.   inherited;
  920. end;
  921.  
  922. procedure Register;
  923. begin
  924.   RegisterComponents('AL',[TALToneCurve]);
  925. end;
  926.  
  927. procedure TALToneCurve.SetHistogram(const Value: boolean);
  928. begin
  929.   FHistogram := Value;
  930.   DrawCurve;
  931. end;
  932.  
  933. function TALToneCurve.AddPreset: string;
  934. var
  935.   i, j, k: Integer;
  936.   txt: String;
  937.   acv: Array[0..255] of Byte;
  938.   Stream: TFileStream;
  939.   ACVFile: String;
  940. begin
  941.   Result := '';
  942.   if InputQuery('Give the curve a name', 'Please type a name ', FPresetName)
  943.   then
  944.   begin
  945.      Result := FPresetName;
  946.      SavePreset(FPresetName);
  947.   end;
  948. end;
  949.  
  950. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement