Ignore:
Timestamp:
Dec 22, 2016, 8:49:19 PM (8 years ago)
Author:
chronos
Message:
  • Modified: Updated BGRABitmap package.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/Packages/bgrabitmap/bgradithering.pas

    r472 r494  
    66
    77uses
    8   Classes, SysUtils, BGRAFilters, BGRAPalette, BGRABitmapTypes;
     8  Classes, SysUtils, BGRAFilterType, BGRAPalette, BGRABitmapTypes;
    99
    1010type
     11  TOutputPixelProc = procedure(X,Y: NativeInt; AColorIndex: NativeInt; AColor: TBGRAPixel) of object;
    1112
    1213  { TDitheringTask }
     
    1718    FIgnoreAlpha: boolean;
    1819    FPalette: TBGRACustomApproxPalette;
     20    FCurrentOutputScanline: PBGRAPixel;
     21    FCurrentOutputY: NativeInt;
     22    FOutputPixel : TOutputPixelProc;
     23    FDrawMode: TDrawMode;
     24    procedure OutputPixel(X,Y: NativeInt; {%H-}AColorIndex: NativeInt; AColor: TBGRAPixel); virtual;
     25    procedure ApproximateColor(const AColor: TBGRAPixel; out AApproxColor: TBGRAPixel; out AIndex: integer);
    1926  public
     27    constructor Create(ASource: IBGRAScanner; APalette: TBGRACustomApproxPalette; ADestination: TBGRACustomBitmap; AIgnoreAlpha: boolean; ABounds: TRect); overload;
    2028    constructor Create(bmp: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; AInPlace: boolean; AIgnoreAlpha: boolean; ABounds: TRect); overload;
    2129    constructor Create(bmp: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette; AInPlace: boolean; AIgnoreAlpha: boolean); overload;
     30    property OnOutputPixel: TOutputPixelProc read FOutputPixel write FOutputPixel;
     31    property DrawMode: TDrawMode read FDrawMode write FDrawMode;
    2232  end;
    2333
     
    3444  protected
    3545    procedure DoExecute; override;
     46  end;
     47
     48  { TDitheringToIndexedImage }
     49
     50  TDitheringToIndexedImage = class
     51  protected
     52    FBitOrder: TRawImageBitOrder;
     53    FByteOrder: TRawImageByteOrder;
     54    FBitsPerPixel: integer;
     55    FLineOrder: TRawImageLineOrder;
     56    FPalette: TBGRACustomApproxPalette;
     57    FIgnoreAlpha: boolean;
     58    FTransparentColorIndex: NativeInt;
     59
     60    //following variables are used during dithering
     61    FCurrentScanlineSize: PtrInt;
     62    FCurrentData: PByte;
     63    FCurrentOutputY: NativeInt;
     64    FCurrentOutputScanline: PByte;
     65    FCurrentBitOrderMask: NativeInt;
     66    FCurrentMaxY: NativeInt;
     67
     68    procedure SetPalette(AValue: TBGRACustomApproxPalette);
     69    procedure SetIgnoreAlpha(AValue: boolean);
     70    procedure SetLineOrder(AValue: TRawImageLineOrder);
     71    procedure SetBitOrder(AValue: TRawImageBitOrder); virtual;
     72    procedure SetBitsPerPixel(AValue: integer); virtual;
     73    procedure SetByteOrder(AValue: TRawImageByteOrder); virtual;
     74    procedure OutputPixelSubByte(X,Y: NativeInt; AColorIndex: NativeInt; {%H-}AColor: TBGRAPixel); virtual;
     75    procedure OutputPixelFullByte(X,Y: NativeInt; AColorIndex: NativeInt; {%H-}AColor: TBGRAPixel); virtual;
     76    function GetScanline(Y: NativeInt): Pointer; virtual;
     77    function GetTransparentColorIndex: integer;
     78    procedure SetTransparentColorIndex(AValue: integer);
     79  public
     80    constructor Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer); //use platform byte order
     81    constructor Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer; AByteOrder: TRawImageByteOrder); //maybe necessary if larger than 8 bits per pixel
     82
     83    function DitherImage(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap): Pointer; overload; //use minimum scanline size
     84    function DitherImage(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): Pointer; overload;
     85    procedure DitherImageTo(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AData: Pointer); overload; //use minimum scanline size
     86    procedure DitherImageTo(AAlgorithm: TDitheringAlgorithm; AImage: TBGRACustomBitmap; AData: Pointer; AScanlineSize: PtrInt); overload;
     87    function ComputeMinimumScanlineSize(AWidthInPixels: integer): PtrInt;
     88    function AllocateSpaceForIndexedData(AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): pointer;
     89
     90    //optional customization of format
     91    property BitsPerPixel: integer read FBitsPerPixel write SetBitsPerPixel;
     92    property BitOrder: TRawImageBitOrder read FBitOrder write SetBitOrder;
     93    property ByteOrder: TRawImageByteOrder read FByteOrder write SetByteOrder;
     94    property LineOrder: TRawImageLineOrder read FLineOrder write SetLineOrder;
     95
     96    property Palette: TBGRACustomApproxPalette read FPalette write SetPalette;
     97    property IgnoreAlpha: boolean read FIgnoreAlpha write SetIgnoreAlpha;
     98
     99    //when there is no transparent color in the palette, or that IgnoreAlpha is set to True,
     100    //this allows to define the index for the fully transparent color
     101    property DefaultTransparentColorIndex: integer read GetTransparentColorIndex write SetTransparentColorIndex;
    36102  end;
    37103
     
    40106function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette;
    41107  AIgnoreAlpha: boolean; ABounds: TRect): TDitheringTask; overload;
     108function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; ABounds: TRect): TDitheringTask; overload;
     109function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm; ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; APalette: TBGRACustomApproxPalette;
     110    AIgnoreAlpha: boolean; ABounds: TRect): TDitheringTask; overload;
     111
     112function DitherImageTo16Bit(AAlgorithm: TDitheringAlgorithm; ABitmap: TBGRACustomBitmap): TBGRACustomBitmap;
    42113
    43114implementation
     115
     116uses BGRABlend;
    44117
    45118function AbsRGBADiff(const c1, c2: TExpandedPixel): NativeInt;
     
    68141end;
    69142
     143function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm;
     144  ASource: IBGRAScanner; ADestination: TBGRACustomBitmap; ABounds: TRect
     145  ): TDitheringTask;
     146begin
     147  result := CreateDitheringTask(AAlgorithm, ASource, ADestination, nil, true, ABounds);
     148end;
     149
     150function CreateDitheringTask(AAlgorithm: TDitheringAlgorithm;
     151  ASource: IBGRAScanner; ADestination: TBGRACustomBitmap;
     152  APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABounds: TRect
     153  ): TDitheringTask;
     154begin
     155  result := nil;
     156  case AAlgorithm of
     157    daNearestNeighbor: result := TNearestColorTask.Create(ASource, APalette, ADestination, AIgnoreAlpha, ABounds);
     158    daFloydSteinberg: result := TFloydSteinbergDitheringTask.Create(ASource, APalette, ADestination, AIgnoreAlpha, ABounds);
     159    else raise exception.Create('Unknown algorithm');
     160  end;
     161end;
     162
     163function DitherImageTo16Bit(AAlgorithm: TDitheringAlgorithm;
     164  ABitmap: TBGRACustomBitmap): TBGRACustomBitmap;
     165var
     166  palette16bit: TBGRA16BitPalette;
     167  dither: TDitheringTask;
     168begin
     169  palette16bit := TBGRA16BitPalette.Create;
     170  dither := CreateDitheringTask(AAlgorithm, ABitmap, palette16bit, false);
     171  result := dither.Execute;
     172  dither.Free;
     173  palette16bit.Free;
     174end;
     175
     176{ TDitheringToIndexedImage }
     177
     178procedure TDitheringToIndexedImage.SetBitsPerPixel(AValue: integer);
     179begin
     180  if not (AValue in [1,2,4,8,16,32]) then
     181    raise exception.Create('Invalid value for bits per pixel. Allowed values: 1,2,4,8,16,32.');
     182  if FBitsPerPixel=AValue then Exit;
     183  FBitsPerPixel:=AValue;
     184end;
     185
     186procedure TDitheringToIndexedImage.SetByteOrder(AValue: TRawImageByteOrder);
     187begin
     188  if FByteOrder=AValue then Exit;
     189  FByteOrder:=AValue;
     190end;
     191
     192procedure TDitheringToIndexedImage.OutputPixelSubByte(X, Y: NativeInt;
     193  AColorIndex: NativeInt; AColor: TBGRAPixel);
     194var p: PByte;
     195begin
     196  if y <> FCurrentOutputY then
     197  begin
     198    FCurrentOutputY := y;
     199    FCurrentOutputScanline := GetScanline(Y);
     200  end;
     201  if AColorIndex = -1 then AColorIndex := FTransparentColorIndex;
     202  case FBitsPerPixel of
     203    1: begin
     204         p := FCurrentOutputScanline+(x shr 3);
     205         p^ := p^ or ((AColorIndex and 1) shl ((x xor FCurrentBitOrderMask) and 7));
     206       end;
     207    2: begin
     208         p := FCurrentOutputScanline+(x shr 2);
     209         p^ := p^ or ((AColorIndex and 1) shl (((x xor FCurrentBitOrderMask) and 3) shl 1));
     210       end;
     211    4: begin
     212         p := FCurrentOutputScanline+(x shr 1);
     213         p^ := p^ or ((AColorIndex and 1) shl (((x xor FCurrentBitOrderMask) and 1) shl 2));
     214       end;
     215  end;
     216end;
     217
     218procedure TDitheringToIndexedImage.OutputPixelFullByte(X, Y: NativeInt;
     219  AColorIndex: NativeInt; AColor: TBGRAPixel);
     220begin
     221  if y <> FCurrentOutputY then
     222  begin
     223    FCurrentOutputY := y;
     224    FCurrentOutputScanline := GetScanline(Y);
     225  end;
     226  if AColorIndex = -1 then AColorIndex := FTransparentColorIndex;
     227  case FBitsPerPixel of
     228    8: (FCurrentOutputScanline+x)^ := AColorIndex;
     229    16: (PWord(FCurrentOutputScanline)+x)^ := AColorIndex;
     230    32: (PDWord(FCurrentOutputScanline)+x)^ := AColorIndex;
     231  end;
     232end;
     233
     234function TDitheringToIndexedImage.GetScanline(Y: NativeInt): Pointer;
     235begin
     236  if FLineOrder = riloTopToBottom then
     237    result := FCurrentData + Y*FCurrentScanlineSize
     238  else
     239    result := FCurrentData + (FCurrentMaxY-Y)*FCurrentScanlineSize
     240end;
     241
     242procedure TDitheringToIndexedImage.SetIgnoreAlpha(AValue: boolean);
     243begin
     244  if FIgnoreAlpha=AValue then Exit;
     245  FIgnoreAlpha:=AValue;
     246end;
     247
     248procedure TDitheringToIndexedImage.SetTransparentColorIndex(AValue: integer);
     249begin
     250  if FTransparentColorIndex=AValue then Exit;
     251  FTransparentColorIndex:=AValue;
     252end;
     253
     254function TDitheringToIndexedImage.GetTransparentColorIndex: integer;
     255begin
     256  result := FTransparentColorIndex;
     257end;
     258
     259procedure TDitheringToIndexedImage.SetPalette(AValue: TBGRACustomApproxPalette);
     260begin
     261  if FPalette=AValue then Exit;
     262  FPalette:=AValue;
     263end;
     264
     265procedure TDitheringToIndexedImage.SetLineOrder(AValue: TRawImageLineOrder);
     266begin
     267  if FLineOrder=AValue then Exit;
     268  FLineOrder:=AValue;
     269end;
     270
     271procedure TDitheringToIndexedImage.SetBitOrder(AValue: TRawImageBitOrder);
     272begin
     273  if FBitOrder=AValue then Exit;
     274  FBitOrder:=AValue;
     275end;
     276
     277constructor TDitheringToIndexedImage.Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer);
     278begin
     279  BitsPerPixel:= ABitsPerPixelForIndices;
     280  BitOrder := riboReversedBits; //convention in BMP format
     281  {$IFDEF ENDIAN_LITTLE}
     282  ByteOrder:= riboLSBFirst;
     283  {$ELSE}
     284  ByteOrder:= riboMSBFirst;
     285  {$ENDIF}
     286  Palette := APalette;
     287  IgnoreAlpha:= AIgnoreAlpha;
     288end;
     289
     290constructor TDitheringToIndexedImage.Create(APalette: TBGRACustomApproxPalette; AIgnoreAlpha: boolean; ABitsPerPixelForIndices: integer;
     291  AByteOrder: TRawImageByteOrder);
     292begin
     293  BitsPerPixel:= ABitsPerPixelForIndices;
     294  BitOrder := riboReversedBits; //convention in BMP format
     295  ByteOrder:= AByteOrder;
     296  Palette := APalette;
     297  IgnoreAlpha:= AIgnoreAlpha;
     298end;
     299
     300function TDitheringToIndexedImage.ComputeMinimumScanlineSize(
     301  AWidthInPixels: integer): PtrInt;
     302begin
     303  result := (AWidthInPixels*FBitsPerPixel+7) shr 3;
     304end;
     305
     306function TDitheringToIndexedImage.AllocateSpaceForIndexedData(AImage: TBGRACustomBitmap;
     307  AScanlineSize: PtrInt): pointer;
     308var size: integer;
     309begin
     310  size := AScanlineSize * AImage.Height;
     311  GetMem(result, size);
     312  Fillchar(result^, size, 0);
     313end;
     314
     315function TDitheringToIndexedImage.DitherImage(AAlgorithm: TDitheringAlgorithm;
     316  AImage: TBGRACustomBitmap): Pointer;
     317begin
     318  result := DitherImage(AAlgorithm, AImage, ComputeMinimumScanlineSize(AImage.Width));
     319end;
     320
     321procedure TDitheringToIndexedImage.DitherImageTo(AAlgorithm: TDitheringAlgorithm;
     322  AImage: TBGRACustomBitmap; AData: Pointer);
     323begin
     324  DitherImageTo(AAlgorithm, AImage, AData, ComputeMinimumScanlineSize(AImage.Width));
     325end;
     326
     327function TDitheringToIndexedImage.DitherImage(AAlgorithm: TDitheringAlgorithm;
     328  AImage: TBGRACustomBitmap; AScanlineSize: PtrInt): Pointer;
     329begin
     330  result := AllocateSpaceForIndexedData(AImage, AScanlineSize);
     331  DitherImageTo(AAlgorithm, AImage, result, AScanlineSize);
     332end;
     333
     334procedure TDitheringToIndexedImage.DitherImageTo(AAlgorithm: TDitheringAlgorithm;
     335  AImage: TBGRACustomBitmap; AData: Pointer; AScanlineSize: PtrInt);
     336var ditherTask: TDitheringTask;
     337begin
     338  FCurrentOutputY := -1;
     339  FCurrentOutputScanline := nil;
     340  FCurrentData := AData;
     341  FCurrentMaxY:= AImage.Height-1;
     342  FCurrentScanlineSize:= AScanlineSize;
     343
     344  ditherTask := CreateDitheringTask(AAlgorithm, AImage, FPalette, FIgnoreAlpha);
     345  try
     346    ditherTask.Inplace := True; //do not allocate destination
     347    if BitsPerPixel >= 8 then
     348      ditherTask.OnOutputPixel := @OutputPixelFullByte
     349    else
     350    begin
     351      ditherTask.OnOutputPixel:= @OutputPixelSubByte;
     352      if BitOrder = riboBitsInOrder then
     353        FCurrentBitOrderMask := 0
     354      else
     355        FCurrentBitOrderMask := $ff;
     356    end;
     357    ditherTask.Execute;
     358  finally
     359    ditherTask.Free;
     360  end;
     361end;
     362
    70363{ TDitheringTask }
     364
     365procedure TDitheringTask.OutputPixel(X, Y: NativeInt; AColorIndex: NativeInt;
     366  AColor: TBGRAPixel);
     367begin
     368  if Y <> FCurrentOutputY then
     369  begin
     370    FCurrentOutputY := Y;
     371    FCurrentOutputScanline := Destination.ScanLine[y];
     372  end;
     373  PutPixels(FCurrentOutputScanline+x, @AColor, 1, FDrawMode, 255);
     374end;
     375
     376procedure TDitheringTask.ApproximateColor(const AColor: TBGRAPixel;
     377  out AApproxColor: TBGRAPixel; out AIndex: integer);
     378begin
     379  if FPalette <> nil then
     380  begin
     381    AIndex := FPalette.FindNearestColorIndex(AColor, FIgnoreAlpha);
     382    if AIndex = -1 then
     383      AApproxColor := BGRAPixelTransparent
     384    else
     385      AApproxColor := FPalette.Color[AIndex];
     386  end else
     387  begin
     388    if AColor.alpha = 0 then
     389    begin
     390      AApproxColor := BGRAPixelTransparent;
     391      AIndex := -1;
     392    end else
     393    begin
     394      AApproxColor := AColor;
     395      AIndex := 0;
     396    end;
     397  end;
     398end;
     399
     400constructor TDitheringTask.Create(ASource: IBGRAScanner;
     401  APalette: TBGRACustomApproxPalette; ADestination: TBGRACustomBitmap;
     402  AIgnoreAlpha: boolean; ABounds: TRect);
     403begin
     404  FPalette := APalette;
     405  SetSource(ASource);
     406  FBounds := ABounds;
     407  FIgnoreAlpha:= AIgnoreAlpha;
     408  FCurrentOutputY := -1;
     409  FCurrentOutputScanline:= nil;
     410  OnOutputPixel:= @OutputPixel;
     411  Destination := ADestination;
     412  FDrawMode:= dmSet;
     413end;
    71414
    72415constructor TDitheringTask.Create(bmp: TBGRACustomBitmap;
     
    75418begin
    76419  FPalette := APalette;
    77   FSource := bmp;
     420  SetSource(bmp);
    78421  FBounds := ABounds;
    79422  FIgnoreAlpha:= AIgnoreAlpha;
    80   if AInPlace then Destination := FSource;
     423  FCurrentOutputY := -1;
     424  FCurrentOutputScanline:= nil;
     425  OnOutputPixel:= @OutputPixel;
     426  InPlace := AInPlace;
     427  FDrawMode:= dmSet;
    81428end;
    82429
     
    85432begin
    86433  FPalette := APalette;
    87   FSource := bmp;
     434  SetSource(bmp);
    88435  FBounds := rect(0,0,bmp.Width,bmp.Height);
    89436  FIgnoreAlpha:= AIgnoreAlpha;
    90   if AInPlace then Destination := FSource;
     437  FCurrentOutputY := -1;
     438  FCurrentOutputScanline:= nil;
     439  OnOutputPixel:= @OutputPixel;
     440  InPlace := AInPlace;
     441  FDrawMode:= dmSet;
    91442end;
    92443
     
    125476
    126477var
    127   p,pNext,pDest: PBGRAPixel;
     478  p,pNext: PExpandedPixel;
     479  destX,destY: NativeInt;
    128480  orig,cur,approxExp: TExpandedPixel;
    129481  approx: TBGRAPixel;
     482  approxIndex: integer;
    130483  curPix,diff: TAccPixel;
    131484  i: NativeInt;
    132485  yWrite: NativeInt;
    133486  tempLine, currentLine, nextLine: TLine;
     487
     488  nextScan,curScan: PExpandedPixel;
    134489
    135490  function ClampWordDiv(AValue: NativeInt): Word; inline;
     
    158513  setlength(currentLine,w);
    159514  setlength(nextLine,w);
     515  curScan := nil;
     516  nextScan := RequestSourceExpandedScanLine(FBounds.Left, FBounds.Top, FBounds.Right-FBounds.Left);
    160517  for yWrite := 0 to h-1 do
    161518  begin
    162519    if GetShouldStop(yWrite) then break;
    163     p := FSource.ScanLine[yWrite+FBounds.Top]+FBounds.Left;
    164     pDest := FDestination.ScanLine[yWrite+FBounds.Top]+FBounds.Left;
     520    ReleaseSourceExpandedScanLine(curScan);
     521    curScan := nextScan;
     522    nextScan := nil;
     523    p := curScan;
     524    destX := FBounds.Left;
     525    destY := yWrite+FBounds.Top;
    165526    if yWrite < h-1 then
    166       pNext := FSource.ScanLine[yWrite+FBounds.Top+1]+FBounds.Left
    167     else
    168       pNext := nil;
     527      nextScan := RequestSourceExpandedScanLine(FBounds.Left,yWrite+FBounds.Top+1, FBounds.Right-FBounds.Left);
     528    pNext := nextScan;
    169529    if odd(yWrite) then
    170530    begin
    171531      inc(p, w);
    172       inc(pDest, w);
     532      inc(destX, w);
    173533      if pNext<>nil then inc(pNext, w);
    174534      for i := w-1 downto 0 do
    175535      begin
    176536        dec(p);
    177         dec(pDest);
     537        dec(destX);
    178538        if pNext<>nil then dec(pNext);
    179539        if p^.alpha <> 0 then
    180540        begin
    181           orig := GammaExpansion(p^);
     541          orig := p^;
    182542          with currentLine[i] do
    183543          begin
     
    191551            cur.blue := ClampWordDiv(curPix.blue);
    192552          end;
    193           approx := FPalette.FindNearestColor(GammaCompression(cur), FIgnoreAlpha);
     553          ApproximateColor(GammaCompression(cur), approx, approxIndex);
    194554          approxExp := GammaExpansion(approx);
    195555          diff.alpha := Div16(curPix.alpha - (approxExp.alpha shl ErrorPrecisionShift));
     
    207567          if i > 0 then
    208568          begin
    209             if AbsRGBADiff(GammaExpansion((p-1)^),orig) < MaxColorDiffForDiffusion then
     569            if AbsRGBADiff((p-1)^,orig) < MaxColorDiffForDiffusion then
    210570              AddError(currentLine[i-1], diff, 7);
    211571          end;
     
    214574            if i > 0 then
    215575            begin
    216               if AbsRGBADiff(GammaExpansion((pNext-1)^),orig) < MaxColorDiffForDiffusion then
     576              if AbsRGBADiff((pNext-1)^,orig) < MaxColorDiffForDiffusion then
    217577                AddError(nextLine[i-1], diff, 1);
    218578            end;
    219             if AbsRGBADiff(GammaExpansion(pNext^),orig) < MaxColorDiffForDiffusion then
     579            if AbsRGBADiff(pNext^,orig) < MaxColorDiffForDiffusion then
    220580              AddError(nextLine[i], diff, 5);
    221581            if i < w-1 then
    222582            begin
    223               if AbsRGBADiff(GammaExpansion((pNext+1)^),orig) < MaxColorDiffForDiffusion then
     583              if AbsRGBADiff((pNext+1)^,orig) < MaxColorDiffForDiffusion then
    224584                AddError(nextLine[i+1], diff, 3);
    225585            end;
    226586          end;
    227           pDest^ := approx;
     587          OnOutputPixel(destX,destY,approxIndex,approx);
    228588        end;
    229589      end
     
    234594      if p^.alpha <> 0 then
    235595      begin
    236         orig := GammaExpansion(p^);
     596        orig := p^;
    237597        with currentLine[i] do
    238598        begin
     
    246606          cur.blue := ClampWordDiv(curPix.blue);
    247607        end;
    248         approx := FPalette.FindNearestColor(GammaCompression(cur), FIgnoreAlpha);
     608        ApproximateColor(GammaCompression(cur), approx, approxIndex);
    249609        approxExp := GammaExpansion(approx);
    250610        diff.alpha := Div16(curPix.alpha - (approxExp.alpha shl ErrorPrecisionShift));
     
    262622        if i < w-1 then
    263623        begin
    264           if AbsRGBADiff(GammaExpansion((p+1)^),orig) < MaxColorDiffForDiffusion then
     624          if AbsRGBADiff((p+1)^,orig) < MaxColorDiffForDiffusion then
    265625            AddError(currentLine[i+1], diff, 7);
    266626        end;
    267         if nextLine <> nil then
     627        if pNext <> nil then
    268628        begin
    269629          if i > 0 then
    270630          begin
    271             if AbsRGBADiff(GammaExpansion((pNext-1)^),orig) < MaxColorDiffForDiffusion then
     631            if AbsRGBADiff((pNext-1)^,orig) < MaxColorDiffForDiffusion then
    272632              AddError(nextLine[i-1], diff, 3);
    273633          end;
    274           if AbsRGBADiff(GammaExpansion(pNext^),orig) < MaxColorDiffForDiffusion then
     634          if AbsRGBADiff(pNext^,orig) < MaxColorDiffForDiffusion then
    275635            AddError(nextLine[i], diff, 5);
    276636          if i < w-1 then
    277637          begin
    278             if AbsRGBADiff(GammaExpansion((pNext+1)^),orig) < MaxColorDiffForDiffusion then
     638            if AbsRGBADiff((pNext+1)^,orig) < MaxColorDiffForDiffusion then
    279639              AddError(nextLine[i+1], diff, 1);
    280640          end;
    281641        end;
    282         pDest^ := approx;
     642        OnOutputPixel(destX,destY,approxIndex,approx);
    283643      end;
    284644      inc(p);
    285       inc(pDest);
     645      inc(destX);
    286646      if pNext<>nil then inc(pNext);
    287647    end;
     
    300660      end;
    301661  end;
    302   FDestination.InvalidateBitmap;
     662  ReleaseSourceExpandedScanLine(curScan);
     663  ReleaseSourceExpandedScanLine(nextScan);
     664  Destination.InvalidateBitmap;
    303665end;
    304666
     
    306668
    307669procedure TNearestColorTask.DoExecute;
    308 var yb,xb: integer;
    309   psrc,pdest: PBGRAPixel;
     670var yb,xb: NativeInt;
     671  curScan,psrc: PBGRAPixel;
     672  colorIndex: LongInt;
     673  colorValue: TBGRAPixel;
    310674begin
    311675  for yb := FBounds.Top to FBounds.Bottom - 1 do
    312676  begin
    313677    if GetShouldStop(yb) then break;
    314     psrc := FSource.ScanLine[yb] + FBounds.Left;
    315     pdest := FDestination.ScanLine[yb] + FBounds.Left;
    316     for xb := FBounds.Right - FBounds.Left -1 downto 0 do
     678    curScan := RequestSourceScanLine(FBounds.Left,yb,FBounds.Right-FBounds.Left);
     679    psrc := curScan;
     680    for xb := FBounds.Left to FBounds.Right-1 do
    317681    begin
    318       pdest^ := FPalette.FindNearestColor(psrc^, FIgnoreAlpha);
    319       inc(pdest);
     682      ApproximateColor(psrc^, colorValue, colorIndex);
     683      OnOutputPixel(xb,yb,colorIndex,colorValue);
    320684      inc(psrc);
    321685    end;
    322   end;
    323   FDestination.InvalidateBitmap;
     686    ReleaseSourceScanLine(curScan);
     687  end;
     688  Destination.InvalidateBitmap;
    324689end;
    325690
Note: See TracChangeset for help on using the changeset viewer.