Board index » cppbuilder » drawing on canvas with code

drawing on canvas with code


2006-09-27 12:37:29 PM
cppbuilder80
Hello:
I want to draw some diagrams, flowcharts etc. on canvas (instead of using
images with TImage)
A sample figure can be found here
www.geocities.com/alwaysinecstasy/figure.jpg
How to go about it? How can I get coordinates etc. for drawing
programmatically? I also need to resize the drawing when form is rezized.
Guidelined are appreciated.
Thanks,
Sudesh
 
 

Re:drawing on canvas with code

"Sudesh" < XXXX@XXXXX.COM >wrote:
Quote

I want to draw some diagrams, flowcharts etc.
This question would have best been asked in the graphics group.
Quote
on canvas (instead of using images with TImage)
A Canvas is a Canvas. Be it a TImage or a TPanel or a
TPaintBox ect. ect., the drawing code remains the same.
The difference with the object is how it gets displayed.
Quote
How can I get coordinates etc. for drawing programmatically?
Your best bet would be some sort of dynamic array that holds a
struct that contains all of the individual objects information
needed to draw it.
The real question that I have is what is the source of the
objects? If it will be dynamic from a user then you'll need
to build some sort of interface where the user defines the
objects and builds the structures for you.
If all you're doing is converting an image file to a vector of
objects, then once you define the objects, you'll have to fill
in the positioning and sizing through trial and error.
Quote
I also need to resize the drawing when form is rezized.
I would suggest that you use a TBitmap to draw on that you
size as large as you need for all of your objects. Then use
a second TBitmap that you use to scale the image to what ever
size you need to display.
To display it, use a TScrollBox with a TPaintBox inside it and
add an OnPaint event to the TPaintBox where all you do is use
the TPaintBox's Canvas's CopyRect method to draw the bitmap.
Set the TScrollBox's Align property to alClient and position
the TPaintBox at 0,0. When you want to change the zoom ratio,
size the TPaintBox and the secondary bitmap accordingly and
then use StretchDraw to copy the image to the secondary bitmap
at the correct ratio.
~ JD
 

Re:drawing on canvas with code

Hi JD
I have some fixed number of drawings(figures) in BMP format and I have to
show only one figure at a time.
I have 2 options.
1. Using TImage load and show one BMP at a time.
2. I draw this figure using TShapes.
I am interested in second option.
This is not dynamic. i.e. user is not going to draw his/her own figures etc.
The figures are predefined and total number is fixed.
Thanks,
Sudesh
 

{smallsort}

Re:drawing on canvas with code

"Sudesh" < XXXX@XXXXX.COM >wrote:
Quote
I have some fixed number of drawings(figures) in BMP format
and I have to show only one figure at a time.
I have 2 options.
1. Using TImage load and show one BMP at a time.
2. I draw this figure using TShapes.
I am interested in second option.
You have bmp files. Bmp files contain pixels. If a line of just
1 pixel width is shown in the picture and the picture gets
displayed in 40% size then how wide is your line? I suppose that
is your problem and reason not to want to use scaling of TImage?
You would like to have vectors instead, i.e. shape descriptions
instead of an array of pixels (e.g. square starting at 5% of
picture's heigth from top and 10% of pictures width from right,
having length of 3% of picture width and a border of 0.01% of
picture width). Problem is: a bmp file is not a vector file, so
you don't have that information. Two solutions for that: Either
get vector files instead of bmp or try to convert from pixels
to vector. The latter is almost impossible, but there are tools
that try it though. You might get help on that in the graphics
group.
 

Re:drawing on canvas with code

"Sudesh" < XXXX@XXXXX.COM >wrote:
Quote

I have some fixed number of drawings(figures) in BMP format
and I have to show only one figure at a time.

I have 2 options.

1. Using TImage load and show one BMP at a time.
2. I draw this figure using TShapes.

