this slowpoke moves

Convert Bitmap to Original 8 Bit

Ein Bitmap in verschiedene Farbtiefen um zurechnen ist eigentlich kein als zu großes Problem, dazu reichen meist ein paar Code Zeilen wie diese.
BitMap := TBitMap.Create;
  try
    BitMap.LoadFromFile(FileName);
    BitMap.PixelFormat:=pf16bit;
    BitMap.SaveToFile(FileName);
  finally
  BitMap.Free;
end;

Doch das gilt nicht für 8 Bit. 

Wenn der obige Code für 8 Bit angewendet wird, bekommt man ein Fake Bild, deren Farbblöcke nur halbiert oder geviertelt wurden. In den Header Informationen ist zwar 8 Bit eingetragen, doch das Bild ist nicht wirklich 8 Bit. 

Tatsächlich ist es ein Problem ein echtes 8 Bit Bitmap herzustellen, dazu müssen nun mal alle Pixel des Bitmaps neu berechnet werden. 

 ...und so lösen wir das Problem. 

 Erstellt ein neues Projekt und legt diese Komponenten auf die Form. 

4 x Button 
3 x TrackBar 
3 x Paint Box
1 x ProgressBar
1 x CheckBox
1 x SaveDialog
1 x OpenPictureDialog
6 x Panel

 Dann bindet ihr diese Unit in das Projekt mit ein.
unit hackbard;

interface

uses
  SysUtils,Classes,Dialogs,Windows,Contnrs,ComCtrls,Graphics,Commctrl;

type
  TComponents=class;

  TTreeHistogram=class
  public
    FCount,FValue:Cardinal;
    FParent:TTreeHistogram;
    FBounds:array of Cardinal;
    FChilds:TObjectList;
    FLeaf:Boolean;

    constructor SubCreate(AParent:TTreeHistogram;AValues:array of Cardinal);

    function GetSubHistogram(Index: Integer): TTreeHistogram;
    property SubHistogram[Index:Integer]:TTreeHistogram read GetSubHistogram;

    function CalculateWeight:Double;
    function CalculateMean:Double;
    function CalculateMin:Cardinal;
    function CalculateMax:Cardinal;
  public
    constructor Create(ABounds:array of Cardinal);

    procedure Add(Values:array of Cardinal);overload;
    procedure Add(AHistogram:TTreeHistogram);overload;

    destructor Destroy;override;
  end;

  TComponents=class
  public
    FWeight,FMean,FHistoCount:Double;
    FSubComponents:TObjectList;
    FHistogram:TTreeHistogram;
    FMin,FMax:Cardinal;
    FUnused:Boolean;

    procedure Split;
    procedure MultiSplit(ACount:Integer);
    function GetComponents(Index: Cardinal): TComponents;
    procedure InsertComponents(o:TCOmponents);

    function Interpolate(t:array of Cardinal):TComponents;
  public
    constructor Create(AHistogram:TTreeHistogram;AMin,AMax:Cardinal);
    constructor CreateRoot(AHistogram:TTreeHistogram);

    function GetComponentIndex(Value:Cardinal):Integer;
    function GetValueWeight(Value:Cardinal):Cardinal;

    property Components[Index:Cardinal]:TComponents read GetComponents;

    destructor Destroy;override;
  end;

  TColorFunc=function(Values,Bounds:array of Cardinal):TColor;

  TInterpolator=class
  private
    FComponents:TComponents;
    FEntryCount,FNDims:Integer;
    FLeafComponents:TObjectList;
    FEntries:array of Cardinal;
    FColorFunc:TColorFunc;

    procedure TreeViewAdvancedCustomDrawItem(Sender:TCustomTreeView;Node:TTreeNode;State:TCustomDrawState;Stage:TCustomDrawStage;var PaintImages,DefaultDraw:Boolean);
  public
    constructor Create(AHistogram:TTreeHistogram;ASplitCount:Integer);

    procedure AssignToTreeView(t:TTreeView;AColorFunc:TColorFunc);
    procedure GetEntry(Index:Integer;Values:array of PCardinal);
    property EntryCount:Integer read FEntryCount;
    procedure Interpolate(AEntryCount:Integer);
    function GetEntryIndex(Values:array of Cardinal;UseMethod1:Boolean):Integer;

    destructor Destroy;override;
  end;

