Custom Canvas Optimization
DanielH

Hello all,

I'm looking for advice/comments for a canvas class I created.

Back story: I was playing a game on my phone and during the game you could zoom in/out and could wrap the viewing portion. I thought about creating a class that could do the same.

Here is the result:

#SelectExpand
1#ifndef dLibraryCanvasHeader 2#define dLibraryCanvasHeader 3 4#include <allegro5/allegro.h> 5 6#define dMin( x, y ) ((x)<(y)?(x):(y)) 7#define dMax( x, y ) ((x)>(y)?(x):(y)) 8 9namespace dLibrary 10{ 11 class dCanvas 12 { 13 public: 14 enum dDistinguishers 15 { 16 dWidth, 17 dHeight, 18 dXPos, 19 dYPos, 20 }; 21 22 dCanvas() 23 { 24 this->mDelete = false; 25 this->mZoom = 0.0f; 26 this->mWrap = false; 27 this->mBitmapSize[ dCanvas::dWidth ] = 0; 28 this->mBitmapSize[ dCanvas::dHeight ] = 0; 29 this->mCamera[ dCanvas::dXPos ] = 0; 30 this->mCamera[ dCanvas::dYPos ] = 0; 31 this->mCamera[ dCanvas::dWidth ] = 0; 32 this->mCamera[ dCanvas::dHeight ] = 0; 33 this->mBitmap = 0; 34 this->mDrawBitmap = al_draw_scaled_bitmap; 35 } 36 37 dCanvas( const int width, const int height ) 38 { 39 this->mDelete = false; 40 this->mZoom = 0.0f; 41 this->mWrap = false; 42 this->mBitmapSize[ dCanvas::dWidth ] = 0; 43 this->mBitmapSize[ dCanvas::dHeight ] = 0; 44 this->mCamera[ dCanvas::dXPos ] = 0; 45 this->mCamera[ dCanvas::dYPos ] = 0; 46 this->mCamera[ dCanvas::dWidth ] = 0; 47 this->mCamera[ dCanvas::dHeight ] = 0; 48 49 int flags = al_get_new_bitmap_flags(); 50 51 al_set_new_bitmap_flags( ALLEGRO_VIDEO_BITMAP | ALLEGRO_MAG_LINEAR | ALLEGRO_MIN_LINEAR ); 52 53 this->mBitmap = al_create_bitmap( width, height ); 54 55 al_set_new_bitmap_flags( flags ); 56 57 this->mDrawBitmap = al_draw_scaled_bitmap; 58 59 if ( this->mBitmap ) 60 { 61 this->mBitmapSize[ dCanvas::dWidth ] = width; 62 this->mBitmapSize[ dCanvas::dHeight ] = height; 63 this->mCamera[ dCanvas::dXPos ] = 0; 64 this->mCamera[ dCanvas::dYPos ] = 0; 65 this->mCamera[ dCanvas::dWidth ] = this->mBitmapSize[ dCanvas::dWidth ]; 66 this->mCamera[ dCanvas::dHeight ] = this->mBitmapSize[ dCanvas::dHeight ]; 67 this->mZoom = 1.0f; 68 this->mDelete = true; 69 } 70 } 71 72 dCanvas( ALLEGRO_BITMAP *bitmap, bool doDelete = false ) 73 { 74 this->mDelete = doDelete; 75 this->mZoom = 0.0f; 76 this->mWrap = false; 77 this->mBitmapSize[ dCanvas::dWidth ] = 0; 78 this->mBitmapSize[ dCanvas::dHeight ] = 0; 79 this->mCamera[ dCanvas::dXPos ] = 0; 80 this->mCamera[ dCanvas::dYPos ] = 0; 81 this->mCamera[ dCanvas::dWidth ] = 0; 82 this->mCamera[ dCanvas::dHeight ] = 0; 83 this->mBitmap = bitmap; 84 this->mDrawBitmap = al_draw_scaled_bitmap; 85 86 if ( this->mBitmap ) 87 { 88 this->mBitmapSize[ dCanvas::dWidth ] = al_get_bitmap_width( this->mBitmap ); 89 this->mBitmapSize[ dCanvas::dHeight ] = al_get_bitmap_height( this->mBitmap ); 90 this->mCamera[ dCanvas::dXPos ] = 0; 91 this->mCamera[ dCanvas::dYPos ] = 0; 92 this->mCamera[ dCanvas::dWidth ] = this->mBitmapSize[ dCanvas::dWidth ]; 93 this->mCamera[ dCanvas::dHeight ] = this->mBitmapSize[ dCanvas::dHeight ]; 94 this->mZoom = 1.0f; 95 this->mDelete = true; 96 } 97 } 98 99 ~dCanvas() 100 { 101 if ( this->mBitmap && this->mDelete ) 102 { 103 al_destroy_bitmap( this->mBitmap ); 104 } 105 } 106 107 void beginDrawing() 108 { 109 al_set_target_bitmap( this->mBitmap ); 110 } 111 112 void flipToDisplay( ALLEGRO_DISPLAY *display ) 113 { 114 al_set_target_backbuffer( display ); 115 116 int targetBitmapSize[ 2 ] = { al_get_display_width( display ), al_get_display_height( display ) }; 117 118 if ( this->mWrap ) 119 { 120 if ( this->mCamera[ dCanvas::dXPos ] >= 0 && 121 this->mCamera[ dCanvas::dXPos ] <= ( this->mBitmapSize[ dCanvas::dWidth ] - this->mCamera[ dCanvas::dWidth ] ) ) 122 { 123 if ( this->mCamera[ dCanvas::dYPos ] >= 0 && 124 this->mCamera[ dCanvas::dYPos ] <= ( this->mBitmapSize[ dCanvas::dHeight ] - this->mCamera[ dCanvas::dHeight ] ) ) 125 { 126 this->mDrawBitmap( this->mBitmap, 127 this->mCamera[ dCanvas::dXPos ], this->mCamera[ dCanvas::dYPos ], this->mCamera[ dCanvas::dWidth ], this->mCamera[ dCanvas::dHeight ], 128 0, 0, targetBitmapSize[ dCanvas::dWidth ], targetBitmapSize[ dCanvas::dHeight ], 129 0 ); 130 } 131 else 132 { 133 int h1 = this->mBitmapSize[ dCanvas::dHeight ] - this->mCamera[ dCanvas::dYPos ]; 134 int h2 = this->mCamera[ dCanvas::dHeight ] - h1; 135 int y2 = ( targetBitmapSize[ dCanvas::dHeight ] * h1 ) / this->mCamera[ dCanvas::dHeight ]; 136 137 138 this->mDrawBitmap( this->mBitmap, 139 this->mCamera[ dCanvas::dXPos ], this->mCamera[ dCanvas::dYPos ], this->mCamera[ dCanvas::dWidth ], h1, 140 0, 0, targetBitmapSize[ dCanvas::dWidth ], y2, 141 0 ); 142 143 this->mDrawBitmap( this->mBitmap, 144 this->mCamera[ dCanvas::dXPos ], 0, this->mCamera[ dCanvas::dWidth ], h2, 145 0, y2, targetBitmapSize[ dCanvas::dWidth ], targetBitmapSize[ dCanvas::dHeight ] - y2, 146 0 ); 147 } 148 } 149 else 150 { 151 if ( this->mCamera[ dCanvas::dYPos ] >= 0 && 152 this->mCamera[ dCanvas::dYPos ] <= ( this->mBitmapSize[ dCanvas::dHeight ] - this->mCamera[ dCanvas::dHeight ] ) ) 153 { 154 int w1 = this->mBitmapSize[ dCanvas::dWidth ] - this->mCamera[ dCanvas::dXPos ]; 155 int w2 = this->mCamera[ dCanvas::dWidth ] - w1; 156 157 int x2 = ( targetBitmapSize[ dCanvas::dWidth ] * w1 ) / this->mCamera[ dCanvas::dWidth ]; 158 159 160 this->mDrawBitmap( this->mBitmap, 161 this->mCamera[ dCanvas::dXPos ], this->mCamera[ dCanvas::dYPos ], w1, this->mCamera[ dCanvas::dHeight ], 162 0, 0, x2, targetBitmapSize[ dCanvas::dHeight ], 163 0 ); 164 165 this->mDrawBitmap( this->mBitmap, 166 0, this->mCamera[ dCanvas::dYPos ], w2, this->mCamera[ dCanvas::dHeight ], 167 x2, 0, targetBitmapSize[ dCanvas::dWidth ] - x2, targetBitmapSize[ dCanvas::dHeight ], 168 0 ); 169 } 170 else 171 { 172 int w1 = this->mBitmapSize[ dCanvas::dWidth ] - this->mCamera[ dCanvas::dXPos ]; 173 int w2 = this->mCamera[ dCanvas::dWidth ] - w1; 174 int h1 = this->mBitmapSize[ dCanvas::dHeight ] - this->mCamera[ dCanvas::dYPos ]; 175 int h2 = this->mCamera[ dCanvas::dHeight ] - h1; 176 177 int y2 = ( targetBitmapSize[ dCanvas::dHeight ] * h1 ) / this->mCamera[ dCanvas::dHeight ]; 178 int x2 = ( targetBitmapSize[ dCanvas::dWidth ] * w1 ) / this->mCamera[ dCanvas::dWidth ]; 179 180 181 this->mDrawBitmap( this->mBitmap, 182 this->mCamera[ dCanvas::dXPos ], this->mCamera[ dCanvas::dYPos ], w1, h1, 183 0, 0, x2, y2, 184 0 ); 185 186 this->mDrawBitmap( this->mBitmap, 187 this->mCamera[ dCanvas::dXPos ], 0, w1, h2, 188 0, y2, x2, targetBitmapSize[ dCanvas::dHeight ] - y2, 189 0 ); 190 191 this->mDrawBitmap( this->mBitmap, 192 0, this->mCamera[ dCanvas::dYPos ], w2, h1, 193 x2, 0, targetBitmapSize[ dCanvas::dWidth ] - x2, y2, 194 0 ); 195 196 this->mDrawBitmap( this->mBitmap, 197 0, 0, w2, h2, 198 x2, y2, targetBitmapSize[ dCanvas::dWidth ] - x2, targetBitmapSize[ dCanvas::dHeight ] - y2, 199 0 ); 200 } 201 } 202 } 203 else 204 { 205 this->mDrawBitmap( this->mBitmap, 206 this->mCamera[ dCanvas::dXPos ], this->mCamera[ dCanvas::dYPos ], this->mCamera[ dCanvas::dWidth ], this->mCamera[ dCanvas::dHeight ], 207 0, 0, targetBitmapSize[ dCanvas::dWidth ], targetBitmapSize[ dCanvas::dHeight ], 208 0 ); 209 } 210 211 al_flip_display(); 212 } 213 214 ALLEGRO_BITMAP *getBitmap() 215 { 216 return this->mBitmap; 217 } 218 219 void setZoom( float zoom ) 220 { 221 this->mZoom = dMax( 1.0f, zoom ); 222 223 this->mCamera[ dCanvas::dXPos ] = 0; 224 this->mCamera[ dCanvas::dYPos ] = 0; 225 this->mCamera[ dCanvas::dWidth ] = int( float( this->mBitmapSize[ dCanvas::dWidth ] ) / this->mZoom ); 226 this->mCamera[ dCanvas::dHeight ] = int( float( this->mBitmapSize[ dCanvas::dHeight ] ) / this->mZoom ); 227 } 228 229 void setWrap( bool wrap ) 230 { 231 this->mWrap = wrap; 232 } 233 234 void doScroll( int dx, int dy ) 235 { 236 if ( dx != 0 ) 237 { 238 this->mCamera[ dCanvas::dXPos ] += dx; 239 240 if ( this->mWrap ) 241 { 242 if ( this->mCamera[ dCanvas::dXPos ] < 0 ) 243 { 244 this->mCamera[ dCanvas::dXPos ] += this->mBitmapSize[ dCanvas::dWidth ]; 245 } 246 247 if ( this->mCamera[ dCanvas::dXPos ] >= this->mBitmapSize[ dCanvas::dWidth ] ) 248 { 249 this->mCamera[ dCanvas::dXPos ] -= this->mBitmapSize[ dCanvas::dWidth ]; 250 } 251 } 252 else 253 { 254 this->mCamera[ dCanvas::dXPos ] = dMax( 0, dMin( this->mCamera[ dCanvas::dXPos ], this->mBitmapSize[ dCanvas::dWidth ] - this->mCamera[ dCanvas::dWidth ] ) ); 255 } 256 } 257 258 if ( dy != 0 ) 259 { 260 this->mCamera[ dCanvas::dYPos ] += dy; 261 262 if ( this->mWrap ) 263 { 264 if ( this->mCamera[ dCanvas::dYPos ] < 0 ) 265 { 266 this->mCamera[ dCanvas::dYPos ] += this->mBitmapSize[ dCanvas::dHeight ]; 267 } 268 269 if ( this->mCamera[ dCanvas::dYPos ] >= this->mBitmapSize[ dCanvas::dHeight ] ) 270 { 271 this->mCamera[ dCanvas::dYPos ] -= this->mBitmapSize[ dCanvas::dHeight ]; 272 } 273 } 274 else 275 { 276 this->mCamera[ dCanvas::dYPos ] = dMax( 0, dMin( this->mCamera[ dCanvas::dYPos ], this->mBitmapSize[ dCanvas::dHeight ] - this->mCamera[ dCanvas::dHeight ] ) ); 277 } 278 } 279 } 280 281 void convertMouseCoordinates( ALLEGRO_DISPLAY *display, int &x, int &y ) 282 { 283 x = ( x * this->mBitmapSize[ dCanvas::dWidth ] ) / al_get_display_width( display ); 284 y = ( y * this->mBitmapSize[ dCanvas::dHeight ] ) / al_get_display_height( display ); 285 286 if ( ( this->mZoom - 1.0f ) < 0.0000001f ) 287 { 288 x += ( this->mBitmapSize[ dCanvas::dWidth ] - this->mCamera[ dCanvas::dXPos ] ); 289 y += ( this->mBitmapSize[ dCanvas::dHeight ] - this->mCamera[ dCanvas::dYPos ] ); 290 } 291 else 292 { 293 x = ( int( float( x ) / this->mZoom ) ) + this->mCamera[ dCanvas::dXPos ]; 294 y = ( int( float( y ) / this->mZoom ) ) + this->mCamera[ dCanvas::dYPos ]; 295 } 296 297 x %= this->mBitmapSize[ dCanvas::dWidth ]; 298 y %= this->mBitmapSize[ dCanvas::dHeight ]; 299 } 300 301 void setDrawScaledBitmapFunction( void (*drawfunc)( ALLEGRO_BITMAP *bitmap, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, int flags ) ) 302 { 303 this->mDrawBitmap = drawfunc; 304 } 305 306 int getWidth() 307 { 308 return this->mBitmapSize[ dCanvas::dWidth ]; 309 } 310 311 int getHeight() 312 { 313 return this->mBitmapSize[ dCanvas::dHeight ]; 314 } 315 316 private: 317 ALLEGRO_BITMAP *mBitmap; 318 bool mDelete; 319 int mBitmapSize[ 2 ]; 320 int mCamera[ 4 ]; 321 float mZoom; 322 bool mWrap; 323 void (*mDrawBitmap)( ALLEGRO_BITMAP *bitmap, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, int flags ); 324 }; 325} 326 327#endif

