LazUtils: New LazFreeType drawer for TFPCustomImage. Issue #28020, patch from Johann.

git-svn-id: trunk@49104 -
This commit is contained in:
juha 2015-05-19 16:11:36 +00:00
parent 74776f4677
commit 7bf388fe91
2 changed files with 504 additions and 0 deletions

1
.gitattributes vendored
View File

@ -2933,6 +2933,7 @@ components/lazutils/lazfileutils.inc svneol=native#text/plain
components/lazutils/lazfileutils.pas svneol=native#text/pascal
components/lazutils/lazfreetype.pas svneol=native#text/pascal
components/lazutils/lazfreetypefontcollection.pas svneol=native#text/plain
components/lazutils/lazfreetypefpimagedrawer.pas svneol=native#text/pascal
components/lazutils/lazlistclasses.pas svneol=native#text/plain
components/lazutils/lazlogger.pas svneol=native#text/pascal
components/lazutils/lazloggerbase.pas svneol=native#text/pascal

View File

@ -0,0 +1,503 @@
unit LazFreeTypeFPImageDrawer;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, EasyLazFreeType, FPimage;
type
TLazIntfImageGetPixelAtProc = procedure(p: pointer; out Color: TFPColor);
TLazIntfImageSetPixelAtProc = procedure(p: pointer; const Color: TFPColor);
{ TFPImageFreeTypeDrawer }
TFPImageFreeTypeDrawer = class(TFreeTypeDrawer)
private
FColor: TFPColor;
FCurX,FCurY: integer;
protected
FImage: TFPCustomImage;
procedure MoveToPixel(x,y: integer); virtual;
function GetCurrentColor: TFPColor; virtual;
procedure SetCurrentColorAndMoveRight(const AColor: TFPColor); virtual;
procedure MoveRight; virtual;
function GetClipRect: TRect; virtual;
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 DrawPixelAndMoveRight(const c: TFPColor);
procedure DrawPixelAndMoveRight(const c: TFPColor; applyOpacity: byte);
procedure ClearTypePixelAndMoveRight(Cr,Cg,Cb: byte; const Color: TFPColor);
procedure UnclippedDrawPixel(x,y: integer; const c: TFPColor);
public
ClearTypeRGBOrder: boolean;
constructor Create(AImage: TFPCustomImage); virtual;
procedure DrawPixel(x,y: integer; const c: TFPColor);
procedure ClearTypePixel(x,y: integer; Cr,Cg,Cb: byte; const Color: TFPColor);
procedure SetVertLine(x,y1,y2: integer; const c: 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;
destructor Destroy; override;
property Image: TFPCustomImage read FImage;
end;
{ TFPMemoryImageWithScanline }
TFPMemoryImageWithScanline = class(TFPMemoryImage)
protected
function GetScanline(y: integer): PFPColor;
procedure SetUsePalette ({%H-}Value:boolean);override;
public
property ScanLine[y: integer]: PFPColor read GetScanline;
end;
{ TFPImageWithScanlineFreeTypeDrawer }
TFPImageWithScanlineFreeTypeDrawer= class(TFPImageFreeTypeDrawer)
protected
FCurrentColor: PFPColor;
procedure MoveToPixel(x,y: integer); override;
function GetCurrentColor: TFPColor; override;
procedure SetCurrentColorAndMoveRight(const AColor: TFPColor); override;
procedure MoveRight; override;
function GetClipRect: TRect; override;
public
constructor Create(AImage: TFPCustomImage); override; //requires TFPMemoryImageWithScanline
end;
implementation
{ TFPImageFreeTypeDrawer }
procedure TFPImageFreeTypeDrawer.MergeColorOver(var merge: TFPColor; const c: TFPColor);
begin
InternalMergeColorOver(merge,c,c.alpha);
end;
procedure TFPImageFreeTypeDrawer.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 TFPImageFreeTypeDrawer.UnclippedDrawPixel(x, y: integer; const c: TFPColor);
var
merge: TFPColor;
begin
if c.alpha = 0 then exit;
MoveToPixel(x,y);
if c.alpha = $ffff then
SetCurrentColorAndMoveRight(c)
else
begin
merge := GetCurrentColor;
MergeColorOver(merge,c);
SetCurrentColorAndMoveRight(merge);
end;
end;
procedure TFPImageFreeTypeDrawer.DrawPixelAndMoveRight(const c: TFPColor; applyOpacity: byte);
var
merge: TFPColor;
calpha: longword;
begin
calpha := c.alpha*applyOpacity div 255;
if calpha = 0 then
begin
MoveRight;
exit;
end;
if calpha = $ffff then
SetCurrentColorAndMoveRight(c)
else
begin
merge := GetCurrentColor;
InternalMergeColorOver(merge,c,calpha);
SetCurrentColorAndMoveRight(merge);
end;
end;
procedure TFPImageFreeTypeDrawer.DrawPixelAndMoveRight(const c: TFPColor);
var
merge: TFPColor;
begin
if (c.alpha = 0) then
begin
MoveRight;
exit;
end;
if (c.alpha = $ffff) then
SetCurrentColorAndMoveRight(c)
else
begin
merge := GetCurrentColor;
InternalMergeColorOver(merge,c,c.alpha);
SetCurrentColorAndMoveRight(merge);
end;
end;
procedure TFPImageFreeTypeDrawer.SetVertLine(x, y1, y2: integer;
const c: TFPColor);
var y: integer;
begin
with GetClipRect do
begin
if (x < Left) or (x >= Right) then exit;
if (y1 > y2) then
begin
y := y1;
y1:= y2;
y2 := y;
end;
if y1 < Top then y1 := Top;
if y2 >= Bottom then y2 := Bottom-1;
end;
for y := y1 to y2 do
begin
MoveToPixel(x,y1);
SetCurrentColorAndMoveRight(c);
end;
end;
procedure TFPImageFreeTypeDrawer.DrawVertLine(x, y1, y2: integer; const c: TFPColor
);
var y: integer;
begin
with GetClipRect do
begin
if (x < Left) or (x >= Right) then exit;
if (y1 > y2) then
begin
y := y1;
y1:= y2;
y2 := y;
end;
if y1 < Top then y1 := Top;
if y2 >= Bottom then y2 := Bottom-1;
end;
for y := y1 to y2 do
UnclippedDrawPixel(x,y, c);
end;
procedure TFPImageFreeTypeDrawer.SetHorizLine(x1, y, x2: integer; const c: TFPColor);
var i: integer;
begin
with GetClipRect do
begin
if (y < Top) or (y >= Bottom) then exit;
if (x1 > x2) then
begin
i := x1;
x1:= x2;
x2 := i;
end;
if x1 < Left then x1 := Left;
if x2 >= Right then x2 := Right-1;
end;
MoveToPixel(x1,y);
i := x2-x1+1;
while i > 0 do
begin
SetCurrentColorAndMoveRight(c);
dec(i);
end;
end;
procedure TFPImageFreeTypeDrawer.DrawHorizLine(x1, y, x2: integer;
const c: TFPColor);
var i: integer;
begin
with GetClipRect do
begin
if (y < Top) or (y >= Bottom) then exit;
if (x1 > x2) then
begin
i := x1;
x1:= x2;
x2 := i;
end;
if x1 < Left then x1 := Left;
if x2 >= Right then x2 := Right-1;
end;
MoveToPixel(x1,y);
i := x2-x1+1;
while i > 0 do
begin
DrawPixelAndMoveRight(c);
dec(i);
end;
end;
procedure TFPImageFreeTypeDrawer.FillPixels(const c: TFPColor);
var yb: integer;
begin
with GetClipRect do
begin
for yb := Top to Bottom-1 do
SetHorizLine(Left,yb,Right-1,c);
end;
end;
procedure TFPImageFreeTypeDrawer.ClearTypePixel(x, y: integer; Cr, Cg, Cb: byte; const Color: TFPColor);
begin
with GetClipRect do
if (x < Left) or (y < Top) or (x >= Right) or (y >= Bottom) then exit;
MoveToPixel(x,y);
ClearTypePixelAndMoveRight(Cr,Cg,Cb,Color);
end;
procedure TFPImageFreeTypeDrawer.ClearTypePixelAndMoveRight(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;
merge := GetCurrentColor;
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
SetCurrentColorAndMoveRight(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;
SetCurrentColorAndMoveRight(merge);
end;
end;
procedure TFPImageFreeTypeDrawer.MoveToPixel(x, y: integer);
begin
FCurX := x;
FCurY := y;
end;
function TFPImageFreeTypeDrawer.GetCurrentColor: TFPColor;
begin
result := FImage.Colors[FCurX,FCurY];
end;
procedure TFPImageFreeTypeDrawer.SetCurrentColorAndMoveRight(
const AColor: TFPColor);
begin
FImage.Colors[FCurX,FCurY] := AColor;
Inc(FCurX);
end;
procedure TFPImageFreeTypeDrawer.MoveRight;
begin
inc(FCurX);
end;
function TFPImageFreeTypeDrawer.GetClipRect: TRect;
begin
result := rect(0,0,FImage.Width,FImage.Height);
end;
procedure TFPImageFreeTypeDrawer.RenderDirectly( x,y,tx: integer;
data: pointer );
var psrc: pbyte;
c: TFPColor;
tempValue: byte;
begin
//ensure rendering in bounds
with GetClipRect do
if (y < Top) or (y >= Bottom) or (x < Left) or (x > Right-tx) then exit;
c := FColor;
psrc := pbyte(data);
MoveToPixel(x,y);
inc(psrc,tx);
while tx > 0 do
begin
tempValue := (psrc-tx)^;
if tempValue <> 0 then
DrawPixelAndMoveRight(c,tempValue)
else
MoveRight;
dec(tx);
end;
end;
procedure TFPImageFreeTypeDrawer.RenderDirectlyClearType(x, y, tx: integer; data: pointer);
var xb: integer;
psrc: pbyte;
Cr,Cg,Cb: byte;
begin
//ClearType position in third of pixels horizontally (multiple of 3)
x := x div 3;
tx := tx div 3;
//ensure rendering in bounds
with GetClipRect do
if (y < Top) or (y >= Bottom) or (x < Left) or (x > Right-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;
MoveToPixel(x,y);
if Cr+Cg+Cb <> 0 then
ClearTypePixelAndMoveRight(Cr,Cg,Cb, FColor)
else
MoveRight;
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
ClearTypePixelAndMoveRight(Cr,Cg,Cb, FColor)
else
MoveRight;
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
ClearTypePixelAndMoveRight(Cr,Cg,Cb, FColor);
end;
end;
procedure TFPImageFreeTypeDrawer.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 TFPImageFreeTypeDrawer.Create(AImage: TFPCustomImage);
begin
ClearTypeRGBOrder:= true;
FImage := AImage;
end;
procedure TFPImageFreeTypeDrawer.DrawPixel(x, y: integer; const c: TFPColor
);
begin
with GetClipRect do
if (x < Left) or (y < Top) or (x >= Right) or (y >= Bottom) then exit;
UnclippedDrawPixel(x,y,c);
end;
procedure TFPImageFreeTypeDrawer.DrawText(AText: string; AFont: TFreeTypeRenderableFont; x, y: single;
AColor: TFPColor);
begin
FColor := AColor;
if AFont.ClearType then
AFont.RenderText(AText, x, y, GetClipRect, @RenderDirectlyClearType)
else
AFont.RenderText(AText, x, y, GetClipRect, @RenderDirectly);
end;
destructor TFPImageFreeTypeDrawer.Destroy;
begin
inherited Destroy;
end;
{ TFPImageWithScanlineFreeTypeDrawer }
procedure TFPImageWithScanlineFreeTypeDrawer.MoveToPixel(x, y: integer);
begin
FCurrentColor:= TFPMemoryImageWithScanline(FImage).ScanLine[y]+x;
end;
function TFPImageWithScanlineFreeTypeDrawer.GetCurrentColor: TFPColor;
begin
result := FCurrentColor^;
end;
procedure TFPImageWithScanlineFreeTypeDrawer.SetCurrentColorAndMoveRight(
const AColor: TFPColor);
begin
FCurrentColor^ := AColor;
inc(FCurrentColor);
end;
procedure TFPImageWithScanlineFreeTypeDrawer.MoveRight;
begin
inc(FCurrentColor);
end;
function TFPImageWithScanlineFreeTypeDrawer.GetClipRect: TRect;
begin
result := rect(0,0,FImage.Width,FImage.Height);
end;
constructor TFPImageWithScanlineFreeTypeDrawer.Create(AImage: TFPCustomImage);
begin
inherited Create(AImage);
if not (AImage is TFPMemoryImageWithScanline) then
raise Exception.Create('Scanline not available');
end;
{ TFPMemoryImageWithScanline }
function TFPMemoryImageWithScanline.GetScanline(y: integer): PFPColor;
begin
if (y < 0) or (y >= Height) then
raise ERangeError.Create('Scanline out of bounds');
result := PFPColor(FData)+(y*Width);
end;
procedure TFPMemoryImageWithScanline.SetUsePalette(Value: boolean);
begin
if Value then
raise Exception.Create('Palette not supported with scanlines');
end;
end.