/*
 * Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
 * Copyright 2015, Augustin Cavalier <waddlesplash>. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Effect from corTeX / Optimum.
 */
 
 
#include <AppKit.h>
#include <Catalog.h>
#include <ColorMenuItem.h>
#include <ControlLook.h>
#include <InterfaceKit.h>
#include <LayoutBuilder.h>
#include <MenuField.h>
#include <ScreenSaver.h>
#include <String.h>
#include <SupportDefs.h>
#include <Window.h>
 
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Nebula Screen Saver"
 
 
typedef struct
{
	int x, y, z, r;
} p3;
 
typedef float matrix[3][3];
 
#define GMAX 5000
p3 gal[GMAX];
float precos[512];
float presin[512];
 
typedef unsigned short word;
 
extern "C" {
#include "Draw.h"
#include "DrawStars.h"
}
 
const uint32 kMsgWidth  = 'widt';
const uint32 kMsgColorScheme = 'cols';
const uint32 kMsgBlankBorders = 'blbr';
const uint32 kMsgMotionBlur = 'blur';
const uint32 kMsgSpeed = 'sped';
const uint32 kMsgFrames = 'mfps';
 
float	gSpeed;
bool	gMotionBlur;
int32	gSettingsWidth;
int32	gWidth;
int32	gHeight;
float	gMaxFramesPerSecond;
BBitmap* gBitmap;
BScreenSaver* gScreenSaver;
uint32	gPalette[256];
int8	gPaletteScheme;
int8	gBlankBorders;
char*	gBuffer8;   /* working 8bit buffer */
 
 
inline float
ocos(float a)
{
	return (precos[(int)(a * 256 / M_PI) & 511]);
}
 