implementation

{ TTreeHistogram }

procedure TTreeHistogram.Add(Values: array of Cardinal);
var
  a,b:Integer;
  t:array of Cardinal;
begin
  Assert(Values[0]<=FBounds[0],'Value out of range');
  a:=0;
  while (a<FChilds.Count) and (Values[0]>SubHistogram[a].FValue) do
    Inc(a);
  if (a<FChilds.Count) and (Values[0]=SubHistogram[a].FValue) then begin
    Inc(SubHistogram[a].FCount);
    if High(Values)>0 then begin
      SetLength(t,High(Values));
      for b:=1 to High(Values) do
        t[b-1]:=Values[b];
      SubHistogram[a].Add(t);
      SetLength(t,0);
    end;
  end else
    FChilds.Insert(a,TTreeHistogram.SubCreate(Self,Values));
end;

procedure TTreeHistogram.Add(AHistogram: TTreeHistogram);
var
  a,b:Integer;
  t:array of Cardinal;
begin
  a:=0;
  while (a<FChilds.Count) and (AHistogram.FValue>SubHistogram[a].FValue) do
    Inc(a);
  if (a=FChilds.Count) or (AHistogram.FValue<>SubHistogram[a].FValue) then begin
    SetLength(t,High(FBounds));
    for b:=1 to High(FBounds) do
      t[b-1]:=FBounds[b];
    FChilds.Insert(a,TTreeHistogram.Create(t));
    SetLength(t,0);
    SubHistogram[a].FValue:=AHistogram.FValue;
  end;
  Inc(SubHistogram[a].FCount,AHistogram.FCount);
  for b:=0 to AHistogram.FChilds.Count-1 do
    SubHistogram[a].Add(AHistogram.SubHistogram[b]);
end;

function TTreeHistogram.CalculateMax: Cardinal;
begin
  Result:=SubHistogram[FChilds.Count-1].FValue;
end;

function TTreeHistogram.CalculateMean: Double;
var
  a:Integer;
begin
  Result:=0;
  for a:=0 to FChilds.Count-1 do
    with SubHistogram[a] do
      Result:=Result+FCount*FValue;
  Result:=Result/CalculateWeight;
end;

function TTreeHistogram.CalculateMin: Cardinal;
begin
  Result:=SubHistogram[0].FValue;
end;

function TTreeHistogram.CalculateWeight: Double;
var
  a:Integer;
begin
  Result:=0;
  for a:=0 to FChilds.Count-1 do
    with SubHistogram[a] do
      Result:=Result+FCount;
end;

constructor TTreeHistogram.Create(ABounds: array of Cardinal);
var
  a:Integer;
begin
  SetLength(FBounds,High(ABounds)+1);
  for a:=0 to High(ABounds) do
    FBounds[a]:=ABounds[a];
  FChilds:=TObjectList.Create(True);
  FLeaf:=High(ABounds)=0;
  FCount:=0;
end;

destructor TTreeHistogram.Destroy;
begin
  FChilds.Destroy;
  SetLength(FBounds,0);
  inherited;
end;

function TTreeHistogram.GetSubHistogram(Index: Integer): TTreeHistogram;
begin
  Result:=FChilds[Index] as TTreeHistogram;
end;

constructor TTreeHistogram.SubCreate(AParent: TTreeHistogram; AValues: array of Cardinal);
var
  a:Integer;
  t:array of Cardinal;
begin
  SetLength(t,High(AParent.FBounds));
  for a:=1 to High(AParent.FBounds) do
    t[a-1]:=AParent.FBounds[a];
  Create(t);
  FParent:=AParent;
  FValue:=AValues[0];
  FCount:=1;
  for a:=1 to High(AValues) do
    t[a-1]:=AValues[a];
  if High(AValues)>0 then
    FChilds.Add(TTreeHistogram.SubCreate(Self,t));
  SetLength(t,0);
end;

{ TComponent }

constructor TComponents.Create(AHistogram: TTreeHistogram; AMin, AMax: Cardinal);
var
  a,b:Integer;
  c:TComponents;
  h:TTreeHistogram;
