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
23static const char* kAppSignature = "application/x-vnd.Haiku-Transformation";
24
25
26class BitmapTest : public Test {
27public:
28	BitmapTest(const char* name)
29		:
30		Test(name),
31		fBitmap(_LoadBitmap(555))
32	{
33	}
34
35private:
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
80protected:
81	BBitmap*	fBitmap;
82};
83
84
85// #pragma mark - Test1
86
87
88class RectsTest : public Test {
89public:
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
129class AlphaMaskBitmapTest : public BitmapTest {
130public:
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
186class GradientTest : public Test {
187public:
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
225class NestedStatesTest : public Test {
226public:
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
272class ClippingTest : public Test {
273public:
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
301class TextClippingTest : public Test {
302public:
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
363class BitmapClipTest : public BitmapTest {
364public:
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
405class PixelAlignTest : public Test {
406public:
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
434int
435main(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