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.
6 * Effect from corTeX / Optimum.
12 #include <ColorMenuItem.h>
13 #include <ControlLook.h>
14 #include <InterfaceKit.h>
15 #include <LayoutBuilder.h>
16 #include <MenuField.h>
17 #include <ScreenSaver.h>
19 #include <SupportDefs.h>
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "Nebula Screen Saver"
37 typedef float matrix
[3][3];
44 typedef unsigned short word
;
48 #include "DrawStars.h"
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';
63 float gMaxFramesPerSecond
;
65 BScreenSaver
* gScreenSaver
;
69 char* gBuffer8
; /* working 8bit buffer */
75 return (precos
[(int)(a
* 256 / M_PI
) & 511]);
81 return (presin
[(int)(a
* 256 / M_PI
) & 511]);
86 mulmat(matrix
* a
, matrix
* b
, matrix
* c
)
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
];
101 mulvec(matrix
* a
, float* x
, float* y
, float* z
)
103 float nx
= *x
, ny
= *y
, nz
= *z
;
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];
112 setrmat(float a
, float b
, float c
, matrix
* m
)
115 for (i
= 0; i
< 3; i
++)
116 for (j
= 0; j
< 3; j
++)
117 (*m
)[i
][j
] = (float)(i
== j
);
120 (*m
)[0][0] = cos(a
); (*m
)[0][1] = sin(a
);
121 (*m
)[1][0] = sin(a
); (*m
)[1][1] = -cos(a
);
125 (*m
)[0][0] = cos(b
); (*m
)[0][2] = sin(b
);
126 (*m
)[2][0] = sin(b
); (*m
)[2][2] = -cos(b
);
129 (*m
)[1][1] = cos(c
); (*m
)[1][2] = sin(c
);
130 (*m
)[2][1] = sin(c
); (*m
)[2][2] = -cos(c
);
135 rotate3d(float* xr
, float* yr
, float* zr
, /* point to rotate */
136 float ax
, float ay
, float az
) /* the 3 angles (order ?..) */
140 xr2
= (*xr
* ocos(az
) + *yr
* osin(az
));
141 yr2
= (*xr
* osin(az
) - *yr
* ocos(az
));
145 xr2
= (*xr
* ocos(ay
) + *zr
* osin(ay
));
146 zr2
= (*xr
* osin(ay
) - *zr
* ocos(ay
));
150 zr2
= (*zr
* ocos(ax
) + *yr
* osin(ax
));
151 yr2
= (*zr
* osin(ax
) - *yr
* ocos(ax
));
158 drawshdisk(int x0
, int y0
, int r
)
164 int c
; /* color at center */
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;
177 if (r
< SLIMIT
+ SRANGE
)
178 r
= ((r
- SLIMIT
) * SLIMIT
) / SRANGE
+ 1;
180 y
= ly
= r
; /* AAaargh */
185 /* dont overlap these lines */
186 c
= ((r
- y
+ 1) << 13) / r
;
189 if (y
== x
+ 1) /* this would overlap with the next x lines */
190 goto TOTO
; /* WHY NOT */
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
197 This is clearly a stupid, unmaintanable, unreadable
198 "optimization". But i like it :)
200 if ((uint32
)(y0
- y
- 1) < gHeight
- 3)
201 memshset(&gBuffer8
[x0
+ gWidth
* (y0
- y
+ 1)], c
, d
, x
);
203 if ((uint32
)(y0
+ y
- 1) < gHeight
- 3)
204 memshset(&gBuffer8
[x0
+ gWidth
*(y0
+ y
)], c
, d
, x
);
207 c
= ((r
- x
+ 1) << 13) / r
;
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
);
219 delta
+= 4 * (x
- y
) + 10;
237 matrix ma
, mb
, mc
, mr
;
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)
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);
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
);
262 /* mblur does something like that:
263 * (or did, perhaps it's another version!..)
265 for (i = 0; i < W * H; i++)
266 gBuffer8[i]= (gBuffer8[i] >> 3) + (gBuffer8[i] >> 1);
268 mblur (gBuffer8
, gWidth
* gHeight
);
270 memset(gBuffer8
, 0, gWidth
* gHeight
);
272 for (i
= 0; i
< GMAX
; i
++) {
277 mulvec(&mr
, &rx
, &ry
, &rz
);
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;
291 if (x
> 5 && x
< gWidth
- 6 && y
> 5 && y
< gHeight
- 6)
292 // if ((uint32)x < gWidth - 1 && (uint32)y < gHeight - 1)
304 switch (gPaletteScheme
) {
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);
312 for (i
= 30; i
< 256; i
++) {
314 uint8 g
= (i
* i
>> 8); //(i*8/10);
315 uint8 b
= i
>= 240 ? (i
- 240) << 3 : 0; //(i * 2 / 10);
317 gPalette
[i
] = ((r
<< 16) | (g
<< 8) | (b
));
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);
327 for (i
= 30; i
< 256; i
++) {
329 uint8 g
= (i
* i
>> 8); //(i * 8 / 10);
330 uint8 r
= i
>= 240 ? (i
- 240) << 3 : 0; //(i * 2 / 10);
332 gPalette
[i
] = ((r
<< 16) | (g
<< 8) | (b
));
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);
342 for (i
= 128;i
< 256;i
++)
345 uint8 c
= (uint8
)((cos((i
- 256) / 42.0) * 0.5 + 0.5) * 225);
347 gPalette
[i
] = ((r
<< 16) | (c
<< 8) | c
);
349 /* for (i = 192; i < 224; i++)
352 gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
354 for (i = 224; i < 256; i++)
356 uint8 c = (i-224) / 2;
357 c = 32 + c * c * 6 / 10;
358 gPalette[i] = gPalette[i] & 0xff0000 | c << 8 | c;
363 for (i
= 0; i
< 30; i
++)
364 gPalette
[i
] = (uint8
)(i
* 8 / 10) << 8;
365 // << 16 | (uint8)(i * 6 / 10) << 8;
366 // | (uint8)(i * 3 / 10);
368 for (i
= 30; i
< 256; i
++) {
370 uint8 r
= (i
* i
>> 8); //(i * 8 / 10);
371 uint8 b
= i
>= 240 ? (i
-240) << 3 : 0; //(i * 2 / 10);
373 gPalette
[i
] = ((r
<< 16) | (g
<< 8) | (b
));
378 for (i
= 0; i
< 256; i
++) {
379 uint8 c
= i
* 15 / 16 + 10;
380 gPalette
[i
] = c
<< 16 | c
<< 8 | c
;
384 for (i
= 0; i
< 30; i
++)
385 gPalette
[i
] = (uint8
)(i
* 8 / 10) << 16;
386 // << 16 | (uint8)(i * 6 / 10) << 8;
387 // | (uint8)(i * 3 / 10);
389 for (i
= 30; i
< 256; i
++) {
391 uint8 c
= (uint8
)((cos((i
- 255) / 82.0) * 0.5 + 0.5) * 255);
393 gPalette
[i
] = ((r
<< 16) | (c
<< 8) | c
);
398 for (i
= 0; i
< 256; i
++) {
399 uint32 c
= *(char *)&i
;
400 gPalette
[i
] = c
<< 16 | c
<< 8;
404 /* for (i = 0;i < 256;i++)
407 uint8 g = (i * i >> 8); //(i * 8 / 10);
408 uint8 b = 0; //(i * 2 / 10);
410 gPalette[i] = ((r << 16) | (g << 8) | (b));
413 /* for (i = 240; i < 256; i++)
414 gPalette[i] = (uint8)i << 16 | (uint8)i << 8 | (uint8)(i * 6 / 10);
419 // #pragma mark - SimpleSlider
422 class SimpleSlider
: public BSlider
{
424 SimpleSlider(const char* label
, BMessage
* message
)
426 BSlider(B_EMPTY_STRING
, B_EMPTY_STRING
, message
, 1, 100, B_HORIZONTAL
)
428 SetLimitLabels("1", "100");
429 SetHashMarks(B_HASH_MARKS_BOTTOM
);
430 SetHashMarkCount(11);
434 const char* UpdateText() const
436 fText
.SetToFormat("%s: %d", fLabel
, Value());
437 return fText
.String();
441 mutable BString fText
;
446 // #pragma mark - SettingsView
449 class SettingsView
: public BView
{
451 SettingsView(BRect frame
);
453 virtual void AttachedToWindow();
454 virtual void MessageReceived(BMessage
* message
);
457 BMenuField
* fWidthMenuField
;
458 BMenuField
* fColorMenuField
;
459 BMenuField
* fBorderMenuField
;
460 BCheckBox
* fMotionCheck
;
461 BSlider
* fSpeedSlider
;
462 BSlider
* fFramesSlider
;
466 SettingsView::SettingsView(BRect frame
)
468 BView(frame
, "", B_FOLLOW_ALL
, B_WILL_DRAW
)
470 SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
472 BStringView
* titleString
= new BStringView(B_EMPTY_STRING
,
473 B_TRANSLATE("Nebula"));
474 titleString
->SetFont(be_bold_font
);
476 BStringView
* copyrightString
= new BStringView(B_EMPTY_STRING
,
477 B_TRANSLATE("© 2001-2004 Axel Dörfler."));
479 BPopUpMenu
* popUpMenu
;
495 size_t widthsLength
= sizeof(widths
) / sizeof(widths
[0]);
497 popUpMenu
= new BPopUpMenu("");
498 for (size_t i
= 0; i
< widthsLength
; i
++) {
501 label
.SetTo("screen resolution");
503 label
.SetToFormat("%" B_PRId32
" pixels", widths
[i
]);
505 BMessage
* message
= new BMessage(kMsgWidth
);
506 message
->AddInt32("width", widths
[i
]);
508 const char* l
= label
.String();
509 BMenuItem
* item
= new BMenuItem(B_TRANSLATE(l
), message
);
510 popUpMenu
->AddItem(item
);
511 item
->SetMarked(gSettingsWidth
== widths
[i
]);
514 fWidthMenuField
= new BMenuField("res", B_TRANSLATE("Internal width:"),
517 const char* colorSchemeLabels
[] = {
518 B_TRANSLATE("yellow"),
521 B_TRANSLATE("green"),
524 B_TRANSLATE("orange (original)")
527 rgb_color colorSchemeColors
[] = {
528 (rgb_color
){ 255, 220, 0 },
529 (rgb_color
){ 127, 219, 255 },
530 (rgb_color
){ 255, 65, 54 },
531 (rgb_color
){ 46, 204, 64 },
532 (rgb_color
){ 170, 170, 170 },
533 (rgb_color
){ 234, 234, 234 },
534 (rgb_color
){ 255, 133, 27 }
537 popUpMenu
= new BPopUpMenu("");
538 for (int i
= 0; i
< 7; i
++) {
539 BMessage
* message
= new BMessage(kMsgColorScheme
);
540 message
->AddInt8("scheme", (int8
)i
);
541 BColorMenuItem
* item
= new BColorMenuItem(colorSchemeLabels
[i
],
542 message
, colorSchemeColors
[i
]);
543 popUpMenu
->AddItem(item
);
544 item
->SetMarked(gPaletteScheme
== i
);
547 fColorMenuField
= new BMenuField("col", B_TRANSLATE("Color:"), popUpMenu
);
549 const char* blankBorderFormats
[] = {
550 B_TRANSLATE("fullscreen, no borders"),
551 B_TRANSLATE("16:9, wide-screen"),
552 B_TRANSLATE("2:3.5, cinemascope"),
553 B_TRANSLATE("only a slit")
556 popUpMenu
= new BPopUpMenu("");
557 for (int8 i
= 0; i
< 4; i
++) {
558 BMessage
* message
= new BMessage(kMsgBlankBorders
);
559 message
->AddInt8("border", i
);
560 BMenuItem
* item
= new BMenuItem(blankBorderFormats
[i
], message
);
561 popUpMenu
->AddItem(item
);
562 item
->SetMarked(gBlankBorders
== i
);
565 fBorderMenuField
= new BMenuField("cinema", B_TRANSLATE("Format:"),
568 fMotionCheck
= new BCheckBox(B_EMPTY_STRING
,
569 B_TRANSLATE("Enable motion blur"), new BMessage(kMsgMotionBlur
));
570 fMotionCheck
->SetValue((int)gMotionBlur
);
572 fSpeedSlider
= new SimpleSlider(B_TRANSLATE("Speed"),
573 new BMessage(kMsgSpeed
));
574 fSpeedSlider
->SetValue((gSpeed
- 0.002) / 0.05);
576 fFramesSlider
= new SimpleSlider(B_TRANSLATE("Maximum Frames Per Second"),
577 new BMessage(kMsgFrames
));
578 fFramesSlider
->SetValue(gMaxFramesPerSecond
);
580 BLayoutBuilder::Group
<>(this, B_VERTICAL
, B_USE_HALF_ITEM_SPACING
)
581 .SetInsets(B_USE_DEFAULT_SPACING
)
583 .Add(copyrightString
)
584 .AddStrut(roundf(be_control_look
->DefaultItemSpacing() / 2))
586 .AddGrid(B_USE_DEFAULT_SPACING
, B_USE_SMALL_SPACING
)
587 .Add(fColorMenuField
->CreateLabelLayoutItem(), 0, 0)
588 .AddGroup(B_HORIZONTAL
, 0.0f
, 1, 0)
589 .Add(fColorMenuField
->CreateMenuBarLayoutItem(), 0.0f
)
592 .Add(fWidthMenuField
->CreateLabelLayoutItem(), 0, 1)
593 .AddGroup(B_HORIZONTAL
, 0.0f
, 1, 1)
594 .Add(fWidthMenuField
->CreateMenuBarLayoutItem(), 0.0f
)
597 .Add(fBorderMenuField
->CreateLabelLayoutItem(), 0, 2)
598 .AddGroup(B_HORIZONTAL
, 0.0f
, 1, 2)
599 .Add(fBorderMenuField
->CreateMenuBarLayoutItem(), 0.0f
)
602 .Add(fMotionCheck
, 1, 3)
611 SettingsView::AttachedToWindow()
613 fWidthMenuField
->Menu()->SetTargetForItems(this);
614 fColorMenuField
->Menu()->SetTargetForItems(this);
615 fBorderMenuField
->Menu()->SetTargetForItems(this);
616 fMotionCheck
->SetTarget(this);
617 fSpeedSlider
->SetTarget(this);
618 fFramesSlider
->SetTarget(this);
623 SettingsView::MessageReceived(BMessage
* message
)
625 switch(message
->what
) {
627 message
->FindInt32("width", &gSettingsWidth
);
630 case kMsgColorScheme
:
631 if (message
->FindInt8("scheme", &gPaletteScheme
) == B_OK
)
635 case kMsgBlankBorders
:
636 message
->FindInt8("border", &gBlankBorders
);
640 gMotionBlur
= fMotionCheck
->Value() > 0;
644 gSpeed
= 0.002 + 0.05 * fSpeedSlider
->Value();
648 gMaxFramesPerSecond
= fFramesSlider
->Value();
649 gScreenSaver
->SetTickSize(
650 (bigtime_t
)(1000000LL / gMaxFramesPerSecond
));
657 // #pragma mark - Nebula
660 class Nebula
: public BScreenSaver
{
662 Nebula(BMessage
* message
, image_id id
);
664 virtual void StartConfig(BView
* view
);
665 virtual status_t
SaveState(BMessage
* state
) const;
667 virtual status_t
StartSaver(BView
* view
, bool preview
);
668 virtual void StopSaver();
669 virtual void Draw(BView
* view
, int32 frame
);
677 Nebula::Nebula(BMessage
* message
, image_id id
)
679 BScreenSaver(message
, id
),
682 message
->FindFloat("speed", 0, &gSpeed
);
683 message
->FindInt32("width", 0, &gSettingsWidth
);
684 message
->FindBool("motionblur", 0, &gMotionBlur
);
685 message
->FindFloat("max_fps", 0, &gMaxFramesPerSecond
);
686 message
->FindInt8("scheme", 0, &gPaletteScheme
);
687 message
->FindInt8("border", 0, &gBlankBorders
);
692 if (gMaxFramesPerSecond
< 1.f
)
693 gMaxFramesPerSecond
= 40.0f
;
700 Nebula::StartConfig(BView
* view
)
702 view
->AddChild(new SettingsView(view
->Bounds()));
707 Nebula::SaveState(BMessage
* state
) const
709 state
->AddFloat("speed", gSpeed
);
710 state
->AddInt32("width", gSettingsWidth
);
711 state
->AddBool("motionblur", gMotionBlur
);
712 state
->AddFloat("max_fps", gMaxFramesPerSecond
);
713 state
->AddInt8("scheme", gPaletteScheme
);
714 state
->AddInt8("border", gBlankBorders
);
721 Nebula::StartSaver(BView
* view
, bool preview
)
723 // initialize palette
727 for (i
= 0; i
< 512; i
++) {
728 precos
[i
]=cos(i
* M_PI
/ 256);
729 presin
[i
]=sin(i
* M_PI
/ 256);
733 /* for (i = 0;i < GMAX;i++)
735 gal[i].x = 1 * ((rand()&1023) - 512);
736 gal[i].y = 1 * ((rand()&1023) - 512);
737 gal[i].z = 1 * ((rand()&1023) - 512);
738 gal[i].r = rand() & 63;
742 for (i
= 0; i
< GMAX
; i
++) {
745 r
= rand() * 1.0 / RAND_MAX
;
746 r
= (1 - r
) * (1 - r
) + 0.05;
749 th
= rand() * M_PI
* 2 / RAND_MAX
;
751 th
= (rand() & 3) * M_PI_2
+ r
* r
* 2;
752 dth
= rand() * 1.0 / RAND_MAX
;
756 gal
[i
].x
= (int)(512 * r
* cos(th
));
757 gal
[i
].z
= (int)(512 * r
* sin(th
));
758 h
= (1 + cos(r
* M_PI
)) * 150;
759 dth
= rand() * 1.0 / RAND_MAX
;
760 gal
[i
].y
= (int)(h
* (dth
- 0.5));
761 gal
[i
].r
= (int)((2 - r
) * 60 + 31);
763 gal
[0].x
= gal
[0].y
= gal
[0].z
= 0;
766 if (gSettingsWidth
== 0)
767 gWidth
= view
->Bounds().Width();
769 gWidth
= gSettingsWidth
;
771 fFactor
= (view
->Bounds().Width()+1) / gWidth
;
772 if ((int)fFactor
!= fFactor
)
776 gHeight
= (int32
)((view
->Bounds().Height()+1)/fFactor
+ 0.5f
);
777 // calculate blank border format (if not in preview)
778 if (!preview
) switch (gBlankBorders
) {
780 gHeight
= (int32
)(gHeight
* 0.703125 + 0.5);
783 gHeight
= (int32
)(gHeight
* 0.534 + 0.5);
789 view
->SetScale(fFactor
);
791 gBitmap
= new BBitmap(BRect(0, 0, gWidth
- 1, gHeight
- 1), B_RGB32
);
792 gBuffer8
= (char*)malloc(gWidth
* gHeight
);
794 SetTickSize((bigtime_t
)(1000000LL / gMaxFramesPerSecond
));
813 Nebula::Draw(BView
* view
, int32
)
816 view
->SetHighColor(0, 0, 0, 0);
817 view
->FillRect(view
->Frame());
819 (view
->Bounds().Height() / fFactor
- 1 - gHeight
) / 2);
823 uint32
* buffer32
= (uint32
*)gBitmap
->Bits();
827 for (int x
= 0, end
= gWidth
* gHeight
; x
< end
; x
++)
828 buffer32
[x
] = gPalette
[(uint8
)gBuffer8
[x
]];
830 view
->DrawBitmap(gBitmap
);
834 // #pragma mark - instantiate_screen_saver
837 extern "C" _EXPORT BScreenSaver
*
838 instantiate_screen_saver(BMessage
* message
, image_id image
)
840 return new Nebula(message
, image
);