begin
  FMin:=AMin;
  FMax:=AMax;
  FSubComponents:=TObjectList.Create(True);
  FHistogram:=AHistogram;
  if not FHistogram.FLeaf then begin
    h:=TTreeHistogram.Create(AHistogram.SubHistogram[0].FBounds);
    for a:=0 to FHistogram.FChilds.Count-1 do
      with FHistogram.SubHistogram[a] do
        for b:=0 to FChilds.Count-1 do
          h.Add(SubHistogram[b]);
    c:=TComponents.Create(h,0,FHistogram.FBounds[0]);
    FSubComponents.Add(c);
  end;
  FMean:=FHistogram.CalculateMean;
  FHistoCount:=FHistogram.CalculateWeight;
  FWeight:=FHistoCount*(Integer(FMax)-Integer(FMin)+1);
end;

constructor TComponents.CreateRoot(AHistogram: TTreeHistogram);
var
  h:TTreeHistogram;
  a:Integer;
begin
  FHistogram:=AHistogram;
  FSubComponents:=TObjectList.Create(True);
  h:=TTreeHistogram.Create(AHistogram.FBounds);
  for a:=0 to AHistogram.FChilds.Count-1 do
    h.Add(AHistogram.SubHistogram[a]);
  FSubComponents.Add(TComponents.Create(h,0,AHistogram.FBounds[0]));
  FMean:=FHistogram.CalculateMean;
  FHistoCount:=FHistogram.CalculateWeight;
  FWeight:=FHistoCount*(FMax-FMin+1);
end;

destructor TComponents.Destroy;
begin
  FHistogram.Destroy;
  FSubComponents.Destroy;
  inherited;
end;

function TComponents.GetComponentIndex(Value: Cardinal): Integer;
var
  a:Integer;
begin
  Result:=-1;
  for a:=0 to FSubComponents.Count-1 do
    with Components[a] do
      if (Value>=FMin) and (Value<=FMax) then
        Result:=a;
  Assert(Result>-1);
end;

function TComponents.GetComponents(Index: Cardinal): TComponents;
begin
  Result:=FSubComponents[Index] as TComponents;
end;

function TComponents.GetValueWeight(Value: Cardinal): Cardinal;
var
  a:Integer;
begin
  Result:=0;
  for a:=0 to FHistogram.FChilds.Count-1 do
    if FHistogram.SubHistogram[a].FValue=Value then
      Result:=FHistogram.SubHistogram[a].FCount;
end;

procedure TComponents.InsertComponents(o: TCOmponents);
var
  a:Integer;
begin
  a:=0;
  while (a<FSubComponents.Count) and ((FSubComponents[a] as TComponents).FWeight>o.FWeight) do
    Inc(a);
  FSubComponents.Insert(a,o);
end;

function TComponents.Interpolate(t: array of Cardinal):TComponents;
var
  a,b:Integer;
  u:array of Cardinal;
  r:Double;
begin
  Assert(not FUnused);
  r:=1E10;
  b:=-1;
  for a:=0 to FSubComponents.Count-1 do
    with Components[a] do
      if (not FUnused) and (Abs(t[0]-FMean)<r) then begin
        r:=Abs(t[0]-FMean);
        b:=a;
      end;
  SetLength(u,High(t));
  for a:=1 to High(t) do
    u[a-1]:=t[a];
  if High(t)>0 then
    Result:=Components[b].Interpolate(u)
  else
    Result:=Components[b];
  SetLength(u,0);
end;

procedure TComponents.MultiSplit(ACount: Integer);
var
  a:Integer;
begin
  for a:=0 to ACount-1 do
    Split;
  for a:=0 to FSubComponents.Count-1 do
    COmponents[a].MultiSplit(ACount);
end;

procedure TComponents.Split;
var
  a,c1,c2,c3:Integer;
  s,m1,m2,m3:Double;
  h1,h2:TTreeHistogram;
  o1,o2:TComponents;
