1 var gColorPalette = [];
2 var gPref = Components.classes["@mozilla.org/preferences-service;1"]
3 .getService(Components.interfaces.nsIPrefService)
10 document.getElementById("statusLabel").value =
11 document.getElementById("mbrotBundle").getString("statusEmpty");
14 function drawImage() {
15 let canvas = document.getElementById("mbrotImage");
16 if (canvas.getContext) {
17 let context = canvas.getContext("2d");
19 document.getElementById("statusLabel").value =
20 document.getElementById("mbrotBundle").getString("statusDrawing");
23 // context.fillStyle = "rgb(200,0,0)";
24 // context.fillRect (10, 10, 55, 50); // x, y, width, height
26 // context.fillStyle = "rgba(0, 0, 200, 0.5)";
27 // context.fillRect (30, 30, 55, 50);
31 let Cr_scale = Cr_max - Cr_min;
35 let Ci_scale = Ci_max - Ci_min;
37 let iterMax = gPref.getIntPref("mandelbrot.iteration_max");
38 let algorithm = gPref.getCharPref("mandelbrot.use_algorithm");
40 gStartTime = new Date();
42 for (let img_x = 0; img_x < canvas.width; img_x++) {
43 for (let img_y = 0; img_y < canvas.height; img_y++) {
44 let C = new complex(Cr_min + (img_x / canvas.width) * Cr_scale,
45 Ci_min + (img_y / canvas.height) * Ci_scale);
46 window.setTimeout(drawPoint, 0, context, img_x, img_y, C, iterMax, algorithm);
49 window.setTimeout(EndCalc, 0);
54 let endTime = new Date();
55 let timeUsed = (endTime.getTime() - gStartTime.getTime()) / 1000;
56 document.getElementById("statusLabel").value =
57 document.getElementById("mbrotBundle").getFormattedString("statusTime", [timeUsed.toFixed(3)]);
60 function complex(aReal, aImag) {
63 this.square = function() {
64 return new complex(this.r * this.r - this.i * this.i,
67 this.dist = function() {
68 return Math.sqrt(this.r * this.r + this.i * this.i);
70 this.add = function(aComplex) {
71 return new complex(this.r + aComplex.r, this.i + aComplex.i);
75 function mandelbrotValueOO (aC, aIterMax) {
76 // this would be nice code in general but it looks like JS objects are too heavy for normal use.
77 let Z = new complex(0.0, 0.0);
78 for (var iter = 0; iter < aIterMax; iter++) {
79 Z = Z.square().add(aC);
80 if (Z.r * Z.r + Z.i * Z.i > 256) { break; }
85 function mandelbrotValueNumeric (aC, aIterMax) {
86 // optimized numeric code for fast calculation
87 let Cr = aC.r, Ci = aC.i;
88 let Zr = 0.0, Zi = 0.0;
89 let Zr2 = Zr * Zr, Zi2 = Zi * Zi;
90 for (var iter = 0; iter < aIterMax; iter++) {
91 Zi = 2 * Zr * Zi + Ci;
94 Zr2 = Zr * Zr; Zi2 = Zi * Zi;
95 if (Zr2 + Zi2 > 256) { break; }
100 function getColor(aIterValue, aIterMax) {
101 let standardizedValue = Math.round(aIterValue * 1024 / aIterMax);
102 if (gColorPalette && gColorPalette.length)
103 return gColorPalette[standardizedValue];
105 // fallback to simple b/w if for some reason we don't have a palette
106 if (aIterValue == aIterMax)
109 return "rgb(255,255,255)";
112 function getColorPalette(palName) {
116 for (let i = 0; i < 1024; i++) {
117 palette[i] = 'rgb(255,255,255)';
119 palette[1024] = 'rgb(0,0,0)';
123 for (let i = 0; i < 32; i++) {
124 let cc1 = Math.floor(i * 127 / 31);
125 let cc2 = 170 - Math.floor(i * 43 / 31);
126 palette[i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc1 + ')';
129 for (let i = 0; i < 51; i++) {
130 let cc = Math.floor(i * 170 / 50);
131 palette[32 + i] = 'rgb(' + cc + ',0,' + (170 + cc) + ')';
134 for (let i = 0; i < 101; i++) {
135 let cc = Math.floor(i * 200 / 100);
136 palette[83 + i] = 'rgb(255,' + cc + ',0)';
139 for (let i = 0; i < 201; i++) {
140 let cc1 = 255 - Math.floor(i * 85 / 200);
141 let cc2 = 200 - Math.floor(i * 30 / 200);
142 let cc3 = Math.floor(i * 170 / 200);
143 palette[184 + i] = 'rgb(' + cc1 + ',' + cc2 + ',' + cc3 + ')';
145 for (let i = 0; i < 301; i++) {
146 let cc1 = 170 - Math.floor(i * 43 / 300);
147 let cc2 = 170 + Math.floor(i * 85 / 300);
148 palette[385 + i] = 'rgb(' + cc1 + ',' + cc1 + ',' + cc2 + ')';
150 for (let i = 0; i < 338; i++) {
151 let cc = 127 + Math.floor(i * 128 / 337);
152 palette[686 + i] = 'rgb(' + cc + ',' + cc + ',255)';
154 palette[1024] = 'rgb(0,0,0)';
156 case 'rainbow-linear1':
157 for (let i = 0; i < 256; i++) {
158 palette[i] = 'rgb(' + i + ',0,0)';
159 palette[256 + i] = 'rgb(255,' + i + ',0)';
160 palette[512 + i] = 'rgb(' + (255 - i) + ',255,' + i + ')';
161 palette[768 + i] = 'rgb(' + i + ',' + (255 - i) + ',255)';
163 palette[1024] = 'rgb(0,0,0)';
168 Case 1 'Standard-Palette (QB-Colors)
170 xx = CInt(i * 500 / 1024 + 2)
171 If xx <= 15 Then clr = xx
172 If xx > 15 Then clr = CInt(Sqr((xx - 15 + 1) * 15 ^ 2 / 485))
173 If xx >= 500 Then clr = 0
174 palette(i) = QBColor(clr)
176 Case 3 'Regenbogen-Palette 1 (qu.)
178 clr = CInt(i * 255 / 33)
179 palette(i) = RGB(clr, 0, 0)
182 clr = CInt(i * 255 / 136)
183 palette(34 + i) = RGB(255, clr, 0)
186 clr = CInt(i * 255 / 306)
187 palette(171 + i) = RGB(255 - clr, 255, clr)
190 clr = CInt(i * 255 / 545)
191 palette(478 + i) = RGB(clr, 255 - clr, 255)
193 Case 4 'Regenbogen-Palette 2 (linear)
195 clr = CInt(i * 255 / 204)
196 palette(i) = RGB(255, clr, 0)
197 palette(204 + i) = RGB(255 - clr, 255, 0)
198 palette(409 + i) = RGB(0, 255, clr)
199 palette(614 + i) = RGB(0, 255 - clr, 255)
200 palette(819 + i) = RGB(clr, 0, 255)
202 Case 5 'Regenbogen-Palette 2 (qu.)
204 clr = CInt(i * 255 / 18)
205 palette(i) = RGB(255, clr, 0)
208 clr = CInt(i * 255 / 73)
209 palette(20 + i) = RGB(255 - clr, 255, 0)
212 clr = CInt(i * 255 / 167)
213 palette(93 + i) = RGB(0, 255, clr)
216 clr = CInt(i * 255 / 297)
217 palette(261 + i) = RGB(0, 255 - clr, 255)
220 clr = CInt(i * 255 / 464)
221 palette(559 + i) = RGB(clr, 0, 255)
227 function drawPoint(context, img_x, img_y, C, iterMax, algorithm) {
231 itVal = mandelbrotValueOO(C, iterMax);
235 itVal = mandelbrotValueNumeric(C, iterMax);
238 context.fillStyle = getColor(itVal, iterMax);
239 context.fillRect (img_x, img_y, 1, 1); // x, y, width, height
242 /***** pure UI functions *****/
244 function saveImage() {
245 // XXX: should call filepicker!
246 saveCanvas(document.getElementById("mbrotImage"), "/home/robert/temp/canvas-save.png")
249 function updateIterMenu() {
251 var currentIter = gPref.getIntPref("mandelbrot.iteration_max");
256 if (currentIter < 10) {
258 setIter(currentIter);
261 var popup = document.getElementById("menu_iterPopup");
262 var item = popup.firstChild;
264 if (item.getAttribute("name") == "iter") {
265 if (item.getAttribute("value") == currentIter)
266 item.setAttribute("checked","true");
268 item.removeAttribute("checked");
270 item = item.nextSibling;
274 function setIter(aIter) {
275 gPref.setIntPref("mandelbrot.iteration_max", aIter);
278 function updatePaletteMenu() {
280 var currentPalette = gPref.getCharPref("mandelbrot.color_palette");
283 var currentPalette = '';
285 if (!currentPalette.length) {
286 currentPalette = 'kairo';
287 setPalette(currentPalette);
289 if (!gColorPalette || !gColorPalette.length)
290 gColorPalette = getColorPalette(currentPalette);
292 var popup = document.getElementById("menu_palettePopup");
293 var item = popup.firstChild;
295 if (item.getAttribute("name") == "palette") {
296 if (item.getAttribute("value") == currentPalette)
297 item.setAttribute("checked", "true");
299 item.removeAttribute("checked");
301 item = item.nextSibling;
305 function setPalette(aPaletteID) {
306 gPref.setCharPref("mandelbrot.color_palette", aPaletteID);
307 gColorPalette = getColorPalette(aPaletteID);
310 function updateDebugMenu() {
311 var jitMenuItem = document.getElementById("jitEnabled");
312 jitMenuItem.setAttribute("checked", gPref.getBoolPref("javascript.options.jit.chrome"));
315 function toggleJITState(jitMenuItem) {
316 var jitEnabled = !gPref.getBoolPref("javascript.options.jit.chrome");
317 gPref.setBoolPref("javascript.options.jit.chrome", jitEnabled)
318 jitMenuItem.setAttribute("checked", jitEnabled? "true" : "false");
321 function updateAlgoMenu() {
323 var currentAlgo = gPref.getCharPref("mandelbrot.use_algorithm");
326 var currentAlgo = '';
328 if (!currentAlgo.length) {
329 currentAlgo = 'numeric';
330 setAlgorithm(currentAlgo);
333 var popup = document.getElementById("menu_algoPopup");
334 var item = popup.firstChild;
336 if (item.getAttribute("name") == "algorithm") {
337 if (item.getAttribute("value") == currentAlgo)
338 item.setAttribute("checked", "true");
340 item.removeAttribute("checked");
342 item = item.nextSibling;
346 function setAlgorithm(algoID) {
347 gPref.setCharPref("mandelbrot.use_algorithm", algoID);
351 /***** helper functions from external sources *****/
353 // function below is from from http://developer.mozilla.org/en/docs/Code_snippets:Canvas
354 function saveCanvas(canvas, destFile) {
355 // convert string filepath to an nsIFile
356 var file = Components.classes["@mozilla.org/file/local;1"]
357 .createInstance(Components.interfaces.nsILocalFile);
358 file.initWithPath(destFile);
360 // create a data url from the canvas and then create URIs of the source and targets
361 var io = Components.classes["@mozilla.org/network/io-service;1"]
362 .getService(Components.interfaces.nsIIOService);
363 var source = io.newURI(canvas.toDataURL("image/png", ""), "UTF8", null);
364 var target = io.newFileURI(file);
366 // prepare to save the canvas data
367 var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
368 .createInstance(Components.interfaces.nsIWebBrowserPersist);
370 persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
371 persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
373 // displays a download dialog (remove these 3 lines for silent download)
374 var xfer = Components.classes["@mozilla.org/transfer;1"]
375 .createInstance(Components.interfaces.nsITransfer);
376 xfer.init(source, target, "", null, null, null, persist);
377 persist.progressListener = xfer;
379 // save the canvas data to the file
380 persist.saveURI(source, null, null, null, null, file);
383 // function below is from http://developer.mozilla.org/en/docs/How_to_Quit_a_XUL_Application
384 function quitApp(aForceQuit) {
385 var appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1']
386 .getService(Components.interfaces.nsIAppStartup);
388 // eAttemptQuit will try to close each XUL window, but the XUL window can cancel the quit
389 // process if there is unsaved data. eForceQuit will quit no matter what.
390 var quitSeverity = aForceQuit ? Components.interfaces.nsIAppStartup.eForceQuit :
391 Components.interfaces.nsIAppStartup.eAttemptQuit;
392 appStartup.quit(quitSeverity);