inline float
osin(float a)
{
	return (presin[(int)(a * 256 / M_PI) & 511]);
}
 
 
void
mulmat(matrix* a, matrix* b, matrix* c)
{
	int i, j;
 
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			(*c)[i][j] = (*a)[i][0] * (*b)[0][j] +
						 (*a)[i][1] * (*b)[1][j] +
						 (*a)[i][2] * (*b)[2][j];
		}
	}
}
 
 
inline void
mulvec(matrix* a, float* x, float* y, float* z)
{
	float nx = *x, ny = *y, nz = *z;
 
	*x = nx * (*a)[0][0] + ny * (*a)[0][1] + nz * (*a)[0][2];
	*y = nx * (*a)[1][0] + ny * (*a)[1][1] + nz * (*a)[1][2];
	*z = nx * (*a)[2][0] + ny * (*a)[2][1] + nz * (*a)[2][2];
}
 
 
void
setrmat(float a, float b, float c, matrix* m)
{
	int i, j;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 3; j++)
			(*m)[i][j] = (float)(i == j);
 
	if (a != 0) {
		(*m)[0][0] = cos(a);	(*m)[0][1] = sin(a);
		(*m)[1][0] = sin(a);	(*m)[1][1] = -cos(a);
		return;
	}
	if (b != 0) {
		(*m)[0][0] = cos(b);	(*m)[0][2] = sin(b);
		(*m)[2][0] = sin(b);	(*m)[2][2] = -cos(b);
		return;
	}
	(*m)[1][1] = cos(c);	(*m)[1][2] = sin(c);
	(*m)[2][1] = sin(c);	(*m)[2][2] = -cos(c);
}
 
 
void
rotate3d(float* xr, float* yr, float* zr,  /* point to rotate */
	float ax, float ay, float az)     /* the 3 angles (order ?..) */
{
	float xr2, yr2, zr2;
 
	xr2 = (*xr * ocos(az) + *yr * osin(az));
	yr2 = (*xr * osin(az) - *yr * ocos(az));
	*xr = xr2;
	*yr = yr2;
 
	xr2 = (*xr * ocos(ay) + *zr * osin(ay));
	zr2 = (*xr * osin(ay) - *zr * ocos(ay));
	*xr = xr2;
	*zr = zr2;
 
	zr2 = (*zr * ocos(ax) + *yr * osin(ax));
	yr2 = (*zr * osin(ax) - *yr * ocos(ax));
	*zr = zr2;
	*yr = yr2;
}
 
 
void
drawshdisk(int x0, int y0, int r)
{
	int x = 0;
	int y;
	int ly;		/* last y */
	int delta;
	int c;		/* color at center */
	int d;		/* delta */
 
#define SLIMIT 17
#define SRANGE 15
 
	if (r <= SLIMIT) {
		/* range checking is already (more or less) done... */
		draw_stars(gWidth, &gBuffer8[x0 + gWidth * y0], 10 + r * 5);
		//gBuffer8[x0 + W * y0] = 10 + r * 5;
		return;
	}
 
	if (r < SLIMIT + SRANGE)
		r = ((r - SLIMIT) * SLIMIT) / SRANGE + 1;
 
	y = ly = r;     /* AAaargh */
	delta = 3 - 2 * r;
 
	do {
		if (y != ly) {
			/* dont overlap these lines */
			c = ((r - y + 1) << 13) / r;
			d = -c / (x + 1);
 
			if (y == x + 1)		/* this would overlap with the next x lines */
				goto TOTO;		/* WHY NOT */
 
			/*  note : for "normal" numbers (not too big) :
				(unsigned int)(x) < M   <=>  0<=x<H
				(because if x<0, then (unsigned)(x) = 2**32-|x| which is
				BIG and thus >H )
 
				This is clearly a stupid, unmaintanable, unreadable
				"optimization". But i like it :)
			*/
			if ((uint32)(y0 - y - 1) < gHeight - 3)
				memshset(&gBuffer8[x0 + gWidth * (y0 - y + 1)], c, d, x);
 
			if ((uint32)(y0 + y - 1) < gHeight - 3)
				memshset(&gBuffer8[x0 + gWidth*(y0 + y)], c, d, x);
		}
		TOTO:
		c = ((r - x + 1) << 13) / r;
		d = -c / (y);
 
		if ((uint32)(y0 - x - 1) < gHeight - 3)
			memshset(&gBuffer8[x0 + gWidth*(y0 - x)], c, d, y);
		if ((uint32)(y0 + x - 1) < gHeight - 3)
			memshset(&gBuffer8[x0 + gWidth * (y0 + x + 1)], c, d, y);
 
		ly = y;
		if (delta < 0)
			delta += 4 * x + 6;
		else {
			delta += 4 * (x - y) + 10;
			y--;
		}
		x++;
	} while (x < y);
}
 
 
void
drawGalaxy()
{
	int r;
	int x, y;
	float rx, ry, rz;
	int i;
	float oa, ob, oc;
	float t;
	float a, b, c;
	matrix ma, mb, mc, mr;
 
	/* t is the parametric coordinate for the animation;
	change the scale value to change the speed of anim
	(independant of processor speed)
	*/
	static bigtime_t firstTime = system_time();
	t = ((double)gSpeed * system_time() - firstTime) / 1000000.0;
		//opti_scale_time(0.418, &demo_elapsed_time);
 
	a = 0.9 * t;
	b = t;
	c = 1.1 * t;
 
	setrmat(a, 0, 0, &ma);
	setrmat(0, b, 0, &mb);
	mulmat(&ma, &mb, &mc);
	setrmat(0, 0, c, &ma);
	mulmat(&ma, &mc, &mr);
 
	oa = 140 * osin(a);
	ob = 140 * ocos(b);
	oc = 240 * osin(c);
 
	if (gMotionBlur) {
		/* mblur does something like that:
		 * (or did, perhaps it's another version!..)
 
		for (i = 0; i < W * H; i++)
			gBuffer8[i]= (gBuffer8[i] >> 3) + (gBuffer8[i] >> 1);
		*/
		mblur (gBuffer8, gWidth * gHeight);
	} else
		memset(gBuffer8, 0, gWidth * gHeight);
 
	for (i = 0; i < GMAX; i++) {
		rx = gal[i].x;
		ry = gal[i].y;
		rz = gal[i].z;
 
		mulvec(&mr, &rx, &ry, &rz);
 
		rx += oa;
		ry += ob;
		rz += oc;
		rz += 300;
 
		if (rz > 5) {
			x = (int)(15 * rx / (rz / 5 + 1)) + gWidth / 2;
				/* tain jcomprend plus rien  */
			y = (int)(15 * ry/ (rz / 5 + 1)) + gHeight / 2;
				/* a ces formules de daube !! */
			r = (int)(3 * gal[i].r / (rz / 4 + 3)) + 2;
 
			if (x > 5 && x < gWidth - 6 && y > 5 && y < gHeight - 6)
//			if ((uint32)x < gWidth - 1 && (uint32)y < gHeight - 1)
				drawshdisk(x, y, r);
		}
	}
}
 
 
void
setPalette()
{
	int i;
 
	switch (gPaletteScheme) {
		case 0:		// yellow
		default:
			for (i = 0; i < 30; i++)
				gPalette[i] = (uint8)(i * 8 / 10) << 16
					| (uint8)(i * 6 / 10) << 8;
					// | (uint8)(i*3/10);
 
			for (i = 30; i < 256; i++) {
				uint8 r = (i);
				uint8 g = (i * i >> 8); //(i*8/10);
				uint8 b = i >= 240 ? (i - 240) << 3 : 0; //(i * 2 / 10);
 
				gPalette[i] = ((r << 16) | (g << 8) | (b));
			}
			break;
 
		case 1:		// blue
			for (i = 0; i < 30; i++)
				gPalette[i] = (uint8)(i * 8 / 10);
					// << 16 | (uint8)(i * 6 / 10) << 8;
					// | (uint8)(i * 3 / 10);
 
			for (i = 30; i < 256; i++) {
				uint8 b = (i);
				uint8 g = (i * i >> 8); //(i * 8 / 10);
				uint8 r = i >= 240 ? (i - 240) << 3 : 0; //(i * 2 / 10);
 
				gPalette[i] = ((r << 16) | (g << 8) | (b));
			}
			break;
 
		case 2:		// red
			for (i = 0; i < 128; i++)
				gPalette[i] = (uint8)i << 16;
					// << 16 | (uint8)(i * 6/10) << 8;
					// | (uint8)(i * 3 / 10);
 
			for (i = 128;i < 256; i++) {
				uint8 r = i;
				uint8 c = (uint8)((cos((i - 256) / 42.0) * 0.5 + 0.5) * 225);
 
				gPalette[i] = ((r << 16) | (c << 8) | c);
			}
/*			for (i = 192; i < 224; i++) {
				uint8 c = (i - 192);
				gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
			}
			for (i = 224; i < 256; i++) {
				uint8 c = (i-224) / 2;
				c = 32 + c * c * 6 / 10;
				gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
			}
*/			break;
 
		case 3:		// green
			for (i = 0; i < 30; i++)
				gPalette[i] = (uint8)(i * 8 / 10) << 8;
					// << 16 | (uint8)(i * 6 / 10) << 8;
					// | (uint8)(i * 3 / 10);
 
			for (i = 30; i < 256; i++) {
				uint8 g = (i);
				uint8 r = (i * i >> 8); //(i * 8 / 10);
				uint8 b = i >= 240 ? (i-240) << 3 : 0; //(i * 2 / 10);
 
				gPalette[i] = ((r << 16) | (g << 8) | (b));
			}
			break;
 
		case 4:		// grey
			for (i = 0; i < 256; i++) {
				uint8 c = i * 15 / 16 + 10;
				gPalette[i] = c << 16 | c << 8 | c;
			}
			break;
		case 5:		// cold
			for (i = 0; i < 30; i++)
				gPalette[i] = (uint8)(i * 8 / 10) << 16;
					// << 16 | (uint8)(i * 6 / 10) << 8;
					// | (uint8)(i * 3 / 10);
 
			for (i = 30; i < 256; i++) {
				uint8 r = i;
				uint8 c = (uint8)((cos((i - 255) / 82.0) * 0.5 + 0.5) * 255);
 
				gPalette[i] = ((r << 16) | (c << 8) | c);
			}
			break;
 
		case 6:		// original
			for (i = 0; i < 256; i++) {
				uint32 c = *(char *)&i;
				gPalette[i] = c << 16 | c << 8;
			}
			break;
	}
/*	for (i = 0;i < 256; i++) {
		uint8 r = (i);
		uint8 g = (i * i >> 8); //(i * 8 / 10);
		uint8 b = 0; //(i * 2 / 10);
 
		gPalette[i] = ((r << 16) | (g << 8) | (b));
	}
*/
/*	for (i = 240; i < 256; i++)
		gPalette[i] = (uint8)i << 16 | (uint8)i << 8 | (uint8)(i * 6 / 10);
*/
}
 
 
// #pragma mark - SimpleSlider
 
 
class SimpleSlider : public BSlider {
public:
	SimpleSlider(const char* label, BMessage* message)
	:
	BSlider(B_EMPTY_STRING, B_EMPTY_STRING, message, 1, 100, B_HORIZONTAL)
	{
		SetLimitLabels("1", "100");
		SetHashMarks(B_HASH_MARKS_BOTTOM);
		SetHashMarkCount(11);
		fLabel = label;
	};
 
