unit LazFreeTypeIntfDrawer;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics, EasyLazFreeType, IntfGraphics, FPimage;

type
  TLazIntfImageGetPixelAtProc = procedure(p: pointer; out Color: TFPColor);
  TLazIntfImageSetPixelAtProc = procedure(p: pointer; const Color: TFPColor);

  { TIntfFreeTypeDrawer }

  TIntfFreeTypeDrawer = class(TFreeTypeDrawer)
  private
    FColor: TFPColor;
    FDestination: TLazIntfImage;
    FHasPixelAtProc: boolean;
    FGetPixelAtProc: TLazIntfImageGetPixelAtProc;
    FSetPixelAtProc: TLazIntfImageSetPixelAtProc;
    FPixelSizeInBytes: longword;
    FWidth, FHeight: integer;
    procedure SetDestination(AValue: TLazIntfImage);
  protected
    procedure RenderDirectly(x, y, tx: integer; data: pointer);
    procedure RenderDirectlyClearType(x, y, tx: integer; data: pointer);
    procedure InternalMergeColorOver(var merge: TFPColor; const c: TFPColor; calpha: word); inline;
    procedure MergeColorOver(var merge: TFPColor; const c: TFPColor); inline;
    procedure MergeColorOver(var merge: TFPColor; const c: TFPColor; ApplyOpacity: byte); inline;
    procedure DrawPixelAt(p: pointer; const c: TFPColor);
    procedure DrawPixelAt(p: pointer; const c: TFPColor; applyOpacity: byte);
    procedure ClearTypePixelAt(p: pointer; Cr,Cg,Cb: byte; const Color: TFPColor);
    function UnclippedGetPixelAddress(x, y: integer): pointer; inline;
    function ClippedGetPixelAddress(x, y: integer): pointer; inline;
  public
    ClearTypeRGBOrder: boolean;
    constructor Create(ADestination: TLazIntfImage);
    procedure ClippedDrawPixel(x,y: integer; const c: TFPColor);
    procedure UnclippedDrawPixel(x,y: integer; const c: TFPColor);
    procedure ClippedClearTypePixel(x,y: integer; Cr,Cg,Cb: byte; const Color: TFPColor);
    procedure UnclippedClearTypePixel(x,y: integer; Cr,Cg,Cb: byte; const Color: TFPColor);
    procedure DrawVertLine(x,y1,y2: integer; const c: TFPColor);
    procedure SetHorizLine(x1,y,x2: integer; const c: TFPColor);
    procedure DrawHorizLine(x1,y,x2: integer; const c: TFPColor);
    procedure FillPixels(const c: TFPColor);
    procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TFPColor); override;
    property Destination: TLazIntfImage read FDestination write SetDestination;
    destructor Destroy; override;
  end;

implementation

uses LCLType, GraphType;

type
  PFPColorBytes = ^TFPColorBytes;
  TFPColorBytes = record
    {$ifdef ENDIAN_LITTLE}
    Rl, Rh, Gl, Gh, Bl, Bh, Al, Ah: Byte;
    {$else}
    Rh, Rl, Gh, Gl, Bh, Bl, Ah, Al: Byte;
    {$endif}
  end;

  PFourBytes = ^TFourBytes;
  TFourBytes = record
    B0, B1, B2, B3: Byte;
  end;

{ TIntfFreeTypeDrawer }

function TIntfFreeTypeDrawer.ClippedGetPixelAddress(x, y: integer): pointer;
begin
  if (x < 0) or (x >= Destination.Width) then
    raise FPImageException.CreateFmt(ErrorText[StrInvalidIndex],[ErrorText[StrImageX],x]);
  if (y < 0) or (y >= Destination.Height) then
    raise FPImageException.CreateFmt(ErrorText[StrInvalidIndex],[ErrorText[StrImageY],y]);

  result := pbyte(Destination.GetDataLineStart(y))+(x*FPixelSizeInBytes);
end;

procedure InternalGetPixelAtWithoutAlphaRGB(p: pointer; out Color: TFPColor);
begin
  with PFourBytes(p)^ do
  begin
    TFPColorBytes(color).Rh := B0;
    TFPColorBytes(color).Rl := B0;
    TFPColorBytes(color).Gh := B1;
    TFPColorBytes(color).Gl := B1;
    TFPColorBytes(color).Bh := B2;
    TFPColorBytes(color).Bl := B2;
    color.alpha := $ffff;
  end;
end;

