How to change bitmap from VCL.Graphics.TBitmap to FMX.Graphics.TBitmap - delphi

I'm trying to show an image taken with a camera on a multi-device form with paintbox after processing it with opencv. However, cvImage2Bitmap returns VCL.Graphics.TBitmap. So I need to convert this to FMX.Graphics.TBitmap.
unit xml_cam2;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, VCL.Graphics, FMX.Dialogs, FMX.ScrollBox,
FMX.Memo, FMX.Objects, FMX.Controls.Presentation, FMX.StdCtrls,
ocv.highgui_c,
ocv.core_c,
ocv.core.types_c,
ocv.imgproc_c,
ocv.imgproc.types_c,
ocv.utils;
type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
PaintBox1: TPaintBox;
Memo1: TMemo;
procedure PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
procedure FormCreate(Sender: TObject);
private
capture: pCvCapture;
frame: pIplImage;
procedure OnIdle(Sender: TObject; var Done: Boolean);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var
Bitmap, PaintBoxBitmap: FMX.Graphics.TBitmap;
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
capture := cvCreateCameraCapture(CV_CAP_ANY);
if Assigned(capture) then
Application.OnIdle := OnIdle;
end;
procedure TForm1.OnIdle(Sender: TObject; var Done: Boolean);
begin
if Assigned(capture) then
begin
frame := cvQueryFrame(capture);
if Assigned(frame) then
begin
Bitmap := cvImage2Bitmap(frame);
//cvImage2Bitmap returns VCL.Graphics.TBitmap
end;
end;
Memo1.Lines.Add(IntToStr(Bitmap.Width));
Memo1.Lines.Add(IntToStr(Bitmap.Height));
if (PaintBoxBitmap = nil) then
PaintBoxBitmap := FMX.Graphics.TBitmap.Create;
PaintBoxBitmap.Assign(Bitmap);
Invalidate;
Bitmap.Free;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
if Assigned(PaintBoxBitmap) then
PaintBox1.Canvas.DrawBitmap(PaintBoxBitmap, PaintBox1.ClipRect, PaintBox1.ClipRect, 1);
Memo1.Lines.Add('b');
end;
end.
If you know any other efficient way to show iplimage to paintbox, please let us know.

It would be nice to move image data directly from one bitmap to the other, but doesn't seems to be an easy task.
Instead, the simplest way ( as it was answered here VCL.Bitmap To FMX.Bitmap ) seems to be to save image to a memory stream and load again in FMX bitmap object.
This simple code just works. You pay the penalty for moving image data to memory to load again in the new bitmap, but seems fair in exchange for simplicity.
procedure TForm1.FormCreate(Sender: TObject);
var
ms : TMemoryStream;
begin
ms := TMemoryStream.Create;
try
VCL_bmp := VCL.Graphics.TBitmap.Create;
try
VCL_bmp.LoadFromFile('file.bmp');
VCL_bmp.SaveToStream(ms);
ms.Seek(0, soFromBeginning);
finally
FreeAndNil(VCL_bmp);
end;
FMX_bmp := FMX.Graphics.TBitmap.Create();
try
FMX_bmp.LoadFromStream(ms);
... do something with the image ...
finally
FreeAndNil(FMX_bmp);
end;
finally
FreeAndNil(ms);
end;
end;

There are a couple of ways to do this. One way is to write the VCL bitmap to a TStream and then read it into the FMX bitmap. However, you can't convert the other way like that and it may be quite slow. I prefer to use Scanlines to convert between one and the other. In my code below I'm using 24 bit VCL bitmaps because I've found that the Windows API prefers these (AVIFile32 for example). Both bitmaps need to be created before calling the procedures. Of course you need to be creating an FMX application for Windows and include VCL.Graphics in your uses. Any transparency in the FMX bitmap will be lost when converting to a 24 bit VCL bitmap.
Convert 24 bit VCL bitmap to FMX bitmap
procedure VCLtoFMX_Bitmap(const VCLBmp : VCL.Graphics.TBitmap ; out FMXBmp : FMX.Graphics.TBitmap);
var
bData : TBitmapData;
x, y : Integer;
pfmxbyte, pvclbyte : PByte;
begin
VCLBmp.PixelFormat := pf24bit;
FMXBmp.SetSize(VCLBmp.Width, VCLBmp.Height);
FMXBmp.Map(TMapAccess.ReadWrite, bdata);
try
for y := 0 to FMXBmp.Height - 1 do begin
pfmxbyte := bdata.GetScanline(y);
pvclbyte := VCLBmp.Scanline[y];
for x := 0 to FMXBmp.Width - 1 do begin
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := pvclbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pfmxbyte^ := $FF; Inc(pfmxbyte); // Full opacity
end;
end;
finally
FMXBmp.Unmap(bdata);
end;
end;
Convert FMX bitmap to 24 bit VCL bitmap
procedure FMXtoVCL_Bitmap(const FMXBmp : FMX.Graphics.TBitmap ; out VCLBmp : VCL.Graphics.TBitmap);
var
bData : TBitmapData;
x, y : Integer;
pfmxbyte, pvclbyte : PByte;
begin
VCLBmp.PixelFormat := pf24bit;
VCLBmp.SetSize(FMXBmp.Width, FMXBmp.Height);
FMXBmp.Map(TMapAccess.Read, bdata);
try
for y := 0 to FMXBmp.Height - 1 do begin
pfmxbyte := bdata.GetScanline(y);
pvclbyte := VCLBmp.Scanline[y];
for x := 0 to FMXBmp.Width - 1 do begin
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte);
pvclbyte^ := pfmxbyte^; Inc(pvclbyte); Inc(pfmxbyte, 2);
end;
end;
finally
FMXBmp.Unmap(bdata);
end;
end;

