borgo
asked on
How to covert a Bitmap to an array of...
How I can covert a Bitmap to an array of integer or to an array of string.
And How I can convert this array to a Bitmap ?
Thanks.
And How I can convert this array to a Bitmap ?
Thanks.
P.S: If you like pure winAPI more, look at CreateDIBSection, SetDIBits, ...
ASKER
The example is from the Delphi Help. I have just read it.
The problem is: I have a bitmap. I want to convert it to an array of intger (or string). Then I have to reverse the process.
ScanLine proprerty is a read only property.
Please con you write me a loop example (Delphi4) to do it ?
The problem is: I have a bitmap. I want to convert it to an array of intger (or string). Then I have to reverse the process.
ScanLine proprerty is a read only property.
Please con you write me a loop example (Delphi4) to do it ?
borgo,
yes, the ScanLine property is a readonly property. But that does not mean, that you can't use it to change the bitmap pixels!
It is a readonly property because it gives you a pointer to the bitmap buffer. It makes no sense to change the pointer to the bitmap buffer, but nevertheless you can use the readonly pointer to write to the read&write bitmap pixel buffer.
You're right, the example is from the Delphi help. It's just because it fits your needs. Please look at it a little bit close again! It loads a 256 color bitmap, then changes each and every byte of it, then displays the result of this operation. Please compile it and look at the results!
Regards, Madshi.
yes, the ScanLine property is a readonly property. But that does not mean, that you can't use it to change the bitmap pixels!
It is a readonly property because it gives you a pointer to the bitmap buffer. It makes no sense to change the pointer to the bitmap buffer, but nevertheless you can use the readonly pointer to write to the read&write bitmap pixel buffer.
You're right, the example is from the Delphi help. It's just because it fits your needs. Please look at it a little bit close again! It loads a 256 color bitmap, then changes each and every byte of it, then displays the result of this operation. Please compile it and look at the results!
Regards, Madshi.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
williams2,
of course you know, that this stream method is very very very slow!!! AND you get the bitmap file header and all this stuff. So you don't know which bits/bytes you have to read/write in order to read/write specific pixels.
I don't think that is what borgo needs, right borgo?
I really prefer the ScanLine method. It is much faster and gives you only the pixels. Of course you have to look at the palette yourself (that means if you're using 16 or 256 color bitmaps).
I think it depends on for what borgo needs this all.
Regards, Madshi.
of course you know, that this stream method is very very very slow!!! AND you get the bitmap file header and all this stuff. So you don't know which bits/bytes you have to read/write in order to read/write specific pixels.
I don't think that is what borgo needs, right borgo?
I really prefer the ScanLine method. It is much faster and gives you only the pixels. Of course you have to look at the palette yourself (that means if you're using 16 or 256 color bitmaps).
I think it depends on for what borgo needs this all.
Regards, Madshi.
Madshi
Yes, that is correct, I am saving the headers within the bitmap and I'm fully aware of that. Now he also asked how to convert it to a string . You might be aware of the fact that the pallette is always saved within the header, or you might go read the SaveToStream method in Graphics.pas.
You say it is slow, maybe it is, but it is the answer to the question. But as you say, it depends on the purpose.
At least you should be sure about the width, as I mentioned before. If you hsve a 24 bit image you'll only have copied a third of the original bitmap.
Hmm.. I'm working on tomething, that might be able to solve this problem in generel.
Regards
/Williams
Yes, that is correct, I am saving the headers within the bitmap and I'm fully aware of that. Now he also asked how to convert it to a string . You might be aware of the fact that the pallette is always saved within the header, or you might go read the SaveToStream method in Graphics.pas.
You say it is slow, maybe it is, but it is the answer to the question. But as you say, it depends on the purpose.
At least you should be sure about the width, as I mentioned before. If you hsve a 24 bit image you'll only have copied a third of the original bitmap.
Hmm.. I'm working on tomething, that might be able to solve this problem in generel.
Regards
/Williams
ASKER
Williams2: Your code do what I have tryed to do some days ago.
Thank you very much.
I'm coding an application which sends a bmp between an UDP connection. I think to convert the bmp in a array of integer (or string) and then to send it. Do you think I'm wrong ? What do you think is the best way to do it ?
(You will get your points.)
Thank you very much.
I'm coding an application which sends a bmp between an UDP connection. I think to convert the bmp in a array of integer (or string) and then to send it. Do you think I'm wrong ? What do you think is the best way to do it ?
(You will get your points.)
borgo,
if you had written in the beginning for what purpose you need it. I thought you wanted to manipulate some pixels in a fast way...
Ok, now it's clear:
You should save it to a memoryStream. The memoryStream object has a memory property that is a pointer to the data buffer.
function BitmapToStr(bmp: TBitmap) : string;
var ms : TMemoryStream;
begin
ms:=TMemoryStream;
try
bmp.WriteToStream(ms);
// now you can access all the bitmap data through memoryStream's memory property. E.g. converting to a string:
setLength(result,ms.size);
Move(ms.Memory^,pointer(re sult)^,ms. size);
// now you have all the bitmap data in a string.
finally ms.Free end;
end;
BTW, this code is much much much faster than Williams' code. :-)) (Sorry, Williams...)
If you really want to convert it to a string, you can use the following code to convert it back to an image:
function StrToBitmap(str: string) : TBitmap;
var ms : TMemoryStream;
begin
ms:=TMemoryStream.Create;
try
ms.size:=length(str);
Move(pointer(str)^,ms.Memo ry^,ms.siz e);
result:=TBitmap.Create;
result.loadFromStream(ms);
finally ms.Free end;
end;
If you want an array of byte, do something like this:
type TAByte : array [0..maxInt] of byte;
TPAByte : ^TAByte;
var abyte : TPAByte;
abyte:=ms.Memory^; // Now you can use e.g. abyte^[0] to access the first byte
If you want an array of integer, do something like this:
type TAInteger : array[0..maxInt shr 2] of integer;
TPAInteger : ^TAInteger;
var aInt : TPAInteger;
aint:=ms.Memory^; // Now you can use e.g. aint^[0] to access the first byte
// Note, that you now have to transfer (ms.size div 4) integers... So I wouldn't choose this method...
Regards, Madshi.
if you had written in the beginning for what purpose you need it. I thought you wanted to manipulate some pixels in a fast way...
Ok, now it's clear:
You should save it to a memoryStream. The memoryStream object has a memory property that is a pointer to the data buffer.
function BitmapToStr(bmp: TBitmap) : string;
var ms : TMemoryStream;
begin
ms:=TMemoryStream;
try
bmp.WriteToStream(ms);
// now you can access all the bitmap data through memoryStream's memory property. E.g. converting to a string:
setLength(result,ms.size);
Move(ms.Memory^,pointer(re
// now you have all the bitmap data in a string.
finally ms.Free end;
end;
BTW, this code is much much much faster than Williams' code. :-)) (Sorry, Williams...)
If you really want to convert it to a string, you can use the following code to convert it back to an image:
function StrToBitmap(str: string) : TBitmap;
var ms : TMemoryStream;
begin
ms:=TMemoryStream.Create;
try
ms.size:=length(str);
Move(pointer(str)^,ms.Memo
result:=TBitmap.Create;
result.loadFromStream(ms);
finally ms.Free end;
end;
If you want an array of byte, do something like this:
type TAByte : array [0..maxInt] of byte;
TPAByte : ^TAByte;
var abyte : TPAByte;
abyte:=ms.Memory^; // Now you can use e.g. abyte^[0] to access the first byte
If you want an array of integer, do something like this:
type TAInteger : array[0..maxInt shr 2] of integer;
TPAInteger : ^TAInteger;
var aInt : TPAInteger;
aint:=ms.Memory^; // Now you can use e.g. aint^[0] to access the first byte
// Note, that you now have to transfer (ms.size div 4) integers... So I wouldn't choose this method...
Regards, Madshi.
P.S: borgo, give the points to Williams, if you like his answer more than mine. (Otherwise of course to me...) :-)))
To Madshi:
I will just say, that the purpose of your example was right, but the questions was in some way easy to misunderstand.
To Madshi:
I have allready done this once, where I'll send the whole screen from a server to a client on request.
I think you will appreciate the following example, since byte or char stuffing is both slow and oldfashioned.
I din't use UDP, and I will not recommend using it, as it is unreliable and mostly used for messageservices and server notifications.
In the following example, you should do the following things:
Server:
create New application
Add a OnCreate event procedure from the form object, then replace unit 1 with Section A.
Client:
You do the same here as with the server, just replace unit1 with Section B.
Section A:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ExtCtrls, StdCtrls, ExtDlgs;
Const
//You may set this to False if you would like the whole image!
STRETCH = True;
STRETCH_WIDTH = 200;
STRETCH_HEIGHT = 150;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
ServerSocket1: TServerSocket;
procedure ServerSocket1ClientRead(Se nder: TObject;
Socket: TCustomWinSocket);
private
{ Private declarations }
Stream: TMemoryStream;
procedure SendNextPart(Socket: TCustomWinSocket);
public
{ Public declarations }
Timer: TTimer;
procedure OnTime(Sender: TObject);
procedure SendScreen(Socket: TCustomWinSocket);
protected
Bitmap: TBitmap;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Bitmap:= TBitmap.Create;
if STRETCH then
begin
Bitmap.Width:= STRETCH_WIDTH;
Bitmap.height:= STRETCH_HEIGHT;
End else
begin
Bitmap.Width:= Screen.Width;
Bitmap.Height:= Screen.Height;
End;
Timer:= TTimer.Create(Self);
Timer.Interval:= 1000;
Timer.OnTimer:= OnTime;
Timer.Enabled:= True;
ServerSocket1:= TServerSocket.Create(Self) ;
With ServerSocket1 do
begin
Port:= 2500;
Active:= True;
ServerType:= stNonBlocking;
OnClientRead:= ServerSocket1ClientRead;
End;
Stream:= TMemoryStream.Create;
end;
procedure TForm1.ServerSocket1Client Read(Sende r: TObject;
Socket: TCustomWinSocket);
var
S: String;
Begin
S:= Socket.ReceiveText;
If S='Get me the screen' then SendScreen(Socket) else
If S='Send next' then SendNextPart(Socket) else
If S='Done' then Socket.Close;
End;
procedure TForm1.SendScreen(Socket: TCustomWinSocket);
var
Size: Integer;
Begin
Bitmap.SaveToStream(Stream );
Size:= Stream.Size;
Stream.Position:= 0;
Socket.SendBuf(Size,SizeOf (Size));
End;
procedure TForm1.SendNextPart(Socket : TCustomWinSocket);
const
MaxChunkSize = 8192; { copy in 8K chunks }
var
ChunkSize: Integer;
CopyBuffer: Array[0..MaxChunkSize] of Byte;
Begin
Chunksize:= Stream.Size-Stream.Positio n;
If ChunkSize>0 then
begin
If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
Stream.Read(CopyBuffer,Chu nkSize);
Socket.SendBuf(CopyBuffer, ChunkSize) ;
End else
Stream.Clear; //The final
End;
procedure TForm1.OnTime(Sender: TObject);
var
dwRop: DWord;
DC: hDC;
begin
dwRop:= SRCCOPY;
DC:= GetDC(0);
If STRETCH then
StretchBlt(
Bitmap.Canvas.Handle, // handle to destination device context
0, // x-coordinate of destination rectangle's upper-left corner
0, // y-coordinate of destination rectangle's upper-left corner
Bitmap.Width, // width of destination rectangle
Bitmap.Height, // height of destination rectangle
DC, // handle to source device context
0, // x-coordinate of source rectangle's upper-left corner
0, // y-coordinate of source rectangle's upper-left corner
Screen.Width, // width of source rectangle
Screen.Height, // height of source rectangle
dwRop // raster operation code (See below)
)
else
BitBlt(
Bitmap.Canvas.Handle, // handle to destination device context
0, // x-coordinate of destination rectangle's upper-left corner
0, // y-coordinate of destination rectangle's upper-left corner
Bitmap.Width, // width of destination rectangle
Bitmap.Height, // height of destination rectangle
DC, // handle to source device context
0, // x-coordinate of source rectangle's upper-left corner
0, // y-coordinate of source rectangle's upper-left corner
dwRop // raster operation code (See below)
);
ReleaseDC(0,DC)
{
BLACKNESS Fills the destination rectangle using the color associated with
index 0 in the physical palette. (This color is black for the
default physical palette.)
DSTINVERT Inverts the destination rectangle.
MERGECOPY Merges the colors of the source rectangle with the specified
pattern by using the Boolean AND operator.
MERGEPAINT Merges the colors of the inverted source rectangle with the
colors of the destination rectangle by using the Boolean OR
operator.
NOTSRCCOPY Copies the inverted source rectangle to the destination.
NOTSRCERASE Combines the colors of the source and destination rectangles
by using the Boolean OR operator and then inverts the resultant
color.
PATCOPY Copies the specified pattern into the destination bitmap.
PATINVERT Combines the colors of the specified pattern with the colors of
the destination rectangle by using the Boolean XOR operator.
PATPAINT Combines the colors of the pattern with the colors of the
inverted source rectangle by using the Boolean OR operator. The
result of this operation is combined with the colors of the
destination rectangle by using the Boolean OR operator.
SRCAND Combines the colors of the source and destination rectangles
by using the Boolean AND operator.
SRCCOPY Copies the source rectangle directly to the destination
rectangle.
SRCERASE Combines the inverted colors of the destination rectangle with
the colors of the source rectangle by using the Boolean AND operator.
SRCINVERT Combines the colors of the source and destination rectangles
by using the Boolean XOR operator.
SRCPAINT Combines the colors of the source and destination rectangles by
using the Boolean OR operator.
WHITENESS Fills the destination rectangle using the color associated with
index 1 in the physical palette. (This color is white for the
default physical palette.)
}
End;
end.
Section B:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, StdCtrls, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
ProgressBar: TProgressBar;
Button1: TButton;
Button2: TButton;
ClientSocket1: TClientSocket;
Image1: TImage;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Connect(Sende r: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure Button2Click(Sender: TObject);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Se nder: TObject;
Socket: TCustomWinSocket);
private
Stream: TMemoryStream;
public
Procedure UpdateProgressBar;
protected
Bitmap: TBitmap;
Receiving: Boolean;
FSize: Integer;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender : TObject);
begin
Label1.Caption:= 'Connecting..';
ClientSocket1.Open;
end;
procedure TForm1.ClientSocket1Connec t(Sender: TObject;
Socket: TCustomWinSocket);
begin
Label1.Caption:= 'Connected';
Receiving:= False;
FSize:= 0;
end;
procedure TForm1.ClientSocket1Error( Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ShowMessage('Did you startup the server? I cannot find it!');
end;
procedure TForm1.Button2Click(Sender : TObject);
begin
if ClientSocket1.Active then
ClientSocket1.Socket.SendT ext('Get me the screen');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Label1:= TLabel.Create(Self);
Label1.SetBounds(32,72,32, 13);
Label1.Parent:= Self;
Label1.Caption:= 'Idle..';
Button1:= TButton.Create(Self);
Button1.SetBounds(8,8,75,2 5);
Button1.Parent:= Self;
Button1.Caption:= 'Connect';
Button1.OnClick:= Button1Click;
Button2:= TButton.Create(Self);
Button2.SetBounds(8,40,75, 25);
Button2.Parent:= Self;
Button2.Caption:= 'Get screen';
Button2.OnClick:= Button2Click;
ClientSocket1:= TClientSocket.Create(Self) ;
With ClientSocket1 do
begin
Active := False;
ClientType := ctNonBlocking;
Port := 2500;
Address := '127.0.0.1';
OnConnect := ClientSocket1Connect;
OnDisconnect := ClientSocket1Disconnect;
OnRead := ClientSocket1Read;
OnError := ClientSocket1Error;
end;
Image1:= TImage.Create(Self);
Image1.SetBounds(100,0,0,0 );
Image1.AutoSize:= true;
Image1.Parent:= Self;
Stream:= TMemoryStream.Create;
ProgressBar:= TProgressBar.Create(Self);
ProgressBar.Min:= 0;
ProgressBar.Align:= alBottom;
ProgressBar.Parent:= Self;
end;
Procedure TForm1.UpdateprogressBar;
Begin
ProgressBar.Position:= ProgressBar.Max-FSize;
progressBar.Update;
End;
procedure TForm1.ClientSocket1Read(S ender: TObject;
Socket: TCustomWinSocket);
const
MaxChunkSize = 8192; { copy in 8K chunks }
var
BytesReceived: Longint;
CopyBuffer: Array[0..MaxChunkSize] of Byte; { buffer for copying }
ChunkSize: Integer;
TempSize: Integer;
begin
If FSize=0 then
begin
If Socket.ReceiveLength>=Size Of(TempSiz e) then
begin
Socket.ReceiveBuf(TempSize ,SizeOf(Te mpSize)); //get the size
Stream.SetSize(TempSize);
Stream.Position:= 0;
ProgressBar.Max:= TempSize;
FSize:= TempSize; //Threadsafe code!
End;
End;
If (FSize>0) and not(Receiving) then
begin
Receiving:= True;
While Socket.ReceiveLength>0 do
Begin
ChunkSize:= Socket.ReceiveLength;
If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
BytesReceived:= Socket.ReceiveBuf(CopyBuff er,ChunkSi ze);
Stream.Write(CopyBuffer, BytesReceived); { ...write chunk }
Dec(FSize,BytesReceived);
UpdateProgressBar;
End;
//This is called "to Syncronize trasnsmissions"
Socket.SendText('Send next');
If FSize=0 then
begin
Socket.SendText('Done');
Stream.Position:= 0;
Image1.Picture.Bitmap.Load FromStream (Stream);
FSize:= 0;
ProgressBar.Position:= 0;
// Stream.SetSize(0);
Stream.Clear;
End;
Receiving:= False;
End;
end;
procedure TForm1.ClientSocket1Discon nect(Sende r: TObject;
Socket: TCustomWinSocket);
begin
Label1.Caption:= 'Disconnected';
end;
end.
Cheers
Williams
I will just say, that the purpose of your example was right, but the questions was in some way easy to misunderstand.
To Madshi:
I have allready done this once, where I'll send the whole screen from a server to a client on request.
I think you will appreciate the following example, since byte or char stuffing is both slow and oldfashioned.
I din't use UDP, and I will not recommend using it, as it is unreliable and mostly used for messageservices and server notifications.
In the following example, you should do the following things:
Server:
create New application
Add a OnCreate event procedure from the form object, then replace unit 1 with Section A.
Client:
You do the same here as with the server, just replace unit1 with Section B.
Section A:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ExtCtrls, StdCtrls, ExtDlgs;
Const
//You may set this to False if you would like the whole image!
STRETCH = True;
STRETCH_WIDTH = 200;
STRETCH_HEIGHT = 150;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
ServerSocket1: TServerSocket;
procedure ServerSocket1ClientRead(Se
Socket: TCustomWinSocket);
private
{ Private declarations }
Stream: TMemoryStream;
procedure SendNextPart(Socket: TCustomWinSocket);
public
{ Public declarations }
Timer: TTimer;
procedure OnTime(Sender: TObject);
procedure SendScreen(Socket: TCustomWinSocket);
protected
Bitmap: TBitmap;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Bitmap:= TBitmap.Create;
if STRETCH then
begin
Bitmap.Width:= STRETCH_WIDTH;
Bitmap.height:= STRETCH_HEIGHT;
End else
begin
Bitmap.Width:= Screen.Width;
Bitmap.Height:= Screen.Height;
End;
Timer:= TTimer.Create(Self);
Timer.Interval:= 1000;
Timer.OnTimer:= OnTime;
Timer.Enabled:= True;
ServerSocket1:= TServerSocket.Create(Self)
With ServerSocket1 do
begin
Port:= 2500;
Active:= True;
ServerType:= stNonBlocking;
OnClientRead:= ServerSocket1ClientRead;
End;
Stream:= TMemoryStream.Create;
end;
procedure TForm1.ServerSocket1Client
Socket: TCustomWinSocket);
var
S: String;
Begin
S:= Socket.ReceiveText;
If S='Get me the screen' then SendScreen(Socket) else
If S='Send next' then SendNextPart(Socket) else
If S='Done' then Socket.Close;
End;
procedure TForm1.SendScreen(Socket: TCustomWinSocket);
var
Size: Integer;
Begin
Bitmap.SaveToStream(Stream
Size:= Stream.Size;
Stream.Position:= 0;
Socket.SendBuf(Size,SizeOf
End;
procedure TForm1.SendNextPart(Socket
const
MaxChunkSize = 8192; { copy in 8K chunks }
var
ChunkSize: Integer;
CopyBuffer: Array[0..MaxChunkSize] of Byte;
Begin
Chunksize:= Stream.Size-Stream.Positio
If ChunkSize>0 then
begin
If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
Stream.Read(CopyBuffer,Chu
Socket.SendBuf(CopyBuffer,
End else
Stream.Clear; //The final
End;
procedure TForm1.OnTime(Sender: TObject);
var
dwRop: DWord;
DC: hDC;
begin
dwRop:= SRCCOPY;
DC:= GetDC(0);
If STRETCH then
StretchBlt(
Bitmap.Canvas.Handle, // handle to destination device context
0, // x-coordinate of destination rectangle's upper-left corner
0, // y-coordinate of destination rectangle's upper-left corner
Bitmap.Width, // width of destination rectangle
Bitmap.Height, // height of destination rectangle
DC, // handle to source device context
0, // x-coordinate of source rectangle's upper-left corner
0, // y-coordinate of source rectangle's upper-left corner
Screen.Width, // width of source rectangle
Screen.Height, // height of source rectangle
dwRop // raster operation code (See below)
)
else
BitBlt(
Bitmap.Canvas.Handle, // handle to destination device context
0, // x-coordinate of destination rectangle's upper-left corner
0, // y-coordinate of destination rectangle's upper-left corner
Bitmap.Width, // width of destination rectangle
Bitmap.Height, // height of destination rectangle
DC, // handle to source device context
0, // x-coordinate of source rectangle's upper-left corner
0, // y-coordinate of source rectangle's upper-left corner
dwRop // raster operation code (See below)
);
ReleaseDC(0,DC)
{
BLACKNESS Fills the destination rectangle using the color associated with
index 0 in the physical palette. (This color is black for the
default physical palette.)
DSTINVERT Inverts the destination rectangle.
MERGECOPY Merges the colors of the source rectangle with the specified
pattern by using the Boolean AND operator.
MERGEPAINT Merges the colors of the inverted source rectangle with the
colors of the destination rectangle by using the Boolean OR
operator.
NOTSRCCOPY Copies the inverted source rectangle to the destination.
NOTSRCERASE Combines the colors of the source and destination rectangles
by using the Boolean OR operator and then inverts the resultant
color.
PATCOPY Copies the specified pattern into the destination bitmap.
PATINVERT Combines the colors of the specified pattern with the colors of
the destination rectangle by using the Boolean XOR operator.
PATPAINT Combines the colors of the pattern with the colors of the
inverted source rectangle by using the Boolean OR operator. The
result of this operation is combined with the colors of the
destination rectangle by using the Boolean OR operator.
SRCAND Combines the colors of the source and destination rectangles
by using the Boolean AND operator.
SRCCOPY Copies the source rectangle directly to the destination
rectangle.
SRCERASE Combines the inverted colors of the destination rectangle with
the colors of the source rectangle by using the Boolean AND operator.
SRCINVERT Combines the colors of the source and destination rectangles
by using the Boolean XOR operator.
SRCPAINT Combines the colors of the source and destination rectangles by
using the Boolean OR operator.
WHITENESS Fills the destination rectangle using the color associated with
index 1 in the physical palette. (This color is white for the
default physical palette.)
}
End;
end.
Section B:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, StdCtrls, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
ProgressBar: TProgressBar;
Button1: TButton;
Button2: TButton;
ClientSocket1: TClientSocket;
Image1: TImage;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Connect(Sende
Socket: TCustomWinSocket);
procedure ClientSocket1Error(Sender:
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure Button2Click(Sender: TObject);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Se
Socket: TCustomWinSocket);
private
Stream: TMemoryStream;
public
Procedure UpdateProgressBar;
protected
Bitmap: TBitmap;
Receiving: Boolean;
FSize: Integer;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender
begin
Label1.Caption:= 'Connecting..';
ClientSocket1.Open;
end;
procedure TForm1.ClientSocket1Connec
Socket: TCustomWinSocket);
begin
Label1.Caption:= 'Connected';
Receiving:= False;
FSize:= 0;
end;
procedure TForm1.ClientSocket1Error(
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ShowMessage('Did you startup the server? I cannot find it!');
end;
procedure TForm1.Button2Click(Sender
begin
if ClientSocket1.Active then
ClientSocket1.Socket.SendT
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Label1:= TLabel.Create(Self);
Label1.SetBounds(32,72,32,
Label1.Parent:= Self;
Label1.Caption:= 'Idle..';
Button1:= TButton.Create(Self);
Button1.SetBounds(8,8,75,2
Button1.Parent:= Self;
Button1.Caption:= 'Connect';
Button1.OnClick:= Button1Click;
Button2:= TButton.Create(Self);
Button2.SetBounds(8,40,75,
Button2.Parent:= Self;
Button2.Caption:= 'Get screen';
Button2.OnClick:= Button2Click;
ClientSocket1:= TClientSocket.Create(Self)
With ClientSocket1 do
begin
Active := False;
ClientType := ctNonBlocking;
Port := 2500;
Address := '127.0.0.1';
OnConnect := ClientSocket1Connect;
OnDisconnect := ClientSocket1Disconnect;
OnRead := ClientSocket1Read;
OnError := ClientSocket1Error;
end;
Image1:= TImage.Create(Self);
Image1.SetBounds(100,0,0,0
Image1.AutoSize:= true;
Image1.Parent:= Self;
Stream:= TMemoryStream.Create;
ProgressBar:= TProgressBar.Create(Self);
ProgressBar.Min:= 0;
ProgressBar.Align:= alBottom;
ProgressBar.Parent:= Self;
end;
Procedure TForm1.UpdateprogressBar;
Begin
ProgressBar.Position:= ProgressBar.Max-FSize;
progressBar.Update;
End;
procedure TForm1.ClientSocket1Read(S
Socket: TCustomWinSocket);
const
MaxChunkSize = 8192; { copy in 8K chunks }
var
BytesReceived: Longint;
CopyBuffer: Array[0..MaxChunkSize] of Byte; { buffer for copying }
ChunkSize: Integer;
TempSize: Integer;
begin
If FSize=0 then
begin
If Socket.ReceiveLength>=Size
begin
Socket.ReceiveBuf(TempSize
Stream.SetSize(TempSize);
Stream.Position:= 0;
ProgressBar.Max:= TempSize;
FSize:= TempSize; //Threadsafe code!
End;
End;
If (FSize>0) and not(Receiving) then
begin
Receiving:= True;
While Socket.ReceiveLength>0 do
Begin
ChunkSize:= Socket.ReceiveLength;
If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
BytesReceived:= Socket.ReceiveBuf(CopyBuff
Stream.Write(CopyBuffer, BytesReceived); { ...write chunk }
Dec(FSize,BytesReceived);
UpdateProgressBar;
End;
//This is called "to Syncronize trasnsmissions"
Socket.SendText('Send next');
If FSize=0 then
begin
Socket.SendText('Done');
Stream.Position:= 0;
Image1.Picture.Bitmap.Load
FSize:= 0;
ProgressBar.Position:= 0;
// Stream.SetSize(0);
Stream.Clear;
End;
Receiving:= False;
End;
end;
procedure TForm1.ClientSocket1Discon
Socket: TCustomWinSocket);
begin
Label1.Caption:= 'Disconnected';
end;
end.
Cheers
Williams
Correction:
Sorry! Last message ("To Madshi: ") was ment for borgo. :-)
Regards
Williams
Sorry! Last message ("To Madshi: ") was ment for borgo. :-)
Regards
Williams
Williams,
your sources look quite nice. Only one suggestion: Why are you not using the memory property of the TMemoryStream? You could save the CopyBuffer...
Regards, Madshi.
your sources look quite nice. Only one suggestion: Why are you not using the memory property of the TMemoryStream? You could save the CopyBuffer...
Regards, Madshi.
Very simple Madshi:
I'm doing this because I take care of the Delphi structure. When implementing the memory property directly, you are in a potential risc of getting pointer-errors in your application. That is why exceptions were created in the first place.
Maybe you didn't know it, but 72% of most errors using C/C++ are pointer-errors due to the pointerstructure in C, where most things are implemented by reading from or writing to the memory directly. These errors are sometimes VERY difficult to find.
If you'd really like to, I can easily give you an example without using any stream objects at all, but I think that is not why Streams were invented in the first place.
So when I'm posing an example, you will not see me adding things directly to the memory unless it's an important factor to the algorithm or program. So you won't see that either in Delphi's help support or developer support at www.inprise.com .
Regards,
Williams
I'm doing this because I take care of the Delphi structure. When implementing the memory property directly, you are in a potential risc of getting pointer-errors in your application. That is why exceptions were created in the first place.
Maybe you didn't know it, but 72% of most errors using C/C++ are pointer-errors due to the pointerstructure in C, where most things are implemented by reading from or writing to the memory directly. These errors are sometimes VERY difficult to find.
If you'd really like to, I can easily give you an example without using any stream objects at all, but I think that is not why Streams were invented in the first place.
So when I'm posing an example, you will not see me adding things directly to the memory unless it's an important factor to the algorithm or program. So you won't see that either in Delphi's help support or developer support at www.inprise.com .
Regards,
Williams
If you would be right: Why should Inprise add a memory property?
No no, it is there to be used. And if you use it, your programs will be faster, because you save some memory copy actions. Of course you have to look that you handle it right, otherwise you'll get exceptions, but you can't simply say: "I use no pointers because they're dangerous". No, you can't get around pointers. And if you look into the Delphi original sources, you'll find that the Delphi programmers use pointers all the time.
And you're definetely NOT in a potential risk of getting pointer-errors, if you're using the memory property of TMemoryStream! Look at my last That's nonsense (sorry...). I'm using it in all my programs, and I can assure you, I've never had any problems with that!
You're right, that most errors in C++ are pointer-errors. But it's the wrong position to don't use pointers at all or to make long roundabout ways just because you're afraid of pointers. Ok, where you don't need them, very nice.
Regards, Madshi.
P.S: My first answer with the ScanLine stuff IS AN EXAMPLE from the Delphi help. And they're using a pointer to the pixel buffer and read and write directly from/to the pixel buffer memory. So you should think about what you said about the Delphi help and the developer support...
P.P.S: Did't mean to insult or annoy you in any way. I'm just a little bit confused, because you're saying somehow, that it's a bad style of programming to use directly memory access - and so you're implying that my programming style is bad, too. And - you will understand that - I DON'T LIKE THAT.
No no, it is there to be used. And if you use it, your programs will be faster, because you save some memory copy actions. Of course you have to look that you handle it right, otherwise you'll get exceptions, but you can't simply say: "I use no pointers because they're dangerous". No, you can't get around pointers. And if you look into the Delphi original sources, you'll find that the Delphi programmers use pointers all the time.
And you're definetely NOT in a potential risk of getting pointer-errors, if you're using the memory property of TMemoryStream! Look at my last That's nonsense (sorry...). I'm using it in all my programs, and I can assure you, I've never had any problems with that!
You're right, that most errors in C++ are pointer-errors. But it's the wrong position to don't use pointers at all or to make long roundabout ways just because you're afraid of pointers. Ok, where you don't need them, very nice.
Regards, Madshi.
P.S: My first answer with the ScanLine stuff IS AN EXAMPLE from the Delphi help. And they're using a pointer to the pixel buffer and read and write directly from/to the pixel buffer memory. So you should think about what you said about the Delphi help and the developer support...
P.P.S: Did't mean to insult or annoy you in any way. I'm just a little bit confused, because you're saying somehow, that it's a bad style of programming to use directly memory access - and so you're implying that my programming style is bad, too. And - you will understand that - I DON'T LIKE THAT.
Madshi:
Please READ the context of my posing, you do not seem to read it carefully before applying the message above.
1. The Delphi programmers can NOT avoid using direct memory access, and they sure know what they do, because they built it. But when I pose an example of using the memory property, I automatically RECOMMEND to use it without a question. You do not know how experienced borgo is about this; If he gets a "Out of memory" exception, I'm responsible for it!
2. You do not seem to know anything about bytestuffing. To your knowledge you cannot typecast binary data to a string. When doing so, the strings looses everything next to the first #0 character. If strings might be WideSting specific ( using Strict Var's or the {$V+} compiler directive ). In that case the string truncates at the first two #0 characters.
3. I said specifically:
"..you will not see me adding things directly to the memory unless it's an important factor to the algorithm or program.."
If the user is experienced enough, he will see that it can be done faster, and then it's HIS OWN RESPONABILITY how to optimze it.
If you would like to read about the OSI-model, you might find, that the data is beeing copied from 5 to 7 times before the actually transmission takes place.
4. The scanline property is ok to use when implementing direct bitmap manipulation, but it is also a way MUCH faster than the canvas.pixel property. When implementing it, you are MAYBE accessing memory that is prior to the screen, which makes accessing it very very slow compared to other parts of the memory.
When copying memoryblocks in memory (as I do with copybuffer), that is not accessed by any hardware or software, the cpu is actually doing it very fast. If you do not believe me, you should go try looking it up.
Note: I think this discussion is brought too far away from it's origin. And I don't think it will be worth it discussing how fast these things can be done prior on examples of how to do it. So please, if you would have me excused!
Regards
Williams
Please READ the context of my posing, you do not seem to read it carefully before applying the message above.
1. The Delphi programmers can NOT avoid using direct memory access, and they sure know what they do, because they built it. But when I pose an example of using the memory property, I automatically RECOMMEND to use it without a question. You do not know how experienced borgo is about this; If he gets a "Out of memory" exception, I'm responsible for it!
2. You do not seem to know anything about bytestuffing. To your knowledge you cannot typecast binary data to a string. When doing so, the strings looses everything next to the first #0 character. If strings might be WideSting specific ( using Strict Var's or the {$V+} compiler directive ). In that case the string truncates at the first two #0 characters.
3. I said specifically:
"..you will not see me adding things directly to the memory unless it's an important factor to the algorithm or program.."
If the user is experienced enough, he will see that it can be done faster, and then it's HIS OWN RESPONABILITY how to optimze it.
If you would like to read about the OSI-model, you might find, that the data is beeing copied from 5 to 7 times before the actually transmission takes place.
4. The scanline property is ok to use when implementing direct bitmap manipulation, but it is also a way MUCH faster than the canvas.pixel property. When implementing it, you are MAYBE accessing memory that is prior to the screen, which makes accessing it very very slow compared to other parts of the memory.
When copying memoryblocks in memory (as I do with copybuffer), that is not accessed by any hardware or software, the cpu is actually doing it very fast. If you do not believe me, you should go try looking it up.
Note: I think this discussion is brought too far away from it's origin. And I don't think it will be worth it discussing how fast these things can be done prior on examples of how to do it. So please, if you would have me excused!
Regards
Williams
(1) and (3): Ok, my point of view is a little bit different, but that's ok... :-)
(2) You CAN typecast binary data to a Delphi (!) string, as long as you don't convert it to a pchar string, cause the length of Delphi strings is not determined by #0 character but by a length integer in front of the string data. I guess, you know that? I'm doing such stuff every day...
(4) The scanline property is implemented by using DIB functions. That means DeviceIndependentBitmap (I'm sure you know that). So if these bitmaps are device independent, they're surely not in the video memory.
Ok, I agree. We should not spend our time on discussing. It seems that there was a little misunderstanding. I thought you would condemn direct memory access as a bad programming style and YOU just want to protect unexperienced programmers from playing too much with that. Of course I can agree with that... :-)
(2) You CAN typecast binary data to a Delphi (!) string, as long as you don't convert it to a pchar string, cause the length of Delphi strings is not determined by #0 character but by a length integer in front of the string data. I guess, you know that? I'm doing such stuff every day...
(4) The scanline property is implemented by using DIB functions. That means DeviceIndependentBitmap (I'm sure you know that). So if these bitmaps are device independent, they're surely not in the video memory.
Ok, I agree. We should not spend our time on discussing. It seems that there was a little misunderstanding. I thought you would condemn direct memory access as a bad programming style and YOU just want to protect unexperienced programmers from playing too much with that. Of course I can agree with that... :-)
ASKER
Thank you William2 for your answer.
Can you give me one easy and fast advice please ?
This is my code:
procedure TForm1.ClientSocket1Read(S ender: TObject;
Socket: TCustomWinSocket);
var
TempSize: Integer;
begin
TempStr:=Socket.ReceiveTex t;
// I need to use TempStr to read some text
// Then I use your code.But Socket.ReceiveLength is allways = 0
// If I delete the line "TempStr:=Socket.ReceiveTe xt;"
// all is OK. Why ? Any advice ?
If (Socket.ReceiveLength)>=(S izeOf(Temp Size)) then
begin
Socket.ReceiveBuf(TempSize ,SizeOf(Te mpSize));
Stream.SetSize(TempSize);
Stream.Position:= 0;
.......................... .......... ......
...........
Can you give me one easy and fast advice please ?
This is my code:
procedure TForm1.ClientSocket1Read(S
Socket: TCustomWinSocket);
var
TempSize: Integer;
begin
TempStr:=Socket.ReceiveTex
// I need to use TempStr to read some text
// Then I use your code.But Socket.ReceiveLength is allways = 0
// If I delete the line "TempStr:=Socket.ReceiveTe
// all is OK. Why ? Any advice ?
If (Socket.ReceiveLength)>=(S
begin
Socket.ReceiveBuf(TempSize
Stream.SetSize(TempSize);
Stream.Position:= 0;
..........................
...........
Yes! When sending text, you should receive the text with writing f.ex.:
S:= Socket.ReceiveText;
This way, you also should expect the incoming string from the opposite part. Unfortunately you cannot test the receivelength property, but the event is the same.
If you like to know another example, you should try read the example I wrote for "ItsMe" at:
https://www.experts-exchange.com/Q.10104621
it explains how to capture the screen and send it to a client on request. At the bottom I also wrote an example using Francois Piettes freeware components.
Happy C/S developing!
Cheers,
Williams
S:= Socket.ReceiveText;
This way, you also should expect the incoming string from the opposite part. Unfortunately you cannot test the receivelength property, but the event is the same.
If you like to know another example, you should try read the example I wrote for "ItsMe" at:
https://www.experts-exchange.com/Q.10104621
it explains how to capture the screen and send it to a client on request. At the bottom I also wrote an example using Francois Piettes freeware components.
Happy C/S developing!
Cheers,
Williams
ASKER
Many many thanks to Williams :-)
Cheers,
Borgo.
Cheers,
Borgo.
That's allright dear borgo.
Happy C/S-haunting
Cheers,
Williams
Happy C/S-haunting
Cheers,
Williams
with TBitmap.ScanLine property you get access to the rare pixel data. Of course that is not so easy, because you have to look at the color format yourself.
E.g., for 32 bit bitmaps you'll have to read/write width*4 bytes from/to the scanLine property. Then you'll have to write a little loop that read/writes all (height) lines of the bitmap.
Look at this example:
procedure TForm1.Button1Click(Sender
var
x,y : integer;
BitMap : TBitMap;
P : PByteArray;
begin
BitMap := TBitMap.create;
try
BitMap.LoadFromFile('..\Im
for y := 0 to BitMap.height -1 do
begin
P := BitMap.ScanLine[y];
for x := 0 to BitMap.width -1 do
P[x] := y;
end;
canvas.draw(0,0,BitMap);
finally
BitMap.free;
end;
end;
Regards, Madshi.