begin
  if (FSubComponents.Count=0) or (Components[0].FHistogram.FChilds.Count<=1) then Exit;
  m1:=0;
  c1:=0;
  with Components[0].FHistogram do begin
    for a:=0 to FChilds.Count-1 do
      if SubHistogram[a].FCount>=m1 then begin
        m1:=SubHistogram[a].FCount;
        c1:=a;
      end;
  end;
  m2:=0;
  c2:=Components[0].FHistogram.FChilds.Count-1;
  with Components[0].FHistogram do begin
    for a:=0 to FChilds.Count-1 do begin
      s:=SubHistogram[a].FCount*(1-Exp(-0.1*Abs(Integer(SubHistogram[a].FValue)-Integer(SubHistogram[c1].FValue))/(FMax-FMin+1)));
      if s>=m2 then begin
        m2:=s;
        c2:=a;
      end;
    end;
  end;
  if c1>c2 then begin
    a:=c2;
    c2:=c1;
    c1:=a;
  end;
  m3:=1E10;
  c3:=(c1+c2) div 2;
  with Components[0].FHistogram do begin
    for a:=c1 to c2 do begin
      s:=SubHistogram[a].FCount+0.3*(m1+m2)*
         (1-Exp(-5*(Abs(Integer(SubHistogram[a].FValue)-Integer(SubHistogram[c1].FValue))+
                    Abs(Integer(SubHistogram[a].FValue)-Integer(SubHistogram[c2].FValue)))/(FMax-FMin+1)));
      if s <= m3 then begin
        m3:=s;
        c3:=a;
      end;
    end;
  end;
  if c3=0 then
    c3:=1;
  h1:=TTreeHistogram.Create(Components[0].FHistogram.FBounds);
  with Components[0].FHistogram do
    for a:=0 to c3-1 do
      h1.Add(SubHistogram[a]);
  h2:=TTreeHistogram.Create(Components[0].FHistogram.FBounds);
  with Components[0].FHistogram do
    for a:=c3 to FChilds.Count-1 do
      h2.Add(SubHistogram[a]);
  c1:=Components[0].FMin;
  c2:=Components[0].FMax;
  c3:=(Components[0].FHistogram.SubHistogram[c3-1].FValue+Components[0].FHistogram.SubHistogram[c3].FValue) div 2;
  FSubComponents.Delete(0);
  o1:=TComponents.Create(h1,c1,c3);
  o2:=TComponents.Create(h2,c3,c2);
  InsertComponents(o1);
  InsertComponents(o2);
end;

{ TInterpolator }

procedure TInterpolator.AssignToTreeView(t: TTreeView;
  AColorFunc: TColorFunc);

  procedure RecAssign(n:TTreeNode;o:TComponents);
  var
    a:Integer;
    m:TTreeNode;
  begin
    if o.FSubComponents.Count=0 then
      Exit;
    m:=t.Items.AddChild(n,'');
    m.Data:=o;
    for a:=0 to o.FSubComponents.Count-1 do
      if not o.Components[a].FUnused then
        RecAssign(m,o.Components[a]);
  end;

begin
  FColorFunc:=AColorFunc;
  t.Items.Clear;
  SendMessage(t.Handle,TVM_SETITEMHEIGHT,80,0);
  t.OnAdvancedCustomDrawItem:=TreeViewAdvancedCustomDrawItem;
  RecAssign(nil,FComponents);
end;

constructor TInterpolator.Create(AHistogram: TTreeHistogram;
  ASplitCount: Integer);

  procedure Insert(o:TComponents);
  var
    a:Integer;
  begin
    a:=0;
    while (a<FLeafCOmponents.Count) and ((FLeafCOmponents[a] as TComponents).FHistoCount>o.FHistoCount) do
      Inc(a);
    FLeafCOmponents.Insert(a,o);
  end;

  procedure RecList(o:TComponents);
  var
    a:Integer;
  begin
    if o.FSubComponents.Count=0 then
      Insert(o)
    else
      for a:=0 to o.FSubComponents.Count-1 do
        RecList(o.Components[a]);
  end;

begin
  FNDims:=High(AHistogram.FBounds)+1;
  FComponents:=TComponents.CreateRoot(AHistogram);
  FComponents.MultiSplit(ASplitCount);
  FLeafComponents:=TObjectList.Create(False);
  RecList(FComponents);
end;