procedure InternalSetPixelAtWithoutAlphaRGB(p: pointer; const Color: TFPColor);
begin
  with PFourBytes(p)^ do
  begin
    B0 := TFPColorBytes(color).Rh;
    B1 := TFPColorBytes(color).Gh;
    B2 := TFPColorBytes(color).Bh;
  end;
end;

procedure InternalGetPixelAtWithoutAlphaBGR(p: pointer; out Color: TFPColor);
{$IFDEF CPUI386} assembler; {$ASMMODE INTEL}
asm
  mov cl, [eax+2]
  mov [edx], cl
  mov [edx+1], cl
  mov cl, [eax+1]
  mov [edx+2], cl
  mov [edx+3], cl
  mov cl, [eax]
  mov [edx+4], cl
  mov [edx+5], cl
  xor ecx, ecx
  not ecx
  mov [edx+6], cx
end;
{$ELSE}
begin
  with PFourBytes(p)^ do
  begin
    TFPColorBytes(color).Rh := B2;
    TFPColorBytes(color).Rl := B2;
    TFPColorBytes(color).Gh := B1;
    TFPColorBytes(color).Gl := B1;
    TFPColorBytes(color).Bh := B0;
    TFPColorBytes(color).Bl := B0;
    color.alpha := $ffff;
  end;
end;
{$ENDIF}

procedure InternalSetPixelAtWithoutAlphaBGR(p: pointer; const Color: TFPColor);
{$IFDEF CPUI386} assembler; {$ASMMODE INTEL}
asm
  mov cl, [edx+1]
  mov [eax+2], cl
  mov cl, [edx+3]
  mov [eax+1], cl
  mov cl, [edx+5]
  mov [eax], cl
end;
{$ELSE}
begin
  with PFourBytes(p)^ do
  begin
    B2 := TFPColorBytes(color).Rh;
    B1 := TFPColorBytes(color).Gh;
    B0 := TFPColorBytes(color).Bh;
  end;
end;
{$ENDIF}

procedure InternalGetPixelAtWithAlphaRGBA(p: pointer; out Color: TFPColor);
begin
  with PFourBytes(p)^ do
  begin
    TFPColorBytes(color).Rh := B0;
    TFPColorBytes(color).Rl := B0;
    TFPColorBytes(color).Gh := B1;
    TFPColorBytes(color).Gl := B1;
    TFPColorBytes(color).Bh := B2;
    TFPColorBytes(color).Bl := B2;
    TFPColorBytes(color).Ah := B3;
    TFPColorBytes(color).Al := B3;
  end;
end;

procedure InternalSetPixelAtWithAlphaRGBA(p: pointer; const Color: TFPColor);
begin
  with PFourBytes(p)^ do
  begin
    B0 := TFPColorBytes(color).Rh;
    B1 := TFPColorBytes(color).Gh;
    B2 := TFPColorBytes(color).Bh;
    B3 := TFPColorBytes(color).Ah;
  end;
end;

procedure InternalGetPixelAtWithAlphaBGRA(p: pointer; out Color: TFPColor);
begin
  with PFourBytes(p)^ do
  begin
    TFPColorBytes(color).Rh := B2;
    TFPColorBytes(color).Rl := B2;
    TFPColorBytes(color).Gh := B1;
    TFPColorBytes(color).Gl := B1;
    TFPColorBytes(color).Bh := B0;
    TFPColorBytes(color).Bl := B0;
    TFPColorBytes(color).Ah := B3;
    TFPColorBytes(color).Al := B3;
  end;
end;

procedure InternalSetPixelAtWithAlphaBGRA(p: pointer; const Color: TFPColor);
begin
  with PFourBytes(p)^ do
  begin
    B2 := TFPColorBytes(color).Rh;
    B1 := TFPColorBytes(color).Gh;
    B0 := TFPColorBytes(color).Bh;
    B3 := TFPColorBytes(color).Ah;
  end;
end;

procedure TIntfFreeTypeDrawer.MergeColorOver(var merge: TFPColor; const c: TFPColor);
begin
  InternalMergeColorOver(merge,c,c.alpha);
end;

procedure TIntfFreeTypeDrawer.MergeColorOver(var merge: TFPColor;
  const c: TFPColor; ApplyOpacity: byte);
var
  calpha: longword;
begin
  calpha := c.alpha*applyOpacity div 255;
  InternalMergeColorOver(merge,c,calpha);
end;

procedure TIntfFreeTypeDrawer.UnclippedDrawPixel(x, y: integer; const c: TFPColor);
var
  merge: TFPColor;
