2 * Copyright 2016, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
6 * Augustin Cavalier <waddlesplash>
9 #include "FractalEngine.h"
17 #include "Colorsets.h"
20 FractalEngine::FractalEngine(BHandler
* parent
, BLooper
* looper
)
22 BLooper("FractalEngine"),
23 fMessenger(parent
, looper
),
27 fWidth(0), fHeight(0),
30 fColorset(Colorset_Royal
)
32 fDoSet
= &FractalEngine::DoSet_Mandelbrot
;
34 fRenderSem
= create_sem(0, "RenderSem");
35 fRenderFinishedSem
= create_sem(0, "RenderFinishedSem");
37 get_system_info(&info
);
38 fThreadCount
= info
.cpu_count
;
39 if (fThreadCount
>= 4)
41 for (uint8 i
= 0; i
< fThreadCount
; i
++) {
42 fRenderThreads
[i
] = spawn_thread(&FractalEngine::RenderThread
,
43 BString().SetToFormat("RenderThread%d", i
).String(),
44 B_NORMAL_PRIORITY
, this);
45 resume_thread(fRenderThreads
[i
]);
50 FractalEngine::~FractalEngine()
55 void FractalEngine::MessageReceived(BMessage
* msg
)
59 switch (msg
->GetUInt8("set", 0)) {
60 case 0: fDoSet
= &FractalEngine::DoSet_Mandelbrot
; break;
61 case 1: fDoSet
= &FractalEngine::DoSet_BurningShip
; break;
62 case 2: fDoSet
= &FractalEngine::DoSet_Tricorn
; break;
63 case 3: fDoSet
= &FractalEngine::DoSet_Julia
; break;
64 case 4: fDoSet
= &FractalEngine::DoSet_OrbitTrap
; break;
65 case 5: fDoSet
= &FractalEngine::DoSet_Multibrot
; break;
69 switch (msg
->GetUInt8("palette", 0)) {
70 case 0: fColorset
= Colorset_Royal
; break;
71 case 1: fColorset
= Colorset_DeepFrost
; break;
72 case 2: fColorset
= Colorset_Frost
; break;
73 case 3: fColorset
= Colorset_Fire
; break;
74 case 4: fColorset
= Colorset_Midnight
; break;
75 case 5: fColorset
= Colorset_Grassland
; break;
76 case 6: fColorset
= Colorset_Lightning
; break;
77 case 7: fColorset
= Colorset_Spring
; break;
78 case 8: fColorset
= Colorset_HighContrast
; break;
81 case MSG_SET_ITERATIONS
:
82 fIterations
= msg
->GetUInt16("iterations", 0);
86 delete fBitmapStandby
;
87 // We don't delete the "display" bitmap; the viewer now owns it
90 fWidth
= msg
->GetUInt16("width", 320);
91 fHeight
= msg
->GetUInt16("height", 240);
92 BRect
rect(0, 0, fWidth
- 1, fHeight
- 1);
93 fBitmapStandby
= new BBitmap(rect
, B_RGB24
);
94 fBitmapDisplay
= new BBitmap(rect
, B_RGB24
);
95 fRenderBufferLen
= fWidth
* fHeight
* 3;
96 fRenderBuffer
= new uint8
[fRenderBufferLen
];
100 // Render to "standby" bitmap
101 Render(msg
->GetDouble("locationX", 0), msg
->GetDouble("locationY", 0),
102 msg
->GetDouble("size", 0.005));
103 BMessage
message(MSG_RENDER_COMPLETE
);
104 message
.AddPointer("bitmap", (const void*)fBitmapStandby
);
105 fMessenger
.SendMessage(&message
);
106 std::swap(fBitmapStandby
, fBitmapDisplay
);
111 BLooper::MessageReceived(msg
);
117 void FractalEngine::Render(double locationX
, double locationY
, double size
)
119 fLocationX
= locationX
;
120 fLocationY
= locationY
;
122 for (uint8 i
= 0; i
< fThreadCount
; i
++)
123 release_sem(fRenderSem
);
124 for (uint8 i
= 0; i
< fThreadCount
; i
++)
125 acquire_sem(fRenderFinishedSem
);
127 fBitmapStandby
->ImportBits(fRenderBuffer
, fRenderBufferLen
, fWidth
* 3,
132 status_t
FractalEngine::RenderThread(void* data
)
134 FractalEngine
* engine
= static_cast<FractalEngine
*>(data
);
135 thread_id self
= find_thread(NULL
);
137 for (uint8 i
= 0; i
< engine
->fThreadCount
; i
++) {
138 if (engine
->fRenderThreads
[i
] == self
) {
145 acquire_sem(engine
->fRenderSem
);
147 uint16 halfWidth
= engine
->fWidth
/ 2;
148 uint16 halfHeight
= engine
->fHeight
/ 2;
149 const uint32 startY
= (engine
->fHeight
/ engine
->fThreadCount
) * threadNum
,
150 endY
= (engine
->fHeight
/ engine
->fThreadCount
) * (threadNum
+ 1);
151 for (uint32 x
= 0; x
< engine
->fWidth
; x
++) {
152 for (uint32 y
= startY
; y
< endY
; y
++) {
153 engine
->RenderPixel(x
, y
,
154 (x
* engine
->fSize
+ engine
->fLocationX
) - (halfWidth
* engine
->fSize
),
155 (y
* -(engine
->fSize
) + engine
->fLocationY
) - (halfHeight
* -(engine
->fSize
)));
159 release_sem(engine
->fRenderFinishedSem
);
165 void FractalEngine::RenderPixel(uint32 x
, uint32 y
, double real
,
168 int32 iterToEscape
= (this->*fDoSet
)(real
, imaginary
);
170 if (iterToEscape
== -1) {
174 loc
= 998 - (iterToEscape
% 999);
177 uint32 offsetBase
= fWidth
* y
* 3 + x
* 3;
179 fRenderBuffer
[offsetBase
+ 0] = fColorset
[loc
+ 0];
180 fRenderBuffer
[offsetBase
+ 1] = fColorset
[loc
+ 1];
181 fRenderBuffer
[offsetBase
+ 2] = fColorset
[loc
+ 2];
185 // Magic numbers & other general constants
186 const double gJuliaA
= 0;
187 const double gJuliaB
= 1;
189 const uint8 gEscapeHorizon
= 4;
192 int32
FractalEngine::DoSet_Mandelbrot(double real
, double imaginary
)
195 double zImaginary
= 0;
197 for (int32 i
= 0; i
< fIterations
; i
++) {
198 double zRealSq
= zReal
* zReal
;
199 double zImaginarySq
= zImaginary
* zImaginary
;
200 double nzReal
= (zRealSq
+ (-1 * zImaginarySq
));
202 zImaginary
= 2 * (zReal
* zImaginary
);
206 zImaginary
+= imaginary
;
208 // If it is outside the 2 unit circle...
209 if ((zRealSq
+ zImaginarySq
) > gEscapeHorizon
) {
210 return i
; // stop it from running longer
217 int32
FractalEngine::DoSet_BurningShip(double real
, double imaginary
)
220 double zImaginary
= 0;
222 // It looks "upside down" otherwise.
223 imaginary
= -imaginary
;
225 for (int32 i
= 0; i
< fIterations
; i
++) {
227 zImaginary
= fabs(zImaginary
);
229 double zRealSq
= zReal
* zReal
;
230 double zImaginarySq
= zImaginary
* zImaginary
;
231 double nzReal
= (zRealSq
+ (-1 * zImaginarySq
));
233 zImaginary
= 2 * (zReal
* zImaginary
);
237 zImaginary
+= imaginary
;
239 // If it is outside the 2 unit circle...
240 if ((zRealSq
+ zImaginarySq
) > gEscapeHorizon
) {
241 return i
; // stop it from running longer
248 int32
FractalEngine::DoSet_Tricorn(double real
, double imaginary
)
251 double zImaginary
= 0;
255 for (int32 i
= 0; i
< fIterations
; i
++) {
256 double znRe
= zImaginary
* -1;
257 zImaginary
= zReal
* -1;
258 zReal
= znRe
; // Swap the real and complex parts each time.
260 double zRealSq
= zReal
* zReal
;
261 double zImaginarySq
= zImaginary
* zImaginary
;
262 double nzReal
= (zRealSq
+ (-1 * zImaginarySq
));
264 zImaginary
= 2 * (zReal
* zImaginary
);
268 zImaginary
+= imaginary
;
270 // If it is outside the 2 unit circle...
271 if ((zRealSq
+ zImaginarySq
) > gEscapeHorizon
) {
272 return i
; // stop it from running longer
279 int32
FractalEngine::DoSet_Julia(double real
, double imaginary
)
282 double zImaginary
= imaginary
;
284 double muRe
= gJuliaA
;
285 double muIm
= gJuliaB
;
287 for (int32 i
= 0; i
< fIterations
; i
++) {
288 double zRealSq
= zReal
* zReal
;
289 double zImaginarySq
= zImaginary
* zImaginary
;
290 double nzReal
= (zRealSq
+ (-1 * (zImaginarySq
)));
292 zImaginary
= 2 * (zReal
* zImaginary
);
298 // If it is outside the 2 unit circle...
299 if ((zRealSq
+ zImaginarySq
) > gEscapeHorizon
) {
300 return i
; // stop it from running longer
307 int32
FractalEngine::DoSet_OrbitTrap(double real
, double imaginary
)
310 double zImaginary
= 0;
312 double closest
= 10000000;
316 for (int32 i
= 0; i
< fIterations
; i
++) {
317 double zRealSq
= zReal
* zReal
;
318 double zImaginarySq
= zImaginary
* zImaginary
;
319 double nzReal
= (zRealSq
+ (-1 * zImaginarySq
));
321 zImaginary
= 2 * (zReal
* zImaginary
);
325 zImaginary
+= imaginary
;
327 distance
= sqrt(zRealSq
+ zImaginarySq
);
328 lineDist
= fabs(zReal
+ zImaginary
);
330 // If it is closer than ever before...
331 if (lineDist
< closest
)
334 if (distance
> gEscapeHorizon
) {
335 return static_cast<int32
>(floor(4 * log(4 / closest
)));
338 return static_cast<int32
>(floor(4 * log(4 / closest
)));
342 int32
FractalEngine::DoSet_Multibrot(double real
, double imaginary
)
345 double zImaginary
= 0;
347 for (int32 i
= 0; i
< fIterations
; i
++) {
348 double zRealSq
= zReal
* zReal
;
349 double zImaginarySq
= zImaginary
* zImaginary
;
350 double nzReal
= (zRealSq
* zReal
- 3 * zReal
* zImaginarySq
);
352 zImaginary
= 3 * (zRealSq
* zImaginary
) - (zImaginarySq
* zImaginary
);
356 zImaginary
+= imaginary
;
358 // If it is outside the 2 unit circle...
359 if ((zRealSq
+ zImaginarySq
) > gEscapeHorizon
) {
360 return i
; // stop it from running longer