xref: /haiku/src/add-ons/screen_savers/nebula/Nebula.cpp (revision 2876efaafe6389273daf7fb914b59510503dc446)
1 /*
2  * Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Copyright 2015, Augustin Cavalier <waddlesplash>. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Effect from corTeX / Optimum.
7  */
8 
9 
10 #include <AppKit.h>
11 #include <Catalog.h>
12 #include <ColorMenuItem.h>
13 #include <ControlLook.h>
14 #include <InterfaceKit.h>
15 #include <LayoutBuilder.h>
16 #include <MenuField.h>
17 #include <ScreenSaver.h>
18 #include <String.h>
19 #include <SupportDefs.h>
20 #include <Window.h>
21 
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "Nebula Screen Saver"
30 
31 
32 typedef struct
33 {
34 	int x, y, z, r;
35 } p3;
36 
37 typedef float matrix[3][3];
38 
39 #define GMAX 5000
40 p3 gal[GMAX];
41 float precos[512];
42 float presin[512];
43 
44 typedef unsigned short word;
45 
46 extern "C" {
47 #include "Draw.h"
48 #include "DrawStars.h"
49 }
50 
51 const uint32 kMsgWidth  = 'widt';
52 const uint32 kMsgColorScheme = 'cols';
53 const uint32 kMsgBlankBorders = 'blbr';
54 const uint32 kMsgMotionBlur = 'blur';
55 const uint32 kMsgSpeed = 'sped';
56 const uint32 kMsgFrames = 'mfps';
57 
58 float	gSpeed;
59 bool	gMotionBlur;
60 int32	gSettingsWidth;
61 int32	gWidth;
62 int32	gHeight;
63 float	gMaxFramesPerSecond;
64 BBitmap* gBitmap;
65 BScreenSaver* gScreenSaver;
66 uint32	gPalette[256];
67 int8	gPaletteScheme;
68 int8	gBlankBorders;
69 char*	gBuffer8;   /* working 8bit buffer */
70 
71 
72 inline float
ocos(float a)73 ocos(float a)
74 {
75 	return (precos[(int)(a * 256 / M_PI) & 511]);
76 }
77 
78 inline float
osin(float a)79 osin(float a)
80 {
81 	return (presin[(int)(a * 256 / M_PI) & 511]);
82 }
83 
84 
85 void
mulmat(matrix * a,matrix * b,matrix * c)86 mulmat(matrix* a, matrix* b, matrix* c)
87 {
88 	int i, j;
89 
90 	for (i = 0; i < 3; i++) {
91 		for (j = 0; j < 3; j++) {
92 			(*c)[i][j] = (*a)[i][0] * (*b)[0][j] +
93 						 (*a)[i][1] * (*b)[1][j] +
94 						 (*a)[i][2] * (*b)[2][j];
95 		}
96 	}
97 }
98 
99 
100 inline void
mulvec(matrix * a,float * x,float * y,float * z)101 mulvec(matrix* a, float* x, float* y, float* z)
102 {
103 	float nx = *x, ny = *y, nz = *z;
104 
105 	*x = nx * (*a)[0][0] + ny * (*a)[0][1] + nz * (*a)[0][2];
106 	*y = nx * (*a)[1][0] + ny * (*a)[1][1] + nz * (*a)[1][2];
107 	*z = nx * (*a)[2][0] + ny * (*a)[2][1] + nz * (*a)[2][2];
108 }
109 
110 
111 void
setrmat(float a,float b,float c,matrix * m)112 setrmat(float a, float b, float c, matrix* m)
113 {
114 	int i, j;
115 	for (i = 0; i < 3; i++)
116 		for (j = 0; j < 3; j++)
117 			(*m)[i][j] = (float)(i == j);
118 
119 	if (a != 0) {
120 		(*m)[0][0] = cos(a);	(*m)[0][1] = sin(a);
121 		(*m)[1][0] = sin(a);	(*m)[1][1] = -cos(a);
122 		return;
123 	}
124 	if (b != 0) {
125 		(*m)[0][0] = cos(b);	(*m)[0][2] = sin(b);
126 		(*m)[2][0] = sin(b);	(*m)[2][2] = -cos(b);
127 		return;
128 	}
129 	(*m)[1][1] = cos(c);	(*m)[1][2] = sin(c);
130 	(*m)[2][1] = sin(c);	(*m)[2][2] = -cos(c);
131 }
132 
133 
134 void
rotate3d(float * xr,float * yr,float * zr,float ax,float ay,float az)135 rotate3d(float* xr, float* yr, float* zr,  /* point to rotate */
136 	float ax, float ay, float az)     /* the 3 angles (order ?..) */
137 {
138 	float xr2, yr2, zr2;
139 
140 	xr2 = (*xr * ocos(az) + *yr * osin(az));
141 	yr2 = (*xr * osin(az) - *yr * ocos(az));
142 	*xr = xr2;
143 	*yr = yr2;
144 
145 	xr2 = (*xr * ocos(ay) + *zr * osin(ay));
146 	zr2 = (*xr * osin(ay) - *zr * ocos(ay));
147 	*xr = xr2;
148 	*zr = zr2;
149 
150 	zr2 = (*zr * ocos(ax) + *yr * osin(ax));
151 	yr2 = (*zr * osin(ax) - *yr * ocos(ax));
152 	*zr = zr2;
153 	*yr = yr2;
154 }
155 
156 
157 void
drawshdisk(int x0,int y0,int r)158 drawshdisk(int x0, int y0, int r)
159 {
160 	int x = 0;
161 	int y;
162 	int ly;		/* last y */
163 	int delta;
164 	int c;		/* color at center */
165 	int d;		/* delta */
166 
167 #define SLIMIT 17
168 #define SRANGE 15
169 
170 	if (r <= SLIMIT) {
171 		/* range checking is already (more or less) done... */
172 		draw_stars(gWidth, &gBuffer8[x0 + gWidth * y0], 10 + r * 5);
173 		//gBuffer8[x0 + W * y0] = 10 + r * 5;
174 		return;
175 	}
176 
177 	if (r < SLIMIT + SRANGE)
178 		r = ((r - SLIMIT) * SLIMIT) / SRANGE + 1;
179 
180 	y = ly = r;     /* AAaargh */
181 	delta = 3 - 2 * r;
182 
183 	do {
184 		if (y != ly) {
185 			/* dont overlap these lines */
186 			c = ((r - y + 1) << 13) / r;
187 			d = -c / (x + 1);
188 
189 			if (y == x + 1)		/* this would overlap with the next x lines */
190 				goto TOTO;		/* WHY NOT */
191 
192 			/*  note : for "normal" numbers (not too big) :
193 				(unsigned int)(x) < M   <=>  0<=x<H
194 				(because if x<0, then (unsigned)(x) = 2**32-|x| which is
195 				BIG and thus >H )
196 
197 				This is clearly a stupid, unmaintanable, unreadable
198 				"optimization". But i like it :)
199 			*/
200 			if ((uint32)(y0 - y - 1) < gHeight - 3)
201 				memshset(&gBuffer8[x0 + gWidth * (y0 - y + 1)], c, d, x);
202 
203 			if ((uint32)(y0 + y - 1) < gHeight - 3)
204 				memshset(&gBuffer8[x0 + gWidth*(y0 + y)], c, d, x);
205 		}
206 		TOTO:
207 		c = ((r - x + 1) << 13) / r;
208 		d = -c / (y);
209 
210 		if ((uint32)(y0 - x - 1) < gHeight - 3)
211 			memshset(&gBuffer8[x0 + gWidth*(y0 - x)], c, d, y);
212 		if ((uint32)(y0 + x - 1) < gHeight - 3)
213 			memshset(&gBuffer8[x0 + gWidth * (y0 + x + 1)], c, d, y);
214 
215 		ly = y;
216 		if (delta < 0)
217 			delta += 4 * x + 6;
218 		else {
219 			delta += 4 * (x - y) + 10;
220 			y--;
221 		}
222 		x++;
223 	} while (x < y);
224 }
225 
226 
227 void
drawGalaxy()228 drawGalaxy()
229 {
230 	int r;
231 	int x, y;
232 	float rx, ry, rz;
233 	int i;
234 	float oa, ob, oc;
235 	float t;
236 	float a, b, c;
237 	matrix ma, mb, mc, mr;
238 
239 	/* t is the parametric coordinate for the animation;
240 	change the scale value to change the speed of anim
241 	(independant of processor speed)
242 	*/
243 	static bigtime_t firstTime = system_time();
244 	t = ((double)gSpeed * system_time() - firstTime) / 1000000.0;
245 		//opti_scale_time(0.418, &demo_elapsed_time);
246 
247 	a = 0.9 * t;
248 	b = t;
249 	c = 1.1 * t;
250 
251 	setrmat(a, 0, 0, &ma);
252 	setrmat(0, b, 0, &mb);
253 	mulmat(&ma, &mb, &mc);
254 	setrmat(0, 0, c, &ma);
255 	mulmat(&ma, &mc, &mr);
256 
257 	oa = 140 * osin(a);
258 	ob = 140 * ocos(b);
259 	oc = 240 * osin(c);
260 
261 	if (gMotionBlur) {
262 		/* mblur does something like that:
263 		 * (or did, perhaps it's another version!..)
264 
265 		for (i = 0; i < W * H; i++)
266 			gBuffer8[i]= (gBuffer8[i] >> 3) + (gBuffer8[i] >> 1);
267 		*/
268 		mblur (gBuffer8, gWidth * gHeight);
269 	} else
270 		memset(gBuffer8, 0, gWidth * gHeight);
271 
272 	for (i = 0; i < GMAX; i++) {
273 		rx = gal[i].x;
274 		ry = gal[i].y;
275 		rz = gal[i].z;
276 
277 		mulvec(&mr, &rx, &ry, &rz);
278 
279 		rx += oa;
280 		ry += ob;
281 		rz += oc;
282 		rz += 300;
283 
284 		if (rz > 5) {
285 			x = (int)(15 * rx / (rz / 5 + 1)) + gWidth / 2;
286 				/* tain jcomprend plus rien  */
287 			y = (int)(15 * ry/ (rz / 5 + 1)) + gHeight / 2;
288 				/* a ces formules de daube !! */
289 			r = (int)(3 * gal[i].r / (rz / 4 + 3)) + 2;
290 
291 			if (x > 5 && x < gWidth - 6 && y > 5 && y < gHeight - 6)
292 //			if ((uint32)x < gWidth - 1 && (uint32)y < gHeight - 1)
293 				drawshdisk(x, y, r);
294 		}
295 	}
296 }
297 
298 
299 void
setPalette()300 setPalette()
301 {
302 	int i;
303 
304 	switch (gPaletteScheme) {
305 		case 0:		// yellow
306 		default:
307 			for (i = 0; i < 30; i++)
308 				gPalette[i] = (uint8)(i * 8 / 10) << 16
309 					| (uint8)(i * 6 / 10) << 8;
310 					// | (uint8)(i*3/10);
311 
312 			for (i = 30; i < 256; i++) {
313 				uint8 r = (i);
314 				uint8 g = (i * i >> 8); //(i*8/10);
315 				uint8 b = i >= 240 ? (i - 240) << 3 : 0; //(i * 2 / 10);
316 
317 				gPalette[i] = ((r << 16) | (g << 8) | (b));
318 			}
319 			break;
320 
321 		case 1:		// blue
322 			for (i = 0; i < 30; i++)
323 				gPalette[i] = (uint8)(i * 8 / 10);
324 					// << 16 | (uint8)(i * 6 / 10) << 8;
325 					// | (uint8)(i * 3 / 10);
326 
327 			for (i = 30; i < 256; i++) {
328 				uint8 b = (i);
329 				uint8 g = (i * i >> 8); //(i * 8 / 10);
330 				uint8 r = i >= 240 ? (i - 240) << 3 : 0; //(i * 2 / 10);
331 
332 				gPalette[i] = ((r << 16) | (g << 8) | (b));
333 			}
334 			break;
335 
336 		case 2:		// red
337 			for (i = 0; i < 128; i++)
338 				gPalette[i] = (uint8)i << 16;
339 					// << 16 | (uint8)(i * 6/10) << 8;
340 					// | (uint8)(i * 3 / 10);
341 
342 			for (i = 128;i < 256; i++) {
343 				uint8 r = i;
344 				uint8 c = (uint8)((cos((i - 256) / 42.0) * 0.5 + 0.5) * 225);
345 
346 				gPalette[i] = ((r << 16) | (c << 8) | c);
347 			}
348 /*			for (i = 192; i < 224; i++) {
349 				uint8 c = (i - 192);
350 				gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
351 			}
352 			for (i = 224; i < 256; i++) {
353 				uint8 c = (i-224) / 2;
354 				c = 32 + c * c * 6 / 10;
355 				gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
356 			}
357 */			break;
358 
359 		case 3:		// green
360 			for (i = 0; i < 30; i++)
361 				gPalette[i] = (uint8)(i * 8 / 10) << 8;
362 					// << 16 | (uint8)(i * 6 / 10) << 8;
363 					// | (uint8)(i * 3 / 10);
364 
365 			for (i = 30; i < 256; i++) {
366 				uint8 g = (i);
367 				uint8 r = (i * i >> 8); //(i * 8 / 10);
368 				uint8 b = i >= 240 ? (i-240) << 3 : 0; //(i * 2 / 10);
369 
370 				gPalette[i] = ((r << 16) | (g << 8) | (b));
371 			}
372 			break;
373 
374 		case 4:		// grey
375 			for (i = 0; i < 256; i++) {
376 				uint8 c = i * 15 / 16 + 10;
377 				gPalette[i] = c << 16 | c << 8 | c;
378 			}
379 			break;
380 		case 5:		// cold
381 			for (i = 0; i < 30; i++)
382 				gPalette[i] = (uint8)(i * 8 / 10) << 16;
383 					// << 16 | (uint8)(i * 6 / 10) << 8;
384 					// | (uint8)(i * 3 / 10);
385 
386 			for (i = 30; i < 256; i++) {
387 				uint8 r = i;
388 				uint8 c = (uint8)((cos((i - 255) / 82.0) * 0.5 + 0.5) * 255);
389 
390 				gPalette[i] = ((r << 16) | (c << 8) | c);
391 			}
392 			break;
393 
394 		case 6:		// original
395 			for (i = 0; i < 256; i++) {
396 				uint32 c = *(char *)&i;
397 				gPalette[i] = c << 16 | c << 8;
398 			}
399 			break;
400 	}
401 /*	for (i = 0;i < 256; i++) {
402 		uint8 r = (i);
403 		uint8 g = (i * i >> 8); //(i * 8 / 10);
404 		uint8 b = 0; //(i * 2 / 10);
405 
406 		gPalette[i] = ((r << 16) | (g << 8) | (b));
407 	}
408 */
409 /*	for (i = 240; i < 256; i++)
410 		gPalette[i] = (uint8)i << 16 | (uint8)i << 8 | (uint8)(i * 6 / 10);
411 */
412 }
413 
414 
415 // #pragma mark - SimpleSlider
416 
417 
418 class SimpleSlider : public BSlider {
419 public:
SimpleSlider(const char * label,BMessage * message)420 	SimpleSlider(const char* label, BMessage* message)
421 	:
422 	BSlider(B_EMPTY_STRING, B_EMPTY_STRING, message, 1, 100, B_HORIZONTAL)
423 	{
424 		SetLimitLabels("1", "100");
425 		SetHashMarks(B_HASH_MARKS_BOTTOM);
426 		SetHashMarkCount(11);
427 		fLabel = label;
428 	};
429 
UpdateText() const430 	const char* UpdateText() const
431 	{
432 		fText.SetToFormat("%s: %d", fLabel, Value());
433 		return fText.String();
434 	};
435 
436 private:
437 	mutable BString fText;
438 	const char* fLabel;
439 };
440 
441 
442 // #pragma mark - SettingsView
443 
444 
445 class SettingsView : public BView {
446 public:
447 								SettingsView(BRect frame);
448 
449 	virtual	void				AttachedToWindow();
450 	virtual	void				MessageReceived(BMessage* message);
451 
452 	private:
453 			BMenuField*			fWidthMenuField;
454 			BMenuField*			fColorMenuField;
455 			BMenuField*			fBorderMenuField;
456 			BCheckBox*			fMotionCheck;
457 			BSlider*			fSpeedSlider;
458 			BSlider*			fFramesSlider;
459 };
460 
461 
SettingsView(BRect frame)462 SettingsView::SettingsView(BRect frame)
463 	:
464 	BView(frame, "", B_FOLLOW_ALL, B_WILL_DRAW)
465 {
466 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
467 
468 	BStringView* titleString = new BStringView(B_EMPTY_STRING,
469 		B_TRANSLATE("Nebula"));
470 	titleString->SetFont(be_bold_font);
471 
472 	BStringView* copyrightString = new BStringView(B_EMPTY_STRING,
473 		B_TRANSLATE("© 2001-2004 Axel Dörfler."));
474 
475 	BPopUpMenu* popUpMenu;
476 
477 	int32 widths[] = {
478 		0,
479 		320,
480 		512,
481 		576,
482 		640,
483 		800,
484 		1024,
485 		1152,
486 		1280,
487 		1400,
488 		1600
489 	};
490 
491 	size_t widthsLength = sizeof(widths) / sizeof(widths[0]);
492 
493 	popUpMenu = new BPopUpMenu("");
494 	for (size_t i = 0; i < widthsLength; i++) {
495 		BString label;
496 		if (widths[i] == 0)
497 			label.SetTo(B_TRANSLATE("screen resolution"));
498 		else
499 			label.SetToFormat(B_TRANSLATE("%" B_PRId32 " pixels"), widths[i]);
500 
501 		BMessage* message = new BMessage(kMsgWidth);
502 		message->AddInt32("width", widths[i]);
503 
504 		BMenuItem* item = new BMenuItem(label.String(), message);
505 		popUpMenu->AddItem(item);
506 		item->SetMarked(gSettingsWidth == widths[i]);
507 	}
508 
509 	fWidthMenuField = new BMenuField("res", B_TRANSLATE("Internal width:"),
510 		popUpMenu);
511 
512 	const char* colorSchemeLabels[] = {
513 		B_TRANSLATE("yellow"),
514 		B_TRANSLATE("cyan"),
515 		B_TRANSLATE("red"),
516 		B_TRANSLATE("green"),
517 		B_TRANSLATE("grey"),
518 		B_TRANSLATE("cold"),
519 		B_TRANSLATE("orange (original)")
520 	};
521 
522 	rgb_color colorSchemeColors[] = {
523 		(rgb_color){ 255, 220, 0   },
524 		(rgb_color){ 127, 219, 255 },
525 		(rgb_color){ 255, 65,  54  },
526 		(rgb_color){ 46,  204, 64  },
527 		(rgb_color){ 170, 170, 170 },
528 		(rgb_color){ 234, 234, 234 },
529 		(rgb_color){ 255, 133, 27  }
530 	};
531 
532 	popUpMenu = new BPopUpMenu("");
533 	for (int i = 0; i < 7; i++) {
534 		BMessage* message = new BMessage(kMsgColorScheme);
535 		message->AddInt8("scheme", (int8)i);
536 		BColorMenuItem* item = new BColorMenuItem(colorSchemeLabels[i],
537 			message, colorSchemeColors[i]);
538 		popUpMenu->AddItem(item);
539 		item->SetMarked(gPaletteScheme == i);
540 	}
541 
542 	fColorMenuField = new BMenuField("col", B_TRANSLATE("Color:"), popUpMenu);
543 
544 	const char* blankBorderFormats[] = {
545 		B_TRANSLATE("fullscreen, no borders"),
546 		B_TRANSLATE("16:9, wide-screen"),
547 		B_TRANSLATE("2:3.5, cinemascope"),
548 		B_TRANSLATE("only a slit")
549 	};
550 
551 	popUpMenu = new BPopUpMenu("");
552 	for (int8 i = 0; i < 4; i++) {
553 		BMessage* message = new BMessage(kMsgBlankBorders);
554 		message->AddInt8("border", i);
555 		BMenuItem* item = new BMenuItem(blankBorderFormats[i], message);
556 		popUpMenu->AddItem(item);
557 		item->SetMarked(gBlankBorders == i);
558 	}
559 
560 	fBorderMenuField = new BMenuField("cinema", B_TRANSLATE("Format:"),
561 		popUpMenu);
562 
563 	fMotionCheck = new BCheckBox(B_EMPTY_STRING,
564 		B_TRANSLATE("Enable motion blur"), new BMessage(kMsgMotionBlur));
565 	fMotionCheck->SetValue((int)gMotionBlur);
566 
567 	fSpeedSlider = new SimpleSlider(B_TRANSLATE("Speed"),
568 		new BMessage(kMsgSpeed));
569 	fSpeedSlider->SetValue((gSpeed - 0.002) / 0.05);
570 
571 	fFramesSlider = new SimpleSlider(B_TRANSLATE("Maximum Frames Per Second"),
572 		new BMessage(kMsgFrames));
573 	fFramesSlider->SetValue(gMaxFramesPerSecond);
574 
575 	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_HALF_ITEM_SPACING)
576 		.SetInsets(B_USE_DEFAULT_SPACING)
577 		.Add(titleString)
578 		.Add(copyrightString)
579 		.AddStrut(roundf(be_control_look->DefaultItemSpacing() / 2))
580 		.AddGlue()
581 		.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
582 			.Add(fColorMenuField->CreateLabelLayoutItem(), 0, 0)
583 			.AddGroup(B_HORIZONTAL, 0.0f, 1, 0)
584 				.Add(fColorMenuField->CreateMenuBarLayoutItem(), 0.0f)
585 				.AddGlue()
586 				.End()
587 			.Add(fWidthMenuField->CreateLabelLayoutItem(), 0, 1)
588 			.AddGroup(B_HORIZONTAL, 0.0f, 1, 1)
589 				.Add(fWidthMenuField->CreateMenuBarLayoutItem(), 0.0f)
590 				.AddGlue()
591 				.End()
592 			.Add(fBorderMenuField->CreateLabelLayoutItem(), 0, 2)
593 			.AddGroup(B_HORIZONTAL, 0.0f, 1, 2)
594 				.Add(fBorderMenuField->CreateMenuBarLayoutItem(), 0.0f)
595 				.AddGlue()
596 				.End()
597 			.Add(fMotionCheck, 1, 3)
598 			.End()
599 		.Add(fSpeedSlider)
600 		.Add(fFramesSlider)
601 	.End();
602 }
603 
604 
605 void
AttachedToWindow()606 SettingsView::AttachedToWindow()
607 {
608 	fWidthMenuField->Menu()->SetTargetForItems(this);
609 	fColorMenuField->Menu()->SetTargetForItems(this);
610 	fBorderMenuField->Menu()->SetTargetForItems(this);
611 	fMotionCheck->SetTarget(this);
612 	fSpeedSlider->SetTarget(this);
613 	fFramesSlider->SetTarget(this);
614 }
615 
616 
617 void
MessageReceived(BMessage * message)618 SettingsView::MessageReceived(BMessage* message)
619 {
620 	switch(message->what) {
621 		case kMsgWidth:
622 			message->FindInt32("width", &gSettingsWidth);
623 			break;
624 
625 		case kMsgColorScheme:
626 			if (message->FindInt8("scheme", &gPaletteScheme) == B_OK)
627 				setPalette();
628 			break;
629 
630 		case kMsgBlankBorders:
631 			message->FindInt8("border", &gBlankBorders);
632 			break;
633 
634 		case kMsgMotionBlur:
635 			gMotionBlur = fMotionCheck->Value() > 0;
636 			break;
637 
638 		case kMsgSpeed:
639 			gSpeed = 0.002 + 0.05 * fSpeedSlider->Value();
640 			break;
641 
642 		case kMsgFrames:
643 			gMaxFramesPerSecond = fFramesSlider->Value();
644 			gScreenSaver->SetTickSize(
645 				(bigtime_t)(1000000LL / gMaxFramesPerSecond));
646 			break;
647 	}
648 }
649 
650 
651 
652 // #pragma mark - Nebula
653 
654 
655 class Nebula : public BScreenSaver {
656 public:
657 								Nebula(BMessage* message, image_id id);
658 
659 	virtual	void				StartConfig(BView* view);
660 	virtual	status_t			SaveState(BMessage* state) const;
661 
662 	virtual	status_t			StartSaver(BView* view, bool preview);
663 	virtual	void				StopSaver();
664 	virtual	void				Draw(BView* view, int32 frame);
665 
666 private:
667 			float				fFactor;
668 			bool				fStarted;
669 };
670 
671 
Nebula(BMessage * message,image_id id)672 Nebula::Nebula(BMessage* message, image_id id)
673 	:
674 	BScreenSaver(message, id),
675 	fStarted(false)
676 {
677 	message->FindFloat("speed", 0, &gSpeed);
678 	message->FindInt32("width", 0, &gSettingsWidth);
679 	message->FindBool("motionblur", 0, &gMotionBlur);
680 	message->FindFloat("max_fps", 0, &gMaxFramesPerSecond);
681 	message->FindInt8("scheme", 0, &gPaletteScheme);
682 	message->FindInt8("border", 0, &gBlankBorders);
683 
684 	if (gSpeed < 0.01f)
685 		gSpeed = 0.4f;
686 
687 	if (gMaxFramesPerSecond < 1.f)
688 		gMaxFramesPerSecond = 40.0f;
689 
690 	gScreenSaver = this;
691 }
692 
693 
694 void
StartConfig(BView * view)695 Nebula::StartConfig(BView* view)
696 {
697 	view->AddChild(new SettingsView(view->Bounds()));
698 }
699 
700 
701 status_t
SaveState(BMessage * state) const702 Nebula::SaveState(BMessage* state) const
703 {
704 	state->AddFloat("speed", gSpeed);
705 	state->AddInt32("width", gSettingsWidth);
706 	state->AddBool("motionblur", gMotionBlur);
707 	state->AddFloat("max_fps", gMaxFramesPerSecond);
708 	state->AddInt8("scheme", gPaletteScheme);
709 	state->AddInt8("border", gBlankBorders);
710 
711 	return B_OK;
712 }
713 
714 
715 status_t
StartSaver(BView * view,bool preview)716 Nebula::StartSaver(BView* view, bool preview)
717 {
718 	// initialize palette
719 	setPalette();
720 
721 	int i;
722 	for (i = 0; i < 512; i++) {
723 		precos[i]=cos(i * M_PI / 256);
724 		presin[i]=sin(i * M_PI / 256);
725 	}
726 
727 	// uniforme cubique
728 /*	for (i = 0;i < GMAX; i++) {
729 		gal[i].x = 1 * ((rand()&1023) - 512);
730 		gal[i].y = 1 * ((rand()&1023) - 512);
731 		gal[i].z = 1 * ((rand()&1023) - 512);
732 		gal[i].r = rand() & 63;
733 	}
734 */
735 
736 	for (i = 0; i < GMAX; i++) {
737 		float r, th, h, dth;
738 
739 		r = rand() * 1.0 / RAND_MAX;
740 		r = (1 - r) * (1 - r) + 0.05;
741 
742 		if (r < 0.12)
743 			th = rand() * M_PI * 2 / RAND_MAX;
744 		else {
745 			th = (rand() & 3) * M_PI_2 + r * r * 2;
746 			dth = rand() * 1.0 / RAND_MAX;
747 			dth = dth * dth * 2;
748 			th += dth;
749 		}
750 		gal[i].x = (int)(512 * r * cos(th));
751 		gal[i].z = (int)(512 * r * sin(th));
752 		h = (1 + cos(r * M_PI)) * 150;
753 		dth = rand() * 1.0 / RAND_MAX;
754 		gal[i].y = (int)(h * (dth - 0.5));
755 		gal[i].r = (int)((2 - r) * 60 + 31);
756 	}
757 	gal[0].x = gal[0].y = gal[0].z = 0;
758 	gal[0].r = 320;
759 
760 	if (gSettingsWidth == 0)
761 		gWidth = view->Bounds().Width();
762 	else
763 		gWidth = gSettingsWidth;
764 
765 	fFactor = (view->Bounds().Width()+1) / gWidth;
766 	if ((int)fFactor != fFactor)
767 		fFactor += 0.01;
768 
769 	// 4:3
770 	gHeight = (int32)((view->Bounds().Height()+1)/fFactor + 0.5f);
771 	// calculate blank border format (if not in preview)
772 	if (!preview) switch (gBlankBorders) {
773 		case 1:		// 16:9
774 			gHeight = (int32)(gHeight * 0.703125 + 0.5);
775 			break;
776 		case 2:		// 2:3.5
777 			gHeight = (int32)(gHeight * 0.534 + 0.5);
778 			break;
779 		case 3:
780 			gHeight /= 5;
781 			break;
782 	}
783 	view->SetScale(fFactor);
784 
785 	gBitmap = new BBitmap(BRect(0, 0, gWidth - 1, gHeight - 1), B_RGB32);
786 	gBuffer8 = (char*)malloc(gWidth * gHeight);
787 
788 	SetTickSize((bigtime_t)(1000000LL / gMaxFramesPerSecond));
789 	fStarted = true;
790 
791 	return B_OK;
792 }
793 
794 
795 void
StopSaver()796 Nebula::StopSaver()
797 {
798 	free(gBuffer8);
799 	gBuffer8 = NULL;
800 
801 	delete gBitmap;
802 	gBitmap = NULL;
803 }
804 
805 
806 void
Draw(BView * view,int32)807 Nebula::Draw(BView* view, int32)
808 {
809 	if (fStarted) {
810 		view->SetHighColor(0, 0, 0, 0);
811 		view->FillRect(view->Frame());
812 		view->MovePenTo(0,
813 			(view->Bounds().Height() / fFactor - 1 - gHeight) / 2);
814 
815 		fStarted = false;
816 	}
817 	uint32* buffer32 = (uint32*)gBitmap->Bits();
818 
819 	drawGalaxy();
820 
821 	for (int x = 0, end = gWidth * gHeight; x < end; x++)
822 		buffer32[x] = gPalette[(uint8)gBuffer8[x]];
823 
824 	view->DrawBitmap(gBitmap);
825 }
826 
827 
828 // #pragma mark - instantiate_screen_saver
829 
830 
831 extern "C" _EXPORT BScreenSaver*
instantiate_screen_saver(BMessage * message,image_id image)832 instantiate_screen_saver(BMessage* message, image_id image)
833 {
834 	return new Nebula(message, image);
835 }
836