	const char* UpdateText() const
	{
		fText.SetToFormat("%s: %d", fLabel, Value());
		return fText.String();
	};
 
private:
	mutable BString fText;
	const char* fLabel;
};
 
 
// #pragma mark - SettingsView
 
 
class SettingsView : public BView {
public:
								SettingsView(BRect frame);
 
	virtual	void				AttachedToWindow();
	virtual	void				MessageReceived(BMessage* message);
 
	private:
			BMenuField*			fWidthMenuField;
			BMenuField*			fColorMenuField;
			BMenuField*			fBorderMenuField;
			BCheckBox*			fMotionCheck;
			BSlider*			fSpeedSlider;
			BSlider*			fFramesSlider;
};
 
 
SettingsView::SettingsView(BRect frame)
	:
	BView(frame, "", B_FOLLOW_ALL, B_WILL_DRAW)
{
	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
 
	BStringView* titleString = new BStringView(B_EMPTY_STRING,
		B_TRANSLATE("Nebula"));
	titleString->SetFont(be_bold_font);
 
	BStringView* copyrightString = new BStringView(B_EMPTY_STRING,
		B_TRANSLATE("© 2001-2004 Axel Dörfler."));
 
	BPopUpMenu* popUpMenu;
 
	int32 widths[] = {
		0,
		320,
		512,
		576,
		640,
		800,
		1024,
		1152,
		1280,
		1400,
		1600
	};
 
	size_t widthsLength = sizeof(widths) / sizeof(widths[0]);
 
	popUpMenu = new BPopUpMenu("");
	for (size_t i = 0; i < widthsLength; i++) {
		BString label;
		if (widths[i] == 0)
			label.SetTo("screen resolution");
		else
			label.SetToFormat("%" B_PRId32 " pixels", widths[i]);
 
		BMessage* message = new BMessage(kMsgWidth);
		message->AddInt32("width", widths[i]);
 
		const char* l = label.String();
		BMenuItem* item = new BMenuItem(B_TRANSLATE(l), message);
		popUpMenu->AddItem(item);
		item->SetMarked(gSettingsWidth == widths[i]);
	}
 
	fWidthMenuField = new BMenuField("res", B_TRANSLATE("Internal width:"),
		popUpMenu);
 
	const char* colorSchemeLabels[] = {
		B_TRANSLATE("yellow"),
		B_TRANSLATE("cyan"),
		B_TRANSLATE("red"),
		B_TRANSLATE("green"),
		B_TRANSLATE("grey"),
		B_TRANSLATE("cold"),
		B_TRANSLATE("orange (original)")
	};
 
	rgb_color colorSchemeColors[] = {
		(rgb_color){ 255, 220, 0   },
		(rgb_color){ 127, 219, 255 },
		(rgb_color){ 255, 65,  54  },
		(rgb_color){ 46,  204, 64  },
		(rgb_color){ 170, 170, 170 },
		(rgb_color){ 234, 234, 234 },
		(rgb_color){ 255, 133, 27  }
	};
 
	popUpMenu = new BPopUpMenu("");
	for (int i = 0; i < 7; i++) {
		BMessage* message = new BMessage(kMsgColorScheme);
		message->AddInt8("scheme", (int8)i);
		BColorMenuItem* item = new BColorMenuItem(colorSchemeLabels[i],
			message, colorSchemeColors[i]);
		popUpMenu->AddItem(item);
		item->SetMarked(gPaletteScheme == i);
	}
 
	fColorMenuField = new BMenuField("col", B_TRANSLATE("Color:"), popUpMenu);
 
	const char* blankBorderFormats[] = {
		B_TRANSLATE("fullscreen, no borders"),
		B_TRANSLATE("16:9, wide-screen"),
		B_TRANSLATE("2:3.5, cinemascope"),
		B_TRANSLATE("only a slit")
	};
 
	popUpMenu = new BPopUpMenu("");
	for (int8 i = 0; i < 4; i++) {
		BMessage* message = new BMessage(kMsgBlankBorders);
		message->AddInt8("border", i);
		BMenuItem* item = new BMenuItem(blankBorderFormats[i], message);
		popUpMenu->AddItem(item);
		item->SetMarked(gBlankBorders == i);
	}
 
	fBorderMenuField = new BMenuField("cinema", B_TRANSLATE("Format:"),
		popUpMenu);
 
	fMotionCheck = new BCheckBox(B_EMPTY_STRING,
		B_TRANSLATE("Enable motion blur"), new BMessage(kMsgMotionBlur));
	fMotionCheck->SetValue((int)gMotionBlur);
 
	fSpeedSlider = new SimpleSlider(B_TRANSLATE("Speed"),
		new BMessage(kMsgSpeed));
	fSpeedSlider->SetValue((gSpeed - 0.002) / 0.05);
 
	fFramesSlider = new SimpleSlider(B_TRANSLATE("Maximum Frames Per Second"),
		new BMessage(kMsgFrames));
	fFramesSlider->SetValue(gMaxFramesPerSecond);
 
	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_HALF_ITEM_SPACING)
		.SetInsets(B_USE_DEFAULT_SPACING)
		.Add(titleString)
		.Add(copyrightString)
		.AddStrut(roundf(be_control_look->DefaultItemSpacing() / 2))
		.AddGlue()
		.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
			.Add(fColorMenuField->CreateLabelLayoutItem(), 0, 0)
			.AddGroup(B_HORIZONTAL, 0.0f, 1, 0)
				.Add(fColorMenuField->CreateMenuBarLayoutItem(), 0.0f)
				.AddGlue()
				.End()
			.Add(fWidthMenuField->CreateLabelLayoutItem(), 0, 1)
			.AddGroup(B_HORIZONTAL, 0.0f, 1, 1)
				.Add(fWidthMenuField->CreateMenuBarLayoutItem(), 0.0f)
				.AddGlue()
				.End()
			.Add(fBorderMenuField->CreateLabelLayoutItem(), 0, 2)
			.AddGroup(B_HORIZONTAL, 0.0f, 1, 2)
				.Add(fBorderMenuField->CreateMenuBarLayoutItem(), 0.0f)
				.AddGlue()
				.End()
			.Add(fMotionCheck, 1, 3)
			.End()
		.Add(fSpeedSlider)
		.Add(fFramesSlider)
	.End();
}
 
 
void
SettingsView::AttachedToWindow()
{
	fWidthMenuField->Menu()->SetTargetForItems(this);
	fColorMenuField->Menu()->SetTargetForItems(this);
	fBorderMenuField->Menu()->SetTargetForItems(this);
	fMotionCheck->SetTarget(this);
	fSpeedSlider->SetTarget(this);
	fFramesSlider->SetTarget(this);
}
 
 
void
SettingsView::MessageReceived(BMessage* message)
{
	switch(message->what) {
		case kMsgWidth:
			message->FindInt32("width", &gSettingsWidth);
			break;
 
		case kMsgColorScheme:
			if (message->FindInt8("scheme", &gPaletteScheme) == B_OK)
				setPalette();
			break;
 
		case kMsgBlankBorders:
			message->FindInt8("border", &gBlankBorders);
			break;
 
		case kMsgMotionBlur:
			gMotionBlur = fMotionCheck->Value() > 0;
			break;
 
		case kMsgSpeed:
			gSpeed = 0.002 + 0.05 * fSpeedSlider->Value();
			break;
 
		case kMsgFrames:
			gMaxFramesPerSecond = fFramesSlider->Value();
			gScreenSaver->SetTickSize(
				(bigtime_t)(1000000LL / gMaxFramesPerSecond));
			break;
	}
}
 
 
 
