RealBasic: Canvas tutorial lesson 1

Today we will be going to make our framework for the more advanced lessons. I decided to make it a separate lesson as some basic things are done here like buffering and partial redrawing.

Our setup is some covers of comic albums that we want to show. Obviously we could just load the covers and just draw them on a canvas, but me want that extra more.  Moreover, in later lessons we want to be able to click the covers, drag them around so the user can reorder them to her/his liking.

Therefor, we are going to make some classes that will allow us to do just that.

First, we’ll start with subclassing the canvas itself. We create a new class ABCanvas and we set its super to Canvas.
I know in later versions of RB, the Canvas has doublebuffering. However, I like to take control over things so we’ll do our buffering ourselves.

We’ll going to need some new properties to hold the canvas buffer:

pBuffer as picture
gBuffer as graphics

We initialize the buffer in a new Init() function:

sub Init()
  ' we use the width and height of the canvas
  pBuffer = NewPicture(width, height, 32)
  ' just a quick shortcut to the graphics we can use later
  gBuffer = pBuffer.Graphics
end sub

In the paint event, the only thing we do is drawing our buffer. This is one of the main reasons we can get the speed we need.

sub paint(g as graphics)
  if pBuffer <> nil then
    g.DrawPicture pBuffer,0,0
  end if
end sub

Next we create a class to hold our comic covers: ABElement

It will get some properties to set the position, its own buffer etc.

x as integer
y as integer
w as integer
h as integer
Image as picture
pBuffer as picture
gBuffer as graphics
ID as integer
Visible as boolean

Create a constructor that will link this element to our ABCanvas:

Sub Constructor(tmpABCanvas as ABCanvas)
   MyABCanvas = tmpABCanvas
End Sub

It is always good practice to cleanup when you destroy a class, so we create a function cleanUp():

Sub CleanUp()
  gBuffer = nil
  pBuffer = nil
  Image = nil
  MyABCanvas = nil
end Sub

In the destructor of the ABElement class, we’ll call this cleanup function:

Sub Destructor()
  CleanUp()
End Sub

The function DrawMe will draw the comic cover:

sub drawme()
  if Image <> nil then
    gBuffer.DrawPicture Image,0,0
    ' here we will be able to add whatever drawing code we want
  end if

We create an Init function:

Sub Init(tmpID as integer, tmpX as integer, tmpY as integer, tmpImage as picture, tmpVisible as boolean)
  me.ID = tmpID
  me.x = tmpX
  me.y = tmpY
  me.w = tmpImage.Width
  me.h = tmpImage.Height
  me.Visible = tmpVisible

  Image = NewPicture(w,h, 32)
  image.Graphics.DrawPicture tmpImage,0,0

  pBuffer = NewPicture(w,h, 32)
  gBuffer = pBuffer.Graphics

  DrawMe
end Sub

Back to our ABCanvas. We’ll create a new property MyElements that will hold all our comic covers:

MyElements(-1) as ABElement

Some functions to add and remove ABElements from our ABCanvas:

Sub AddElement(tmpElement as ABElement)
    MyElements.Append tmpElement
End Sub

Sub RemoveElement(ID as integer)
  Dim a as integer
  for a = 0 to UBound(MyElements)
    if MyElements(a).ID = ID then
      MyElements.Remove(a)
      return
    end if
  next
End Sub

The function DrawMe() in ABCanvas will draw all our ABElements onto the buffer. Only when everything is drawn on our buffer, we’ll write the result to canvas. This function is optimized for speed because we’ll only draw what has changed.

Sub DrawMe(x as integer, y as integer, w as integer, h as integer)
  #pragma disableBackgroundTasks

  Dim i,maxi As Integer
  Dim tmpElem As ABElement
  Dim picleft, pictop As Integer

  // Redraw the pictures which are within the given bounds.
  maxi = UBound(MyElements)
  for i = 0 to maxi
    tmpElem = MyElements(i)
    if tmpElem.Visible then
      picleft = tmpElem.x-tmpElem.w / 2
      pictop = tmpElem.y-tmpElem.h / 2
      if picleft+tmpElem.w > x and picleft < x+w and pictop+tmpElem.h > y and pictop < y+h then
        gBuffer.DrawPicture tmpElem.pBuffer, picleft, pictop
      end if

    end if
  next

  // the actual drawing to the canvas
  self.Graphics.DrawPicture pBuffer,x,y,w,h,x,y,w,h
End Sub

We also create a cleanup() function to free the memory if we close the object.

Sub CleanUp()
  dim a as integer
  for a = 0 to UBound(MyElements)
    MyElements(a).CleanUp
  next

  Redim MyElements(-1)

  gBuffer = nil
  pBuffer = nil
End Sub

Our framework is ready to be tested! Put a ABCanvas on an empty form and lock it to the form.

Create a new function LoadMe(). This is a quick and dirty way to load our images. In a real app you’ll do it better of course. We also put the comic covers randomly on the screen:

sub LoadMe()

  Dim f as FolderItem
  Dim tmpElem as ABElement
  Dim tmpPic as Picture

  ABCanvas1.Init()

  f = GetFolderItem("Comic1.png")
  tmpPic = f.OpenAsPicture
  tmpElem = new ABElement(ABCanvas1)
  tmpElem.Init(1, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2, tmpPic, true)
  ABCanvas1.AddElement(tmpElem)

  f = GetFolderItem("Comic2.png")
  tmpPic = f.OpenAsPicture
  tmpElem = new ABElement(ABCanvas1)
  tmpElem.Init(2, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2, tmpPic, true)
  ABCanvas1.AddElement(tmpElem)

  f = GetFolderItem("Comic3.png")
  tmpPic = f.OpenAsPicture
  tmpElem = new ABElement(ABCanvas1)
  tmpElem.Init(3, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2, tmpPic, true)
  ABCanvas1.AddElement(tmpElem)

  f = GetFolderItem("Comic4.png")
  tmpPic = f.OpenAsPicture
  tmpElem = new ABElement(ABCanvas1)
  tmpElem.Init(4, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2, tmpPic, true)
  ABCanvas1.AddElement(tmpElem)

  f = GetFolderItem("Comic5.png")
  tmpPic = f.OpenAsPicture
  tmpElem = new ABElement(ABCanvas1)
  tmpElem.Init(5, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2, tmpPic, true)
  ABCanvas1.AddElement(tmpElem)
End Sub

In the forms Open() event, we load everything and draw the canvas. In the close we do a cleanup:

Sub Open()
   loadMe
   ABCanvas1.DrawMe 0,0, ABCanvas1.width, ABCanvas1.Height
End Sub

Sub Close()
    ABCanvas1.CleanUp
End Sub

Ready to run! You’re result would look something like this:

It’s nice and we have a solid framework for the next lessons. In the next lesson, we will spice things up a little by adding a nice background, some shadows around the covers (to give it a 3D effect) and a glow on the covers (to mimic it is covered in plastic).

You can download the project for this tutorial from http://www.gorgeousapps.com/Tut1.zip

See you in the next lesson!

Click here to Donation if you like my work

Advertisements

About Alwaysbusy

My name is Alain Bailleul and I'm the Senior Software Architect/Engineer at One-Two. I like to experiment with new technologies, Computer Vision and A.I. My projects are programmed in B4X , Xojo, C#, java, HTML, CSS and JavaScript. View all posts by Alwaysbusy

One response to “RealBasic: Canvas tutorial lesson 1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: