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