destructor TInterpolator.Destroy;
begin
  FComponents.Destroy;
  FLeafComponents.Destroy;
  SetLength(FEntries,0);
  inherited;
end;

procedure TInterpolator.GetEntry(Index: Integer;
  Values: array of PCardinal);
var
  a:Integer;
begin
  for a:=0 to FNDims-1 do
    Values[a]^:=FEntries[Index*FNDims+a];
end;

function TInterpolator.GetEntryIndex(Values: array of Cardinal; UseMethod1: Boolean): Integer;
var
  a,b:Integer;
  r,s:Double;
begin
  if UseMethod1 then begin
    Result:=-1;
    r:=1E10;
    for a:=0 to FEntryCount-1 do begin
      s:=0;
      for b:=0 to FNDims-1 do
        s:=s+Abs(Integer(FEntries[a*FNDims+b])-Integer(Values[b]));
      if s<r then begin
        Result := a;
        r:= s;
      end;
    end;
  end else begin
    Result := FLeafComponents.IndexOf(FComponents.Interpolate(Values));
    Assert((Result>-1) and (Result<FEntryCount),IntToStr(Result));
  end;
end;

procedure TInterpolator.Interpolate(AEntryCount: Integer);
var
  a,b:Integer;
  p:TComponents;

  function RecFillEntry(o:TComponents):TComponents;
  var
    c:Integer;
    q:TComponents;
  begin
    Assert(Assigned(p));
    Result := nil;
    if o.FSubComponents.IndexOf(p)>-1 then begin
      Result:=o;
      FEntries[a*FNDims+b]:=Round(p.FMean);
    end;
    for c:=0 to o.FSubComponents.Count-1 do begin
      q:=RecFillEntry(o.Components[c]);
      if Assigned(q) then
        Result:=q;
    end;
  end;

  procedure RecUsage(o:TComponents);
  var
    a:Integer;
  begin
    if o.FSubComponents.Count=0 then
      Exit;
    o.FUnused:=True;
    for a:=0 to o.FSubComponents.Count-1 do begin
      RecUsage(o.Components[a]);
      o.FUnused:=o.FUnused and o.Components[a].FUnused;
    end;
  end;

begin
  FEntryCount:=AEntryCount;
  if FEntryCount>FLeafComponents.Count then
    FEntryCount:=FLeafComponents.Count;
  SetLength(FEntries,FEntryCount*FNDims);
  for a:=0 to FLeafComponents.Count-1 do
    (FLeafComponents[a] as TComponents).FUnused:=a>=AEntryCount;
  RecUsage(FComponents);
  for a:=0 to FEntryCount-1 do begin
    p:=FLeafComponents[a] as TComponents;
    for b:=FNDims-1 downto 0 do
      p:=RecFillEntry(FComponents);
  end;
end;

procedure TInterpolator.TreeViewAdvancedCustomDrawItem(
  Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState;
  Stage: TCustomDrawStage; var PaintImages, DefaultDraw: Boolean);
var
  r:TRect;
  t:array of Cardinal;
  a,b,c,d,x,y,w,h,n:Integer;
  o:TComponents;
  s:Double;
  nn:TTreeNode;
begin
  if Stage=cdPostPaint then begin
    Sender.Canvas.Lock;
    r:=Node.DisplayRect(True);
    r.Right:=Sender.ClientWidth;
//    if cdsSelected in State then
//      Sender.Canvas.Pen.Color:=clRed
//    else
      Sender.Canvas.Pen.Color:=0;
