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 double velocity_x; 65 double velocity_y; 66 }; 67 struct color_cycle { 68 uint8 value; 69 double direction; 70 }; 71 72 void _FillBitmap(point* polygon); 73 void _InitPolygon(const BRect& b, point* polygon) const; 74 void _InitColor(color_cycle* color) const; 75 void _MorphPolygon(const BRect& b, point* polygon); 76 void _MorphColor(color_cycle* color); 77 78 BBitmap* fBitmap; 79 BView* fOffscreenView; 80 BMessageRunner* fTicker; 81 BRect fBitmapRect; 82 83 enum { 84 TRACKING_NONE = 0, 85 86 TRACKING_LEFT, 87 TRACKING_RIGHT, 88 TRACKING_TOP, 89 TRACKING_BOTTOM, 90 91 TRACKING_LEFT_TOP, 92 TRACKING_RIGHT_TOP, 93 TRACKING_LEFT_BOTTOM, 94 TRACKING_RIGHT_BOTTOM, 95 96 TRACKING_ALL 97 }; 98 99 uint32 fTracking; 100 BPoint fLastMousePos; 101 102 point fPolygon[4]; 103 color_cycle fColor[3]; 104 }; 105 106 // constructor 107 TestView::TestView(BRect frame, const char* name, 108 uint32 resizeFlags, uint32 flags) 109 : BView(frame, name, resizeFlags, flags), 110 // fBitmap(new BBitmap(BRect(0, 0, kBitmapWidth - 1, kBitmapHeight -1), 0, kBitmapFormat)), 111 // fBitmap(new BBitmap(BRect(0, 0, 32 - 1, 8 - 1), 0, B_CMAP8)), 112 // fBitmap(new BBitmap(BRect(0, 0, 32 - 1, 8 - 1), 0, B_GRAY8)), 113 fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_RGB32, true)), 114 // fBitmap(new BBitmap(BRect(0, 0, 639, 479), B_RGB32, true)), 115 // fBitmap(new BBitmap(BRect(0, 0, 639, 479), B_CMAP8, true)), 116 // fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_CMAP8, true)), 117 // fBitmap(new BBitmap(BRect(0, 0, 199, 99), B_GRAY8, true)), 118 fOffscreenView(new BView(fBitmap->Bounds(), "Offscreen view", 119 B_FOLLOW_ALL, B_WILL_DRAW | B_SUBPIXEL_PRECISE)), 120 fTicker(NULL), 121 fBitmapRect(), 122 fTracking(TRACKING_NONE), 123 fLastMousePos(-1.0, -1.0) 124 { 125 SetViewColor(B_TRANSPARENT_COLOR); 126 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 127 // uint32 size = min_c((uint32)fBitmap->BitsLength(), sizeof(kBitmapBits)); 128 // memcpy(fBitmap->Bits(), kBitmapBits, size); 129 /* uint8* bits = (uint8*)fBitmap->Bits(); 130 uint32 width = fBitmap->Bounds().IntegerWidth() + 1; 131 uint32 height = fBitmap->Bounds().IntegerHeight() + 1; 132 uint32 bpr = fBitmap->BytesPerRow(); 133 printf("width: %ld, height: %ld, bpr: %ld\n", width, height, bpr); 134 int32 index = 0; 135 for (uint32 y = 0; y < height; y++) { 136 uint8* h = bits; 137 for (uint32 x = 0; x < width; x++) { 138 *h = index++; 139 h++; 140 } 141 bits += bpr; 142 } 143 BRect a(0.0, 10.0, 20.0, 10.0); 144 BRect b(0.0, 10.0, 10.0, 30.0); 145 printf("Intersects: %d\n", a.Intersects(b));*/ 146 if (fBitmap->Lock()) { 147 fBitmap->AddChild(fOffscreenView); 148 fOffscreenView->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 149 fBitmap->Unlock(); 150 } 151 152 srand((long int)system_time()); 153 _InitPolygon(fBitmap->Bounds(), fPolygon); 154 _InitColor(fColor); 155 156 _ResetRect(); 157 } 158 159 // AttachedToWindow 160 void 161 TestView::AttachedToWindow() 162 { 163 BMessenger mess(this, Window()); 164 BMessage msg(MSG_TICK); 165 fTicker = new BMessageRunner(mess, &msg, 40000LL); 166 } 167 168 // MessageReceived 169 void 170 TestView::MessageReceived(BMessage* message) 171 { 172 switch (message->what) { 173 case MSG_RESET: { 174 BRect old = fBitmapRect; 175 _ResetRect(); 176 _InvalidateBitmapRect(old | fBitmapRect); 177 break; 178 } 179 case MSG_TICK: 180 _MorphPolygon(fBitmap->Bounds(), fPolygon); 181 _MorphColor(fColor); 182 _FillBitmap(fPolygon); 183 Invalidate(fBitmapRect); 184 break; 185 default: 186 BView::MessageReceived(message); 187 break; 188 } 189 } 190 191 // Draw 192 void 193 TestView::Draw(BRect updateRect) 194 { 195 SetDrawingMode(B_OP_ALPHA); 196 DrawBitmap(fBitmap, fBitmap->Bounds(), fBitmapRect); 197 198 SetDrawingMode(B_OP_COPY); 199 // background arround bitmap 200 BRect topOfBitmap(updateRect.left, updateRect.top, updateRect.right, fBitmapRect.top - 1); 201 if (topOfBitmap.IsValid()) 202 FillRect(topOfBitmap, B_SOLID_LOW); 203 204 BRect leftOfBitmap(updateRect.left, fBitmapRect.top, fBitmapRect.left - 1, fBitmapRect.bottom); 205 if (leftOfBitmap.IsValid()) 206 FillRect(leftOfBitmap, B_SOLID_LOW); 207 208 BRect rightOfBitmap(fBitmapRect.right + 1, fBitmapRect.top, updateRect.right, fBitmapRect.bottom); 209 if (rightOfBitmap.IsValid()) 210 FillRect(rightOfBitmap, B_SOLID_LOW); 211 212 BRect bottomOfBitmap(updateRect.left, fBitmapRect.bottom + 1, updateRect.right, updateRect.bottom); 213 if (bottomOfBitmap.IsValid()) 214 FillRect(bottomOfBitmap, B_SOLID_LOW); 215 216 // indicate the frame to see any errors in the drawing code 217 rgb_color red = (rgb_color){ 255, 0, 0, 255 }; 218 _DrawCross(fBitmapRect.LeftTop() + BPoint(-1.0, -1.0), red); 219 _DrawCross(fBitmapRect.RightTop() + BPoint(1.0, -1.0), red); 220 _DrawCross(fBitmapRect.LeftBottom() + BPoint(-1.0, 1.0), red); 221 _DrawCross(fBitmapRect.RightBottom() + BPoint(1.0, 1.0), red); 222 223 // text 224 SetDrawingMode(B_OP_ALPHA); 225 const char* message = "Click and drag to move and resize the bitmap!"; 226 BPoint textPos(20.0, 30.0); 227 SetHighColor(255, 255, 255, 180); 228 DrawString(message, textPos); 229 SetHighColor(0, 0, 0, 180); 230 DrawString(message, textPos + BPoint(-1.0, -1.0)); 231 } 232 233 // hit_test 234 bool 235 hit_test(BPoint where, BPoint p) 236 { 237 BRect r(p, p); 238 r.InsetBy(-5.0, -5.0); 239 return r.Contains(where); 240 } 241 242 // hit_test 243 bool 244 hit_test(BPoint where, BPoint a, BPoint b) 245 { 246 BRect r(a, b); 247 if (a.x == b.x) 248 r.InsetBy(-3.0, 0.0); 249 else 250 r.InsetBy(0.0, -3.0); 251 return r.Contains(where); 252 } 253 254 // MouseDown 255 void 256 TestView::MouseDown(BPoint where) 257 { 258 fTracking = TRACKING_NONE; 259 260 // check if we hit a corner 261 if (hit_test(where, fBitmapRect.LeftTop())) 262 fTracking = TRACKING_LEFT_TOP; 263 else if (hit_test(where, fBitmapRect.RightTop())) 264 fTracking = TRACKING_RIGHT_TOP; 265 else if (hit_test(where, fBitmapRect.LeftBottom())) 266 fTracking = TRACKING_LEFT_BOTTOM; 267 else if (hit_test(where, fBitmapRect.RightBottom())) 268 fTracking = TRACKING_RIGHT_BOTTOM; 269 // check if we hit a side 270 else if (hit_test(where, fBitmapRect.LeftTop(), fBitmapRect.RightTop())) 271 fTracking = TRACKING_TOP; 272 else if (hit_test(where, fBitmapRect.LeftTop(), fBitmapRect.LeftBottom())) 273 fTracking = TRACKING_LEFT; 274 else if (hit_test(where, fBitmapRect.RightTop(), fBitmapRect.RightBottom())) 275 fTracking = TRACKING_RIGHT; 276 else if (hit_test(where, fBitmapRect.LeftBottom(), fBitmapRect.RightBottom())) 277 fTracking = TRACKING_BOTTOM; 278 // check if we hit inside the rect 279 else if (fBitmapRect.Contains(where)) 280 fTracking = TRACKING_ALL; 281 282 fLastMousePos = where; 283 } 284 285 // MouseUp 286 void 287 TestView::MouseUp(BPoint where) 288 { 289 fTracking = TRACKING_NONE; 290 } 291 292 // MouseMoved 293 void 294 TestView::MouseMoved(BPoint where, uint32 transit, 295 const BMessage* dragMessage) 296 { 297 if (fTracking > TRACKING_NONE) { 298 BRect old = fBitmapRect; 299 BPoint offset = where - fLastMousePos; 300 switch (fTracking) { 301 case TRACKING_LEFT_TOP: 302 fBitmapRect.Set(fBitmapRect.left + offset.x, 303 fBitmapRect.top + offset.y, 304 fBitmapRect.right, 305 fBitmapRect.bottom); 306 break; 307 case TRACKING_RIGHT_BOTTOM: 308 fBitmapRect.Set(fBitmapRect.left, 309 fBitmapRect.top, 310 fBitmapRect.right + offset.x, 311 fBitmapRect.bottom + offset.y); 312 break; 313 case TRACKING_LEFT_BOTTOM: 314 fBitmapRect.Set(fBitmapRect.left + offset.x, 315 fBitmapRect.top, 316 fBitmapRect.right, 317 fBitmapRect.bottom + offset.y); 318 break; 319 case TRACKING_RIGHT_TOP: 320 fBitmapRect.Set(fBitmapRect.left, 321 fBitmapRect.top + offset.y, 322 fBitmapRect.right + offset.x, 323 fBitmapRect.bottom); 324 break; 325 case TRACKING_LEFT: 326 fBitmapRect.Set(fBitmapRect.left + offset.x, 327 fBitmapRect.top, 328 fBitmapRect.right, 329 fBitmapRect.bottom); 330 break; 331 case TRACKING_TOP: 332 fBitmapRect.Set(fBitmapRect.left, 333 fBitmapRect.top + offset.y, 334 fBitmapRect.right, 335 fBitmapRect.bottom); 336 break; 337 case TRACKING_RIGHT: 338 fBitmapRect.Set(fBitmapRect.left, 339 fBitmapRect.top, 340 fBitmapRect.right + offset.x, 341 fBitmapRect.bottom); 342 break; 343 case TRACKING_BOTTOM: 344 fBitmapRect.Set(fBitmapRect.left, 345 fBitmapRect.top, 346 fBitmapRect.right, 347 fBitmapRect.bottom + offset.y); 348 break; 349 case TRACKING_ALL: 350 default: 351 fBitmapRect.OffsetBy(offset); 352 break; 353 } 354 fLastMousePos = where; 355 if (old != fBitmapRect) 356 _InvalidateBitmapRect(old | fBitmapRect); 357 } 358 } 359 360 // _ResetRect 361 void 362 TestView::_ResetRect() 363 { 364 fBitmapRect = fBitmap->Bounds(); 365 fBitmapRect.OffsetBy(floorf((Bounds().Width() - fBitmapRect.Width()) / 2.0 + 0.5), 366 floorf((Bounds().Height() - fBitmapRect.Height()) / 2.0 + 0.5)); 367 } 368 369 // _InvalidateBitmapRect 370 void 371 TestView::_InvalidateBitmapRect(BRect r) 372 { 373 r.InsetBy(-4.0, -4.0); 374 Invalidate(r); 375 } 376 377 // _DrawCross 378 void 379 TestView::_DrawCross(BPoint where, rgb_color c) 380 { 381 BeginLineArray(4); 382 AddLine(BPoint(where.x, where.y - 3), 383 BPoint(where.x, where.y - 1), c); 384 AddLine(BPoint(where.x, where.y + 1), 385 BPoint(where.x, where.y + 3), c); 386 AddLine(BPoint(where.x - 3, where.y), 387 BPoint(where.x - 1, where.y), c); 388 AddLine(BPoint(where.x + 1, where.y), 389 BPoint(where.x + 3, where.y), c); 390 EndLineArray(); 391 } 392 393 // _FillBitmap 394 void 395 TestView::_FillBitmap(point* polygon) 396 { 397 if (fBitmap->Lock()) { 398 fOffscreenView->SetDrawingMode(B_OP_COPY); 399 fOffscreenView->SetHighColor(0, 0, 0, 30); 400 fOffscreenView->FillRect(fOffscreenView->Bounds()); 401 402 fOffscreenView->SetDrawingMode(B_OP_ALPHA); 403 fOffscreenView->SetHighColor(fColor[0].value, 404 fColor[1].value, 405 fColor[2].value, 406 30); 407 fOffscreenView->SetPenSize(4); 408 fOffscreenView->SetLineMode(B_BUTT_CAP, B_ROUND_JOIN); 409 410 BPoint pointList[4]; 411 pointList[0].x = polygon[0].x; 412 pointList[0].y = polygon[0].y; 413 pointList[1].x = polygon[1].x; 414 pointList[1].y = polygon[1].y; 415 pointList[2].x = polygon[2].x; 416 pointList[2].y = polygon[2].y; 417 pointList[3].x = polygon[3].x; 418 pointList[3].y = polygon[3].y; 419 420 fOffscreenView->StrokePolygon(pointList, 4); 421 422 fOffscreenView->Sync(); 423 fBitmap->Unlock(); 424 } 425 } 426 427 // _InitPolygon 428 void 429 TestView::_InitPolygon(const BRect& b, point* polygon) const 430 { 431 polygon[0].x = b.left; 432 polygon[0].y = b.top; 433 polygon[0].direction_x = random_number_between(-SPEED, SPEED); 434 polygon[0].direction_y = random_number_between(-SPEED, SPEED); 435 polygon[0].velocity_x = 0.0; 436 polygon[0].velocity_y = 0.0; 437 polygon[1].x = b.right; 438 polygon[1].y = b.top; 439 polygon[1].direction_x = random_number_between(-SPEED, SPEED); 440 polygon[1].direction_y = random_number_between(-SPEED, SPEED); 441 polygon[1].velocity_x = 0.0; 442 polygon[1].velocity_y = 0.0; 443 polygon[2].x = b.right; 444 polygon[2].y = b.bottom; 445 polygon[2].direction_x = random_number_between(-SPEED, SPEED); 446 polygon[2].direction_y = random_number_between(-SPEED, SPEED); 447 polygon[2].velocity_x = 0.0; 448 polygon[2].velocity_y = 0.0; 449 polygon[3].x = b.left; 450 polygon[3].y = b.bottom; 451 polygon[3].direction_x = random_number_between(-SPEED, SPEED); 452 polygon[3].direction_y = random_number_between(-SPEED, SPEED); 453 polygon[3].velocity_x = 0.0; 454 polygon[3].velocity_y = 0.0; 455 } 456 457 // _InitColor 458 void 459 TestView::_InitColor(color_cycle* color) const 460 { 461 color[0].value = 0; 462 color[0].direction = random_number_between(-SPEED * 4, SPEED * 4); 463 color[1].value = 0; 464 color[1].direction = random_number_between(-SPEED * 4, SPEED * 4); 465 color[2].value = 0; 466 color[2].direction = random_number_between(-SPEED * 4, SPEED * 4); 467 } 468 469 // morph 470 inline void 471 morph(double* value, double* direction, double* velocity, double min, double max) 472 { 473 *value += *velocity; 474 475 // flip direction if necessary 476 if (*value < min && *direction < 0.0) { 477 *direction = -*direction; 478 } else if (*value > max && *direction > 0.0) { 479 *direction = -*direction; 480 } 481 482 // accelerate velocity 483 if (*direction < 0.0) { 484 if (*velocity > *direction) 485 *velocity += *direction / 10.0; 486 // truncate velocity 487 if (*velocity < *direction) 488 *velocity = *direction; 489 } else { 490 if (*velocity < *direction) 491 *velocity += *direction / 10.0; 492 // truncate velocity 493 if (*velocity > *direction) 494 *velocity = *direction; 495 } 496 } 497 498 // morph 499 inline void 500 morph(uint8* value, double* direction) 501 { 502 int32 v = (int32)(*value + *direction); 503 if (v < 0) { 504 v = 0; 505 *direction = -*direction; 506 } else if (v > 255) { 507 v = 255; 508 *direction = -*direction; 509 } 510 *value = (uint8)v; 511 } 512 513 // _MorphPolygon 514 void 515 TestView::_MorphPolygon(const BRect& b, point* polygon) 516 { 517 morph(&polygon[0].x, &polygon[0].direction_x, &polygon[0].velocity_x, b.left, b.right); 518 morph(&polygon[1].x, &polygon[1].direction_x, &polygon[1].velocity_x, b.left, b.right); 519 morph(&polygon[2].x, &polygon[2].direction_x, &polygon[2].velocity_x, b.left, b.right); 520 morph(&polygon[3].x, &polygon[3].direction_x, &polygon[3].velocity_x, b.left, b.right); 521 morph(&polygon[0].y, &polygon[0].direction_y, &polygon[0].velocity_y, b.top, b.bottom); 522 morph(&polygon[1].y, &polygon[1].direction_y, &polygon[1].velocity_y, b.top, b.bottom); 523 morph(&polygon[2].y, &polygon[2].direction_y, &polygon[2].velocity_y, b.top, b.bottom); 524 morph(&polygon[3].y, &polygon[3].direction_y, &polygon[3].velocity_y, b.top, b.bottom); 525 } 526 527 // _MorphColor 528 void 529 TestView::_MorphColor(color_cycle* color) 530 { 531 morph(&color[0].value, &color[0].direction); 532 morph(&color[1].value, &color[1].direction); 533 morph(&color[2].value, &color[2].direction); 534 } 535 536 // show_window 537 void 538 show_window(BRect frame, const char* name) 539 { 540 BWindow* window = new BWindow(frame, name, 541 B_TITLED_WINDOW, 542 B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); 543 544 BView* view = new TestView(window->Bounds(), "test", B_FOLLOW_ALL, 545 B_WILL_DRAW/* | B_FULL_UPDATE_ON_RESIZE*/); 546 547 window->AddChild(view); 548 BRect b(0.0, 0.0, 60.0, 15.0); 549 b.OffsetTo(5.0, view->Bounds().bottom - (b.Height() + 15.0)); 550 BButton* control = new BButton(b, "button", "Reset", new BMessage(MSG_RESET), 551 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 552 view->AddChild(control); 553 control->SetTarget(view); 554 control->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 555 556 window->Show(); 557 } 558 559 // main 560 int 561 main(int argc, char** argv) 562 { 563 BApplication* app = new BApplication("application/x.vnd-Haiku.BitmapDrawing"); 564 565 // BRect frame(10.0, 30.0, 790.0, 590.0); 566 BRect frame(10.0, 30.0, 330.0, 220.0); 567 show_window(frame, "BitmapDrawing"); 568 569 app->Run(); 570 571 delete app; 572 return 0; 573 } 574