This repository has been archived on 2024-02-26. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
MyPresenter1.0/bgracontrols_3_4/bctools.pas
2015-02-08 16:52:18 -08:00

473 lines
15 KiB
ObjectPascal

{ General framework methods for rendering background, borders, text, etc.
Copyright (C) 2012 Krzysztof Dibowski dibowski at interia.pl
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version with the following modification:
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,and
to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms
and conditions of the license of that module. An independent module is a
module which is not derived from or based on this library. If you modify
this library, you may extend this exception to your version of the library,
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
unit BCTools;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Graphics, BGRABitmap, BGRABitmapTypes, bctypes,
Controls, BGRAGradientScanner;
// This method prepare BGRABitmap for rendering BCFont type
procedure AssignBCFont(AFont: TBCFont; out ATargetBGRA: TBGRABitmap);
// Calculate text height and width (doesn't include wordwrap - just single line)
procedure CalculateTextSize(const AText: String; AFont: TBCFont;
out ANewWidth, ANewHeight: integer);
// As long as there are differences between the size of the font, this method is useless
procedure CalculateTextRect(const AText: String; AFont: TBCFont; out ARect: TRect);
// This method correct TRect to border width. As far as border width is bigger,
// BGRA drawing rectangle with offset (half border width)
procedure CalculateBorderRect(ABorder: TBCBorder; var ARect: TRect);
// Create BGRA Gradient Scanner based on BCGradient properties
function CreateGradient(AGradient: TBCGradient; ARect: TRect): TBGRAGradientScanner;
// Render arrow (used by BCButton with DropDownMenu style)
procedure RenderArrow(out ATargetBGRA: TBGRABitmap; const ARect: TRect;
ASize: Integer; ADirection: TBCArrowDirection; AColor: TColor = clBlack;
AOpacity: Byte = 255);
// Render customizable backgroud (used e.g. by TBCButton, TBCPanel, TBCLabel)
procedure RenderBackground(const ARect: TRect; const ABackground: TBCBackground;
out ATargetBGRA: TBGRABitmap; ARounding: TBCRounding = nil);
// Render customizable border (used e.g. by TBCButton, TBCPanel, TBCLabel)
procedure RenderBorder(const ARect: TRect; const ABorder: TBCBorder;
out ATargetBGRA: TBGRABitmap; ARounding: TBCRounding = nil);
// Render BCFont (used e.g. by TBCButton, TBCPanel, TBCLabel)
procedure RenderText(const ARect: TRect; const AFont: TBCFont;
const AText: String; out ATargetBGRA: TBGRABitmap);
// Return LCL horizontal equivalent for BCAlignment
function BCAlign2HAlign(AAlign: TBCAlignment): TAlignment;
// Return LCL vertical equivalent for BCAlignment
function BCAlign2VAlign(AAlign: TBCAlignment): TTextLayout;
implementation
uses Types, BGRAPolygon, BGRAFillInfo, BGRAText, math, LCLType, LCLIntf;
procedure CalculateTextRect(const AText: String; AFont: TBCFont; out ARect: TRect);
var
tmp: TBGRABitmap;
flags: LongInt;
begin
tmp := TBGRABitmap.Create(0,0);
AssignBCFont(AFont,tmp);
flags := 0;
case AFont.TextAlignment of
bcaCenter, bcaCenterBottom, bcaCenterTop: flags := flags or DT_CENTER;
bcaRightCenter, bcaRightBottom, bcaRightTop: flags := flags or DT_RIGHT;
end;
case AFont.TextAlignment of
bcaLeftTop, bcaCenterTop, bcaRightTop: flags := flags or DT_TOP;
bcaLeftCenter, bcaCenter, bcaRightCenter: flags := flags or DT_VCENTER;
bcaLeftBottom, bcaCenterBottom, bcaRightBottom: flags := flags or DT_BOTTOM;
end;
if AFont.EndEllipsis then
flags := flags or DT_END_ELLIPSIS;
// This condition is from TCanvas.TextRect
if AFont.WordBreak then
begin
flags := flags or DT_WORDBREAK;
if AFont.EndEllipsis then
flags := flags and not DT_END_ELLIPSIS;
end;
if AFont.SingleLine then
flags := flags or DT_SINGLELINE;
flags := flags or DT_CALCRECT;
LCLIntf.DrawText(tmp.Canvas.Handle, PChar(AText), Length(AText), ARect, flags);
tmp.Free;
end;
procedure CalculateBorderRect(ABorder: TBCBorder; var ARect: TRect);
begin
if ABorder = nil then Exit;
Inc(ARect.Left, Round(ABorder.Width / 2));
Inc(ARect.Top, Round(ABorder.Width / 2));
Dec(ARect.Right, Round(ABorder.Width / 2) + 1);
Dec(ARect.Bottom, Round(ABorder.Width / 2) + 1);
end;
function CreateGradient(AGradient: TBCGradient; ARect: TRect): TBGRAGradientScanner;
begin
Result := TBGRAGradientScanner.Create(
ColorToBGRA(ColorToRGB(AGradient.StartColor), AGradient.StartColorOpacity),
ColorToBGRA(ColorToRGB(AGradient.EndColor), AGradient.EndColorOpacity),
AGradient.GradientType, PointF(ARect.Left + Round(
((ARect.Right - ARect.Left) / 100) * AGradient.Point1XPercent),
ARect.Top + Round(((ARect.Bottom - ARect.Top) / 100) * AGradient.Point1YPercent)),
PointF(ARect.Left + Round(((ARect.Right - ARect.Left) / 100) *
AGradient.Point2XPercent), ARect.Top + Round(
((ARect.Bottom - ARect.Top) / 100) * AGradient.Point2YPercent)),
AGradient.ColorCorrection, AGradient.Sinus);
end;
procedure RenderBorder(const ARect: TRect; const ABorder: TBCBorder; out
ATargetBGRA: TBGRABitmap; ARounding: TBCRounding = nil);
var
fiLight: TFillBorderRoundRectInfo;
rx,ry: Byte;
ropt: TRoundRectangleOptions;
begin
if ABorder.Style=bboNone then Exit;
if ARounding = nil then
begin
rx := 0;
ry := 0;
ropt := [];
end else
begin
rx := ARounding.RoundX;
ry := ARounding.RoundY;
ropt := ARounding.RoundOptions;
end;
ATargetBGRA.RoundRectAntialias(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom,
rx, ry, ColorToBGRA(ColorToRGB(ABorder.Color),ABorder.ColorOpacity),
ABorder.Width, ropt);
if ABorder.LightWidth > 0 then
begin
//compute light position
fiLight := TFillBorderRoundRectInfo.Create(
ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, rx,
ry, ABorder.Width + ABorder.LightWidth, ropt);
//check if there is an inner position
if fiLight.InnerBorder <> nil then
with fiLight.InnerBorder do //fill with light
ATargetBGRA.RoundRectAntialias(topleft.x, topleft.y, bottomright.x,
bottomright.y, radiusx, radiusY,
ColorToBGRA(ColorToRGB(ABorder.LightColor), ABorder.LightOpacity),
ABorder.LightWidth, ropt);
fiLight.Free;
end;
end;
procedure RenderText(const ARect: TRect; const AFont: TBCFont;
const AText: String; out ATargetBGRA: TBGRABitmap);
var
shd: TBGRABitmap;
hal: TAlignment;
val: TTextLayout;
st: TTextStyle;
begin
AssignBCFont(AFont,ATargetBGRA);
hal := BCAlign2HAlign(AFont.TextAlignment);
val := BCAlign2VAlign(AFont.TextAlignment);
FillChar(st, SizeOf(st),0);
st.Wordbreak := AFont.WordBreak;
st.Alignment := hal;
st.Layout := val;
st.SingleLine := AFont.SingleLine;
st.EndEllipsis := AFont.EndEllipsis;
if AFont.Shadow then
begin
shd := TBGRABitmap.Create(ATargetBGRA.Width,ATargetBGRA.Height,BGRAPixelTransparent);
shd.FontName := ATargetBGRA.FontName;
shd.FontStyle := ATargetBGRA.FontStyle;
shd.FontQuality := ATargetBGRA.FontQuality;
shd.FontHeight := ATargetBGRA.FontHeight;
shd.TextRect(ARect, ARect.Left, ARect.Top, AText, st, ColorToBGRA(ColorToRGB(AFont.ShadowColor),
AFont.ShadowColorOpacity));
BGRAReplace(shd, shd.FilterBlurRadial(AFont.ShadowRadius, rbFast));
ATargetBGRA.BlendImage(AFont.ShadowOffsetX, AFont.ShadowOffsetY,
shd, boLinearBlend);
shd.Free;
end;
ATargetBGRA.TextRect(ARect,ARect.Left,ARect.Top,AText,st,ColorToBGRA(ColorToRGB(AFont.Color)));
end;
function BCAlign2HAlign(AAlign: TBCAlignment): TAlignment;
begin
if AAlign in [bcaCenter, bcaCenterTop, bcaCenterBottom] then
Result := taCenter
else if AAlign in [bcaRightCenter, bcaRightTop, bcaRightBottom] then
Result := taRightJustify
else
Result := taLeftJustify;
end;
function BCAlign2VAlign(AAlign: TBCAlignment): TTextLayout;
begin
if AAlign in [bcaCenter, bcaLeftCenter, bcaRightCenter] then
Result := tlCenter
else if AAlign in [bcaCenterBottom, bcaLeftBottom, bcaRightBottom] then
Result := tlBottom
else
Result := tlTop;
end;
procedure AssignBCFont(AFont: TBCFont; out ATargetBGRA: TBGRABitmap);
var c: TBitmap;
begin
// Canvas is need for calculate font height
c := TBitmap.Create;
c.Canvas.Font.Name := AFont.Name;
c.Canvas.Font.Style := AFont.Style;
case AFont.FontQuality of
fqSystem: c.Canvas.Font.Quality := fqNonAntialiased;
fqFineAntialiasing: c.Canvas.Font.Quality := fqAntialiased;
fqFineClearTypeRGB: c.Canvas.Font.Quality := fqProof;
fqSystemClearType: c.Canvas.Font.Quality := fqCleartype;
end;
// FontAntialias is only backward compability for FontQuality property.
// FontQuality is published in TBCFont so we don't need FontAntialias anymore.
//ATargetBGRA.FontAntialias := AFont.FontAntialias;
ATargetBGRA.FontStyle := AFont.Style;
// If font quality is system, then we can leave default values. LCL will
// handle everything (when name is "default" or height 0)
if AFont.FontQuality in [fqSystem,fqSystemClearType] then
begin
ATargetBGRA.FontName := AFont.Name;
ATargetBGRA.FontHeight := AFont.Height;
end
else
begin
// Getting real font name
if SameText(AFont.Name,'default')
then ATargetBGRA.FontName := GetFontData(c.Canvas.Font.Handle).Name
else ATargetBGRA.FontName := AFont.Name;
// Calculate default height, because when font quality is <> fqSystemXXX
// then if height is 0 then it is 0 for real
if (AFont.Height=0) then
ATargetBGRA.FontHeight := -c.Canvas.TextHeight('Bgra')
else
ATargetBGRA.FontHeight := AFont.Height;
end;
ATargetBGRA.FontQuality := AFont.FontQuality;
c.Free;
end;
procedure CalculateTextSize(const AText: String; AFont: TBCFont; out ANewWidth,
ANewHeight: integer);
var
s: TSize;
tmp: TBGRABitmap;
begin
if (AText = '') or (AFont = nil) then
begin
ANewWidth := 0;
ANewHeight := 0;
Exit;
end;
{TODO: Check why BGRATextSize doesn't work. BGRABitmap call this method
and it work. Temporary I'm creating temp bitmap
}
{s := BGRAText.BGRATextSize(AFont,AFont.FontQuality,AText,FontAntialiasingLevel);
if (s.cy >= 24) and AFont.FontAntialias then
s := BGRAText.BGRATextSize(AFont,AFont.FontQuality,AText,4);}
tmp := TBGRABitmap.Create(0,0);
AssignBCFont(AFont, tmp);
s := tmp.TextSize(AText);
tmp.Free;
{ shadow offset }
if AFont.Shadow then
begin
Inc(s.cx, 2 * Abs(AFont.ShadowOffsetX) + 2 * AFont.ShadowRadius);
Inc(s.cy, 2 * Abs(AFont.ShadowOffsetY) + 2 * AFont.ShadowRadius);
end;
ANewWidth := s.cx;
ANewHeight := s.cy;
end;
procedure RenderArrow(out ATargetBGRA: TBGRABitmap; const ARect: TRect;
ASize: Integer; ADirection: TBCArrowDirection; AColor: TColor; AOpacity: Byte);
var
p: ArrayOfTPointF;
n: byte;
temp: TBGRABitmap;
w: Integer;
begin
// We can't draw outside rect
w := Min(ASize, ARect.Right - ARect.Left);
{ Poly }
SetLength(p, 3);
temp := TBGRABitmap.Create(w+1, w+1,BGRAPixelTransparent);
case ADirection of
badDown:
begin;
p[0].x := 0;
p[0].y := 0;
p[1].x := w;
p[1].y := 0;
p[2].x := Round(w/2);
p[2].y := w;
end;
badUp:
begin
p[0].x := Round(w/2);
p[0].y := 0;
p[1].x := 0;
p[1].y := w;
p[2].x := w;
p[2].y := w;
end;
badLeft:
begin
p[0].x := 0;
p[0].y := Round(w/2);
p[1].x := w;
p[1].y := 0;
p[2].x := w;
p[2].y := w;
end;
badRight:
begin
p[0].x := w;
p[0].y := Round(w/2);
p[1].x := 0;
p[1].y := 0;
p[2].x := 0;
p[2].y := w;
end;
end;
// Fill n times to get best quality
for n := 1 to 6 do
temp.FillPolyAntialias(p, ColorToBGRA(ColorToRGB(AColor),AOpacity));
ATargetBGRA.BlendImage(
ARect.Right-Round( ((ARect.Right-ARect.Left)/2) + (w/2) ),
ARect.Bottom-Round( ((ARect.Bottom-ARect.Top)/2) + (w/2) ),
temp,
boLinearBlend
);
temp.Free;
end;
procedure RenderBackground(const ARect: TRect; const ABackground: TBCBackground; out
ATargetBGRA: TBGRABitmap; ARounding: TBCRounding = nil);
var
backcolor: TBGRAPixel;
multi: TBGRAMultishapeFiller;
back: TBGRABitmap;
grect1, grect2: TRect;
gra: TBGRAGradientScanner;
fiLight: TFillBorderRoundRectInfo;
rx,ry: Byte;
ropt: TRoundRectangleOptions;
begin
if ARounding = nil then
begin
rx := 0;
ry := 0;
ropt := [];
end else
begin
rx := ARounding.RoundX;
ry := ARounding.RoundY;
ropt := ARounding.RoundOptions;
end;
{ Background color }
case ABackground.Style of
bbsClear: backcolor := BGRAPixelTransparent;
// TODO: Why if I use some system colors like clBtnFace, clActiveCaption etc.
// without ColorToRGB, I always get Black? Interface: QT
bbsColor: backcolor := ColorToBGRA(ColorToRGB(ABackground.Color), ABackground.ColorOpacity);
end;
case ABackground.Style of
bbsClear, bbsColor:
{ Solid background color }
ATargetBGRA.FillRoundRectAntialias(ARect.Left, ARect.Top, ARect.Right,
ARect.Bottom, rx, ry, backcolor, ropt);
bbsGradient:
begin
{ Using multishape filler to merge background gradient and border }
multi := TBGRAMultishapeFiller.Create;
multi.PolygonOrder := poFirstOnTop; { Border will replace background }
{ Gradients }
back := TBGRABitmap.Create(ATargetBGRA.Width, ATargetBGRA.Height, BGRAPixelTransparent);
grect1 := ARect;
grect2 := ARect;
{ Gradient 1 }
if ABackground.Gradient1EndPercent > 0 then
begin
grect1.Bottom := Round((grect1.Bottom / 100) * ABackground.Gradient1EndPercent);
gra := CreateGradient(ABackground.Gradient1, grect1);
back.FillRect(grect1.Left, grect1.Top, grect1.Right, grect1.Bottom,
gra, dmSet
);
gra.Free;
end;
{ Gradient 2 }
if ABackground.Gradient1EndPercent < 100 then
begin
if grect1.Bottom < ARect.Bottom then
grect2.Top := grect1.Bottom - 1;
gra := CreateGradient(ABackground.Gradient2, grect2);
back.FillRect(grect2.Left, grect2.Top, grect2.Right, grect2.Bottom,
gra, dmSet
);
gra.Free;
end;
multi.AddRoundRectangle(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom,
rx, ry, back, ropt);
multi.Draw(ATargetBGRA);
multi.Free;
back.Free;
end;
end;
end;
end.