//    Sender.Canvas.Brush.Color:=clWhite;
    Sender.Canvas.Rectangle(r);
    x:=r.Left+1;
    y:=r.Top+1;
    w:=r.Right-r.Left-2;
    h:=r.Bottom-r.Top-12;
    o:=TComponents(Node.Data);
    n:=o.FHistogram.FBounds[0];
    Sender.Canvas.Pen.Color:=0;
    SetLength(t,Node.Level+1);
    nn:=Node;
    for a:=Node.Level-1 downto 0 do begin
      t[a]:=Round(TComponents(nn.Data).FMean);
      nn:=nn.Parent;
    end;
    for a:=0 to n do begin
      if Node.Parent=nil then
        s:=o.GetValueWeight(a)
      else begin
         s:=0;
         for b:=0 to o.FSubComponents.Count-1 do
           s:=s+o.Components[b].GetValueWeight(a);
      end;
      b:=Round(h*s/o.FHistoCount);
      t[High(t)]:=a;
      Sender.Canvas.Brush.Color:=FColorFunc(t,FComponents.FHistogram.FBounds);
      c:=x+Round(a*w/(n+1));
      d:=x+Round((a+1)*w/(n+1));
      Sender.Canvas.FillRect(Classes.Rect(c,y+h+10,d,y+h-b));
      Sender.Canvas.MoveTo(c,y+h);
      Sender.Canvas.LineTo(c,y+h-b);
      Sender.Canvas.LineTo(d,y+h-b);
      Sender.Canvas.LineTo(d,y+h);
    end;
    Sender.Canvas.Pen.Style:=psDashDotDot;
    for a:=0 to o.FSubComponents.Count-1 do
      with o.Components[a] do
        if not FUnused then begin
          c:=x+Round(Integer(FMin)*w/(n+1));
          Sender.Canvas.MoveTo(c,y);
          Sender.Canvas.LineTo(c,y+h);
          Sender.Canvas.TextOut(c+2,y,IntToStr(a));
          c:=x+Round(Integer(FMax)*w/(n+1));
          Sender.Canvas.MoveTo(c,y);
          Sender.Canvas.LineTo(c,y+h);
        end;
    Sender.Canvas.Pen.Style:=psSolid;
    SetLength(t,0);
    DefaultDraw:=False;
    Sender.Canvas.MoveTo(x,y+h+1);
    Sender.Canvas.LineTo(x+w,y+h+1);
    Sender.Canvas.UnLock;
  end else
    DefaultDraw:=True;
end;

end.
Dann kopiert ihr vor der Implementation diese Abschnitte mit rein.
type
  TRGB=packed record
    B,G,R:Byte;
  end;
  TRGBArray=array[0..65535] of TRGB;
  PRGBArray=^TRGBArray;

  THSV=packed record
    H,S,V:Single;
  end;
  
  
 //
 
 public
    SourceBitmap, DestBitmap, SaveBitmap:TBitmap;
    FHistogram:TTreeHistogram;
    FInterpolator:TInterpolator;
    procedure MakeHistogram;
    procedure MakeInterpolator;
    procedure TrackChange(Panel:TPanel;TrackBar:TTrackBar;Button:TButton);
  end;

Das sind die Funktionen:
function MinValue(t:array of single):single;
var
  a:integer;
begin
  Result:=t[0];
  for a:=1 to high(t) do
    if t[a]<Result then
      Result:=t[a];
end;

function MaxValue(t:array of single):single;
var
  a:integer;
begin
  Result:=t[0];
  for a:=1 to high(t) do
    if t[a]>Result then
      Result:=t[a];
end;

procedure RGB2HSV(const RGB:TRGB;var HSV:THSV);
var
  Delta,Min:single;
begin
  with RGB,HSV do begin
    Min:=MinValue([r,g,b]);
    v:=MaxValue([r,g,b]);
    Delta:=v-Min;
    if v=0 then
      s:=0
    else
      s:=Delta/v;
    if s=0 then begin
      h:=0;
      v:=r;
    end else begin
      if r=v then
        h:=60*(g-b)/Delta;
      if g=v then
        h:=120+60*(b-r)/Delta;
      if b=v then
        h:=240+60*(r-g)/Delta;
      if h<0 then
        h:=h+360;
    end;
  end;
end;

procedure HSV2RGB(const HSV:THSV;var RGB:TRGB);
var
  f:Single;
  i:integer;
  p,q,t,vv:Byte;
begin
  with RGB,HSV do begin
    if s=0 then begin
      r:=Round(v);
      g:=r;
      b:=r;
    end else begin
      i:=Trunc(h/60) mod 6;
      f:=Frac(h/60);
      p:=Round(v*(1-s));
      q:=Round(v*(1-s*f));
      t:=Round(v*(1-s*(1-f)));
      vv:=Round(v);
      case i of
        0:begin r:=vv;g:=t;b:=p end;
        1:begin r:=q;g:=vv;b:=p end;
        2:begin r:=p;g:=vv;b:=t end;
        3:begin r:=p;g:=q;b:=vv end;
        4:begin r:=t;g:=p;b:=vv end;
        5:begin r:=vv;g:=p;b:=q end;
      end;
    end;
  end;
