mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-19 23:49:36 +02:00
LazUtils: New LazFreeType drawer for TFPCustomImage. Issue #28020, patch from Johann.
git-svn-id: trunk@49104 -
This commit is contained in:
parent
74776f4677
commit
7bf388fe91
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -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
|
||||
|
503
components/lazutils/lazfreetypefpimagedrawer.pas
Normal file
503
components/lazutils/lazfreetypefpimagedrawer.pas
Normal 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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user