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 <Picture.h> 15 #include <Region.h> 16 #include <Resources.h> 17 #include <Roster.h> 18 #include <String.h> 19 #include <StringView.h> 20 21 #include "harness.h" 22 23 static const char* kAppSignature = "application/x-vnd.Haiku-Transformation"; 24 25 26 class BitmapTest : public Test { 27 public: 28 BitmapTest(const char* name) 29 : 30 Test(name), 31 fBitmap(_LoadBitmap(555)) 32 { 33 } 34 35 private: 36 status_t 37 _GetAppResources(BResources& resources) const 38 { 39 app_info info; 40 status_t status = be_app->GetAppInfo(&info); 41 if (status != B_OK) 42 return status; 43 44 return resources.SetTo(&info.ref); 45 } 46 47 48 BBitmap* _LoadBitmap(int resourceID) const 49 { 50 BResources resources; 51 status_t status = _GetAppResources(resources); 52 if (status != B_OK) 53 return NULL; 54 55 size_t dataSize; 56 const void* data = resources.LoadResource(B_MESSAGE_TYPE, resourceID, 57 &dataSize); 58 if (data == NULL) 59 return NULL; 60 61 BMemoryIO stream(data, dataSize); 62 63 // Try to read as an archived bitmap. 64 BMessage archive; 65 status = archive.Unflatten(&stream); 66 if (status != B_OK) 67 return NULL; 68 69 BBitmap* bitmap = new BBitmap(&archive); 70 71 status = bitmap->InitCheck(); 72 if (status != B_OK) { 73 delete bitmap; 74 bitmap = NULL; 75 } 76 77 return bitmap; 78 } 79 80 protected: 81 BBitmap* fBitmap; 82 }; 83 84 85 // #pragma mark - Test1 86 87 88 class RectsTest : public Test { 89 public: 90 RectsTest() 91 : 92 Test("Rects") 93 { 94 } 95 96 virtual void Draw(BView* view, BRect updateRect) 97 { 98 view->DrawString("Rects", BPoint(20, 30)); 99 100 view->SetDrawingMode(B_OP_ALPHA); 101 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 102 103 BRect rect(view->Bounds()); 104 rect.OffsetTo(B_ORIGIN); 105 106 rect.InsetBy(rect.Width() / 3, rect.Height() / 3); 107 BPoint center( 108 rect.left + rect.Width() / 2, 109 rect.top + rect.Height() / 2); 110 111 for (int32 i = 0; i < 360; i += 40) { 112 BAffineTransform transform; 113 transform.RotateBy(center, i * M_PI / 180.0); 114 view->SetTransform(transform); 115 116 view->SetHighColor(51, 151, 255, 20); 117 view->FillRect(rect); 118 119 view->SetHighColor(51, 255, 151, 180); 120 view->DrawString("Rect", center); 121 } 122 } 123 }; 124 125 126 // #pragma mark - AlphaMaskBitmapTest 127 128 129 class AlphaMaskBitmapTest : public BitmapTest { 130 public: 131 AlphaMaskBitmapTest() 132 : 133 BitmapTest("Alpha Masked Bitmap") 134 { 135 } 136 137 virtual void Draw(BView* view, BRect updateRect) 138 { 139 BRect rect(view->Bounds()); 140 141 if (fBitmap == NULL) { 142 view->SetHighColor(255, 0, 0); 143 view->FillRect(rect); 144 view->SetHighColor(0, 0, 0); 145 view->DrawString("Failed to load the bitmap.", BPoint(20, 20)); 146 return; 147 } 148 149 rect.left = (rect.Width() - fBitmap->Bounds().Width()) / 2; 150 rect.top = (rect.Height() - fBitmap->Bounds().Height()) / 2; 151 rect.right = rect.left + fBitmap->Bounds().Width(); 152 rect.bottom = rect.top + fBitmap->Bounds().Height(); 153 154 BPoint center( 155 rect.left + rect.Width() / 2, 156 rect.top + rect.Height() / 2); 157 158 BPicture picture; 159 view->BeginPicture(&picture); 160 view->SetDrawingMode(B_OP_ALPHA); 161 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 162 BFont font; 163 view->GetFont(&font); 164 font.SetSize(70); 165 view->SetFont(&font); 166 view->SetHighColor(0, 0, 0, 80); 167 view->FillRect(view->Bounds()); 168 view->SetHighColor(0, 0, 0, 255); 169 view->DrawString("CLIPPING", BPoint(0, center.y + 35)); 170 view->EndPicture(); 171 172 view->ClipToPicture(&picture); 173 174 BAffineTransform transform; 175 transform.RotateBy(center, 30 * M_PI / 180.0); 176 view->SetTransform(transform); 177 178 view->DrawBitmap(fBitmap, fBitmap->Bounds(), rect); 179 } 180 }; 181 182 183 // #pragma mark - Gradient 184 185 186 class GradientTest : public Test { 187 public: 188 GradientTest() 189 : 190 Test("Gradient") 191 { 192 } 193 194 virtual void Draw(BView* view, BRect updateRect) 195 { 196 BRect rect(view->Bounds()); 197 rect.InsetBy(rect.Width() / 3, rect.Height() / 3); 198 BPoint center( 199 rect.left + rect.Width() / 2, 200 rect.top + rect.Height() / 2); 201 202 BAffineTransform transform; 203 transform.RotateBy(center, 30.0 * M_PI / 180.0); 204 view->SetTransform(transform); 205 206 rgb_color top = (rgb_color){ 255, 255, 0, 255 }; 207 rgb_color bottom = (rgb_color){ 0, 255, 255, 255 }; 208 209 BGradientLinear gradient; 210 gradient.AddColor(top, 0.0f); 211 gradient.AddColor(bottom, 255.0f); 212 gradient.SetStart(rect.LeftTop()); 213 gradient.SetEnd(rect.LeftBottom()); 214 215 float radius = std::min(rect.Width() / 5, rect.Height() / 5); 216 217 view->FillRoundRect(rect, radius, radius, gradient); 218 } 219 }; 220 221 222 // #pragma mark - NestedStates 223 224 225 class NestedStatesTest : public Test { 226 public: 227 NestedStatesTest() 228 : 229 Test("Nested view states") 230 { 231 } 232 233 virtual void Draw(BView* view, BRect updateRect) 234 { 235 BAffineTransform transform; 236 transform.RotateBy(BPoint(100, 100), 30.0 * M_PI / 180.0); 237 view->SetTransform(transform); 238 239 rgb_color top = (rgb_color){ 255, 0, 0, 255 }; 240 rgb_color bottom = (rgb_color){ 255, 255, 0, 255 }; 241 242 BRect rect(20, 20, 120, 120); 243 244 BGradientLinear gradient; 245 gradient.AddColor(top, 0.0f); 246 gradient.AddColor(bottom, 255.0f); 247 gradient.SetStart(rect.LeftTop()); 248 gradient.SetEnd(rect.LeftBottom()); 249 250 view->FillRoundRect(rect, 20, 20, gradient); 251 252 view->PushState(); 253 // Should be in the same place! 254 view->StrokeRoundRect(rect, 20, 20); 255 256 // Now rotated by another 30 degree 257 view->SetTransform(transform); 258 259 view->SetDrawingMode(B_OP_ALPHA); 260 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 261 view->SetHighColor(0, 0, 255, 120); 262 view->FillRoundRect(rect, 20, 20); 263 264 view->PopState(); 265 } 266 }; 267 268 269 // #pragma mark - Clipping 270 271 272 class ClippingTest : public Test { 273 public: 274 ClippingTest() 275 : 276 Test("View bounds clipping") 277 { 278 } 279 280 virtual void Draw(BView* view, BRect updateRect) 281 { 282 BRect r (20, 20, 50, 50); 283 view->SetHighColor(ui_color(B_FAILURE_COLOR)); 284 view->FillRect(r); 285 286 BAffineTransform transform; 287 transform.TranslateBy(400, 400); 288 view->SetTransform(transform); 289 290 // Make sure this rectangle is drawn, even when the original one is out 291 // of the view bounds (for example because of scrolling). 292 view->SetHighColor(ui_color(B_SUCCESS_COLOR)); 293 view->FillRect(r); 294 } 295 }; 296 297 298 // #pragma mark - Clipping 299 300 301 class TextClippingTest : public Test { 302 public: 303 TextClippingTest() 304 : 305 Test("Text clipping") 306 { 307 } 308 309 virtual void Draw(BView* view, BRect updateRect) 310 { 311 BFont font; 312 view->GetFont(&font); 313 font.SetSize(70); 314 view->SetFont(&font); 315 316 float width = view->Bounds().Width(); 317 318 // The translation make the text, which has negative coordinates, be 319 // visible inside the viewport. 320 BAffineTransform transform; 321 transform.TranslateBy(width, 0); 322 view->SetTransform(transform); 323 324 const char* str = "CLIPPING"; 325 326 // Test the standard DrawString method 327 328 // Draw the text bounds 329 float size = view->StringWidth(str); 330 BRect r(-width, 0, size - width, 70); 331 view->SetHighColor(ui_color(B_SUCCESS_COLOR)); 332 view->FillRect(r); 333 334 // Draw the text (which should fit inside the bounds rectangle) 335 view->SetHighColor(0, 0, 0, 255); 336 view->DrawString(str, BPoint(-width, 70)); 337 338 // Test with offset-based DrawString 339 BPoint offsets[strlen(str)]; 340 for(unsigned int i = 0; i < strlen(str); i++) 341 { 342 offsets[i].x = i * 35 - width; 343 offsets[i].y = 145; 344 } 345 346 // Draw the text bounds 347 view->SetHighColor(ui_color(B_SUCCESS_COLOR)); 348 r = BRect(offsets[0], offsets[strlen(str) - 1]); 349 r.top = 75; 350 view->FillRect(r); 351 352 // Draw the text (which should fit inside the bounds rectangle) 353 view->SetHighColor(0, 0, 0, 255); 354 view->DrawString(str, offsets, strlen(str)); 355 356 } 357 }; 358 359 360 // #pragma mark - BitmapClipTest 361 362 363 class BitmapClipTest : public BitmapTest { 364 public: 365 BitmapClipTest() 366 : 367 BitmapTest("Bitmap clipping") 368 { 369 } 370 371 virtual void Draw(BView* view, BRect updateRect) 372 { 373 BRect rect(view->Bounds()); 374 375 if (fBitmap == NULL) { 376 view->SetHighColor(255, 0, 0); 377 view->FillRect(rect); 378 view->SetHighColor(0, 0, 0); 379 view->DrawString("Failed to load the bitmap.", BPoint(20, 20)); 380 return; 381 } 382 383 rect = fBitmap->Bounds(); 384 385 view->SetHighColor(ui_color(B_FAILURE_COLOR)); 386 view->FillRect(rect); 387 388 // The rect offset should compensate the transform translation, so the 389 // bitmap should be drawn at the view origin. It will then exactly 390 // cover the red rectangle, which should not be visible anymore. 391 rect.OffsetBy(0, 40); 392 393 BAffineTransform transform; 394 transform.TranslateBy(0, -40); 395 view->SetTransform(transform); 396 397 view->DrawBitmap(fBitmap, fBitmap->Bounds(), rect); 398 } 399 }; 400 401 402 // #pragma mark - PixelAlignTest 403 404 405 class PixelAlignTest : public Test { 406 public: 407 PixelAlignTest() 408 : 409 Test("Pixel alignment") 410 { 411 } 412 413 virtual void Draw(BView* view, BRect updateRect) 414 { 415 BRect rect(20, 20, 120, 120); 416 view->SetHighColor(ui_color(B_SUCCESS_COLOR)); 417 view->StrokeRect(rect); 418 419 BAffineTransform transform; 420 transform.TranslateBy(140, 0); 421 view->SetTransform(transform); 422 423 // Translating a pixel-aligned rectangle by an integer number of 424 // pixels should result in a pixel-aligned rectangle. 425 view->SetHighColor(ui_color(B_FAILURE_COLOR)); 426 view->StrokeRect(rect); 427 } 428 }; 429 430 431 // #pragma mark - 432 433 434 int 435 main(int argc, char** argv) 436 { 437 BApplication app(kAppSignature); 438 439 TestWindow* window = new TestWindow("Transformation tests"); 440 441 window->AddTest(new RectsTest()); 442 window->AddTest(new BitmapClipTest()); 443 window->AddTest(new TextClippingTest()); 444 window->AddTest(new AlphaMaskBitmapTest()); 445 window->AddTest(new GradientTest()); 446 window->AddTest(new NestedStatesTest()); 447 window->AddTest(new ClippingTest()); 448 window->AddTest(new PixelAlignTest()); 449 450 window->SetToTest(2); 451 window->Show(); 452 453 app.Run(); 454 return 0; 455 } 456