end;

procedure TForm1.MakeHistogram;
var
  a,b,N:Integer;
  p:PRGBArray;
  h:THSV;
begin
  if Assigned(FInterpolator) then
    FInterpolator.Destroy;
  N:=TrackBar2.Position;
  FHistogram:=TTreeHistogram.Create([N,N,N]);
  ProgressBar1.Max:=SourceBitmap.Height-1;
  for b:=0 to SourceBitmap.Height-1 do begin
    ProgressBar1.Position:=b;
    ProgressBar1.Repaint;
    p:=PRGBArray(SourceBitmap.ScanLine[b]);
    for a:=0 to SourceBitmap.Width-1 do begin
      RGB2HSV(p[a],h);
      with h do
        FHistogram.Add([Round(H*N/360),Round(S*N),Round(V*N/255)]);
    end;
  end;
  FInterpolator:=TInterpolator.Create(FHistogram,TrackBar1.Position);
  GroupBox4.Enabled:=True;
  MakeInterpolator;
  Button1.Enabled:=False;
end;

function ColorFunc(Values,Bounds:array of Cardinal):TColor;
var
  RGB:TRGB;
  HSV:THSV;
begin
  case High(Values) of
    0:with HSV do begin
        H:=360*Values[0]/Bounds[0];
        S:=1;
        V:=255;
      end;
    1:with HSV do begin
        H:=360*Values[0]/Bounds[0];
        S:=Values[1]/Bounds[1];
        V:=255;
      end;
    2:with HSV do begin
        H:=360*Values[0]/Bounds[0];
        S:=Values[1]/Bounds[1];
        V:=255*Values[2]/Bounds[2];
      end;
  end;
  HSV2RGB(HSV,RGB);
  with RGB do
    Result:=Windows.RGB(R,G,B);
end;

procedure TForm1.MakeInterpolator;
var
  Pal:PLogPalette;
  cc:TColor;
  t:array[0..2] of Cardinal;
  a,b,c,N:Integer;
  p,q:PRGBArray;
  h:THSV;
  hh,ss,vv:Cardinal;
type
  TByteArray=array[0..65535] of Byte;
  PByteArray=^TByteArray;
begin
  FInterpolator.Interpolate(TrackBar3.Position);
  FInterpolator.AssignToTreeView(TreeView1,ColorFunc);
  if Assigned(SaveBitmap) then begin
    GetMem(Pal,SizeOf(TLogPalette)+256*SizeOf(TPaletteEntry));
    Pal.palVersion:=$300;
    Pal.palNumEntries:=256;
    for a:=0 to 255 do begin
      FInterpolator.GetEntry(a,[@t[0],@t[1],@t[2]]);
      cc:=ColorFunc(t,[TrackBar2.Position,TrackBar2.Position,TrackBar2.Position]);
      {$R-}
      with Pal.palPalEntry[a] do begin
        peRed:=GetRValue(cc);
        peGreen:=GetGValue(cc);
        peBlue:=GetBValue(cc);
        peFlags:=0;
      end;
      {$R+}
    end;
    SaveBitmap.Palette:=CreatePalette(Pal^);
    FreeMem(Pal);
  end;
  N:=TrackBar2.Position;
  DestBitmap.Assign(SourceBitmap);
  ProgressBar1.Max:=SourceBitmap.Height-1;
  for b:=0 to SourceBitmap.Height-1 do begin
    ProgressBar1.Position:=b;
    Progressbar1.Repaint;
    p:=PRGBArray(SourceBitmap.ScanLine[b]);
    q:=PRGBArray(DestBitmap.ScanLine[b]);
    for a:=0 to SourceBitmap.Width-1 do begin
      RGB2HSV(p[a],h);
      with h do begin
        hh:=Round(H*N/360);
        ss:=Round(S*N);
        vv:=Round(V*N/255);
      end;
      c:=FInterpolator.GetEntryIndex([hh,ss,vv],CheckBox1.Checked);
      if Assigned(SaveBitmap) then
        PByteArray(SaveBitmap.ScanLine[b])[a]:=c;
      FInterpolator.GetEntry(c,[@hh,@ss,@vv]);
      with h do begin
        H:=hh*360/N;
        S:=ss/N;
        V:=vv*255/N;
      end;
      HSV2RGB(h,q[a]);
    end;
  end;
  PaintBox2.Repaint;
  PaintBox3.Repaint;
  Button2.Enabled:=False;