Related

User-selected image file not showing in TImage component

I have this procedure that lets a user select an image file. Once selected, it should be displayed in a TImage component.
Why is it not showing (even when the file path is shown in a label)?
procedure TForm1.btn1_select_imgClick(Sender: TObject);
var
OpenDialog: TOpenDialog;
begin
OpenDialog := TOpenDialog.Create(nil);
OpenDialog.Filter := 'All Files|*.*';
OpenDialog.Options := [ofPathMustExist, ofFileMustExist];
try
if OpenDialog.Execute then
begin
lbl_selected_file.Caption := OpenDialog.FileName;
img1.Picture.LoadFromFile(OpenDialog.FileName);
OriginalImage := TBitmap.Create;
OriginalImage.Assign(img1.Picture.Bitmap);
end;
finally
OpenDialog.Free;
end;
end;
Expected result:
Full code:
unit image_blurrr_unit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Imaging.Jpeg;
var
OriginalImage: TBitmap;
PixellatedImage: TBitmap;
type
TForm1 = class(TForm)
btn1_select_img: TButton;
btn2_select_output_path: TButton;
lbl_selected_file: TLabel;
lbl_output_path: TLabel;
img1: TImage;
pnl_img: TPanel;
pnl_btns: TPanel;
procedure btn1_select_imgClick(Sender: TObject);
procedure btn2_select_output_pathClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btn1_select_imgClick(Sender: TObject);
var
OpenDialog: TOpenDialog;
begin
OpenDialog := TOpenDialog.Create(nil);
OpenDialog.Filter := 'All Files|*.*';
OpenDialog.Options := [ofPathMustExist, ofFileMustExist];
try
if OpenDialog.Execute then
begin
lbl_selected_file.Caption := OpenDialog.FileName;
img1.Picture.LoadFromFile(OpenDialog.FileName);
OriginalImage := TBitmap.Create;
OriginalImage.Assign(img1.Picture.Bitmap);
end;
finally
OpenDialog.Free;
end;
end;
procedure TForm1.btn2_select_output_pathClick(Sender: TObject);
var
FileOpenDialog: TFileOpenDialog;
OutputImage: TBitmap;
begin
FileOpenDialog := TFileOpenDialog.Create(nil);
try
FileOpenDialog.Options := [fdoPickFolders];
FileOpenDialog.Title := 'Select Output Path';
if FileOpenDialog.Execute then
begin
OutputImage := TBitmap.Create;
try
// Create a copy of the original image
OutputImage.Assign(OriginalImage);
// Apply the blur effect to the output image here...
// Save the output image to the selected folder
OutputImage.SaveToFile(FileOpenDialog.FileName + '\output.jpg');
finally
OutputImage.Free;
end;
end;
finally
FileOpenDialog.Free;
end;
end;
end.
The answer is in the TPicture.Bitmap documentation:
If Bitmap is referenced when the picture contains a Metafile or Icon graphic 1, the graphic won't be converted (Types of Graphic Objects). Instead, the original contents of the picture are discarded and Bitmap returns a new, blank bitmap.
1: just replace "a Metafile or Icon graphic" with "any non-BMP graphic".
So, if the user selects a non-.bmp file (which is likely the case, since .bmp files are rarely used nowadays, in favor of other file formats, like PNG), then accessing img1.Picture.Bitmap will wipe out the current image that has been loaded into img1.Picture, replaced with a blank TBitmap object (which you then assign to your OriginalImage object). That is why you don't see anything being displayed in the TImage.
The solution is to access the original loaded image via the TPicture.Graphic property, instead of the TPicture.Bitmap property, eg:
OriginalImage.Assign(img1.Picture.Graphic);
That being said, there is another problem with your code:
OutputImage.SaveToFile(FileOpenDialog.FileName + '\output.jpg');
OutputImage is a TBitmap, so its SaveToFile() method can only create a BMP-encoded file. So, you are creating a BMP file with a .jpg file extension, which does not make it a valid JPG image. To do that, you would need to use TJPEGImage instead of TBitmap, eg:
uses
..., Vcl.Imaging.jpeg;
procedure TForm1.btn2_select_output_pathClick(Sender: TObject);
var
FileOpenDialog: TFileOpenDialog;
BlurredImage: TBitmap;
OutputImage: TJPEGImage;
begin
FileOpenDialog := TFileOpenDialog.Create(nil);
try
FileOpenDialog.Options := [fdoPickFolders];
FileOpenDialog.Title := 'Select Output Path';
if FileOpenDialog.Execute then
begin
BlurredImage := TBitmap.Create;
try
// Create a copy of the original image
BlurredImage.Assign(OriginalImage);
// Apply the blur effect to the output image here...
// Save the output image to the selected folder
OutputImage := TJPEGImage.Create;
try
OutputImage.Assign(BlurredImage);
OutputImage.SaveToFile(FileOpenDialog.FileName + '\output.jpg');
finally
OutputImage.Free;
end;
finally
BlurredImage.Free;
end;
end;
finally
FileOpenDialog.Free;
end;
end;
UPDATE:
That said, if you want the output file to be in the same format as the input file, then you can do this instead:
var
OriginalImage: TGraphic;
OriginalExt: String;
...
procedure TForm1.btn1_select_imgClick(Sender: TObject);
var
OpenDialog: TOpenDialog;
begin
OpenDialog := TOpenDialog.Create(nil);
OpenDialog.Filter := 'All Files|*.*';
OpenDialog.Options := [ofPathMustExist, ofFileMustExist];
try
if OpenDialog.Execute then
begin
lbl_selected_file.Caption := OpenDialog.FileName;
img1.Picture.LoadFromFile(OpenDialog.FileName);
OriginalImage := TGraphicClass(img1.Picture.Graphic.ClassType).Create;
OriginalImage.Assign(img1.Picture.Graphic);
OriginalExt := ExtractFileExt(OpenDialog.FileName);
end;
finally
OpenDialog.Free;
end;
end;
procedure TForm1.btn2_select_output_pathClick(Sender: TObject);
var
FileOpenDialog: TFileOpenDialog;
BlurredImage: TBitmap;
OutputImage: TGraphic;
begin
FileOpenDialog := TFileOpenDialog.Create(nil);
try
FileOpenDialog.Options := [fdoPickFolders];
FileOpenDialog.Title := 'Select Output Path';
if FileOpenDialog.Execute then
begin
BlurredImage := TBitmap.Create;
try
// Create a copy of the original image
BlurredImage.Assign(OriginalImage);
// Apply the blur effect to the output image here...
// Save the output image to the selected folder
OutputImage := TGraphicClass(OriginalImage.ClassType).Create;
try
OutputImage.Assign(BlurredImage);
OutputImage.SaveToFile(FileOpenDialog.FileName + '\output' + OriginalExt);
// alternatively:
// OutputImage.SaveToFile(FileOpenDialog.FileName + '\output.' + GraphicExtension(TGraphicClass(OutputImage.ClassType)));
finally
OutputImage.Free;
end;
finally
BlurredImage.Free;
end;
end;
finally
FileOpenDialog.Free;
end;
end;
Do you know that you can use FMX.TBitmap class in VCL application?
Why would anyone even consider using FMX.TBitmap in VCL application?
For one FMX.TBitmap.LoadFromFile class supports loading and saving of many image types with ease.
Another thing is that you can easily apply any number FMX.Filter.Effects to the said FMX Bitmap.
The only problem is that there is no easy way of of loading FMX Bitmap to or from VCL components.
Here is a simple code example for adding Pixelate effect for chosen file that is then saved into another file
uses FMX.Graphics, FMX.Filter.Effects;
procedure TForm1.BtnOpenImageClick(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
Image1.Picture.LoadFromFile(OPenDialog1.FileName);
OriginalFileName := OpenDialog1.FileName;
end;
end;
procedure TForm1.BtnSaveProcessedImageClick(Sender: TObject);
var FMXOriginalBitmap: FMX.Graphics.TBitmap;
FMXProcessedBitmap: FMX.Graphics.TBitmap;
FMXPixelateFilter: FMX.Filter.Effects.TFilterPixelate;
begin
FMXOriginalBitmap := FMX.Graphics.TBitmap.Create;
FMXOriginalBitmap.LoadFromFile(OriginalFileName);
FMXPixelateFilter := FMX.Filter.Effects.TFilterPixelate.Create(nil);
FMXPixelateFilter.Input := FMXOriginalBitmap;
FMXPixelateFilter.BlockCount := 100;
FMXProcessedBitmap := FMXPixelateFilter.Output;
FMXProcessedBitmap.SaveToFile('D:\Proba.jpg');
Image2.Picture.LoadFromFile('D:\Proba.jpg');
end;

Delphi: JPG to Bitmap: Incompatible types: 'TPersistent' and 'TFileName'

I'm writing a program that:
1.- Ask the user to select a file, Any kind of image (JPG, PNG, etc)
2.- Let's user pixellate the image and shows the new pixellated image.
Since my test image is a JPG, I'm getting error: Incompatible types: 'TPersistent' and 'TFileName'
Before trying to convert the JPG to Bitmap, I was getting:
Bitmap image is not valid
Code:
unit demo_2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Imaging.Jpeg;
var
OpenDialog: TOpenDialog;
OpenFolder: TFileOpenDialog;
OriginalImage: TBitmap;
PixellatedImage: TBitmap;
type
TForm1 = class(TForm)
btn1_select_img: TButton;
btn2_select_output_path: TButton;
lbl_selected_file: TLabel;
lbl_output_path: TLabel;
img1: TImage;
pnl_img: TPanel;
pnl_btns: TPanel;
procedure btn1_select_imgClick(Sender: TObject);
procedure btn2_select_output_pathClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btn1_select_imgClick(Sender: TObject);
begin
OpenDialog := TOpenDialog.Create(nil);
OpenDialog.Filter := 'All Files|*.*';
OpenDialog.Options := [ofPathMustExist, ofFileMustExist];
try
if OpenDialog.Execute then
begin
// Print the selected file's path to the console
//WriteLn(OpenDialog.FileName);
lbl_selected_file.Caption := OpenDialog.FileName;
img1.Picture.LoadFromFile(OpenDialog.FileName);
end;
finally
//OpenDialog.Free;
end;
end;
procedure TForm1.btn2_select_output_pathClick(Sender: TObject);
begin
//OpenFolder := TFileOpenDialog.Create(nil); for folder selection
//OpenFolder.Options := [fdoPickFolders]; for folder selection
try
//if OpenFolder.Execute then
begin
PixellatedImage := TBitmap.Create;
//PixellatedImage.LoadFromFile(OpenDialog.FileName);
PixellatedImage.Assign(OpenDialog.FileName);
// Pixellate the image by setting the Width and Height to a small value
PixellatedImage.Width := 10;
PixellatedImage.Height := 10;
img1.Picture.Bitmap := PixellatedImage;
//lbl_output_path.Caption := OpenFolder.FileName; for folder selection
end;
finally
//OpenFolder.Free;
//OpenDialog.Free;
PixellatedImage.Free;
end;
end;
end.
Since my test image is a JPG, I'm getting error: Incompatible types: 'TPersistent' and 'TFileName'
Actually, that's wrong in the sense that the error has nothing to do with the image being JPG. So while both "my test image is a JPG" and "I'm getting error ..." are correct, the implication ("since") is not.
The TPersistent.Assign method used in
PixellatedImage.Assign(OpenDialog.FileName);
requires a TPersistent object. In this case, when you are dealing with graphics, you typically need a TGraphic instance. Hence, you cannot pass a string, even if that string happens to be a file name of an image file.
So if you want to use the Assign method on a graphics object, you need to pass it another graphics object -- one you may have loaded from file using its own LoadFromFile:
procedure TForm1.FormCreate(Sender: TObject);
begin
var JpegImage := TJPEGImage.Create;
try
var OpenDlg := TFileOpenDialog.Create(Self);
try
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'JPEG images';
FileMask := '*.jpg';
end;
if OpenDlg.Execute then
JpegImage.LoadFromFile(OpenDlg.FileName)
else
Exit;
finally
OpenDlg.Free;
end;
var BmpImage := TBitmap.Create;
try
BmpImage.Assign(JpegImage);
// For example: BmpImage.SaveToFile('K:\bitmap.bmp');
finally
BmpImage.Free;
end;
finally
JpegImage.Free;
end;
end;
Also, please note that you must use the idiom
LFrog := TFrog.Create;
try
// use LFrog
finally
LFrog.Free;
end
and never
try
LFrog := TFrog.Create;
// use LFrog
finally // WRONG!
LFrog.Free;
end
assuming LFrog is a local variable. If LFrog isn't a local variable, it probably should be made local! Otherwise, it is important to do FreeAndNil on it and not only Free.
Update. The Q was changed so it no longer is about JPG -> BMP, but "any" image file to BMP. Then perhaps the best way is to use the Windows Imaging Component:
procedure TForm1.FormCreate(Sender: TObject);
begin
var WicImage := TWICImage.Create;
try
var OpenDlg := TFileOpenDialog.Create(Self);
try
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'All image files';
FileMask := '*.jpg;*.tiff;*.tif;*.png;*.gif;*.bmp';
end;
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'JPEG images';
FileMask := '*.jpg';
end;
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'TIFF images';
FileMask := '*.tiff;*.tif';
end;
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'PNG images';
FileMask := '*.png';
end;
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'GIF images';
FileMask := '*.gif';
end;
with OpenDlg.FileTypes.Add do
begin
DisplayName := 'Bitmap images';
FileMask := '*.bmp';
end;
// etc.
if OpenDlg.Execute then
WicImage.LoadFromFile(OpenDlg.FileName);
finally
OpenDlg.Free;
end;
var BmpImage := TBitmap.Create;
try
BmpImage.Assign(WicImage);
// For example: BmpImage.SaveToFile('K:\bitmap.bmp');
finally
BmpImage.Free;
end;
finally
WicImage.Free;
end;
end;
Finally, I note that you write
// Pixellate the image by setting the Width and Height to a small value
PixellatedImage.Width := 10;
PixellatedImage.Height := 10;
Although not relevant to your main question about TGraphic.Assign, I should note that setting a TBitmap's Width and Height very much doesn't pixelate the image in the usual sense of the word. (Algorithmically, pixelation should be done like in this Pixelate procedure.)

DataSnap ServerMethod functions returned as ftStream parameters being wrongly truncated

As DataSnap users will know, its ServerMethods return values to their callers
as DataSnap parameters.
There have been a number of reports on SO and elsewhere relating to a problem with
DataSnap servers returning ServerMethod results as ftStream parameters, that the stream is truncated
prematurely or returned empty. An example is here:
Can't retrieve TStreams bigger than around 260.000 bytes from a Datasnap Server
I have put together a reproducible test case of this that I intend submitting to
Emba's Quality Portal as an MCVE, but before I do I'd like some help pinning down
where the problem occurs. I'm using Delphi Seattle on Win64, compiling to 32-bits, btw.
My MCVE is completely self-contained (i.e. includes both server and client) and does
not depend on any database data. Its ServerMethods module contains a function
(BuildString in the code below) which returns a string of a caller-specified length
and two ServerMethods GetAsString and GetAsStream which return the result
as parameters of types ftString and ftStream, respectively.
Its GetString method successfully returns a string of any requested length up to
the maximum I've tested, which is 32000000 (32 million) bytes.
Otoh, the GetStream method works up to a requested size of 30716; above that,
the returned stream has a size of -1 and is empty. The expected behaviour of course
that it should be capable of working with much larger sizes, just as GetString does.
On the outbound (server) side, at some point the returned stream is passed into
DataSnap's JSon layer en route to the tcp/ip transport layer and on the inbound side, similarly, the stream is retrieved
from the JSon layer. What I'd like to be able to do, and what this q is about,
is to capture the outbound and inbound JSon representations of the AsStream
parameter value in human-legible form so that I identify whether the unwanted
truncation of its data occurs on the server or client side. How do I do that?
the reason I'm asking this is that despite hours of looking I've been unable to identify exactly
where the JSon conversions occur. It's like looking for a needle in a haystack.
If you take a look at the method TDBXJSonStreamWriter.WriteParameter in Data.DBXStream,
the one thing it doesn't write is the stream's contents!
One thing I have been able to establish is regarding line 4809 in Data.DBXStream
Size := ((FBuf[IncrAfter(FOff)] and 255) shl 8) or (FBuf[IncrAfter(FOff)] and 255)
in the function TDBXRowBuffer.ReadReaderBlobSize. On entry to
this method, Size is initialised to zero, and it is this line which sets Size to 30716
for all requested stream sizes >= that value. But I don't know whether this is cause or effect,
i.e. whether the stream trucation has already taken place or whether it's this line
which causes it.
My code is below; apologies for the length of it, but DataSnap projects require
quite a lot of baggage at the best of times and I've included some code which
initialises some of the components to avoid having to post .DFMs too.
ServerMethods code:
unit ServerMethods2u;
interface
uses System.SysUtils, System.Classes, System.Json, variants, Windows,
Datasnap.DSServer, Datasnap.DSAuth, DataSnap.DSProviderDataModuleAdapter;
{$MethodInfo on}
type
TServerMethods1 = class(TDSServerModule)
public
function GetStream(Len: Integer): TStream;
function GetString(Len: Integer): String;
end;
{$MethodInfo off}
implementation
{$R *.dfm}
uses System.StrUtils;
function BuildString(Len : Integer) : String;
var
S : String;
Count,
LeftToWrite : Integer;
const
scBlock = '%8d bytes'#13#10;
begin
LeftToWrite := Len;
Count := 1;
while Count <= Len do begin
S := Format(scBlock, [Count]);
if LeftToWrite >= Length(S) then
else
S := Copy(S, 1, LeftToWrite);
Result := Result + S;
Inc(Count, Length(S));
Dec(LeftToWrite, Length(S));
end;
if Length(Result) > 0 then
Result[Length(Result)] := '.'
end;
function TServerMethods1.GetStream(Len : Integer): TStream;
var
SS : TStringStream;
begin
SS := TStringStream.Create;
SS.WriteString(BuildString(Len));
SS.Position := 0;
Result := SS;
end;
function TServerMethods1.GetString(Len : Integer): String;
begin
Result := BuildString(Len);
end;
ServerContainer code:
unit ServerContainer2u;
interface
uses System.SysUtils, System.Classes, Datasnap.DSTCPServerTransport,
Datasnap.DSServer, Datasnap.DSCommonServer, Datasnap.DSAuth, IPPeerServer,
DataSnap.DSProviderDataModuleAdapter;
type
TServerContainer1 = class(TDataModule)
DSServer1: TDSServer;
DSTCPServerTransport1: TDSTCPServerTransport;
DSServerClass1: TDSServerClass;
procedure DataModuleCreate(Sender: TObject);
procedure DSServerClass1GetClass(DSServerClass: TDSServerClass;
var PersistentClass: TPersistentClass);
end;
var
ServerContainer1: TServerContainer1;
implementation
{$R *.dfm}
uses ServerMethods2u;
procedure TServerContainer1.DataModuleCreate(Sender: TObject);
begin
DSServerClass1.Server := DSServer1;
DSTCPServerTransport1.Server := DSServer1;
end;
procedure TServerContainer1.DSServerClass1GetClass(
DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
begin
PersistentClass := TServerMethods1;
end;
end.
ServerForm code:
unit ServerForm2u;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, DBXJSON, Data.DBXDataSnap, IPPeerClient,
Data.DBXCommon, Data.FMTBcd, Data.DB, Data.SqlExpr, Data.DbxHTTPLayer,
DataSnap.DSServer;
type
TForm1 = class(TForm)
btnGetStream: TButton;
edStreamSize: TEdit;
SQLConnection1: TSQLConnection;
SMGetStream: TSqlServerMethod;
Memo1: TMemo;
Label1: TLabel;
btnGetString: TButton;
Label2: TLabel;
edStringSize: TEdit;
SMGetString: TSqlServerMethod;
procedure FormCreate(Sender: TObject);
procedure btnGetStreamClick(Sender: TObject);
procedure btnGetStringClick(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
SqlConnection1.ConnectionData.Properties.Values['CommunicationProtocol'] := 'tcp/ip';
SqlConnection1.ConnectionData.Properties.Values['BufferKBSize'] := '64';
SMGetStream.Params.Clear;
SMGetStream.Params.CreateParam(ftInteger, 'Len', ptInput);
SMGetStream.Params.CreateParam(ftStream, 'Result', ptOutput);
SMGetString.Params.Clear;
SMGetString.Params.CreateParam(ftInteger, 'Len', ptInput);
SMGetString.Params.CreateParam(ftString, 'Result', ptOutput);
end;
procedure TForm1.btnGetStreamClick(Sender: TObject);
var
SS : TStringStream;
S : TStream;
begin
Memo1.Lines.Clear;
SS := TStringStream.Create;
try
SMGetStream.Params[0].AsInteger := StrtoInt(edStreamSize.Text);
SMGetStream.ExecuteMethod;
S := SMGetStream.Params[1].AsStream;
S.Position := 0;
if S.Size > 0 then begin
try
SS.CopyFrom(S, S.Size);
Memo1.Lines.BeginUpdate;
Memo1.Lines.Text := SS.DataString;
Memo1.Lines.Insert(0, IntToStr(S.Size));
finally
Memo1.Lines.EndUpdate;
end;
end
else
ShowMessage(IntToStr(S.Size));
finally
SS.Free;
end;
end;
procedure TForm1.btnGetStringClick(Sender: TObject);
var
S : String;
Size : Integer;
begin
Memo1.Lines.Clear;
Size := StrtoInt(edStringSize.Text);
SMGetString.Params[0].AsInteger := Size;
SMGetString.ExecuteMethod;
S := SMGetString.Params[1].AsString;
if Length(S) > 0 then begin
try
Memo1.Lines.BeginUpdate;
Memo1.Lines.Text := S;
Memo1.Lines.Insert(0, IntToStr(Length(S)));
finally
Memo1.Lines.EndUpdate;
end;
end;
end;
end.

Delphi 7, The handle is invalid, when moving cursor on the form :)

Here is small snippet of the code (ready to paste and run) which run one thread.
This thread get a list of the jpg files on disc, then do certain operations on it.
Normally it works okay. If I start moving the cursor on the form I get this error everytime :)
Any Idea?
Thanks!
unit uTest;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, jpeg, gr32;
type
TThreadSafeJpegImage = class(TJPEGImage)
protected
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
end;
TForm1 = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
public
end;
TWatek = class(TThread)
public
procedure Execute;override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btn1Click(Sender: TObject);
var
thr: TWatek;
begin
thr := TWatek.Create(true);
thr.FreeOnTerminate := true;
thr.Resume;
end;
{ TWatek }
procedure TWatek.Execute;
var
sciezka: string;
Rec : TSearchRec;
Path : string;
I: Integer;
function TestFile(path: string): WideString;
var
stream: TMemoryStream;
jpg: TThreadSafeJpegImage;
bmp32: TBitmap32;
strStr: TStringStream;
err: String;
begin
try
stream := TMemoryStream.Create;
jpg := TThreadSafeJpegImage.Create;
try
stream.LoadFromFile(path);
jpg.LoadFromStream(stream);
finally
FreeAndNil(stream);
end;
bmp32 := TBitmap32.Create;
try
bmp32.Assign(jpg);
strStr := TStringStream.Create('');
bmp32.SaveToStream(strStr);
strStr.Seek(0,soFromBeginning);
finally
FreeAndNil(jpg);
FreeAndNil(bmp32);
end;
result := strStr.DataString;
FreeAndNil(strStr);
except
on e: exception do
begin
err := e.Message;
showmessage (err);
end;
end;
end;
begin
sciezka := 'd:\pictures\';
for I := 1 to 100 do
begin
Path := IncludeTrailingPathDelimiter(sciezka) ;
if FindFirst (Path + '*.jpg', faAnyFile - faDirectory, Rec) = 0 then
begin
try
repeat
TestFile (Path + Rec.Name);
until FindNext(Rec) <> 0;
finally
FindClose(Rec) ;
end;
end;
end;
end;
{ TThreadSafeJpegImage }
procedure TThreadSafeJpegImage.Draw(ACanvas: TCanvas; const Rect: TRect);
begin
Bitmap.Canvas.Lock;
try
inherited Draw(ACanvas, Rect);
finally
Bitmap.Canvas.Unlock;
end;
end;
end.
A guy from Graphics32 group found a solution for me. We have to amend some fixes to gr32 unit, as follows:
1) In TBitmap32.AssignTo() replace
DrawTo(Bmp.Canvas.Handle, 0, 0);
with
Bmp.Canvas.Lock;
try
DrawTo(Bmp.Canvas.Handle, 0, 0);
finally
Bmp.Canvas.UnLock;
end;
2) In TBitmap32.Assign() replace
TGraphicAccess(Source).Draw(Canvas, MakeRect(0, 0, Width, Height));
with
Canvas.Lock;
try
TGraphicAccess(Source).Draw(Canvas, MakeRect(0, 0, Width, Height));
finally
Canvas.UnLock;
end;
Now it works!
not 100% sure, please vote me down if I'm delusional.
TThreadSafeJpegImage.Draw locks the Canvas.
When moving the mouse across the form you force a redraw, which the form cannot do (because you've already locked the canvas previously) and this causes the error to be returned.
Change the Draw code like so:
procedure TThreadSafeJpegImage.Draw(ACanvas: TCanvas; const Rect: TRect);
var
OKToDraw: boolean;
begin
OKToDraw:= Bitmap.Canvas.TryLock;
if OKTODraw then try
inherited Draw(ACanvas, Rect);
finally
Bitmap.Canvas.Unlock;
end; {if try}
end;

Why doesn't this D2006 code to fade a PNG Image work?

This question springs from an earlier one. Most of the code is from suggested answers that probably worked in later versions of Delphi. In D2006 I don't get the full range of opacity, and the transparent part of the image shows as white.
Image is from http://upload.wikimedia.org/wikipedia/commons/6/61/Icon_attention_s.png.
It is loaded from the PNGImageCollection into the TImage at run-time because I have found you have to do this as the image doesn't remain intact after the DFM is saved. For the purposes of demonstrating the behaviour you probably don't need the PNGImageCollection and can just load the PNG image into the TImage at design time and then run it from the IDE.
There are four buttons on the form - each one sets a different value of opacity. Opacity=0 works fine (paintbox image is not visible, opacity=16 looks OK except for the white background, opacity=64, 255 are similar - the opacity seems to saturate at around 10%.
Any ideas as to what's up?
unit Unit18;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, pngimage, StdCtrls, Spin, PngImageList;
type
TAlphaBlendForm = class(TForm)
PaintBox1: TPaintBox;
Image1: TImage;
PngImageCollection1: TPngImageCollection;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure PaintBox1Paint(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
FOpacity : Integer ;
FBitmap : TBitmap ;
{ Private declarations }
public
{ Public declarations }
end;
var
AlphaBlendForm: TAlphaBlendForm;
implementation
{$R *.dfm}
procedure TAlphaBlendForm.Button1Click(Sender: TObject);
begin
FOpacity:= 0 ;
PaintBox1.Invalidate;
end;
procedure TAlphaBlendForm.Button2Click(Sender: TObject);
begin
FOpacity:= 16 ;
PaintBox1.Invalidate;
end;
procedure TAlphaBlendForm.Button3Click(Sender: TObject);
begin
FOpacity:= 64 ;
PaintBox1.Invalidate;
end;
procedure TAlphaBlendForm.Button4Click(Sender: TObject);
begin
FOpacity:= 255 ;
PaintBox1.Invalidate;
end;
procedure TAlphaBlendForm.FormCreate(Sender: TObject);
begin
Image1.Picture.Assign (PngImageCollection1.Items [0].PNGImage) ;
FBitmap := TBitmap.Create;
FBitmap.Assign(Image1.Picture.Graphic);//Image1 contains a transparent PNG
FBitmap.PixelFormat := pf32bit ;
PaintBox1.Width := FBitmap.Width;
PaintBox1.Height := FBitmap.Height;
end;
procedure TAlphaBlendForm.PaintBox1Paint(Sender: TObject);
var
fn: TBlendFunction;
begin
fn.BlendOp := AC_SRC_OVER;
fn.BlendFlags := 0;
fn.SourceConstantAlpha := FOpacity;
fn.AlphaFormat := AC_SRC_ALPHA;
Windows.AlphaBlend(
PaintBox1.Canvas.Handle,
0,
0,
PaintBox1.Width,
PaintBox1.Height,
FBitmap.Canvas.Handle,
0,
0,
FBitmap.Width,
FBitmap.Height,
fn
);
end;
end.
** This code (using graphics32 TImage32) almost works **
unit Unit18;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, pngimage, StdCtrls, Spin, PngImageList, GR32_Image;
type
TAlphaBlendForm = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Image321: TImage32;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
AlphaBlendForm: TAlphaBlendForm;
implementation
{$R *.dfm}
procedure TAlphaBlendForm.Button1Click(Sender: TObject);
begin
Image321.Bitmap.MasterAlpha := 0 ;
end;
procedure TAlphaBlendForm.Button2Click(Sender: TObject);
begin
Image321.Bitmap.MasterAlpha := 16 ;
end;
procedure TAlphaBlendForm.Button3Click(Sender: TObject);
begin
Image321.Bitmap.MasterAlpha := 64 ;
end;
procedure TAlphaBlendForm.Button4Click(Sender: TObject);
begin
Image321.Bitmap.MasterAlpha := 255 ;
end;
end.
** (UPDATE) This code (using graphics32 TImage32) DOES work **
The following code is successful in assigning a PNG image to the Graphics32.TImage32 at run-time. The PNG image with alpha channel is loaded into a TPNGImageCollection (really useful component as it allows mixtures of images of arbitrary size) at design time. On form creation it is written to a stream, then read from the stream into the Image32 using LoadPNGintoBitmap32. Once this is done I can control the opacity by assigning to TImage32.Bitmap.MasterAlpha. No bothering with OnPaint handlers.
procedure TAlphaBlendForm.FormCreate(Sender: TObject);
var
FStream : TMemoryStream ;
AlphaChannelUsed : boolean ;
begin
FStream := TMemoryStream.Create ;
try
PngImageCollection1.Items [0].PngImage.SaveToStream (FStream) ;
FStream.Position := 0 ;
LoadPNGintoBitmap32 (Image321.Bitmap, FStream, AlphaChannelUsed) ;
finally
FStream.Free ;
end;
end ;
As David commented to the question, the alpha channel information is lost when you assign the graphic to the bitmap. As such there's no point in setting the pixel format to pf32bit after the assignment, apart from preventing AlphaBlend call to fail, there's no per-pixel alpha in the bitmap anyway.
But the png object knows how to draw on a canvas taking into consideration the transparency information. So the solution would involve drawing on the bitmap canvas instead of assigning the graphic, and then, since there's no Alpha channel, remove the AC_SRC_ALPHA flag from the BLENDFUNCTION.
Below is working code here on D2007:
procedure TAlphaBlendForm.FormCreate(Sender: TObject);
begin
Image1.Picture.LoadFromFile(
ExtractFilePath(Application.ExeName) + 'Icon_attention_s.png');
FBitmap := TBitmap.Create;
FBitmap.Width := Image1.Picture.Graphic.Width;
FBitmap.Height := Image1.Picture.Graphic.Height;
FBitmap.Canvas.Brush.Color := Color; // background color for the image
FBitmap.Canvas.FillRect(FBitmap.Canvas.ClipRect);
FBitmap.Canvas.Draw(0, 0, Image1.Picture.Graphic);
PaintBox1.Width := FBitmap.Width;
PaintBox1.Height := FBitmap.Height;
end;
procedure TAlphaBlendForm.PaintBox1Paint(Sender: TObject);
var
fn: TBlendFunction;
begin
fn.BlendOp := AC_SRC_OVER;
fn.BlendFlags := 0;
fn.SourceConstantAlpha := FOpacity;
fn.AlphaFormat := 0;
Windows.AlphaBlend(
PaintBox1.Canvas.Handle,
0,
0,
PaintBox1.Width,
PaintBox1.Height,
FBitmap.Canvas.Handle,
0,
0,
FBitmap.Width,
FBitmap.Height,
fn
);
end;
or without using a intermediate TImage:
procedure TAlphaBlendForm.FormCreate(Sender: TObject);
var
PNG: TPNGObject;
begin
PNG := TPNGObject.Create;
try
PNG.LoadFromFile(ExtractFilePath(Application.ExeName) + 'Icon_attention_s.png');
FBitmap := TBitmap.Create;
FBitmap.Width := PNG.Width;
FBitmap.Height := PNG.Height;
FBitmap.Canvas.Brush.Color := Color;
FBitmap.Canvas.FillRect(FBitmap.Canvas.ClipRect);
PNG.Draw(FBitmap.Canvas, FBitmap.Canvas.ClipRect);
PaintBox1.Width := FBitmap.Width;
PaintBox1.Height := FBitmap.Height;
finally
PNG.Free;
end;
end;

Resources