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:
BitmapTest(const char * name)28 BitmapTest(const char* name)
29 :
30 Test(name),
31 fBitmap(_LoadBitmap(555))
32 {
33 }
34
35 private:
36 status_t
_GetAppResources(BResources & resources) const37 _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
_LoadBitmap(int resourceID) const48 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:
RectsTest()90 RectsTest()
91 :
92 Test("Rects")
93 {
94 }
95
Draw(BView * view,BRect updateRect)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:
AlphaMaskBitmapTest()131 AlphaMaskBitmapTest()
132 :
133 BitmapTest("Alpha Masked Bitmap")
134 {
135 }
136
Draw(BView * view,BRect updateRect)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:
GradientTest()188 GradientTest()
189 :
190 Test("Gradient")
191 {
192 }
193
Draw(BView * view,BRect updateRect)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:
NestedStatesTest()227 NestedStatesTest()
228 :
229 Test("Nested view states")
230 {
231 }
232
Draw(BView * view,BRect updateRect)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:
ClippingTest()274 ClippingTest()
275 :
276 Test("View bounds clipping")
277 {
278 }
279
Draw(BView * view,BRect updateRect)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:
TextClippingTest()303 TextClippingTest()
304 :
305 Test("Text clipping")
306 {
307 }
308
Draw(BView * view,BRect updateRect)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:
BitmapClipTest()365 BitmapClipTest()
366 :
367 BitmapTest("Bitmap clipping")
368 {
369 }
370
Draw(BView * view,BRect updateRect)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:
PixelAlignTest()407 PixelAlignTest()
408 :
409 Test("Pixel alignment")
410 {
411 }
412
Draw(BView * view,BRect updateRect)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
main(int argc,char ** argv)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