begin
  if c.alpha = 0 then exit;
  if FHasPixelAtProc then
    DrawPixelAt(UnclippedGetPixelAddress(x,y),c)
  else
  begin
    if c.alpha = $ffff then
      Destination.Colors[x,y] := c
    else
    begin
      merge := Destination.Colors[x,y];
      MergeColorOver(merge,c);
      Destination.Colors[x,y] := merge;
    end;
  end;
end;

procedure TIntfFreeTypeDrawer.DrawPixelAt(p: pointer; const c: TFPColor; applyOpacity: byte);
var
  merge: TFPColor;
  calpha: longword;
begin
  calpha := c.alpha*applyOpacity div 255;
  if calpha = 0 then exit;
  if calpha = $ffff then
    FSetPixelAtProc(p, c)
  else
  begin
    FGetPixelAtProc(p, merge);
    InternalMergeColorOver(merge,c,calpha);
    FSetPixelAtProc(p, merge);
  end;
end;

procedure TIntfFreeTypeDrawer.DrawPixelAt(p: pointer; const c: TFPColor);
var
  merge: TFPColor;
begin
  if (c.alpha = 0) then exit;
  if (c.alpha = $ffff) then
    FSetPixelAtProc(p, c)
  else
  begin
    FGetPixelAtProc(p, merge);
    MergeColorOver(merge,c);
    FSetPixelAtProc(p, merge);
  end;
end;

procedure TIntfFreeTypeDrawer.ClippedClearTypePixel(x, y: integer; Cr, Cg,
  Cb: byte; const Color: TFPColor);
begin
  if (x < 0) or (y < 0) or (x >= Destination.Width) or (y >= Destination.Height) then exit;
  UnclippedClearTypePixel(x,y,Cr,Cg,Cb,Color);
end;

procedure TIntfFreeTypeDrawer.DrawVertLine(x, y1, y2: integer; const c: TFPColor
  );
var y: integer;
begin
  if (x < 0) or (x >= Destination.Width) then exit;
  if (y1 > y2) then
  begin
    y := y1;
    y1:= y2;
    y2 := y;
  end;
  if y1 < 0 then y1 := 0;
  if y2 >= Destination.Height then y2 := Destination.Height-1;
  for y := y1 to y2 do
    UnclippedDrawPixel(x,y, c);
end;

procedure TIntfFreeTypeDrawer.SetHorizLine(x1, y, x2: integer; const c: TFPColor);
var i: integer;
  pdest: pbyte;
  step: longword;
begin
  if (y < 0) or (y >= Destination.Height) then exit;
  if (x1 > x2) then
  begin
    i := x1;
    x1:= x2;
    x2 := i;
  end;
  if x1 < 0 then x1 := 0;
  if x2 >= Destination.Width then x2 := Destination.Width-1;
  if FHasPixelAtProc then
  begin
    pdest := UnclippedGetPixelAddress(x1,y);
    step := FPixelSizeInBytes;
    i := x2-x1+1;
    while i > 0 do
    begin
      FSetPixelAtProc(pdest,c);
      inc(pdest,step);
      dec(i);
    end;
  end else
    for i := x1 to x2 do
      Destination.Colors[i,y] := c;
end;

procedure TIntfFreeTypeDrawer.DrawHorizLine(x1, y, x2: integer;
  const c: TFPColor);
var i: integer;
  pdest: pbyte;
  step: longword;
begin
  if (y < 0) or (y >= Destination.Height) then exit;
  if (x1 > x2) then
  begin
    i := x1;
    x1:= x2;
    x2 := i;
  end;
  if x1 < 0 then x1 := 0;
  if x2 >= Destination.Width then x2 := Destination.Width-1;
  if FHasPixelAtProc then
  begin
    pdest := UnclippedGetPixelAddress(x1,y);
    step := FPixelSizeInBytes;
    i := x2-x1+1;
    while i > 0 do
    begin
      DrawPixelAt(pdest,c);
      inc(pdest,step);
      dec(i);
    end;
  end else
    for i := x1 to x2 do
      UnclippedDrawPixel(i,y,c);
end;

procedure TIntfFreeTypeDrawer.FillPixels(const c: TFPColor);
var yb: integer;
begin
  for yb := 0 to Destination.Height-1 do
    SetHorizLine(0,yb,Destination.Width-1,c);
end;

procedure TIntfFreeTypeDrawer.UnclippedClearTypePixel(x, y: integer; Cr, Cg, Cb: byte; const Color: TFPColor);
var merge,mergeClearType: TFPColor;
    acc: longword;
    keep,dont_keep: word;
    p: pointer;