// #pragma mark - Nebula
 
 
class Nebula : public BScreenSaver {
public:
								Nebula(BMessage* message, image_id id);
 
	virtual	void				StartConfig(BView* view);
	virtual	status_t			SaveState(BMessage* state) const;
 
	virtual	status_t			StartSaver(BView* view, bool preview);
	virtual	void				StopSaver();
	virtual	void				Draw(BView* view, int32 frame);
 
private:
			float				fFactor;
			bool				fStarted;
};
 
 
Nebula::Nebula(BMessage* message, image_id id)
	:
	BScreenSaver(message, id),
	fStarted(false)
{
	message->FindFloat("speed", 0, &gSpeed);
	message->FindInt32("width", 0, &gSettingsWidth);
	message->FindBool("motionblur", 0, &gMotionBlur);
	message->FindFloat("max_fps", 0, &gMaxFramesPerSecond);
	message->FindInt8("scheme", 0, &gPaletteScheme);
	message->FindInt8("border", 0, &gBlankBorders);
 
	if (gSpeed < 0.01f)
		gSpeed = 0.4f;
 
	if (gMaxFramesPerSecond < 1.f)
		gMaxFramesPerSecond = 40.0f;
 
	gScreenSaver = this;
}
 
 
void
Nebula::StartConfig(BView* view)
{
	view->AddChild(new SettingsView(view->Bounds()));
}
 
 
status_t
Nebula::SaveState(BMessage* state) const
{
	state->AddFloat("speed", gSpeed);
	state->AddInt32("width", gSettingsWidth);
	state->AddBool("motionblur", gMotionBlur);
	state->AddFloat("max_fps", gMaxFramesPerSecond);
	state->AddInt8("scheme", gPaletteScheme);
	state->AddInt8("border", gBlankBorders);
 
	return B_OK;
}
 
 
status_t
Nebula::StartSaver(BView* view, bool preview)
{
	// initialize palette
	setPalette();
 
	int i;
	for (i = 0; i < 512; i++) {
		precos[i]=cos(i * M_PI / 256);
		presin[i]=sin(i * M_PI / 256);
	}
 
	// uniforme cubique
/*	for (i = 0;i < GMAX; i++) {
		gal[i].x = 1 * ((rand()&1023) - 512);
		gal[i].y = 1 * ((rand()&1023) - 512);
		gal[i].z = 1 * ((rand()&1023) - 512);
		gal[i].r = rand() & 63;
	}
*/
 
	for (i = 0; i < GMAX; i++) {
		float r, th, h, dth;
 
		r = rand() * 1.0 / RAND_MAX;
		r = (1 - r) * (1 - r) + 0.05;
 
		if (r < 0.12)
			th = rand() * M_PI * 2 / RAND_MAX;
		else {
			th = (rand() & 3) * M_PI_2 + r * r * 2;
			dth = rand() * 1.0 / RAND_MAX;
			dth = dth * dth * 2;
			th += dth;
		}
		gal[i].x = (int)(512 * r * cos(th));
		gal[i].z = (int)(512 * r * sin(th));
		h = (1 + cos(r * M_PI)) * 150;
		dth = rand() * 1.0 / RAND_MAX;
		gal[i].y = (int)(h * (dth - 0.5));
		gal[i].r = (int)((2 - r) * 60 + 31);
	}
	gal[0].x = gal[0].y = gal[0].z = 0;
	gal[0].r = 320;
 
	if (gSettingsWidth == 0)
		gWidth = view->Bounds().Width();
	else
		gWidth = gSettingsWidth;
 
	fFactor = (view->Bounds().Width()+1) / gWidth;
	if ((int)fFactor != fFactor)
		fFactor += 0.01;
 
	// 4:3
	gHeight = (int32)((view->Bounds().Height()+1)/fFactor + 0.5f);
	// calculate blank border format (if not in preview)
	if (!preview) switch (gBlankBorders) {
		case 1:		// 16:9
			gHeight = (int32)(gHeight * 0.703125 + 0.5);
			break;
		case 2:		// 2:3.5
			gHeight = (int32)(gHeight * 0.534 + 0.5);
			break;
		case 3:
			gHeight /= 5;
			break;
	}
	view->SetScale(fFactor);
 
	gBitmap = new BBitmap(BRect(0, 0, gWidth - 1, gHeight - 1), B_RGB32);
	gBuffer8 = (char*)malloc(gWidth * gHeight);
 
	SetTickSize((bigtime_t)(1000000LL / gMaxFramesPerSecond));
	fStarted = true;
 
	return B_OK;
}
 
 
void
Nebula::StopSaver()
{
	free(gBuffer8);
	gBuffer8 = NULL;
 
	delete gBitmap;
	gBitmap = NULL;
}
 
 
void
Nebula::Draw(BView* view, int32)
{
	if (fStarted) {
		view->SetHighColor(0, 0, 0, 0);
		view->FillRect(view->Frame());
		view->MovePenTo(0,
			(view->Bounds().Height() / fFactor - 1 - gHeight) / 2);
 
		fStarted = false;
	}
	uint32* buffer32 = (uint32*)gBitmap->Bits();
 
	drawGalaxy();
 
	for (int x = 0, end = gWidth * gHeight; x < end; x++)
		buffer32[x] = gPalette[(uint8)gBuffer8[x]];
 
	view->DrawBitmap(gBitmap);
}
 
 
// #pragma mark - instantiate_screen_saver
 
 
extern "C" _EXPORT BScreenSaver*
instantiate_screen_saver(BMessage* message, image_id image)
{
	return new Nebula(message, image);
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fFactor.