1 /* 2 * Copyright 2014 Stephan Aßmus <superstippi@gmx.de> 3 * All rights reserved. Distributed under the terms of the MIT license. 4 */ 5 6 7 #include <algorithm> 8 #include <stdio.h> 9 #include <string.h> 10 11 #include <Application.h> 12 #include <Bitmap.h> 13 #include <GradientLinear.h> 14 #include <Message.h> 15 #include <Picture.h> 16 #include <LayoutBuilder.h> 17 #include <List.h> 18 #include <PopUpMenu.h> 19 #include <Resources.h> 20 #include <Roster.h> 21 #include <ScrollView.h> 22 #include <String.h> 23 #include <StringView.h> 24 #include <View.h> 25 #include <Window.h> 26 27 28 static const char* kAppSignature = "application/x.vnd-Haiku.Transformation"; 29 30 31 class Test { 32 public: 33 Test(const char* name); 34 virtual ~Test(); 35 36 const char* Name() const 37 { return fName.String(); } 38 39 virtual void Draw(BView* view, BRect updateRect) = 0; 40 41 private: 42 BString fName; 43 }; 44 45 46 Test::Test(const char* name) 47 : 48 fName(name) 49 { 50 } 51 52 53 Test::~Test() 54 { 55 } 56 57 58 // #pragma mark - TestView 59 60 61 class TestView : public BView { 62 public: 63 TestView(); 64 virtual ~TestView(); 65 66 virtual void Draw(BRect updateRect); 67 68 void SetTest(Test* test); 69 70 private: 71 Test* fTest; 72 }; 73 74 75 TestView::TestView() 76 : 77 BView(NULL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 78 fTest(NULL) 79 { 80 } 81 82 83 TestView::~TestView() 84 { 85 } 86 87 88 void 89 TestView::Draw(BRect updateRect) 90 { 91 if (fTest != NULL) 92 fTest->Draw(this, updateRect); 93 } 94 95 96 void 97 TestView::SetTest(Test* test) 98 { 99 fTest = test; 100 Invalidate(); 101 } 102 103 104 // #pragma mark - TestWindow 105 106 107 enum { 108 MSG_SELECT_TEST = 'stst' 109 }; 110 111 112 class TestWindow : public BWindow { 113 public: 114 TestWindow(); 115 virtual ~TestWindow(); 116 117 virtual void MessageReceived(BMessage* message); 118 119 void AddTest(Test* test); 120 void SetToTest(int32 index); 121 122 private: 123 TestView* fTestView; 124 125 BMenuField* fTestSelectionField; 126 127 BList fTests; 128 }; 129 130 131 TestWindow::TestWindow() 132 : 133 BWindow(BRect(50.0, 50.0, 450.0, 250.0), "Transformations Test", 134 B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE 135 | B_AUTO_UPDATE_SIZE_LIMITS) 136 { 137 fTestView = new TestView(); 138 139 BScrollView* scrollView = new BScrollView("scroll", fTestView, 0, true, 140 true); 141 142 fTestSelectionField = new BMenuField("test selection", 143 "Select test:", new BPopUpMenu("select")); 144 145 BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f) 146 .AddGroup(B_HORIZONTAL) 147 .Add(fTestSelectionField) 148 .AddGlue() 149 .SetInsets(B_USE_DEFAULT_SPACING) 150 .End() 151 .Add(scrollView) 152 ; 153 } 154 155 156 TestWindow::~TestWindow() 157 { 158 for (int32 i = fTests.CountItems() - 1; i >= 0; i++) 159 delete (Test*)fTests.ItemAt(i); 160 } 161 162 163 void 164 TestWindow::MessageReceived(BMessage* message) 165 { 166 switch (message->what) { 167 case MSG_SELECT_TEST: 168 { 169 int32 index; 170 if (message->FindInt32("index", &index) == B_OK) 171 SetToTest(index); 172 break; 173 } 174 175 default: 176 BWindow::MessageReceived(message); 177 } 178 } 179 180 181 void 182 TestWindow::AddTest(Test* test) 183 { 184 if (test == NULL || fTests.HasItem(test)) 185 return; 186 187 if (!fTests.AddItem(test)) { 188 delete test; 189 return; 190 } 191 192 BMessage* message = new BMessage(MSG_SELECT_TEST); 193 message->AddInt32("index", fTests.CountItems() - 1); 194 195 BMenuItem* item = new BMenuItem(test->Name(), message); 196 if (!fTestSelectionField->Menu()->AddItem(item)) { 197 fTests.RemoveItem(fTests.CountItems() - 1); 198 delete test; 199 delete item; 200 return; 201 } 202 203 if (fTests.CountItems() == 1) 204 SetToTest(0); 205 } 206 207 208 void 209 TestWindow::SetToTest(int32 index) 210 { 211 Test* test = (Test*)fTests.ItemAt(index); 212 if (test == NULL) 213 return; 214 215 fTestSelectionField->Menu()->ItemAt(index)->SetMarked(true); 216 217 fTestView->SetTest(test); 218 } 219 220 221 // #pragma mark - Test1 222 223 224 class RectsTest : public Test { 225 public: 226 RectsTest() 227 : 228 Test("Rects") 229 { 230 } 231 232 virtual void Draw(BView* view, BRect updateRect) 233 { 234 view->DrawString("Rects", BPoint(20, 30)); 235 236 view->SetDrawingMode(B_OP_ALPHA); 237 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 238 239 BRect rect(view->Bounds()); 240 rect.InsetBy(rect.Width() / 3, rect.Height() / 3); 241 BPoint center( 242 rect.left + rect.Width() / 2, 243 rect.top + rect.Height() / 2); 244 245 for (int32 i = 0; i < 360; i += 40) { 246 BAffineTransform transform; 247 transform.RotateBy(center, i * M_PI / 180.0); 248 view->SetTransform(transform); 249 250 view->SetHighColor(51, 151, 255, 20); 251 view->FillRect(rect); 252 253 view->SetHighColor(51, 255, 151, 180); 254 view->DrawString("Rect", center); 255 } 256 } 257 }; 258 259 260 // #pragma mark - BitmapTest 261 262 263 class BitmapTest : public Test { 264 public: 265 BitmapTest() 266 : 267 Test("Bitmap"), 268 fBitmap(_LoadBitmap(555)) 269 { 270 } 271 272 virtual void Draw(BView* view, BRect updateRect) 273 { 274 BRect rect(view->Bounds()); 275 276 if (fBitmap == NULL) { 277 view->SetHighColor(255, 0, 0); 278 view->FillRect(rect); 279 view->SetHighColor(0, 0, 0); 280 view->DrawString("Failed to load the bitmap.", BPoint(20, 20)); 281 return; 282 } 283 284 rect.left = (rect.Width() - fBitmap->Bounds().Width()) / 2; 285 rect.top = (rect.Height() - fBitmap->Bounds().Height()) / 2; 286 rect.right = rect.left + fBitmap->Bounds().Width(); 287 rect.bottom = rect.top + fBitmap->Bounds().Height(); 288 289 BPoint center( 290 rect.left + rect.Width() / 2, 291 rect.top + rect.Height() / 2); 292 293 BPicture picture; 294 view->BeginPicture(&picture); 295 view->SetDrawingMode(B_OP_ALPHA); 296 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 297 BFont font; 298 view->GetFont(&font); 299 font.SetSize(70); 300 view->SetFont(&font); 301 view->SetHighColor(0, 0, 0, 80); 302 view->FillRect(view->Bounds()); 303 view->SetHighColor(0, 0, 0, 255); 304 view->DrawString("CLIPPING", BPoint(0, center.y + 35)); 305 view->EndPicture(); 306 307 view->ClipToPicture(&picture); 308 309 BAffineTransform transform; 310 transform.RotateBy(center, 30 * M_PI / 180.0); 311 view->SetTransform(transform); 312 313 view->DrawBitmap(fBitmap, fBitmap->Bounds(), rect); 314 } 315 316 private: 317 status_t 318 _GetAppResources(BResources& resources) const 319 { 320 app_info info; 321 status_t status = be_app->GetAppInfo(&info); 322 if (status != B_OK) 323 return status; 324 325 return resources.SetTo(&info.ref); 326 } 327 328 329 BBitmap* _LoadBitmap(int resourceID) const 330 { 331 BResources resources; 332 status_t status = _GetAppResources(resources); 333 if (status != B_OK) 334 return NULL; 335 336 size_t dataSize; 337 const void* data = resources.LoadResource(B_MESSAGE_TYPE, resourceID, 338 &dataSize); 339 if (data == NULL) 340 return NULL; 341 342 BMemoryIO stream(data, dataSize); 343 344 // Try to read as an archived bitmap. 345 BMessage archive; 346 status = archive.Unflatten(&stream); 347 if (status != B_OK) 348 return NULL; 349 350 BBitmap* bitmap = new BBitmap(&archive); 351 352 status = bitmap->InitCheck(); 353 if (status != B_OK) { 354 delete bitmap; 355 bitmap = NULL; 356 } 357 358 return bitmap; 359 } 360 361 private: 362 BBitmap* fBitmap; 363 }; 364 365 366 // #pragma mark - Gradient 367 368 369 class GradientTest : public Test { 370 public: 371 GradientTest() 372 : 373 Test("Gradient") 374 { 375 } 376 377 virtual void Draw(BView* view, BRect updateRect) 378 { 379 BRect rect(view->Bounds()); 380 rect.InsetBy(rect.Width() / 3, rect.Height() / 3); 381 BPoint center( 382 rect.left + rect.Width() / 2, 383 rect.top + rect.Height() / 2); 384 385 BAffineTransform transform; 386 transform.RotateBy(center, 30.0 * M_PI / 180.0); 387 view->SetTransform(transform); 388 389 rgb_color top = (rgb_color){ 255, 255, 0, 255 }; 390 rgb_color bottom = (rgb_color){ 0, 255, 255, 255 }; 391 392 BGradientLinear gradient; 393 gradient.AddColor(top, 0.0f); 394 gradient.AddColor(bottom, 255.0f); 395 gradient.SetStart(rect.LeftTop()); 396 gradient.SetEnd(rect.LeftBottom()); 397 398 float radius = std::min(rect.Width() / 5, rect.Height() / 5); 399 400 view->FillRoundRect(rect, radius, radius, gradient); 401 } 402 }; 403 404 405 // #pragma mark - NestedStates 406 407 408 class NestedStatesTest : public Test { 409 public: 410 NestedStatesTest() 411 : 412 Test("Nested view states") 413 { 414 } 415 416 virtual void Draw(BView* view, BRect updateRect) 417 { 418 BAffineTransform transform; 419 transform.RotateBy(BPoint(100, 100), 30.0 * M_PI / 180.0); 420 view->SetTransform(transform); 421 422 rgb_color top = (rgb_color){ 255, 0, 0, 255 }; 423 rgb_color bottom = (rgb_color){ 255, 255, 0, 255 }; 424 425 BRect rect(20, 20, 120, 120); 426 427 BGradientLinear gradient; 428 gradient.AddColor(top, 0.0f); 429 gradient.AddColor(bottom, 255.0f); 430 gradient.SetStart(rect.LeftTop()); 431 gradient.SetEnd(rect.LeftBottom()); 432 433 view->FillRoundRect(rect, 20, 20, gradient); 434 435 view->PushState(); 436 // Should be in the same place! 437 view->StrokeRoundRect(rect, 20, 20); 438 439 // Now rotated by another 30 degree 440 view->SetTransform(transform); 441 442 view->SetDrawingMode(B_OP_ALPHA); 443 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 444 view->SetHighColor(0, 0, 255, 120); 445 view->FillRoundRect(rect, 20, 20); 446 447 view->PopState(); 448 } 449 }; 450 451 452 // #pragma mark - 453 454 455 int 456 main(int argc, char** argv) 457 { 458 BApplication app(kAppSignature); 459 460 TestWindow* window = new TestWindow(); 461 462 window->AddTest(new RectsTest()); 463 window->AddTest(new BitmapTest()); 464 window->AddTest(new GradientTest()); 465 window->AddTest(new NestedStatesTest()); 466 467 window->SetToTest(3); 468 window->Show(); 469 470 app.Run(); 471 return 0; 472 } 473