begin
  Cr := Cr*(color.alpha+1) shr 16;
  Cg := Cg*(color.alpha+1) shr 16;
  Cb := Cb*(color.alpha+1) shr 16;
  acc := Cr+Cg+Cb;
  if acc = 0 then exit;

  merge := Destination.Colors[x,y];
  mergeClearType.red := (merge.red * (not byte(Cr)) +
                color.red * Cr + 128) div 255;
  mergeClearType.green := (merge.green * (not byte(Cg)) +
                color.green * Cg + 128) div 255;
  mergeClearType.blue := (merge.blue * (not byte(Cb)) +
                color.blue * Cb + 128) div 255;
  mergeClearType.alpha := merge.alpha;

  if (mergeClearType.alpha = $ffff) then
    Destination.Colors[x,y]:= mergeClearType
  else
  begin
    if Cg <> 0 then
      MergeColorOver(merge,color,Cg);
    dont_keep := mergeClearType.alpha shr 1;
    if dont_keep > 0 then
    begin
      keep := 32767 - dont_keep;
      merge.red := (merge.red * keep + mergeClearType.red * dont_keep) div 32767;
      merge.green := (merge.green * keep + mergeClearType.green * dont_keep) div 32767;
      merge.blue := (merge.blue * keep + mergeClearType.blue * dont_keep) div 32767;
      merge.alpha := mergeClearType.alpha + ((not mergeClearType.alpha)*merge.alpha div 65535);
    end;
    Destination.Colors[x,y] := merge;
  end;
end;

procedure TIntfFreeTypeDrawer.ClearTypePixelAt(p: pointer; Cr, Cg, Cb: byte;
  const Color: TFPColor);
var merge,mergeClearType: TFPColor;
    acc: longword;
    keep,dont_keep: word;
begin
  Cr := Cr*(color.alpha+1) shr 16;
  Cg := Cg*(color.alpha+1) shr 16;
  Cb := Cb*(color.alpha+1) shr 16;
  acc := Cr+Cg+Cb;
  if acc = 0 then exit;

  FGetPixelAtProc(p, merge);
  mergeClearType.red := (merge.red * (not byte(Cr)) +
                color.red * Cr + 128) div 255;
  mergeClearType.green := (merge.green * (not byte(Cg)) +
                color.green * Cg + 128) div 255;
  mergeClearType.blue := (merge.blue * (not byte(Cb)) +
                color.blue * Cb + 128) div 255;
  mergeClearType.alpha := merge.alpha;

  if (mergeClearType.alpha = $ffff) then
    FSetPixelAtProc(p, mergeClearType)
  else
  begin
    if Cg <> 0 then
      MergeColorOver(merge,color,Cg);
    dont_keep := mergeClearType.alpha shr 1;
    if dont_keep > 0 then
    begin
      keep := 32767 - dont_keep;
      merge.red := (merge.red * keep + mergeClearType.red * dont_keep) div 32767;
      merge.green := (merge.green * keep + mergeClearType.green * dont_keep) div 32767;
      merge.blue := (merge.blue * keep + mergeClearType.blue * dont_keep) div 32767;
      merge.alpha := mergeClearType.alpha + ((not mergeClearType.alpha)*merge.alpha div 65535);
    end;
    FSetPixelAtProc(p, merge);
  end;
end;

function TIntfFreeTypeDrawer.UnclippedGetPixelAddress(x, y: integer): pointer;
begin
  result := pbyte(Destination.GetDataLineStart(y))+(x*FPixelSizeInBytes);
end;

procedure TIntfFreeTypeDrawer.SetDestination(AValue: TLazIntfImage);
var CanBeOptimized: boolean;
    RedShiftInBytes,GreenShiftInBytes,BlueShiftInBytes,AlphaShiftInBytes: integer;
