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