1 // main.cpp 2 3 #include <math.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #include <Application.h> 9 #include <Bitmap.h> 10 #include <Button.h> 11 #include <Message.h> 12 #include <MessageRunner.h> 13 #include <Messenger.h> 14 #include <View.h> 15 #include <Window.h> 16 17 #include "bitmap.h" 18 19 enum { 20 MSG_RESET = 'rset', 21 MSG_TICK = 'tick', 22 }; 23 24 #define SPEED 2.0 25 26 // random_number_between 27 float 28 random_number_between(float v1, float v2) 29 { 30 if (v1 < v2) 31 return v1 + fmod(rand() / 1000.0, (v2 - v1)); 32 else if (v2 < v1) 33 return v2 + fmod(rand() / 1000.0, (v1 - v2)); 34 return v1; 35 } 36 37 // TestView 38 class TestView : public BView { 39 40 public: 41 TestView(BRect frame, const char* name, 42 uint32 resizeFlags, uint32 flags); 43 44 virtual void AttachedToWindow(); 45 virtual void MessageReceived(BMessage* message); 46 47 virtual void Draw(BRect updateRect); 48 49 virtual void MouseDown(BPoint where); 50 virtual void MouseUp(BPoint where); 51 virtual void MouseMoved(BPoint where, uint32 transit, 52 const BMessage* dragMessage); 53 54 private: 55 void _ResetRect(); 56 void _InvalidateBitmapRect(BRect r); 57 void _DrawCross(BPoint where, rgb_color c); 58 59 struct point { 60 double x; 61 double y; 62 double direction_x; 63 double direction_y; 64 }; 65 66 void _FillBitmap(point* polygon); 67 void _InitPolygon(const BRect& b, point* polygon) const; 68 void _MorphPolygon(const BRect& b, point* polygon); 69 70 BBitmap* fBitmap; 71 BView* fOffscreenView; 72 BMessageRunner* fTicker; 73 BRect fBitmapRect; 74 75 enum { 76 TRACKING_NONE = 0, 77 78 TRACKING_LEFT, 79 TRACKING_RIGHT, 80 TRACKING_TOP, 81 TRACKING_BOTTOM, 82 83 TRACKING_LEFT_TOP, 84 TRACKING_RIGHT_TOP, 85 TRACKING_LEFT_BOTTOM, 86 TRACKING_RIGHT_BOTTOM, 87 88 TRACKING_ALL 89 }; 90 91 uint32 fTracking; 92 BPoint fLastMousePos; 93 94 point fPolygon[4]; 95 }; 96 97 // constructor 98 TestView::TestView(BRect frame, const char* name, 99 uint32 resizeFlags, uint32 flags) 100 : BView(frame, name, resizeFlags, flags), 101 // fBitmap(new BBitmap(BRect(0, 0, kBitmapWidth - 1, kBitmapHeight -1), 0, kBitmapFormat)), 102 // fBitmap(new BBitmap(BRect(0, 0, 32 - 1, 8 - 1), 0, B_CMAP8)), 103 // fBitmap(new BBitmap(BRect(0, 0, 32 - 1, 8 - 1), 0, B_GRAY8)), 104 fBitmap(new BBitmap(BRect(0, 0, 99, 99), B_RGB32, true)), 105 // fBitmap(new BBitmap(BRect(0, 0, 99, 99), B_CMAP8, true)), 106 // fBitmap(new BBitmap(BRect(0, 0, 31, 31), B_GRAY8, true)), 107 fOffscreenView(new BView(fBitmap->Bounds(), "Offscreen view", 108 B_FOLLOW_ALL, B_WILL_DRAW | B_SUBPIXEL_PRECISE)), 109 fTicker(NULL), 110 fBitmapRect(), 111 fTracking(TRACKING_NONE), 112 fLastMousePos(-1.0, -1.0) 113 { 114 SetViewColor(B_TRANSPARENT_COLOR); 115 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 116 // uint32 size = min_c((uint32)fBitmap->BitsLength(), sizeof(kBitmapBits)); 117 // memcpy(fBitmap->Bits(), kBitmapBits, size); 118 /* uint8* bits = (uint8*)fBitmap->Bits(); 119 uint32 width = fBitmap->Bounds().IntegerWidth() + 1; 120 uint32 height = fBitmap->Bounds().IntegerHeight() + 1; 121 uint32 bpr = fBitmap->BytesPerRow(); 122 printf("width: %ld, height: %ld, bpr: %ld\n", width, height, bpr); 123 int32 index = 0; 124 for (uint32 y = 0; y < height; y++) { 125 uint8* h = bits; 126 for (uint32 x = 0; x < width; x++) { 127 *h = index++; 128 h++; 129 } 130 bits += bpr; 131 } 132 BRect a(0.0, 10.0, 20.0, 10.0); 133 BRect b(0.0, 10.0, 10.0, 30.0); 134 printf("Intersects: %d\n", a.Intersects(b));*/ 135 if (fBitmap->Lock()) { 136 fBitmap->AddChild(fOffscreenView); 137 fOffscreenView->SetHighColor(255, 0, 0); 138 fBitmap->Unlock(); 139 } 140 141 srand((long int)system_time()); 142 _InitPolygon(fBitmap->Bounds(), fPolygon); 143 144 _ResetRect(); 145 } 146 147 // AttachedToWindow 148 void 149 TestView::AttachedToWindow() 150 { 151 BMessenger mess(this, Window()); 152 BMessage msg(MSG_TICK); 153 fTicker = new BMessageRunner(mess, &msg, 40000LL); 154 } 155 156 // MessageReceived 157 void 158 TestView::MessageReceived(BMessage* message) 159 { 160 switch (message->what) { 161 case MSG_RESET: { 162 BRect old = fBitmapRect; 163 _ResetRect(); 164 _InvalidateBitmapRect(old | fBitmapRect); 165 break; 166 } 167 case MSG_TICK: 168 _MorphPolygon(fBitmap->Bounds(), fPolygon); 169 _FillBitmap(fPolygon); 170 Invalidate(fBitmapRect); 171 break; 172 default: 173 BView::MessageReceived(message); 174 break; 175 } 176 } 177 178 // Draw 179 void 180 TestView::Draw(BRect updateRect) 181 { 182 // background arround bitmap 183 BRect topOfBitmap(updateRect.left, updateRect.top, updateRect.right, fBitmapRect.top - 1); 184 if (topOfBitmap.IsValid()) 185 FillRect(topOfBitmap, B_SOLID_LOW); 186 187 BRect leftOfBitmap(updateRect.left, fBitmapRect.top, fBitmapRect.left - 1, fBitmapRect.bottom); 188 if (leftOfBitmap.IsValid()) 189 FillRect(leftOfBitmap, B_SOLID_LOW); 190 191 BRect rightOfBitmap(fBitmapRect.right + 1, fBitmapRect.top, updateRect.right, fBitmapRect.bottom); 192 if (rightOfBitmap.IsValid()) 193 FillRect(rightOfBitmap, B_SOLID_LOW); 194 195 BRect bottomOfBitmap(updateRect.left, fBitmapRect.bottom + 1, updateRect.right, updateRect.bottom); 196 if (bottomOfBitmap.IsValid()) 197 FillRect(bottomOfBitmap, B_SOLID_LOW); 198 199 // bitmap 200 DrawBitmap(fBitmap, fBitmap->Bounds(), fBitmapRect); 201 202 // indicate the frame to see any errors in the drawing code 203 rgb_color red = (rgb_color){ 255, 0, 0, 255 }; 204 _DrawCross(fBitmapRect.LeftTop() + BPoint(-1.0, -1.0), red); 205 _DrawCross(fBitmapRect.RightTop() + BPoint(1.0, -1.0), red); 206 _DrawCross(fBitmapRect.LeftBottom() + BPoint(-1.0, 1.0), red); 207 _DrawCross(fBitmapRect.RightBottom() + BPoint(1.0, 1.0), red); 208 209 // text 210 SetDrawingMode(B_OP_ALPHA); 211 const char* message = "Click and drag to move and resize the bitmap!"; 212 BPoint textPos(20.0, 30.0); 213 SetHighColor(255, 255, 255, 180); 214 DrawString(message, textPos); 215 SetHighColor(0, 0, 0, 180); 216 DrawString(message, textPos + BPoint(-1.0, -1.0)); 217 } 218 219 // hit_test 220 bool 221 hit_test(BPoint where, BPoint p) 222 { 223 BRect r(p, p); 224 r.InsetBy(-5.0, -5.0); 225 return r.Contains(where); 226 } 227 228 // hit_test 229 bool 230 hit_test(BPoint where, BPoint a, BPoint b) 231 { 232 BRect r(a, b); 233 if (a.x == b.x) 234 r.InsetBy(-3.0, 0.0); 235 else 236 r.InsetBy(0.0, -3.0); 237 return r.Contains(where); 238 } 239 240 // MouseDown 241 void 242 TestView::MouseDown(BPoint where) 243 { 244 fTracking = TRACKING_NONE; 245 246 // check if we hit a corner 247 if (hit_test(where, fBitmapRect.LeftTop())) 248 fTracking = TRACKING_LEFT_TOP; 249 else if (hit_test(where, fBitmapRect.RightTop())) 250 fTracking = TRACKING_RIGHT_TOP; 251 else if (hit_test(where, fBitmapRect.LeftBottom())) 252 fTracking = TRACKING_LEFT_BOTTOM; 253 else if (hit_test(where, fBitmapRect.RightBottom())) 254 fTracking = TRACKING_RIGHT_BOTTOM; 255 // check if we hit a side 256 else if (hit_test(where, fBitmapRect.LeftTop(), fBitmapRect.RightTop())) 257 fTracking = TRACKING_TOP; 258 else if (hit_test(where, fBitmapRect.LeftTop(), fBitmapRect.LeftBottom())) 259 fTracking = TRACKING_LEFT; 260 else if (hit_test(where, fBitmapRect.RightTop(), fBitmapRect.RightBottom())) 261 fTracking = TRACKING_RIGHT; 262 else if (hit_test(where, fBitmapRect.LeftBottom(), fBitmapRect.RightBottom())) 263 fTracking = TRACKING_BOTTOM; 264 // check if we hit inside the rect 265 else if (fBitmapRect.Contains(where)) 266 fTracking = TRACKING_ALL; 267 268 fLastMousePos = where; 269 } 270 271 // MouseUp 272 void 273 TestView::MouseUp(BPoint where) 274 { 275 fTracking = TRACKING_NONE; 276 } 277 278 // MouseMoved 279 void 280 TestView::MouseMoved(BPoint where, uint32 transit, 281 const BMessage* dragMessage) 282 { 283 if (fTracking > TRACKING_NONE) { 284 BRect old = fBitmapRect; 285 BPoint offset = where - fLastMousePos; 286 switch (fTracking) { 287 case TRACKING_LEFT_TOP: 288 fBitmapRect.Set(fBitmapRect.left + offset.x, 289 fBitmapRect.top + offset.y, 290 fBitmapRect.right, 291 fBitmapRect.bottom); 292 break; 293 case TRACKING_RIGHT_BOTTOM: 294 fBitmapRect.Set(fBitmapRect.left, 295 fBitmapRect.top, 296 fBitmapRect.right + offset.x, 297 fBitmapRect.bottom + offset.y); 298 break; 299 case TRACKING_LEFT_BOTTOM: 300 fBitmapRect.Set(fBitmapRect.left + offset.x, 301 fBitmapRect.top, 302 fBitmapRect.right, 303 fBitmapRect.bottom + offset.y); 304 break; 305 case TRACKING_RIGHT_TOP: 306 fBitmapRect.Set(fBitmapRect.left, 307 fBitmapRect.top + offset.y, 308 fBitmapRect.right + offset.x, 309 fBitmapRect.bottom); 310 break; 311 case TRACKING_LEFT: 312 fBitmapRect.Set(fBitmapRect.left + offset.x, 313 fBitmapRect.top, 314 fBitmapRect.right, 315 fBitmapRect.bottom); 316 break; 317 case TRACKING_TOP: 318 fBitmapRect.Set(fBitmapRect.left, 319 fBitmapRect.top + offset.y, 320 fBitmapRect.right, 321 fBitmapRect.bottom); 322 break; 323 case TRACKING_RIGHT: 324 fBitmapRect.Set(fBitmapRect.left, 325 fBitmapRect.top, 326 fBitmapRect.right + offset.x, 327 fBitmapRect.bottom); 328 break; 329 case TRACKING_BOTTOM: 330 fBitmapRect.Set(fBitmapRect.left, 331 fBitmapRect.top, 332 fBitmapRect.right, 333 fBitmapRect.bottom + offset.y); 334 break; 335 case TRACKING_ALL: 336 default: 337 fBitmapRect.OffsetBy(offset); 338 break; 339 } 340 fLastMousePos = where; 341 if (old != fBitmapRect) 342 _InvalidateBitmapRect(old | fBitmapRect); 343 } 344 } 345 346 // _ResetRect 347 void 348 TestView::_ResetRect() 349 { 350 fBitmapRect = fBitmap->Bounds(); 351 fBitmapRect.OffsetBy(floorf((Bounds().Width() - fBitmapRect.Width()) / 2.0 + 0.5), 352 floorf((Bounds().Height() - fBitmapRect.Height()) / 2.0 + 0.5)); 353 } 354 355 // _InvalidateBitmapRect 356 void 357 TestView::_InvalidateBitmapRect(BRect r) 358 { 359 r.InsetBy(-4.0, -4.0); 360 Invalidate(r); 361 } 362 363 // _DrawCross 364 void 365 TestView::_DrawCross(BPoint where, rgb_color c) 366 { 367 BeginLineArray(4); 368 AddLine(BPoint(where.x, where.y - 3), 369 BPoint(where.x, where.y - 1), c); 370 AddLine(BPoint(where.x, where.y + 1), 371 BPoint(where.x, where.y + 3), c); 372 AddLine(BPoint(where.x - 3, where.y), 373 BPoint(where.x - 1, where.y), c); 374 AddLine(BPoint(where.x + 1, where.y), 375 BPoint(where.x + 3, where.y), c); 376 EndLineArray(); 377 } 378 379 // _FillBitmap 380 void 381 TestView::_FillBitmap(point* polygon) 382 { 383 if (fBitmap->Lock()) { 384 fOffscreenView->SetDrawingMode(B_OP_COPY); 385 fOffscreenView->FillRect(fOffscreenView->Bounds(), B_SOLID_LOW); 386 387 fOffscreenView->SetDrawingMode(B_OP_OVER); 388 389 fOffscreenView->StrokeLine(BPoint(polygon[0].x, polygon[0].y), 390 BPoint(polygon[1].x, polygon[1].y)); 391 fOffscreenView->StrokeLine(BPoint(polygon[1].x, polygon[1].y), 392 BPoint(polygon[2].x, polygon[2].y)); 393 fOffscreenView->StrokeLine(BPoint(polygon[2].x, polygon[2].y), 394 BPoint(polygon[3].x, polygon[3].y)); 395 fOffscreenView->StrokeLine(BPoint(polygon[3].x, polygon[3].y), 396 BPoint(polygon[0].x, polygon[0].y)); 397 398 fOffscreenView->Sync(); 399 fBitmap->Unlock(); 400 } 401 } 402 403 // _InitPolygon 404 void 405 TestView::_InitPolygon(const BRect& b, point* polygon) const 406 { 407 polygon[0].x = b.left; 408 polygon[0].y = b.top; 409 polygon[0].direction_x = random_number_between(-SPEED, SPEED); 410 polygon[0].direction_y = random_number_between(-SPEED, SPEED); 411 polygon[1].x = b.right; 412 polygon[1].y = b.top; 413 polygon[1].direction_x = random_number_between(-SPEED, SPEED); 414 polygon[1].direction_y = random_number_between(-SPEED, SPEED); 415 polygon[2].x = b.right; 416 polygon[2].y = b.bottom; 417 polygon[2].direction_x = random_number_between(-SPEED, SPEED); 418 polygon[2].direction_y = random_number_between(-SPEED, SPEED); 419 polygon[3].x = b.left; 420 polygon[3].y = b.bottom; 421 polygon[3].direction_x = random_number_between(-SPEED, SPEED); 422 polygon[3].direction_y = random_number_between(-SPEED, SPEED); 423 } 424 425 // morph 426 inline void 427 morph(double* value, double* direction, double min, double max) 428 { 429 *value += *direction; 430 if (*value < min) { 431 *value = min; 432 *direction = -*direction; 433 } else if (*value > max) { 434 *value = max; 435 *direction = -*direction; 436 } 437 } 438 439 // _MorphPolygon 440 void 441 TestView::_MorphPolygon(const BRect& b, point* polygon) 442 { 443 morph(&polygon[0].x, &polygon[0].direction_x, b.left, b.right); 444 morph(&polygon[1].x, &polygon[1].direction_x, b.left, b.right); 445 morph(&polygon[2].x, &polygon[2].direction_x, b.left, b.right); 446 morph(&polygon[3].x, &polygon[3].direction_x, b.left, b.right); 447 morph(&polygon[0].y, &polygon[0].direction_y, b.top, b.bottom); 448 morph(&polygon[1].y, &polygon[1].direction_y, b.top, b.bottom); 449 morph(&polygon[2].y, &polygon[2].direction_y, b.top, b.bottom); 450 morph(&polygon[3].y, &polygon[3].direction_y, b.top, b.bottom); 451 } 452 453 454 // show_window 455 void 456 show_window(BRect frame, const char* name) 457 { 458 BWindow* window = new BWindow(frame, name, 459 B_TITLED_WINDOW, 460 B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); 461 462 BView* view = new TestView(window->Bounds(), "test", B_FOLLOW_ALL, 463 B_WILL_DRAW/* | B_FULL_UPDATE_ON_RESIZE*/); 464 465 window->AddChild(view); 466 BRect b(0.0, 0.0, 60.0, 15.0); 467 b.OffsetTo(5.0, view->Bounds().bottom - (b.Height() + 15.0)); 468 BButton* control = new BButton(b, "button", "Reset", new BMessage(MSG_RESET), 469 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 470 view->AddChild(control); 471 control->SetTarget(view); 472 control->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 473 474 window->Show(); 475 } 476 477 // main 478 int 479 main(int argc, char** argv) 480 { 481 BApplication* app = new BApplication("application/x.vnd-Haiku.BitmapDrawing"); 482 483 BRect frame(50.0, 50.0, 400.0, 250.0); 484 show_window(frame, "Bitmap Drawing"); 485 486 app->Run(); 487 488 delete app; 489 return 0; 490 } 491