begin
  if FDestination=AValue then Exit;
  FDestination := AValue;

  FGetPixelAtProc := nil;
  FSetPixelAtProc := nil;

  if FDestination = nil then
  begin
    FWidth := 0;
    FHeight := 0;
  end else
  begin
    FWidth := FDestination.Width;
    FHeight := FDestination.Height;

    with Destination.DataDescription do
      CanBeOptimized := (BitsPerPixel and 7 = 0) and
      (Format = ricfRGBA) and (RedPrec = 8) and (GreenPrec = 8) and (BluePrec = 8) and
      (RedShift and 7 = 0) and (GreenPrec and 7 = 0) and (BluePrec and 7 = 0) and
      (((AlphaPrec = 8) and (AlphaShift and 7 = 0)) or (AlphaPrec = 0));

    if CanBeOptimized then
    begin
      FPixelSizeInBytes := Destination.DataDescription.BitsPerPixel div 8;

      RedShiftInBytes := Destination.DataDescription.RedShift div 8;
      GreenShiftInBytes := Destination.DataDescription.GreenShift div 8;
      BlueShiftInBytes := Destination.DataDescription.BlueShift div 8;
      AlphaShiftInBytes := Destination.DataDescription.AlphaShift div 8;

      if Destination.DataDescription.ByteOrder = riboMSBFirst then
      begin
        RedShiftInBytes := FPixelSizeInBytes-1 - RedShiftInBytes;
        GreenShiftInBytes := FPixelSizeInBytes-1 - GreenShiftInBytes;
        BlueShiftInBytes := FPixelSizeInBytes-1 - BlueShiftInBytes;
        AlphaShiftInBytes := FPixelSizeInBytes-1 - AlphaShiftInBytes;
      end;

      if Destination.DataDescription.AlphaPrec = 0 then
      begin
        if (RedShiftInBytes = 0) and (GreenShiftInBytes = 1) and
           (BlueShiftInBytes = 2) then
        begin
          FGetPixelAtProc := @InternalGetPixelAtWithoutAlphaRGB;
          FSetPixelAtProc := @InternalSetPixelAtWithoutAlphaRGB;
        end else
        if (RedShiftInBytes = 2) and (GreenShiftInBytes = 1) and
           (BlueShiftInBytes = 0) then
        begin
          FGetPixelAtProc := @InternalGetPixelAtWithoutAlphaBGR;
          FSetPixelAtProc := @InternalSetPixelAtWithoutAlphaBGR;
        end;
      end else
      begin
        if (RedShiftInBytes = 0) and (GreenShiftInBytes = 1) and
           (BlueShiftInBytes = 2) then
        begin
          FGetPixelAtProc := @InternalGetPixelAtWithAlphaRGBA;
          FSetPixelAtProc := @InternalSetPixelAtWithAlphaRGBA;
        end else
        if (RedShiftInBytes = 2) and (GreenShiftInBytes = 1) and
           (BlueShiftInBytes = 0) then
        begin
          FGetPixelAtProc := @InternalGetPixelAtWithAlphaBGRA;
          FSetPixelAtProc := @InternalSetPixelAtWithAlphaBGRA;
        end;
      end;
    end;
  end;

  FHasPixelAtProc := (FGetPixelAtProc<>nil) and (FSetPixelAtProc <> nil);
end;

procedure TIntfFreeTypeDrawer.RenderDirectly( x,y,tx: integer;
                          data: pointer );
var psrc: pbyte;
    c: TFPColor;
    pdest: pbyte;
    step: longword;
    tempValue: byte;
begin
  if Destination <> nil then
  begin
    //ensure rendering in bounds
    if (y < 0) or (y >= Destination.height) or (x < 0) or (x > Destination.width-tx) then exit;

    c := FColor;
    psrc := pbyte(data);

    if FHasPixelAtProc then
    begin
      step := FPixelSizeInBytes;
      pdest := UnclippedGetPixelAddress(x,y);
      inc(psrc,tx);
      while tx > 0 do
      begin
        tempValue := (psrc-tx)^;
        if tempValue <> 0 then
          DrawPixelAt(pdest,c,tempValue);
        inc(pdest,step);
        dec(tx);
      end;
    end else
    while tx > 0 do
    begin
      tempValue := psrc^;
      if tempValue <> 0 then
      begin
        c.alpha:= FColor.alpha * tempValue div 255;
        UnclippedDrawPixel(x,y,c);
      end;
      inc(psrc);
      inc(x);
      dec(tx);
    end;
  end;
end;

procedure TIntfFreeTypeDrawer.RenderDirectlyClearType(x, y, tx: integer; data: pointer);
var xb: integer;
    psrc: pbyte;
    Cr,Cg,Cb: byte;
    pdest: pbyte;
    step: longword;
