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