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
32typedef struct
33{
34	int x, y, z, r;
35} p3;
36
37typedef float matrix[3][3];
38
39#define GMAX 5000
40p3 gal[GMAX];
41float precos[512];
42float presin[512];
43
44typedef unsigned short word;
45
46extern "C" {
47#include "Draw.h"
48#include "DrawStars.h"
49}
50
51const uint32 kMsgWidth  = 'widt';
52const uint32 kMsgColorScheme = 'cols';
53const uint32 kMsgBlankBorders = 'blbr';
54const uint32 kMsgMotionBlur = 'blur';
55const uint32 kMsgSpeed = 'sped';
56const uint32 kMsgFrames = 'mfps';
57
58float	gSpeed;
59bool	gMotionBlur;
60int32	gSettingsWidth;
61int32	gWidth;
62int32	gHeight;
63float	gMaxFramesPerSecond;
64BBitmap* gBitmap;
65BScreenSaver* gScreenSaver;
66uint32	gPalette[256];
67int8	gPaletteScheme;
68int8	gBlankBorders;
69char*	gBuffer8;   /* working 8bit buffer */
70
71
72inline float
73ocos(float a)
74{
75	return (precos[(int)(a * 256 / M_PI) & 511]);
76}
77
78inline float
79osin(float a)
80{
81	return (presin[(int)(a * 256 / M_PI) & 511]);
82}
83
84
85void
86mulmat(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
100inline void
101mulvec(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
111void
112setrmat(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
134void
135rotate3d(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
157void
158drawshdisk(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
227void
228drawGalaxy()
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
299void
300setPalette()
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
418class SimpleSlider : public BSlider {
419public:
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
430	const char* UpdateText() const
431	{
432		fText.SetToFormat("%s: %d", fLabel, Value());
433		return fText.String();
434	};
435
436private:
437	mutable BString fText;
438	const char* fLabel;
439};
440
441
442// #pragma mark - SettingsView
443
444
445class SettingsView : public BView {
446public:
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
462SettingsView::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
605void
606SettingsView::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
617void
618SettingsView::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
655class Nebula : public BScreenSaver {
656public:
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
666private:
667			float				fFactor;
668			bool				fStarted;
669};
670
671
672Nebula::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
694void
695Nebula::StartConfig(BView* view)
696{
697	view->AddChild(new SettingsView(view->Bounds()));
698}
699
700
701status_t
702Nebula::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
715status_t
716Nebula::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
795void
796Nebula::StopSaver()
797{
798	free(gBuffer8);
799	gBuffer8 = NULL;
800
801	delete gBitmap;
802	gBitmap = NULL;
803}
804
805
806void
807Nebula::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
831extern "C" _EXPORT BScreenSaver*
832instantiate_screen_saver(BMessage* message, image_id image)
833{
834	return new Nebula(message, image);
835}
836