begin
  if Destination <> nil then
  begin
    //ClearType position in third of pixels horizontally (multiple of 3)
    x := x div 3;
    tx := tx div 3;
    //ensure rendering in bounds
    if (y < 0) or (y >= Destination.height) or (x < 0) or (x > Destination.width-tx) then exit;
    if tx=0 then exit;

    psrc := pbyte(data);
    Cr := (psrc^ + psrc^ + (psrc+1)^) div 3;
    Cg := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
    if tx > 1 then
      Cb := ((psrc+1)^ + (psrc+2)^ + (psrc+3)^) div 3
    else
      Cb := ((psrc+1)^ + (psrc+2)^ + (psrc+2)^) div 3;

    if FHasPixelAtProc then
    begin
      step := FPixelSizeInBytes;
      pdest := UnclippedGetPixelAddress(x,y);
      if Cr+Cg+Cb <> 0 then
        ClearTypePixelAt(pdest,Cr,Cg,Cb, FColor);
      inc(pdest,step);
      inc(psrc,3);
      for xb := 1 to tx-2 do
      begin
        Cr := ((psrc-1)^+ psrc^ + (psrc+1)^) div 3;
        Cg := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
        Cb := ((psrc+1)^ + (psrc+2)^ + (psrc+3)^) div 3;
        if Cr+Cg+Cb <> 0 then
          ClearTypePixelAt(pdest,Cr,Cg,Cb, FColor);
        inc(pdest,step);
        inc(psrc,3);
      end;
      if tx > 1 then
      begin
        Cr := ((psrc-1)^+ psrc^ + (psrc+1)^) div 3;
        Cg := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
        Cb := ((psrc+1)^ + (psrc+2)^ + (psrc+2)^) div 3;
        if Cr+Cg+Cb <> 0 then
          ClearTypePixelAt(pdest,Cr,Cg,Cb, FColor);
      end;
    end else
    begin
      if Cr+Cg+Cb <> 0 then
        UnclippedClearTypePixel(x,y,Cr,Cg,Cb, FColor);
      inc(x);
      inc(psrc,3);
      for xb := 1 to tx-2 do
      begin
        Cr := ((psrc-1)^+ psrc^ + (psrc+1)^) div 3;
        Cg := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
        Cb := ((psrc+1)^ + (psrc+2)^ + (psrc+3)^) div 3;
        if Cr+Cg+Cb <> 0 then
          UnclippedClearTypePixel(x,y,Cr,Cg,Cb, FColor);
        inc(x);
        inc(psrc,3);
      end;
      if tx > 1 then
      begin
        Cr := ((psrc-1)^+ psrc^ + (psrc+1)^) div 3;
        Cg := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
        Cb := ((psrc+1)^ + (psrc+2)^ + (psrc+2)^) div 3;
        if Cr+Cg+Cb <> 0 then
          UnclippedClearTypePixel(x,y,Cr,Cg,Cb, FColor);
      end;
    end;
  end;
end;

procedure TIntfFreeTypeDrawer.InternalMergeColorOver(var merge: TFPColor;
  const c: TFPColor; calpha: word);
var
  a1f, a2f, a12, a12m: cardinal;
begin
  if calpha = 0 then exit;
  a12  := 65534 - ((not merge.alpha) * (not calpha) shr 16);
  a12m := a12 shr 1;

  a1f := merge.alpha * (not calpha) shr 16;
  a2f := calpha - (calpha shr 15);

  merge.red := (merge.red * a1f + c.red * a2f + a12m) div a12;
  merge.green := (merge.green * a1f + c.green * a2f + a12m) div a12;
  merge.blue := (merge.blue * a1f + c.blue * a2f + a12m) div a12;
  merge.alpha := a12 + (a12 shr 15);
end;

constructor TIntfFreeTypeDrawer.Create(ADestination: TLazIntfImage);
begin
  Destination := ADestination;
  ClearTypeRGBOrder:= true;
end;

procedure TIntfFreeTypeDrawer.ClippedDrawPixel(x, y: integer; const c: TFPColor
  );
begin
  if (x < 0) or (y < 0) or (x >= Destination.Width) or (y >= Destination.Height) then exit;
  UnclippedDrawPixel(x,y,c);
end;

procedure TIntfFreeTypeDrawer.DrawText(AText: string; AFont: TFreeTypeRenderableFont; x, y: single;
  AColor: TFPColor);
begin
  FColor := AColor;
  if AFont.ClearType then
    AFont.RenderText(AText, x, y, rect(0,0,Destination.Width,Destination.Height), @RenderDirectlyClearType)
  else
    AFont.RenderText(AText, x, y, rect(0,0,Destination.Width,Destination.Height), @RenderDirectly);
end;

destructor TIntfFreeTypeDrawer.Destroy;
begin
  inherited Destroy;
end;

end.