There is also a function to convert mouse display coordinates to corresponding position of canvas.

Example:

void dGameView::draw( ALLEGRO_DISPLAY *display )
{
  this->mCanvas->beginDrawing();

  // TODO: do all drawing here

  this->mCanvas->flipToDisplay( display );
}

The advice I'm looking for is if there are any optimizations that I could do to speed things up.

jmasterx

Any particular reason you don't use the graphics card to do your transformations?
https://www.allegro.cc/manual/5/transformations.html

And this for the mouse: http://alleg.sourceforge.net/a5docs/refman/transformations.html#al_transform_coordinates

DanielH

I was reading this article about resolution independence. It said that "using a stretched buffer" ... "is preferred if your game is using a lot of bitmaps."

Still trying to wrap my head about the whole resolution independence idea.

jmasterx

I wonder where that conclusion came from.

If you use transformations, everything you draw is sent to the graphics card, then the graphics card multiplies the view matrix by your transformation matrix to figure out where the fragments will be in screen space.

Using a stretched buffer I would fear would drive a game closer to being fill limited. Graphics cards have a limit to the number of pixels they can fill. So first you 'fill' your buffer, then you blit that, effectivly doing tripple buffering amd filling a lot more pixels.

Thomas Fjellstrom
jmasterx said:

Using a stretched buffer I would fear would drive a game closer to being fill limited. Graphics cards have a limit to the number of pixels they can fill. So first you 'fill' your buffer, then you blit that, effectivly doing tripple buffering amd filling a lot more pixels.

Allegro 5 uses geometry and tex coords to stretch bitmaps to the display or textures (most bitmaps) afaik. So I don't think it's much different than using transforms.

Thread #613281. Printed from Allegro.cc