[solved]Custom Drawing

General help with the Ecere Cross Platform GUI toolkit: Window, common controls, events, etc.
Help with the 2D Graphics library: Surface, Display, Bitmap, Font and others.
Post Reply
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

[solved]Custom Drawing

Post by samsam598 »

Greetings!

I am studying the drawing in eC.Many examples in the eC package I found so far does drawing in the virtual function OnRedraw(Surface surface) which have provide a surface to draw on it.However what if I want to draw something outside the OnRedraw event,say,a button will fire such drawing?Below code draws nothing.

Please help.Thanks.
PS:Besides surface.DrawLine,surface.Rectangle,is there other functions such as surface.Ellipse etc?Thanks.

Code: Select all

 
class MainForm:WIndow
{
 ...
 Bitmap bitmap {};
 ...
 Button btnDraw 
   {      
      this, text = "(D)绘图", altD, size = { 74, 29 }, position = { 472, 16 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
 
         Surface surface=bitmap.GetSurface(0,0,null);
         surface.Rectangle(10,10,this.clientSize.w-10,this.clientSize.h-10);
         surface.DrawLine(0,0,this.clientSize.w,this.clientSize.h); 
         return true;
      }
   };    
 ...
}
 
Last edited by samsam598 on Thu Sep 08, 2011 12:12 am, edited 1 time in total.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Custom Drawing

Post by jerome »

Hi Sam,

All drawing with Ecere should happen in the OnRedraw event. The system calls OnRedraw when it needs to redraw the window, and you can force an update by marking the window as dirty by calling Update() with an optional extent to be updated or null to have the entire window redrawn. So typically your Window class uses member variables to describe its state, and OnRedraw draws the window based on those states. If you want to change something on a Button press, you can modify those states from within a Button's NotifyClicked event, and call Update() on the window. Here is some sample code:

Code: Select all

import "ecere"
 
class MainForm : Window
{
   hasClose = true;
   size = { 640, 480 };
 
   bool draw;
 
   void OnRedraw(Surface surface)
   {
      if(draw)
      {
         surface.Rectangle(10,10,clientSize.w-10,clientSize.h-10);
         surface.DrawLine(0,0,clientSize.w,clientSize.h);
      }
   }
 
   Button btnDraw 
   {      
      this, text = "(D)绘图", altD, size = { 74, 29 }, position = { 472, 16 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         draw ^= true; // Toggle between true & false
         Update(null);
         return true;
      }
   };    
};
 
MainForm form1 {};
This built-in design of the Ecere GUI system ensures that what you're showing on the screen will remain there (e.g. if the window is minimized and brought back up, which will trigger an OnRedraw call, that would erase your non-state based direct drawing!).

Now of course, from within the OnRedraw calls direct rendering does happen. And it is possible (but highly unrecommended) to make those same calls from outside OnRedraw, as you can see in this following example. Notice how more code is required (setting up the foreground drawing color, updating the display, using the proper offset to render to the client area). This is using the Graphics components of Ecere directly, as opposed to using them through the GUI system.

Code: Select all

import "ecere"
 
class MainForm : Window
{
   hasClose = true;
   size = { 640, 480 };
 
   Button btnDraw 
   {      
      this, text = "(D)绘图", altD, size = { 74, 29 }, position = { 472, 16 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         Surface surface;
         display.StartUpdate();
         surface=display.GetSurface(clientStart.x,clientStart.y,null);
         surface.foreground = black;
         surface.Rectangle(10,10,clientSize.w-10,clientSize.h-10);
         surface.DrawLine(0,0,clientSize.w,clientSize.h); 
         delete surface;         
         display.Update({ 0, 0, size.w-1, size.h-1 });
         display.EndUpdate();
         return true;
      }
   };    
};
 
MainForm form1 {};
Due to the same problem discussed above (the system deciding when drawing occurs, clearing the display), this code doesn't work well with hardware accelerated display drivers like OpenGL or Direct3D, because they do buffer-flipping and your image gets erased right away.

It is worth noting that you were invoking Bitmap::GetSurface in your own sample code, which obtains a drawing surface from a Bitmap, i.e. to draw in that bitmap memory. You didn't allocate memory for the bitmap however (Bitmap::Allocate) so drawing fails. Note also that any surface obtained (whether from a Display or a Bitmap) must be deleted to free its memory. However you should not delete the surface passed to you in OnRedraw, because the GUI system manages it for you. Here's an example obtaining a surface from a bitmap and then displaying it on the window. Notice how the bitmap is smaller than the window so you only see a small square of your image.

Code: Select all

import "ecere"
 
class MainForm : Window
{
   hasClose = true;
   size = { 640, 480 };
 
   bool draw;
 
   Bitmap bitmap { };
   MainForm()
   {
      bitmap.Allocate(null, 200, 200, 0, pixelFormat888, false);
   }
 
   void OnRedraw(Surface surface)
   {
      if(draw)
      {
         Surface bmpSurface = bitmap.GetSurface(0,0, null);
         bmpSurface.Rectangle(10,10,clientSize.w-10,clientSize.h-10);
         bmpSurface.DrawLine(0,0,clientSize.w,clientSize.h);
         delete bmpSurface;
 
         surface.Blit(bitmap, 0,0,0,0, bitmap.width, bitmap.height);
      }
   }
 
   Button btnDraw 
   {      
      this, text = "(D)绘图", altD, size = { 74, 29 }, position = { 472, 16 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         draw ^= true; // Toggle between true & false
         Update(null);
         return true;
      }
   };    
};
 
MainForm form1 {};
This technique is useful for rendering things to a memory bitmap and then saving it to a file, for example. (Bitmap::Save)

At this point, Ecere only draws text, bitmaps, pixels, lines, rectangles, square areas and gradients. The focus of the 2D graphics engine has been GUIs, and geometric shapes are rarely used in GUIs (bitmaps are used instead). If you don't mind making your code graphics mode-specific, there are ways you can implement your own shapes drawing functions: in OpenGL mode use GL calls, or even Ecere's 3D drawing methods which will work in both OpenGL and Direct3D; in Software mode access Surface::bitmap directly and write directly into the memory bitmap. For a typical GUI application, the best approach is usually to make bitmaps resources for anything you want to draw that is not available in the graphics API.

There are plans to enrich the graphics API with shapes support in the future.

All the best,

Jerome
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

Re: Custom Drawing

Post by samsam598 »

Thanks Jerome,your first example is exactly what I want.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Custom Drawing

Post by jerome »

Yes, that's the one you should be using :D
Post Reply