end;
Zuletzt die OnEvents der Komponenten:
procedure TForm1.Button3Click(Sender: TObject);
var
  p:TPicture;
begin
  if OpenPictureDialog1.Execute then begin
    p:=TPicture.Create;
    p.LoadFromFile(OpenPictureDialog1.FileName);
    SourceBitmap.Assign(p.Graphic);
    p.Destroy;
    PaintBox1.Repaint;
    PaintBox2.Repaint;
    MakeHistogram;
    Button4.Enabled:=True;
    GroupBox3.Enabled:=True;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SourceBitmap:=TBitmap.Create;
  SourceBitmap.PixelFormat:=pf24bit;
  DestBitmap:=TBitmap.Create;
  SourceBitmap.PixelFormat:=pf24bit;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  PaintBox1.Canvas.StretchDraw(Rect(0,0,PaintBox1.Width,PaintBox1.Height),SourceBitmap);
end;

procedure TForm1.PaintBox2Paint(Sender: TObject);
begin
  PaintBox2.Canvas.StretchDraw(Rect(0,0,PaintBox2.Width,PaintBox2.Height),DestBitmap);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  MakeInterpolator;
end;

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

procedure TForm1.PaintBox3Paint(Sender: TObject);
var
  a,n:Integer;
  t:array[0..2] of Cardinal;
begin
  if Assigned(FInterpolator) then begin
    n:=FInterpolator.EntryCount;
    for a:=0 to n-1 do begin
      FInterpolator.GetEntry(a,[@t[0],@t[1],@t[2]]);
      PaintBox3.Canvas.Brush.Color:=ColorFunc(t,[TrackBar2.Position,TrackBar2.Position,TrackBar2.Position]);
      PaintBox3.Canvas.FillRect(Rect(Round(a*PaintBox3.Width/n),0,Round((a+1)*PaintBox3.Width/n),PaintBox3.Height));
    end;
  end;
end;

procedure TForm1.TrackChange(Panel: TPanel; TrackBar: TTrackBar;
  Button: TButton);
begin
  Panel.Caption:=Panel.Hint+' ('+IntToStr(TrackBar.Position)+')';
  Button.Enabled:=True;
end;

procedure TForm1.TrackBar2Change(Sender: TObject);
begin
  TrackChange(Panel5,TrackBar2,Button1);
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  TrackChange(Panel4,TrackBar1,Button1);
end;

procedure TForm1.TrackBar3Change(Sender: TObject);
begin
  TrackChange(Panel6,TrackBar3,Button2);
end;

procedure TForm1.TreeView1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (htOnRight in TreeView1.GetHitTestInfoAt(X,Y)) and (TreeView1.GetNodeAt(X,Y)<>nil) then
    TreeView1.Selected:=TreeView1.GetNodeAt(X,Y);
end;

procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  Button2.Enabled:=True;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  if SaveDialog1.Execute then begin
    TrackBar3.Position:=256;
    SaveBitmap:=TBitmap.Create;
    SaveBitmap.PixelFormat:=pf8bit;
    SaveBitmap.Width:=SourceBitmap.Width;
    SaveBitmap.Height:=SourceBitmap.Height;
    MakeInterpolator;
    SaveBitmap.SaveToFile(SaveDialog1.FileName);
    SaveBitmap.Destroy;
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(FInterpolator) then
    FInterpolator.Destroy;
  SourceBitmap.Destroy;
  DestBitmap.Destroy;
end;




Nun müsste euer Code ein echtes 8 Bit Bitmap berechnen können.

Ein fertiges Projekt kann hier heruntergeladen werden.



Keine Kommentare:

Kommentar veröffentlichen

Beliebte Posts

Translate