You could make resizeBitmap()
be a class method of a Generic class, eg:
type
TBitmapUtility<T> = class
public
class procedure resizeBitmap(const aBitmap: T; const w, h: integer);
end;
class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
begin
...
end;
Then you can specify either FMX.Graphics.TBitmap
or VCL.Graphics.TBitmap
as the Generic type:
var
bmp: FMX.Graphics.TBitmap;
TBitmapUtility<FMX.Graphics.TBitmap>.resizeBitmap(bmp, ...);
var
bmp: VCL.Graphics.TBitmap;
TBitmapUtility<VCL.Graphics.TBitmap>.resizeBitmap(...);
If you specify just TBitmap
as the type, the compiler can decide to use FMX.Graphics.TBitmap
or VCL.Graphics.TBitmap
based on which unit you have in the uses
clause, which you can control conditionally:
uses
...,
{$IF Declared(FireMonkeyVersion)}
FMX.Graphics,
{$ELSE}
VCL.Graphics,
{$IFEND}
...;
var
bmp: TBitmap;
TBitmapUtility<TBitmap>.resizeBitmap(bmp, ...);
Or, use the project's "Unit Scope Names" list instead:
uses
...,
Graphics, // <-- specify either 'Vcl' or 'Fmx' in the Unit Scope Names list...
...;
var
bmp: TBitmap;
TBitmapUtility<TBitmap>.resizeBitmap(bmp, ...);
With that said, you do run into a problem - FMX.Graphics.TBitmap
and VCL.Graphics.TBitmap
do not have a common ancestor beyond TPersistent
, so you can't apply a Generic contraint to T
so code like this can compile:
class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
begin
aBitmap.Width := w;
aBitmap.Height := h;
end;
You will have to resort to using RTTI to solve this, eg:
uses
..., System.Rtti;
type
TBitmapUtility<T: class> = class
public
class procedure resizeBitmap(const aBitmap: T; const w, h: integer);
end;
class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
var
Ctx: TRttiContext;
Typ: TRttiType;
begin
Typ := Ctx.GetType(TypeInfo(T));
Typ.GetProperty('Width').SetValue(Pointer(aBitmap), w);
Typ.GetProperty('Height').SetValue(Pointer(aBitmap), h);
end;
Or:
class procedure TBitmapUtility<T>.resizeBitmap(const aBitmap: T; const w, h: integer);
var
Ctx: TRttiContext;
Typ: TRttiType;
Mth: TRttiMethod;
begin
Typ := Ctx.GetType(TypeInfo(T));
Mth := Typ.GetMethod('Resize'); // FMX
if Mth = nil then
Mth := Typ.GetMethod('SetSize'); // VCL
// or use an $IF/$IFDEF to decide which method to lookup...
if Mth <> nil then
Mth.Invoke(TObject(aBitmap), [w, h])
else
begin
Typ.GetProperty('Width').SetValue(Pointer(aBitmap), w);
Typ.GetProperty('Height').SetValue(Pointer(aBitmap), h);
end;
end;
Actually, if you go the {$IF}
or "Unit Scope Names" list approach, and let the compiler decide which TBitmap
type to use, then you don't actually need the Generic at all, and don't need RTTI when accessing properties/methods that are common to both TBitmap
types (even though they don't have a common ancestor):
uses
...,
{$IF Declared(FireMonkeyVersion)}
FMX.Graphics,
{$ELSE}
VCL.Graphics,
{$ENDIF}
// or, just 'Graphics' unconditionally...
...;
procedure resizeBitmap(const aBitmap: TBitmap; const w, h: integer);
...
procedure resizeBitmap(const aBitmap: TBitmap; const w, h: integer);
begin
aBitmap.Width := w;
aBitmap.Height := h;
end;
...
var
bmp: TBitmap;
resizeBitmap(bmp, ...);