1543 lines
47 KiB
ObjectPascal
1543 lines
47 KiB
ObjectPascal
{
|
|
Version 11.9
|
|
Copyright (c) 1995-2008 by L. David Baldwin,
|
|
Copyright (c) 2008-2018 by HtmlViewer Team
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do
|
|
so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Note that the source modules HTMLGIF1.PAS and DITHERUNIT.PAS
|
|
are covered by separate copyright notices located in those modules.
|
|
}
|
|
|
|
{$I frxHTMLCons.inc}
|
|
|
|
unit frxHTMLImages;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes,
|
|
{$ifdef LCL}
|
|
LclIntf, IntfGraphics, FpImage, LclType, LResources, LMessages, frxHTMLMisc,
|
|
{$else}
|
|
Windows, Jpeg,
|
|
{$endif}
|
|
Contnrs, Graphics,
|
|
//Messages,
|
|
//Variants,
|
|
Types,
|
|
Math,
|
|
frxHTMLURLSubs,
|
|
frxHTMLCaches,
|
|
frxHTMLGlobals,
|
|
frxGif1, frxGif2,
|
|
frxHTMLStyleTypes,
|
|
{$ifdef Compiler20_Plus}
|
|
PngImage,
|
|
{$endif}
|
|
frxBaseGraphicsTypes;
|
|
|
|
{$ifdef LCL}
|
|
{$else}
|
|
const
|
|
DefaultBitmap = 1002;
|
|
ErrBitmap = 1001;
|
|
ErrBitmapMask = 1005;
|
|
{$endif}
|
|
|
|
type
|
|
|
|
//------------------------------------------------------------------------------
|
|
// TfrxHtImage is an image wrapper.
|
|
//------------------------------------------------------------------------------
|
|
// It is used in place of any bitmap / image type used in HtmlViewer < 11.
|
|
// Currently it supports ThtBitmap, (animated) TGifImage, TGpImage or ThtMetafile
|
|
// It replaces the unspecific TgpObject and TBitmapItem of the old image cache.
|
|
//------------------------------------------------------------------------------
|
|
|
|
ThtImageTransparency = (itrNone, itrIntrinsic, itrLLCorner);
|
|
|
|
//BG, 09.04.2011
|
|
TfrxHtImage = class(ThtCachable)
|
|
protected
|
|
FIndex: Integer;
|
|
FRefCount: Integer;
|
|
FIsInternal: Boolean;
|
|
function GetBitmap: TBitmap; virtual; abstract;
|
|
function GetGraphic: TGraphic; virtual;
|
|
function GetImageHeight: Integer; virtual; abstract;
|
|
function GetImageWidth: Integer; virtual; abstract;
|
|
function GetMask: TBitmap; virtual; abstract;
|
|
|
|
function EnlargeBitmap(Bitmap: TBitmap; W, H: Integer): TBitmap; virtual;
|
|
function EnlargeImage(W, H: Integer): TfrxHtImage; virtual;
|
|
procedure DoBaseDraw(Canvas: TCanvas; X, Y, W, H: Integer; DrawProps: TfrxGraphicDrawProps; DrawQuality: TfrxGraphicQuality);
|
|
public
|
|
Transp: ThtImageTransparency; {identifies what the mask is for}
|
|
constructor Create(Tr: ThtImageTransparency); overload;
|
|
procedure AddRef;
|
|
procedure ClearRef;
|
|
function IsUsed: Boolean;
|
|
procedure TileImage(PRec: PtPositionRec; DstW, DstH: Integer; var TiledImage: TfrxHtImage; var NoMask: Boolean); virtual;
|
|
|
|
procedure Draw(Canvas: TCanvas; X, Y, W, H: Integer); virtual;
|
|
procedure Print(Canvas: TCanvas; X, Y, W, H: Integer; BgColor: TColor); virtual;
|
|
procedure DrawTiled(Canvas: TCanvas; XStart, YStart, XEnd, YEnd, W, H: Integer); virtual;
|
|
// used to draw/print background images.
|
|
// BG, 06.09.2015: Currently only TfrxHtBitmapImage and TfrxHtGdipImage implement these methods.
|
|
procedure DrawUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean); virtual;
|
|
procedure PrintUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean); virtual;
|
|
procedure SaveToStream(Stream: TStream); virtual;
|
|
|
|
property Bitmap: TBitmap read GetBitmap;
|
|
property Mask: TBitmap read GetMask;
|
|
property Graphic: TGraphic read GetGraphic;
|
|
property Height: Integer read GetImageHeight;
|
|
property Width: Integer read GetImageWidth;
|
|
property Index: Integer read FIndex write FIndex;
|
|
property IsInternal: Boolean read FIsInternal write FIsInternal;
|
|
end;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// TfrxHtImageCache is the image cache, that holds the above image wrappers.
|
|
//------------------------------------------------------------------------------
|
|
|
|
TfrxHtImageCache = class(ThtCache)
|
|
private
|
|
FOnCacheImageChanged: TNotifyEvent;
|
|
{a list of image filenames and their ThtImages}
|
|
public
|
|
function AddObject(const S: ThtString; AObject: TfrxHtImage): Integer; reintroduce; {$ifdef UseInline} inline; {$endif}
|
|
function AddNullCachedObject(const S: ThtString; CachedIndex: Integer): Integer; {$ifdef UseInline} inline; {$endif}
|
|
function GetImage(I: Integer): TfrxHtImage; {$ifdef UseInline} inline; {$endif}
|
|
function IsNeedExternalCacheUpdate(CacheType: TfrxCachedGraphicType): Boolean; {$ifdef UseInline} inline; {$endif}
|
|
procedure ClearUnused; override;
|
|
procedure Clear; override;
|
|
procedure FillPictureCache(const PictureCache: IfrxPictureCache);
|
|
procedure FillFromPictureCache(const PictureCache: IfrxPictureCache; CacheType: TfrxCachedGraphicType);
|
|
|
|
procedure WriteToStream(Stream: TStream);
|
|
procedure ReadFromStream(Stream: TStream);
|
|
|
|
procedure WriteIndexesToStream(Stream: TStream);
|
|
procedure ReadIndexesFromStream(Stream: TStream);
|
|
property OnCacheImageChanged: TNotifyEvent read FOnCacheImageChanged write FOnCacheImageChanged;
|
|
end;
|
|
|
|
//BG, 09.04.2011
|
|
TfrxHtBitmapImage = class(TfrxHtImage)
|
|
private
|
|
Bitmap, Mask: TBitmap;
|
|
OwnsBitmap, OwnsMask: Boolean;
|
|
protected
|
|
function GetBitmap: TBitmap; override;
|
|
function GetImageHeight: Integer; override;
|
|
function GetImageWidth: Integer; override;
|
|
function GetMask: TBitmap; override;
|
|
public
|
|
constructor Create(AImage: TfrxHtBitmap; Tr: ThtImageTransparency; AOwnsBitmap: Boolean = True); overload;
|
|
constructor Create(AImage, AMask: TBitmap; Tr: ThtImageTransparency; AOwnsBitmap: Boolean = True; AOwnsMask: Boolean = True); overload;
|
|
constructor Create(AImage: TBitmap; Tr: ThtImageTransparency; Color: TColor; AOwnsBitmap: Boolean = True); overload;
|
|
destructor Destroy; override;
|
|
procedure Print(Canvas: TCanvas; X, Y, W, H: Integer; BgColor: TColor); override;
|
|
procedure DrawUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean); override;
|
|
procedure PrintUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean); override;
|
|
end;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// TfrxHtGraphicImage: support for all TGraphics without own TBitmap, e.g. meta files
|
|
//------------------------------------------------------------------------------
|
|
|
|
//BG, 09.04.2011
|
|
TfrxHtGraphicImage = class(TfrxHtImage)
|
|
private
|
|
FGraphic: TGraphic;
|
|
FImage: TfrxHtBitmapImage;
|
|
procedure Construct;
|
|
protected
|
|
function GetBitmap: TBitmap; override;
|
|
function GetGraphic: TGraphic; override;
|
|
function GetImageHeight: Integer; override;
|
|
function GetImageWidth: Integer; override;
|
|
function GetMask: TBitmap; override;
|
|
public
|
|
constructor Create(AGraphic: TGraphic); overload;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
|
|
TfrxCachedGraphicImage = class(TfrxHtGraphicImage)
|
|
private
|
|
FCachedGraphic: IfrxCachedGraphic;
|
|
FCacheType: TfrxCachedGraphicType;
|
|
protected
|
|
function GetGraphic: TGraphic; override;
|
|
function GetImageHeight: Integer; override;
|
|
function GetImageWidth: Integer; override;
|
|
public
|
|
constructor Create(ACachedGraphic: IfrxCachedGraphic; CacheType: TfrxCachedGraphicType); overload;
|
|
destructor Destroy; override;
|
|
procedure SaveToStream(Stream: TStream); override;
|
|
end;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// TfrxHtImageLoader is the base class for image loaders, that the global function
|
|
// LoadImageFromStream() uses to load images of various kinds.
|
|
//
|
|
// HtmlViewer components use LoadImageFromStream() to load images.
|
|
//
|
|
// You may derive your own image loader from this class to support further
|
|
// image file formats. Your loader must load the images into a derivate of
|
|
// the TfrxHtImage declared above.
|
|
//------------------------------------------------------------------------------
|
|
|
|
type
|
|
//BG, 24.08.2015:
|
|
TfrxHtImageLoader = class
|
|
public
|
|
function LoadImageFromStream(Stream: TStream; Transparent: ThtImageTransparency): TfrxHtImage; virtual;
|
|
end;
|
|
|
|
// LoadImageFromStream() tries to load an image from stream using the currently
|
|
// set global image loader.
|
|
//
|
|
// The HtmlViewer components use LoadImageFromStream() to load images from streams.
|
|
function LoadImageFromStream(Stream: TStream; Transparent: ThtImageTransparency): TfrxHtImage;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// image methods
|
|
//------------------------------------------------------------------------------
|
|
|
|
function EnlargeImage(Image: TBitmap; W, H: Integer): TBitmap;
|
|
|
|
procedure PrintBitmap(Canvas: TCanvas; X, Y, W, H: Integer; Bitmap: TBitmap);
|
|
procedure PrintTransparentBitmap3(Canvas: TCanvas; X, Y, NewW, NewH: Integer; Bitmap, Mask: TBitmap; YI, HI: Integer);
|
|
|
|
procedure DrawBackground(ACanvas: TCanvas; ARect: TRect; XStart, YStart, XLast, YLast: Integer;
|
|
Image: TfrxHtImage; BW, BH: Integer; BGColor: TColor);
|
|
{draw the background color and any tiled images on it}
|
|
{ARect, the cliprect, drawing outside this will not show but images may overhang
|
|
XStart, YStart are first image position already calculated for the cliprect and parameters.
|
|
XLast, YLast Tiling stops here.
|
|
BW, BH bitmap dimensions.
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
var
|
|
DefBitMap, ErrorBitMap, ErrorBitmapMask: TBitMap;
|
|
DefImage: TfrxHtBitmapImage;
|
|
ErrorImage: TfrxHtBitmapImage;
|
|
|
|
implementation
|
|
|
|
uses
|
|
frxPictureGraphics, frxGIFGraphic, frxHelpers;
|
|
|
|
var
|
|
ImageLoader: TfrxHtImageLoader;
|
|
|
|
{----------------GetImageAndMaskFromStream}
|
|
|
|
function GetImageMask(Image: TBitmap; ColorValid: boolean; AColor: TColor): TBitmap;
|
|
begin
|
|
Result := nil;
|
|
try
|
|
if ColorValid then
|
|
Image.TransparentColor := AColor; {color has already been selected}
|
|
{else the transparent color is the lower left pixel of the bitmap}
|
|
|
|
Image.Transparent := True;
|
|
|
|
Result := TBitmap.Create;
|
|
Result.Handle := Image.ReleaseMaskHandle;
|
|
Image.Transparent := False;
|
|
except
|
|
FreeAndNil(Result);
|
|
end;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 24.08.2015 --
|
|
function LoadImageFromStream(Stream: TStream; Transparent: ThtImageTransparency): TfrxHtImage;
|
|
begin
|
|
Result := ImageLoader.LoadImageFromStream(Stream, Transparent);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 26.09.2010 --
|
|
function TfrxHtImageLoader.LoadImageFromStream(Stream: TStream; Transparent: ThtImageTransparency): TfrxHtImage;
|
|
// extracted from TfrxHtDocument.GetTheBitmap(), TfrxHtDocument.InsertImage(), and TfrxHtDocument.ReplaceImage()
|
|
|
|
var
|
|
GHelper: TfrxCustomGraphicFormatClass;
|
|
LGraphic: TGraphic;
|
|
|
|
begin
|
|
Result := nil;
|
|
if not Assigned(Stream) then
|
|
Exit;
|
|
|
|
GHelper := GetGraphicFormats.FindByFormat(Stream, [gcLoadFrom, gcDraw, gcConvertToBitmap]);
|
|
if GHelper <> nil then
|
|
begin
|
|
LGraphic := GHelper.CreateFromStream(Stream);
|
|
try
|
|
if Assigned(LGraphic) then
|
|
Result := TfrxHtGraphicImage.Create(LGraphic);
|
|
except
|
|
LGraphic.Free;
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{----------------BitmapToRegion}
|
|
|
|
function BitmapToRegion(ABmp: TBitmap; XForm: PXForm; TransparentColor: TColor): HRGN;
|
|
{Find a Region corresponding to the non-transparent area of a bitmap.
|
|
|
|
Thanks to Felipe Machado. See http://www.delphi3000.com/
|
|
Minor modifications made.}
|
|
const
|
|
AllocUnit = 100;
|
|
type
|
|
PRectArray = ^TRectArray;
|
|
TRectArray = array[0..(MaxInt div SizeOf(TRect)) - 1] of TRect;
|
|
var
|
|
pr: PRectArray; // used to access the rects array of RgnData by index
|
|
h: HRGN; // Handles to regions
|
|
RgnData: PRgnData; // Pointer to structure RGNDATA used to create regions
|
|
lr, lg, lb: Byte; // values for lowest and hightest trans. colors
|
|
x, y, x0: Integer; // coordinates of current rect of visible pixels
|
|
maxRects: Cardinal; // Number of rects to realloc memory by chunks of AllocUnit
|
|
{$ifdef LCL}
|
|
bmp: TLazIntfImage;
|
|
b: TFpColor;
|
|
{$else}
|
|
b: PByteArray; // used to easy the task of testing the byte pixels (R,G,B)
|
|
ScanLinePtr: Pointer; // Pointer to current ScanLine being scanned
|
|
ScanLineInc: Integer; // Offset to next bitmap scanline (can be negative)
|
|
bmp: TBitmap;
|
|
{$endif}
|
|
begin
|
|
Result := 0;
|
|
lr := GetRValue(TransparentColor);
|
|
lg := GetGValue(TransparentColor);
|
|
lb := GetBValue(TransparentColor);
|
|
{ ensures that the pixel format is 32-bits per pixel }
|
|
{$ifdef LCL}
|
|
bmp := TLazIntfImage.Create(0,0);
|
|
try
|
|
bmp.LoadFromBitmap(ABmp.Handle, 0);
|
|
{ alloc initial region data }
|
|
maxRects := AllocUnit;
|
|
GetMem(RgnData, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects));
|
|
FillChar(RgnData^, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), 0);
|
|
try
|
|
with RgnData^.rdh do
|
|
begin
|
|
dwSize := SizeOf(TRgnDataHeader);
|
|
iType := RDH_RECTANGLES;
|
|
nCount := 0;
|
|
nRgnSize := 0;
|
|
SetRect(rcBound, MAXLONG, MAXLONG, 0, 0);
|
|
end;
|
|
{ scan each bitmap row - the orientation doesn't matter (Bottom-up or not) }
|
|
for y := 0 to bmp.Height - 1 do
|
|
begin
|
|
x := 0;
|
|
while x < bmp.Width do
|
|
begin
|
|
x0 := x;
|
|
while x < bmp.Width do
|
|
begin
|
|
b := bmp[x,y];
|
|
if (b.red = lr) and (b.green = lg) and (b.blue = lb) then
|
|
Break; // pixel is transparent
|
|
Inc(x);
|
|
end;
|
|
{ test to see if we have a non-transparent area in the image }
|
|
if x > x0 then
|
|
begin
|
|
{ increase RgnData by AllocUnit rects if we exceeds maxRects }
|
|
if RgnData^.rdh.nCount >= maxRects then
|
|
begin
|
|
Inc(maxRects, AllocUnit);
|
|
ReallocMem(RgnData, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects));
|
|
pr := @RgnData^.Buffer;
|
|
FillChar(pr^[maxRects - AllocUnit], AllocUnit * SizeOf(TRect), 0);
|
|
end;
|
|
{ Add the rect (x0, y)-(x, y+1) as a new visible area in the region }
|
|
pr := @RgnData^.Buffer; // Buffer is an array of rects
|
|
with RgnData^.rdh do
|
|
begin
|
|
SetRect(pr[nCount], x0, y, x, y + 1);
|
|
{ adjust the bound rectangle of the region if we are "out-of-bounds" }
|
|
if x0 < rcBound.Left then
|
|
rcBound.Left := x0;
|
|
if y < rcBound.Top then
|
|
rcBound.Top := y;
|
|
if x > rcBound.Right then
|
|
rcBound.Right := x;
|
|
if y + 1 > rcBound.Bottom then
|
|
rcBound.Bottom := y + 1;
|
|
Inc(nCount);
|
|
end;
|
|
end; // if x > x0
|
|
{ Need to create the region by muliple calls to ExtCreateRegion, 'cause }
|
|
{ it will fail on Windows 98 if the number of rectangles is too large }
|
|
if RgnData^.rdh.nCount = 2000 then
|
|
begin
|
|
h := ExtCreateRegion(XForm, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), RgnData^);
|
|
if Result > 0 then
|
|
begin // Expand the current region
|
|
CombineRgn(Result, Result, h, RGN_OR);
|
|
DeleteObject(h);
|
|
end
|
|
else // First region, assign it to Result
|
|
Result := h;
|
|
RgnData^.rdh.nCount := 0;
|
|
SetRect(RgnData^.rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
|
|
end;
|
|
Inc(x);
|
|
end; // scan every sample byte of the image
|
|
end;
|
|
{ need to call ExCreateRegion one more time because we could have left }
|
|
{ a RgnData with less than 2000 rects, so it wasn't yet created/combined }
|
|
if RgnData^.rdh.nCount > 0 then {LDB 0 Count causes exception and abort in Win98}
|
|
h := ExtCreateRegion(XForm, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects), RgnData^)
|
|
else
|
|
h := 0;
|
|
if Result > 0 then
|
|
begin
|
|
CombineRgn(Result, Result, h, RGN_OR);
|
|
DeleteObject(h);
|
|
end
|
|
else
|
|
Result := h;
|
|
finally
|
|
FreeMem(RgnData, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects));
|
|
end;
|
|
finally
|
|
bmp.Free;
|
|
end;
|
|
{$else}
|
|
bmp := TBitmap.Create;
|
|
try
|
|
bmp.Assign(ABmp);
|
|
bmp.PixelFormat := pf32bit;
|
|
{ alloc initial region data }
|
|
maxRects := AllocUnit;
|
|
GetMem(RgnData, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects));
|
|
FillChar(RgnData^, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), 0);
|
|
try
|
|
with RgnData^.rdh do
|
|
begin
|
|
dwSize := SizeOf(TRgnDataHeader);
|
|
iType := RDH_RECTANGLES;
|
|
nCount := 0;
|
|
nRgnSize := 0;
|
|
SetRect(rcBound, MAXLONG, MAXLONG, 0, 0);
|
|
end;
|
|
{ scan each bitmap row - the orientation doesn't matter (Bottom-up or not) }
|
|
ScanLinePtr := bmp.ScanLine[0];
|
|
if bmp.Height > 1 then
|
|
ScanLineInc := PtrSub(bmp.ScanLine[1], ScanLinePtr)
|
|
else
|
|
ScanLineInc := 0;
|
|
for y := 0 to bmp.Height - 1 do
|
|
begin
|
|
x := 0;
|
|
while x < bmp.Width do
|
|
begin
|
|
x0 := x;
|
|
while x < bmp.Width do
|
|
begin
|
|
b := @PByteArray(ScanLinePtr)[x * SizeOf(TRGBQuad)];
|
|
// BGR-RGB: Windows 32bpp BMPs are made of BGRa quads (not RGBa)
|
|
if (b[2] = lr) and (b[1] = lg) and (b[0] = lb) then
|
|
Break; // pixel is transparent
|
|
Inc(x);
|
|
end;
|
|
{ test to see if we have a non-transparent area in the image }
|
|
if x > x0 then
|
|
begin
|
|
{ increase RgnData by AllocUnit rects if we exceeds maxRects }
|
|
if RgnData^.rdh.nCount >= maxRects then
|
|
begin
|
|
Inc(maxRects, AllocUnit);
|
|
ReallocMem(RgnData, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects));
|
|
pr := @RgnData^.Buffer;
|
|
FillChar(pr^[maxRects - AllocUnit], AllocUnit * SizeOf(TRect), 0);
|
|
end;
|
|
{ Add the rect (x0, y)-(x, y+1) as a new visible area in the region }
|
|
pr := @RgnData^.Buffer; // Buffer is an array of rects
|
|
with RgnData^.rdh do
|
|
begin
|
|
SetRect(pr[nCount], x0, y, x, y + 1);
|
|
{ adjust the bound rectangle of the region if we are "out-of-bounds" }
|
|
if x0 < rcBound.Left then
|
|
rcBound.Left := x0;
|
|
if y < rcBound.Top then
|
|
rcBound.Top := y;
|
|
if x > rcBound.Right then
|
|
rcBound.Right := x;
|
|
if y + 1 > rcBound.Bottom then
|
|
rcBound.Bottom := y + 1;
|
|
Inc(nCount);
|
|
end;
|
|
end; // if x > x0
|
|
{ Need to create the region by muliple calls to ExtCreateRegion, 'cause }
|
|
{ it will fail on Windows 98 if the number of rectangles is too large }
|
|
if RgnData^.rdh.nCount = 2000 then
|
|
begin
|
|
h := ExtCreateRegion(XForm, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), RgnData^);
|
|
if Result > 0 then
|
|
begin // Expand the current region
|
|
CombineRgn(Result, Result, h, RGN_OR);
|
|
DeleteObject(h);
|
|
end
|
|
else // First region, assign it to Result
|
|
Result := h;
|
|
RgnData^.rdh.nCount := 0;
|
|
SetRect(RgnData^.rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
|
|
end;
|
|
Inc(x);
|
|
end; // scan every sample byte of the image
|
|
PtrInc(ScanLinePtr, ScanLineInc);
|
|
end;
|
|
{ need to call ExCreateRegion one more time because we could have left }
|
|
{ a RgnData with less than 2000 rects, so it wasn't yet created/combined }
|
|
if RgnData^.rdh.nCount > 0 then {LDB 0 Count causes exception and abort in Win98}
|
|
h := ExtCreateRegion(XForm, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects), RgnData^)
|
|
else
|
|
h := 0;
|
|
if Result > 0 then
|
|
begin
|
|
CombineRgn(Result, Result, h, RGN_OR);
|
|
DeleteObject(h);
|
|
end
|
|
else
|
|
Result := h;
|
|
finally
|
|
FreeMem(RgnData, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects));
|
|
end;
|
|
finally
|
|
bmp.Free;
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
{----------------DrawBackground}
|
|
|
|
procedure DrawBackground(ACanvas: TCanvas; ARect: TRect; XStart, YStart, XLast, YLast: Integer;
|
|
Image: TfrxHtImage; BW, BH: Integer; BGColor: TColor);
|
|
{draw the background color and any tiled images on it}
|
|
{ARect, the cliprect, drawing outside this will not show but images may overhang
|
|
XStart, YStart are first image position already calculated for the cliprect and parameters.
|
|
XLast, YLast Tiling stops here.
|
|
BW, BH bitmap dimensions.
|
|
}
|
|
var
|
|
OldBrush: HBrush;
|
|
OldPal: HPalette;
|
|
DC: HDC;
|
|
OldBack, OldFore: TColorRef;
|
|
begin
|
|
DC := ACanvas.handle;
|
|
if DC <> 0 then
|
|
begin
|
|
OldPal := SelectPalette(DC, ThePalette, False);
|
|
RealizePalette(DC);
|
|
ACanvas.Brush.Color :=BGColor;
|
|
OldBrush := SelectObject(DC, ACanvas.Brush.Handle);
|
|
OldBack := SetBkColor(DC, clWhite);
|
|
OldFore := SetTextColor(DC, clBlack);
|
|
try
|
|
ACanvas.FillRect(ARect); {background color}
|
|
if Image <> nil then {tile the animated gif}
|
|
Image.DrawTiled(ACanvas, XStart, YStart, XLast, YLast, BW, BH);
|
|
finally
|
|
SelectObject(DC, OldBrush);
|
|
SelectPalette(DC, OldPal, False);
|
|
RealizePalette(DC);
|
|
SetBkColor(DC, OldBack);
|
|
SetTextColor(DC, OldFore);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TgpObject }
|
|
|
|
function EnlargeImage(Image: TBitmap; W, H: Integer): TBitmap;
|
|
{enlarge 1 pixel images for tiling. Returns a TBitmap regardless of Image type}
|
|
begin
|
|
Result := TBitmap.Create;
|
|
Result.Assign(Image);
|
|
if Image.Width = 1 then
|
|
Result.Width := Min(100, W)
|
|
else
|
|
Result.Width := Image.Width;
|
|
if Image.Height = 1 then
|
|
Result.Height := Min(100, H)
|
|
else
|
|
Result.Height := Image.Height;
|
|
Result.Canvas.StretchDraw(Rect(0, 0, Result.Width, Result.Height), Image);
|
|
end;
|
|
|
|
{----------------PrintBitmap}
|
|
|
|
{$ifdef LCL}
|
|
{$else}
|
|
type
|
|
ThtAllocRec = class(TObject)
|
|
public
|
|
Ptr: Pointer;
|
|
ASize: Integer;
|
|
AHandle: HGLOBAL;
|
|
end;
|
|
{$endif}
|
|
|
|
procedure PrintBitmap(Canvas: TCanvas; X, Y, W, H: Integer; Bitmap: TBitmap);
|
|
{Y relative to top of display here}
|
|
|
|
{$ifdef LCL}
|
|
begin
|
|
Canvas.StretchDraw(Rect(X, Y, X + W, Y + H), Bitmap);
|
|
end;
|
|
{$else}
|
|
|
|
function Allocate(Size: Integer): ThtAllocRec;
|
|
begin
|
|
Result := ThtAllocRec.Create;
|
|
with Result do
|
|
begin
|
|
ASize := Size;
|
|
if Size < $FF00 then
|
|
GetMem(Ptr, Size)
|
|
else
|
|
begin
|
|
AHandle := GlobalAlloc(HeapAllocFlags, Size);
|
|
if AHandle = 0 then
|
|
Abort;
|
|
Ptr := GlobalLock(AHandle);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure DeAllocate(AR: ThtAllocRec);
|
|
begin
|
|
with AR do
|
|
if ASize < $FF00 then
|
|
Freemem(Ptr, ASize)
|
|
else
|
|
begin
|
|
GlobalUnlock(AHandle);
|
|
GlobalFree(AHandle);
|
|
end;
|
|
AR.Free;
|
|
end;
|
|
|
|
var
|
|
OldPal: HPalette;
|
|
DC: HDC;
|
|
Info: PBitmapInfo;
|
|
Image: ThtAllocRec;
|
|
ImageSize: DWord;
|
|
InfoSize: DWord;
|
|
|
|
begin
|
|
if (Bitmap = nil) or (Bitmap.Handle = 0) then
|
|
Exit;
|
|
DC := Canvas.Handle;
|
|
try
|
|
GetDIBSizes(Bitmap.Handle, InfoSize, ImageSize);
|
|
GetMem(Info, InfoSize);
|
|
try
|
|
Image := Allocate(ImageSize);
|
|
OldPal := SelectPalette(DC, ThePalette, False);
|
|
try
|
|
GetDIB(Bitmap.Handle, ThePalette, Info^, Image.Ptr^);
|
|
RealizePalette(DC);
|
|
with Info^.bmiHeader do
|
|
StretchDIBits(DC, X, Y, W, H, 0, 0, biWidth, biHeight, Image.Ptr, Info^, DIB_RGB_COLORS, SRCCOPY);
|
|
finally
|
|
DeAllocate(Image);
|
|
SelectPalette(DC, OldPal, False);
|
|
end;
|
|
finally
|
|
FreeMem(Info, InfoSize);
|
|
end;
|
|
except
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
{----------------PrintTransparentBitmap3}
|
|
|
|
procedure PrintTransparentBitmap3(Canvas: TCanvas; X, Y, NewW, NewH: Integer;
|
|
Bitmap, Mask: TBitmap; YI, HI: Integer);
|
|
{Y relative to top of display here}
|
|
{This routine prints transparently on complex background by printing through a clip region}
|
|
{X, Y are point where upper left corner will be printed.
|
|
NewW, NewH are the Width and Height of the output (possibly stretched)
|
|
Vertically only a portion of the Bitmap, Mask may be printed starting at
|
|
Y=YI in the bitmap and a height of HI
|
|
}
|
|
var
|
|
DC: HDC;
|
|
Rgn, OldRgn: HRGN;
|
|
Rslt: Integer;
|
|
XForm: TXForm;
|
|
SizeV, SizeW: TSize;
|
|
HF, VF: double;
|
|
ABitmap, AMask: TBitmap;
|
|
BitmapCopy: boolean;
|
|
Origin: TPoint; //BG, 29.08.2009: window origin for correct mask translation
|
|
begin
|
|
{the following converts the black masked area in the image to white. This may look
|
|
better in WPTools which currently doesn't handle the masking}
|
|
if (Bitmap.Handle = 0) or (HI <= 0) or (Bitmap.Width <= 0) then
|
|
Exit;
|
|
BitmapCopy := Bitmap.Height <> HI;
|
|
try
|
|
if BitmapCopy then
|
|
begin
|
|
ABitmap := TBitmap.Create;
|
|
AMask := TBitmap.Create;
|
|
end
|
|
else
|
|
begin
|
|
ABitmap := Bitmap;
|
|
AMask := Mask;
|
|
end;
|
|
try
|
|
if BitmapCopy then
|
|
begin
|
|
Abitmap.Assign(Bitmap);
|
|
ABitmap.Height := HI;
|
|
BitBlt(ABitmap.Canvas.Handle, 0, 0, Bitmap.Width, HI, Bitmap.Canvas.Handle, 0, YI, SrcCopy);
|
|
AMask.Assign(Mask);
|
|
AMask.Height := HI;
|
|
BitBlt(AMask.Canvas.Handle, 0, 0, AMask.Width, HI, Mask.Canvas.Handle, 0, YI, SrcCopy);
|
|
end;
|
|
|
|
SetBkColor(ABitmap.Canvas.Handle, clWhite);
|
|
SetTextColor(ABitmap.Canvas.Handle, clBlack);
|
|
BitBlt(ABitmap.Canvas.Handle, 0, 0, Bitmap.Width, HI, AMask.Canvas.Handle, 0, 0, SRCPAINT);
|
|
|
|
DC := Canvas.Handle;
|
|
{calculate a transform for the clip region as it may be a different size than
|
|
the mask and needs to be positioned on the canvas.}
|
|
GetViewportExtEx(DC, SizeV);
|
|
GetWindowExtEx(DC, SizeW);
|
|
GetWindowOrgEx(DC, Origin); //BG, 29.08.2009: get origin for correct mask translation
|
|
|
|
HF := (SizeV.cx / SizeW.cx); {Horizontal adjustment factor}
|
|
VF := (SizeV.cy / SizeW.cy); {Vertical adjustment factor}
|
|
|
|
XForm.eM11 := HF * (NewW / Bitmap.Width);
|
|
XForm.eM12 := 0;
|
|
XForm.eM21 := 0;
|
|
XForm.eM22 := VF * (NewH / HI);
|
|
XForm.edx := HF * (X - Origin.X); //BG, 29.08.2009: subtract origin
|
|
XForm.edy := VF * Y;
|
|
|
|
{Find the region for the white area of the Mask}
|
|
Rgn := BitmapToRegion(AMask, @XForm, $FFFFFF);
|
|
if Rgn <> 0 then {else nothing to output--this would be unusual}
|
|
begin
|
|
OldRgn := CreateRectRgn(0, 0, 1, 1); {a valid region is needed for the next call}
|
|
Rslt := GetClipRgn(DC, OldRgn); {save the Old clip region}
|
|
try
|
|
if Rslt = 1 then
|
|
CombineRgn(Rgn, Rgn, OldRgn, RGN_AND);
|
|
SelectClipRgn(DC, Rgn);
|
|
PrintBitmap(Canvas, X, Y, NewW, NewH, ABitmap);
|
|
finally
|
|
if Rslt = 1 then
|
|
SelectClipRgn(DC, OldRgn)
|
|
else
|
|
SelectClipRgn(DC, 0);
|
|
DeleteObject(Rgn);
|
|
DeleteObject(OldRgn);
|
|
end;
|
|
end;
|
|
finally
|
|
if BitmapCopy then
|
|
begin
|
|
ABitmap.Free;
|
|
AMask.Free;
|
|
end;
|
|
end;
|
|
except
|
|
end;
|
|
end;
|
|
|
|
{ TfrxHtImage }
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
procedure TfrxHtImage.AddRef;
|
|
begin
|
|
Inc(FRefCount);
|
|
end;
|
|
|
|
procedure TfrxHtImage.ClearRef;
|
|
begin
|
|
FRefCount := 0;
|
|
end;
|
|
|
|
constructor TfrxHtImage.Create(Tr: ThtImageTransparency);
|
|
begin
|
|
inherited Create;
|
|
Transp := Tr;
|
|
FIndex := -1;
|
|
FRefCount := 0;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 31.08.2015 --
|
|
procedure TfrxHtImage.DoBaseDraw(Canvas: TCanvas; X, Y, W, H: Integer;
|
|
DrawProps: TfrxGraphicDrawProps; DrawQuality: TfrxGraphicQuality);
|
|
var
|
|
GProps: TfrxDrawGraphicExt;
|
|
GHelper: TfrxCustomGraphicFormatClass;
|
|
begin
|
|
if Graphic = nil then Exit;
|
|
GHelper := GetGraphicFormats.FindByGraphic(TGraphicClass(Graphic.ClassType));
|
|
if GHelper = nil then Exit;
|
|
GProps.Quality := DrawQuality;
|
|
GProps.DrawProps := DrawProps;
|
|
if GHelper.IsVector then
|
|
Include(GProps.DrawProps, fgdKeepAspectRatio);
|
|
GHelper.DrawExt(GProps, Canvas, Graphic, Rect(X, Y, X + W, Y + H), 1, 1);
|
|
end;
|
|
|
|
procedure TfrxHtImage.Draw(Canvas: TCanvas; X, Y, W, H: Integer);
|
|
begin
|
|
DoBaseDraw(Canvas, X, Y, W, H, [fgdStretch, fgdDefaultMaskColor], gqDefault);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
procedure TfrxHtImage.DrawUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean);
|
|
begin
|
|
DoBaseDraw(Canvas, X, Y, W, H, [fgdDefaultMaskColor], gqDefault);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
function TfrxHtImage.EnlargeBitmap(Bitmap: TBitmap; W, H: Integer): TBitmap;
|
|
{enlarge 1 pixel images for tiling. Returns a TBitmap regardless of Image type}
|
|
begin
|
|
if Bitmap = nil then
|
|
Result := nil
|
|
else
|
|
begin
|
|
Result := TfrxHtBitmap.Create;
|
|
Result.Assign(Bitmap);
|
|
if Result.Width = 1 then
|
|
Result.Width := Min(100, W);
|
|
if Result.Height = 1 then
|
|
Result.Height := Min(100, H);
|
|
Result.Canvas.StretchDraw(Rect(0, 0, Result.Width, Result.Height), Bitmap);
|
|
end;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
function TfrxHtImage.EnlargeImage(W, H: Integer): TfrxHtImage;
|
|
begin
|
|
Result := TfrxHtBitmapImage.Create(
|
|
EnlargeBitmap(Bitmap, W, H),
|
|
EnlargeBitmap(Mask , W, H), Transp);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
procedure TfrxHtImage.DrawTiled(Canvas: TCanvas; XStart, YStart, XEnd, YEnd, W, H: Integer);
|
|
var
|
|
X, Y: Integer;
|
|
begin
|
|
Y := YStart;
|
|
while Y < YEnd do
|
|
begin
|
|
X := XStart;
|
|
while X < XEnd do
|
|
begin
|
|
Draw(Canvas, X, Y, W, H);
|
|
Inc(X, W);
|
|
end;
|
|
Inc(Y, H);
|
|
end;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 10.04.2011 --
|
|
function TfrxHtImage.GetGraphic: TGraphic;
|
|
begin
|
|
Result := GetBitmap;
|
|
end;
|
|
|
|
function TfrxHtImage.IsUsed: Boolean;
|
|
begin
|
|
Result := FRefCount > 0;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 31.08.2015 --
|
|
procedure TfrxHtImage.Print(Canvas: TCanvas; X, Y, W, H: Integer; BgColor: TColor);
|
|
begin
|
|
DoBaseDraw(Canvas, X, Y, W, H, [fgdStretch], gqPrint);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
procedure TfrxHtImage.PrintUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean);
|
|
begin
|
|
DoBaseDraw(Canvas, X, Y, W, H, [fgdDefaultMaskColor], gqPrint);
|
|
end;
|
|
|
|
procedure TfrxHtImage.SaveToStream(Stream: TStream);
|
|
var
|
|
LGraphic: TGraphic;
|
|
begin
|
|
LGraphic := GetGraphic;
|
|
if Assigned(LGraphic) then
|
|
LGraphic.SaveToStream(Stream);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
procedure TfrxHtImage.TileImage(PRec: PtPositionRec; DstW, DstH: Integer; var TiledImage: TfrxHtImage; var NoMask: Boolean);
|
|
{EnlargeImage returns a TfrxHtBitmapImage in TiledImage regardless of Self type. Descendants may return other types}
|
|
|
|
procedure Enlarge(W, H: Integer);
|
|
var
|
|
LargerImage: TfrxHtImage;
|
|
begin
|
|
LargerImage := EnlargeImage(W + 1, H + 1);
|
|
try
|
|
LargerImage.TileImage(PRec, DstW, DstH, TiledImage, NoMask);
|
|
finally
|
|
LargerImage.Free;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
OW, OH, IW, IH: Integer;
|
|
X, XX, Y, X2, Y2: Integer;
|
|
|
|
procedure ValidateTiledImage;
|
|
var
|
|
TiledBitmap, TiledMask: TBitmap;
|
|
begin
|
|
{TiledImage becomes a TfrxHtBitmapImage}
|
|
if TiledImage = nil then
|
|
begin
|
|
TiledBitmap := TBitmap.Create;
|
|
TiledBitmap.Palette := CopyPalette(ThePalette);
|
|
TiledBitmap.Height := IH;
|
|
TiledBitmap.Width := IW;
|
|
PatBlt(TiledBitmap.Canvas.Handle, 0, 0, IW, IH, Blackness);
|
|
|
|
if not NoMask then
|
|
begin
|
|
TiledMask := TBitmap.Create;
|
|
TiledMask.Monochrome := True;
|
|
TiledMask.Height := IH;
|
|
TiledMask.Width := IW;
|
|
if Mask = nil then
|
|
PatBlt(TiledMask.Canvas.Handle, 0, 0, IW, IH, Whiteness);
|
|
end
|
|
else
|
|
TiledMask := nil;
|
|
|
|
TiledImage := TfrxHtBitmapImage.Create(TiledBitmap, TiledMask, Transp);
|
|
end;
|
|
end;
|
|
|
|
procedure Tile(W, H: Integer);
|
|
begin
|
|
|
|
repeat {tile BGImage in the various dc's}
|
|
XX := X;
|
|
repeat
|
|
TiledImage.Bitmap.Canvas.Draw(XX, Y, Bitmap);
|
|
if Mask <> nil then
|
|
TiledImage.Mask.Canvas.Draw(XX, Y, Mask)
|
|
else if not NoMask then
|
|
PatBlt(TiledImage.Mask.Canvas.Handle, XX, Y, Bitmap.Width, Bitmap.Height, Blackness);
|
|
Inc(XX, Bitmap.Width);
|
|
until XX >= X2;
|
|
Inc(Y, Bitmap.Height);
|
|
until Y >= Y2;
|
|
end;
|
|
|
|
begin
|
|
IW := DstW;
|
|
IH := DstH;
|
|
OW := Width;
|
|
OH := Height;
|
|
|
|
if (IW = 0) or (IH = 0) then
|
|
begin
|
|
FreeAndNil(TiledImage);
|
|
NoMask := True;
|
|
end
|
|
else if (OW = 1) or (OH = 1) then
|
|
begin
|
|
{in case a 1 pixel bitmap is being tiled}
|
|
Enlarge(IW, IH);
|
|
end
|
|
else
|
|
begin
|
|
{compute the location and tiling of BGImage in the background}
|
|
with PRec.X do
|
|
begin
|
|
X := GetPositionInRange(PosType, Value, IW - OW);
|
|
AdjustForTiling(RepeatD, 0, IW, OW, X, X2);
|
|
end;
|
|
with PRec.Y do
|
|
begin
|
|
Y := GetPositionInRange(PosType, Value, IH - OH);
|
|
AdjustForTiling(RepeatD, 0, IH, OH, Y, Y2);
|
|
end;
|
|
|
|
NoMask := not Assigned(Mask) and PRec.X.RepeatD and PRec.Y.RepeatD;
|
|
|
|
ValidateTiledImage;
|
|
Tile(OW, OH);
|
|
end;
|
|
end;
|
|
|
|
{ TfrxHtBitmapImage }
|
|
|
|
//-- BG ---------------------------------------------------------- 29.09.2015 --
|
|
constructor TfrxHtBitmapImage.Create(AImage: TfrxHtBitmap; Tr: ThtImageTransparency; AOwnsBitmap: Boolean);
|
|
begin
|
|
if AImage = nil then
|
|
raise EInvalidImage.Create('TfrxHtBitmapImage requires an image');
|
|
inherited Create(Tr);
|
|
Bitmap := AImage;
|
|
OwnsBitmap := AOwnsBitmap;
|
|
Mask := AImage.BitmapMask;
|
|
OwnsMask := False;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
constructor TfrxHtBitmapImage.Create(AImage, AMask: TBitmap; Tr: ThtImageTransparency; AOwnsBitmap, AOwnsMask: Boolean);
|
|
begin
|
|
if AImage = nil then
|
|
raise EInvalidImage.Create('TfrxHtBitmapImage requires an image');
|
|
inherited Create(Tr);
|
|
Bitmap := AImage;
|
|
OwnsBitmap := AOwnsBitmap;
|
|
Mask := AMask;
|
|
OwnsMask := AOwnsMask;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 10.01.2015 --
|
|
constructor TfrxHtBitmapImage.Create(AImage: TBitmap; Tr: ThtImageTransparency; Color: TColor; AOwnsBitmap: Boolean);
|
|
begin
|
|
if AImage = nil then
|
|
raise EInvalidImage.Create('TfrxHtBitmapImage requires an image');
|
|
inherited Create(Tr);
|
|
Bitmap := AImage;
|
|
OwnsBitmap := AOwnsBitmap;
|
|
case Transp of
|
|
itrIntrinsic: Mask := GetImageMask(Bitmap, True, Color);
|
|
itrLLCorner: Mask := GetImageMask(Bitmap, False, Color);
|
|
end;
|
|
end;
|
|
|
|
destructor TfrxHtBitmapImage.Destroy;
|
|
begin
|
|
if OwnsBitmap then
|
|
Bitmap.Free;
|
|
if OwnsMask then
|
|
Mask.Free;
|
|
inherited;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
procedure TfrxHtBitmapImage.DrawUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean);
|
|
var
|
|
FullBG: TBitmap;
|
|
begin
|
|
if Bitmap = nil then
|
|
Exit;
|
|
if (Mask = nil) or (Transp = itrNone) then
|
|
BitBlt(Canvas.Handle, X, Y, W, H, Bitmap.Canvas.Handle, SrcX, SrcY, SRCCOPY)
|
|
else
|
|
begin
|
|
FullBG := nil;
|
|
InitFullBG(FullBG, W, H, False);
|
|
try
|
|
if FillBackground then
|
|
begin
|
|
FullBG.Canvas.Brush := Canvas.Brush;
|
|
FullBG.Canvas.FillRect( Rect(0, 0, W, H));
|
|
end
|
|
else
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Canvas.Handle, X, Y, SRCCOPY );
|
|
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Bitmap.Canvas.Handle, 0, SrcY, SRCINVERT );
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Mask.Canvas.Handle, 0, SrcY, SRCAND );
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Bitmap.Canvas.Handle, 0, SrcY, SRCPAINT );
|
|
|
|
BitBlt( Canvas.Handle, X, Y, W, H, FullBG.Canvas.Handle, 0, 0, SRCCOPY );
|
|
finally
|
|
FullBG.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtBitmapImage.GetBitmap: TBitmap;
|
|
begin
|
|
Result := Bitmap;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtBitmapImage.GetImageHeight: Integer;
|
|
begin
|
|
if Bitmap <> nil then
|
|
Result := Bitmap.Height
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtBitmapImage.GetImageWidth: Integer;
|
|
begin
|
|
if Bitmap <> nil then
|
|
Result := Bitmap.Width
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtBitmapImage.GetMask: TBitmap;
|
|
begin
|
|
Result := Mask;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 31.08.2015 --
|
|
procedure TfrxHtBitmapImage.Print(Canvas: TCanvas; X, Y, W, H: Integer; BgColor: TColor);
|
|
begin
|
|
if Transp = itrNone then
|
|
inherited
|
|
else if Mask = nil then
|
|
PrintBitmap(Canvas, X, Y, W, H, Bitmap)
|
|
else
|
|
PrintTransparentBitmap3(Canvas, X, Y, W, H, Bitmap, Mask, 0, Bitmap.Height);
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 06.09.2015 --
|
|
procedure TfrxHtBitmapImage.PrintUnstretched(Canvas: TCanvas; X, Y, W, H, SrcX, SrcY: Integer; FillBackground: Boolean);
|
|
var
|
|
FullBG: TBitmap;
|
|
begin
|
|
if Bitmap = nil then
|
|
Exit;
|
|
if (Mask = nil) or (Transp = itrNone) then
|
|
PrintBitmap(Canvas, X, Y, W, H, Bitmap)
|
|
else if FillBackground then
|
|
begin
|
|
FullBG := nil;
|
|
InitFullBG(FullBG, W, H, True);
|
|
try
|
|
FullBG.Canvas.Brush := Canvas.Brush;
|
|
FullBG.Canvas.FillRect( Rect(0, 0, W, H));
|
|
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Bitmap.Canvas.Handle, 0, SrcY, SRCINVERT );
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Mask.Canvas.Handle, 0, SrcY, SRCAND );
|
|
BitBlt(FullBG.Canvas.Handle, 0, 0, W, H, Bitmap.Canvas.Handle, 0, SrcY, SRCPAINT );
|
|
|
|
PrintBitmap(Canvas, X, Y, W, H, FullBG);
|
|
finally
|
|
FullBG.Free;
|
|
end;
|
|
end
|
|
else
|
|
PrintTransparentBitmap3(Canvas, X, Y, W, H, Bitmap, Mask, SrcY, H);
|
|
end;
|
|
|
|
{ TfrxHtGraphicImage }
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
constructor TfrxHtGraphicImage.Create(AGraphic: TGraphic);
|
|
begin
|
|
inherited Create(itrNone);
|
|
FGraphic := AGraphic;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 10.04.2011 --
|
|
destructor TfrxHtGraphicImage.Destroy;
|
|
begin
|
|
FImage.Free;
|
|
FGraphic.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TfrxHtGraphicImage.Construct;
|
|
var
|
|
Tmp: TBitmap;
|
|
pe: TPaletteEntry;
|
|
Color: TColor;
|
|
FBitmap: TBitmap;
|
|
begin
|
|
if not Assigned(FImage) then
|
|
begin
|
|
FBitmap := TBitmap.Create;
|
|
try
|
|
FBitmap.Width := Width;
|
|
FBitmap.Height := Height;
|
|
PatBlt(FBitmap.Canvas.Handle, 0, 0, Width, Height, Blackness);
|
|
FBitmap.Canvas.Draw(0, 0, Graphic);
|
|
|
|
Tmp := TBitmap.Create;
|
|
try
|
|
Tmp.Width := Width;
|
|
Tmp.Height := Height;
|
|
Tmp.PixelFormat := pf8Bit;
|
|
{pick an odd color from the palette to represent the background color,
|
|
one not likely in the metafile}
|
|
GetPaletteEntries(Tmp.Palette, 115, 1, pe);
|
|
Color := pe.peBlue shl 16 or pe.peGreen shl 8 or pe.peRed;
|
|
Tmp.Canvas.Brush.Color := Color;
|
|
Tmp.Canvas.FillRect(Rect(0, 0, Width, Height));
|
|
Tmp.Canvas.Draw(0, 0, Graphic);
|
|
|
|
FImage := TfrxHtBitmapImage.Create(FBitmap, GetImageMask(Tmp, False, Color), itrLLCorner);
|
|
finally
|
|
Tmp.Free;
|
|
end;
|
|
except
|
|
FreeAndNil(FBitmap);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtGraphicImage.GetBitmap: TBitmap;
|
|
begin
|
|
Construct;
|
|
Result := FImage.Bitmap;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtGraphicImage.GetGraphic: TGraphic;
|
|
begin
|
|
Result := FGraphic;
|
|
end;
|
|
|
|
function TfrxHtGraphicImage.GetImageHeight: Integer;
|
|
begin
|
|
Result := Graphic.Height;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtGraphicImage.GetImageWidth: Integer;
|
|
begin
|
|
Result := Graphic.Width;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 09.04.2011 --
|
|
function TfrxHtGraphicImage.GetMask: TBitmap;
|
|
begin
|
|
Construct;
|
|
Result := FImage.Mask;
|
|
end;
|
|
|
|
{ TfrxHtImageCache }
|
|
|
|
//------------------------------------------------------------------------------
|
|
function TfrxHtImageCache.AddNullCachedObject(const S: ThtString; CachedIndex: Integer): Integer;
|
|
var
|
|
Img: TfrxCachedGraphicImage;
|
|
begin
|
|
Img := TfrxCachedGraphicImage.Create(nil, cgNone);
|
|
Img.Index := CachedIndex;
|
|
Result := AddObject(S, Img);
|
|
end;
|
|
|
|
function TfrxHtImageCache.AddObject(const S: ThtString; AObject: TfrxHtImage): Integer;
|
|
begin
|
|
Result := inherited AddObject(S, AObject);
|
|
if Assigned(FOnCacheImageChanged) then
|
|
FOnCacheImageChanged(Self);
|
|
end;
|
|
|
|
procedure TfrxHtImageCache.Clear;
|
|
begin
|
|
inherited;
|
|
if Assigned(FOnCacheImageChanged) then
|
|
FOnCacheImageChanged(Self);
|
|
end;
|
|
|
|
procedure TfrxHtImageCache.ClearUnused;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := 0;
|
|
while i < Count do
|
|
begin
|
|
if not GetImage(i).IsUsed then
|
|
begin
|
|
Objects[i].Free;
|
|
Delete(i);
|
|
end
|
|
else
|
|
begin
|
|
GetImage(i).ClearRef;
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TfrxHtImageCache.FillFromPictureCache(const PictureCache: IfrxPictureCache; CacheType: TfrxCachedGraphicType);
|
|
var
|
|
i: Integer;
|
|
Img: TfrxHtImage;
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Img := GetImage(i);
|
|
if Img is TfrxCachedGraphicImage then
|
|
begin
|
|
if PictureCache.IsAutoRefMode then
|
|
TfrxCachedGraphicImage(img).FCachedGraphic := PictureCache.GetCachedBitmap(CacheType, Img.Index)
|
|
else
|
|
TfrxCachedGraphicImage(img).FGraphic := PictureCache.GetGraphic(Img.Index);
|
|
TfrxCachedGraphicImage(img).FCacheType := CacheType;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrxHtImageCache.FillPictureCache(const PictureCache: IfrxPictureCache);
|
|
var
|
|
i: Integer;
|
|
Img: TfrxHtImage;
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
Img := GetImage(I);
|
|
if Img.Index < 0 then
|
|
Img.Index := PictureCache.AddPicture(Img.Graphic);
|
|
end;
|
|
end;
|
|
|
|
//-- BG ---------------------------------------------------------- 11.04.2011 --
|
|
|
|
function TfrxHtImageCache.GetImage(I: Integer): TfrxHtImage;
|
|
begin
|
|
Result := TfrxHtImage(inherited GetCachable(I));
|
|
end;
|
|
|
|
function TfrxHtImageCache.IsNeedExternalCacheUpdate(
|
|
CacheType: TfrxCachedGraphicType): Boolean;
|
|
var
|
|
i: Integer;
|
|
img: TfrxHtImage;
|
|
begin
|
|
Result := False;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
img := GetImage(i);
|
|
if img is TfrxCachedGraphicImage then
|
|
if TfrxCachedGraphicImage(img).FCacheType <> CacheType then
|
|
begin
|
|
Result := True;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrxHtImageCache.ReadFromStream(Stream: TStream);
|
|
var
|
|
C, i: Integer;
|
|
WS: WideString;
|
|
HtImage: TfrxHtImage;
|
|
StreamSize, StreamPos: Int64;
|
|
begin
|
|
Clear;
|
|
Stream.ReadBuffer(C, SizeOf(C));
|
|
StreamSize := 0;
|
|
|
|
for i := 0 to C - 1 do
|
|
begin
|
|
WS := ReadWideStringFromStream(Stream);
|
|
Stream.ReadBuffer(StreamSize, SizeOf(StreamSize));
|
|
StreamPos := Stream.Position;
|
|
HtImage := LoadImageFromStream(Stream, itrNone);
|
|
Stream.Position := StreamPos + StreamSize;
|
|
AddObject(WS, HtImage);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrxHtImageCache.ReadIndexesFromStream(Stream: TStream);
|
|
var
|
|
Cnt, i, Index: Integer;
|
|
WS: WideString;
|
|
begin
|
|
Clear;
|
|
Stream.ReadBuffer(Cnt, SizeOf(Cnt));
|
|
for i := 0 to Cnt - 1 do
|
|
begin
|
|
WS := ReadWideStringFromStream(Stream);
|
|
Stream.Read(Index, Sizeof(Index));
|
|
AddNullCachedObject(WS, Index);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrxHtImageCache.WriteIndexesToStream(Stream: TStream);
|
|
var
|
|
Cnt, i, Index: Integer;
|
|
WS: WideString;
|
|
begin
|
|
Cnt := Count;
|
|
Stream.WriteBuffer(Cnt, SizeOf(Cnt));
|
|
for i := 0 to Cnt - 1 do
|
|
begin
|
|
WS := Strings[i];
|
|
WriteWideStringToStream(Stream, WS);
|
|
Index := GetImage(I).Index;
|
|
Stream.Write(Index, SizeOf(Index));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TfrxHtImageCache.WriteToStream(Stream: TStream);
|
|
var
|
|
C, i: Integer;
|
|
WS: WideString;
|
|
StreamPos, StreamSize: Int64;
|
|
begin
|
|
C := 0;
|
|
Stream.WriteBuffer(C, SizeOf(C));
|
|
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
if (Objects[i] is TfrxCachedGraphicImage) or TfrxHtImage(Objects[i]).IsInternal or not Assigned(TfrxHtGraphicImage(Objects[i]).Graphic) then
|
|
Continue;
|
|
WS := Strings[i];
|
|
WriteWideStringToStream(Stream, WS);
|
|
StreamPos := Stream.Position;
|
|
StreamSize := 0;
|
|
Stream.WriteBuffer(StreamSize, SizeOf(StreamSize));
|
|
TfrxHtGraphicImage(Objects[i]).SaveToStream(Stream);
|
|
StreamSize := Stream.Position - (StreamPos + SizeOf(StreamSize));
|
|
Stream.Position := StreamPos;
|
|
Stream.WriteBuffer(StreamSize, SizeOf(StreamSize));
|
|
Stream.Seek(0, soEnd);
|
|
Inc(C);
|
|
end;
|
|
|
|
Stream.Position := 0;
|
|
Stream.WriteBuffer(C, SizeOf(C));
|
|
end;
|
|
|
|
{ TfrxCachedGraphicImage }
|
|
|
|
constructor TfrxCachedGraphicImage.Create(ACachedGraphic: IfrxCachedGraphic;
|
|
CacheType: TfrxCachedGraphicType);
|
|
var
|
|
LGraphic: TGraphic;
|
|
begin
|
|
LGraphic := nil;
|
|
if Assigned(ACachedGraphic) then
|
|
begin
|
|
LGraphic := ACachedGraphic.GetGraphic(CacheType);
|
|
FCachedGraphic := ACachedGraphic;
|
|
end;
|
|
inherited Create(LGraphic);
|
|
end;
|
|
|
|
destructor TfrxCachedGraphicImage.Destroy;
|
|
begin
|
|
FCachedGraphic := nil;
|
|
inherited;
|
|
end;
|
|
|
|
function TfrxCachedGraphicImage.GetGraphic: TGraphic;
|
|
begin
|
|
if Assigned(FCachedGraphic) then
|
|
Result := FCachedGraphic.GetGraphic(FCacheType)
|
|
else
|
|
Result := FGraphic;
|
|
end;
|
|
|
|
function TfrxCachedGraphicImage.GetImageHeight: Integer;
|
|
begin
|
|
Result := -1;
|
|
if Assigned(FCachedGraphic) then
|
|
Result := FCachedGraphic.GetOriginalSize.cy;
|
|
if Result <=0 then
|
|
Result := GetGraphic.Height;
|
|
end;
|
|
|
|
function TfrxCachedGraphicImage.GetImageWidth: Integer;
|
|
begin
|
|
Result := -1;
|
|
if Assigned(FCachedGraphic) then
|
|
Result := FCachedGraphic.GetOriginalSize.cx;
|
|
if Result <=0 then
|
|
Result := GetGraphic.Width;
|
|
end;
|
|
|
|
procedure TfrxCachedGraphicImage.SaveToStream(Stream: TStream);
|
|
var
|
|
sz: TSize;
|
|
LGraphic: TGraphic;
|
|
begin
|
|
if Assigned(FCachedGraphic) then
|
|
begin
|
|
LGraphic := Graphic;
|
|
sz := FCachedGraphic.GetOriginalSize;
|
|
if (sz.cx <> LGraphic.Width) or (sz.cy <> LGraphic.Height) then
|
|
begin
|
|
LGraphic := GetGraphicFormats.ScaleGraphicToNew(LGraphic, sz.cx, sz.cy);
|
|
try
|
|
LGraphic.SaveToStream(Stream);
|
|
finally
|
|
LGraphic.Free;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
inherited SaveToStream(Stream);
|
|
end;
|
|
|
|
initialization
|
|
DefBitMap := TBitmap.Create;
|
|
ErrorBitMap := TBitmap.Create;
|
|
ErrorBitMapMask := TBitmap.Create;
|
|
{$ifdef LCL}
|
|
{$I frxHTMLun2.lrs}
|
|
DefBitMap.LoadFromLazarusResource('DefaultBitmap');
|
|
ErrorBitMap.LoadFromLazarusResource('ErrBitmap');
|
|
ErrorBitMapMask.LoadFromLazarusResource('ErrBitmapMask');
|
|
{$else}
|
|
{$R frxHTML32.res}
|
|
DefBitMap.Handle := LoadBitmap(HInstance, MakeIntResource(DefaultBitmap));
|
|
ErrorBitMap.Handle := LoadBitmap(HInstance, MakeIntResource(ErrBitmap));
|
|
ErrorBitMapMask.Handle := LoadBitmap(HInstance, MakeIntResource(ErrBitmapMask));
|
|
{$endif}
|
|
|
|
DefImage := TfrxHtBitmapImage.Create(DefBitmap, nil, itrNone);
|
|
ErrorImage := TfrxHtBitmapImage.Create(ErrorBitmap, ErrorBitmapMask, itrLLCorner);
|
|
ImageLoader := TfrxHtImageLoader.Create;
|
|
finalization
|
|
ImageLoader.Free;
|
|
ErrorImage.Free;
|
|
DefImage.Free;
|
|
end.
|