I am interested in second option.
Why? Do you not want to distribute the bitmaps? That's the
only reason that I can think of that would justify not using
a TBitmap and LoadFromFile and it appears to me that the
drawings are specialized enough so that it's safe to assume
that it's an in-house project so distribution is not an issue.
Besides, what happens if the drawing is modified? That would
mean that you'd have to recode, recompile and redistribute
your application.
So you know, you don't need a TImage to display images. There
are many different ways to paint them. Additionally, if I were
to draw the images myself, I certainly wouldn't use TShapes.
The code and overhead would be more than what I'd be willing
to do.
For what ever reason, if you *must* draw them, you'll need to
define some sort of structure that will contain all of the
information needed to draw the object. For example:
enum TMyType
{
myCircle,
myRect,
myLine,
myDashedLine,
myCaption,
myLArrow,
myRArrow,
....
myCount
};
struct TMyObject
{
TMyType Type;
int Left;
int Top;
int Width;
int Height;
....
TColor Color;
AnsiString Font;
AnsiString Text;
};
Then you'll need to code functions that actually draw the
different objects. Note that you would not use all of the
TMyObject members for all of the objects and some of them
would be used for something other than what their names might
suggest. For example, if drawing a circle, Left and Top would
be where you'd position the Pen and then you'd use Width and
Height to know how big to draw the circle and how thick to
draw it.
Once you complete the above, you can add all of the structures
to a .res file that gets compiled into the executable that you
can extract at runtime (quite simple once you've done it). You
might also want to consider a resource-only dll that you can
dynamically load and unload at runtime. It will reduce the exe
size and if changes are made to the images, you'll only need to
redistribute the resource dll.
Now, since you have the images, it should only be a matter
of trial-and-error to setup the structures but you're most
likely going to spend a couple of days on this when IMO you
don't need to. In any case, you still need to display the
images and once you have that setup, all of the above can be
replaced with one call to LoadFromFile.
You expressed a desire to resize the image as the form size
changed but what if the user wants to narrow the form but not
have the image reduced? As I suggested before:
Drop a TScrollBox onto a form and set it's Align property to
alClient. Then drop a TPaintBox onto the TScrollBox, position
it at 0,0 and add an OnPaint event to the TPaintBox. In the
OnPaint event add one line of code. Something like:
PaintBox1->Canvas->CopyRect( Rect(0,0,Bitmap->Width,Bitmap->Height),
Bitmap->Canvas, Rect(0,0,Bitmap->Width,Bitmap->Height) );
Note that the TPaintBox needs to have the same width and
height as the TBitmap unless you want to size the image
in which case you'd use StretchDraw instead of CopyRect.
For example (assumes that TPaintBox has already been sized
as desired):
PaintBox1->Canvas->StretchDraw( Rect(0,0,PaintBox1->Width,PaintBox1->Height), Bitmap );
However, StretchDraw is so slow that you'll see flicker
so what I do is allocate a second TBitmap that is used to
display the image. This allows me to still use CopyRect
in the OnPaint event and the calls to StretchDraw are fewer
and done in memory. So when the scale changes, resize the
TPaintBox and the second TBitmap to the desired size and then
call StretchDraw. For example (assumes that sizing has been
completed):
SecondBitmap->Canvas->StretchDraw(
Rect(0,0,SecondBitmap->Width,SecondBitmap->Height), Bitmap );
and then you'd use the second bitmap instead in the OnPaint
event.
No matter what, you'll still need to display the image so to
start, just add the TScrollBox and TPaintBox as noted and
allocate a TBitmap and use CopyRect in the OnPaint event.
You'll also need to add a TMenu or TToolBar so that you can
execute events to test your code. Should take 5 minutes tops.
For example:
private: // User declarations
Graphics::TBitmap *Bitmap;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
__fastcall ~TForm1();
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// set this in the IDE
ScrollBox1->Align = alClient;
// just need some initialization to prevent AV and so it looks right
PaintBox1->Width = ScrollBox1->ClientWidth;
PaintBox1->Height = ScrollBox1->ClientHeight;
Bitmap = new Graphics::TBitmap();
Bitmap->PixelFormat = pf24bit; // or pf32bit or what ever;
Bitmap->Width = PaintBox1->Width;
Bitmap->Height = PaintBox1->Height;
Bitmap->Canvas->Brush->Style = bsClear; // Reported to speed-up bitmap operations
Bitmap->Canvas->Brush->Color = clWhite;
Bitmap->Canvas->FillRect( Rect(0,0,Bitmap->Width,Bitmap->Height) );
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
delete Bitmap;
}
//-------------------------------------------------------------
void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
PaintBox1->Canvas->CopyRect( Rect(0,0,Bitmap->Width,Bitmap->Height),
Bitmap->Canvas, Rect(0,0,Bitmap->Width,Bitmap->Height) );
}
//-------------------------------------------------------------
void __fastcall TForm1::LoadClick( TObject *Sender )
{
// disable painting while loading
::SendMessage( ScrollBox1->Handle, WM_SETREDRAW, FALSE, 0 );
Bitmap->LoadFromFile( "SomeValidPathToABitmap" );
// need to set scrollbars before you size the paint box
ScrollBox1->HorzScrollBar->Position = 0;
ScrollBox1->VertScrollBar->Position = 0;
// size the paintbox
PaintBox1->SetBounds( 0, 0, Bitmap->Width, Bitmap->Height );
// enable painting
::SendMessage( ScrollBox1->Handle, WM_SETREDRAW, TRUE, 0 );
// cause the new image to be painted
ScrollBox1->Refresh();
}
//-------------------------------------------------------------
Note that the TScrollBox will handle all of the scrolling for
you automatically and it's quite smooth and there is no need
to scale the image. That can be offered as a Menu choice and
coded as noted above.
If you're going to be doing the drawing yourself, replace the
call to LoadFromFile in the LoadClick event with a call to
your drawing code.
~ JD
 

Re:drawing on canvas with code

"Sudesh" < XXXX@XXXXX.COM >wrote in message
Quote
I have 2 options.
<snip>The figures are predefined and total number is fixed.
3) store the BMPs into a TImageList, and then rotate through the images as
needed.
Gambit
 

Re:drawing on canvas with code

Hi All
Thanks for all your replies.
No problem of distributing bitmaps or bitmap modifications.
My worry is about the exe file size if TImage TImagelist is used.
Loading at runtime is ok. So it has no effect on exe file size.
May I know what are the overheads of using TShapes?
Thanks,
Sudesh
 

Re:drawing on canvas with code

"Sudesh" < XXXX@XXXXX.COM >wrote:
Quote

[...] May I know what are the overheads of using TShapes?
That's easy enough for you to observe for yourself. Start a
new application and compile a blank form and note the size of
the exe. Then drop a bunch of TShapes on the form and compile
again and then compare the exe size.
The actual overhead in the exe is minimal but still more than
using LoadFromFile. However, that jumps dramatically if you
start adding new forms to display each image or if you do it
dynamically in code. What I was talking about was programmer
overhead to reproduce the images when you already have everything
that you need.
~ JD