Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / test / data / third_party / kraken / hosted / explanations / darkroom.js
blob001c0544bfda573bcdf1bc5eff81dabee0cdc411
1 /* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40; -*- */
3 // The if (0) block of function definitions here tries to use
4 // faster math primitives, based on being able to reinterpret
5 // floats as ints and vice versa. We do that using the
6 // WebGL arrays.
8 if (0) {
10 var gConversionBuffer = new ArrayBuffer(4);
11 var gFloatConversion = new WebGLFloatArray(gConversionBuffer);
12 var gIntConversion = new WebGLIntArray(gConversionBuffer);
14 function AsFloat(i) {
15 gIntConversion[0] = i;
16 return gFloatConversion[0];
19 function AsInt(f) {
20 gFloatConversion[0] = f;
21 return gIntConversion[0];
24 // magic constants used for various floating point manipulations
25 var kMagicFloatToInt = (1 << 23);
26 var kOneAsInt = 0x3F800000;
27 var kScaleUp = AsFloat(0x00800000);
28 var kScaleDown = 1.0 / kScaleUp;
30 function ToInt(f) {
31 // force integer part into lower bits of mantissa
32 var i = ReinterpretFloatAsInt(f + kMagicFloatToInt);
33 // return lower bits of mantissa
34 return i & 0x3FFFFF;
37 function FastLog2(x) {
38 return (AsInt(x) - kOneAsInt) * kScaleDown;
41 function FastPower(x, p) {
42 return AsFloat(p * AsInt(x) + (1.0 - p) * kOneAsInt);
45 var LOG2_HALF = FastLog2(0.5);
47 function FastBias(b, x) {
48 return FastPower(x, FastLog2(b) / LOG2_HALF);
51 } else {
53 function FastLog2(x) {
54 return Math.log(x) / Math.LN2;
57 var LOG2_HALF = FastLog2(0.5);
59 function FastBias(b, x) {
60 return Math.pow(x, FastLog2(b) / LOG2_HALF);
65 function FastGain(g, x) {
66 return (x < 0.5) ?
67 FastBias(1.0 - g, 2.0 * x) * 0.5 :
68 1.0 - FastBias(1.0 - g, 2.0 - 2.0 * x) * 0.5;
71 function Clamp(x) {
72 return (x < 0.0) ? 0.0 : ((x > 1.0) ? 1.0 : x);
75 function ProcessImageData(imageData, params) {
76 var saturation = params.saturation;
77 var contrast = params.contrast;
78 var brightness = params.brightness;
79 var blackPoint = params.blackPoint;
80 var fill = params.fill;
81 var temperature = params.temperature;
82 var shadowsHue = params.shadowsHue;
83 var shadowsSaturation = params.shadowsSaturation;
84 var highlightsHue = params.highlightsHue;
85 var highlightsSaturation = params.highlightsSaturation;
86 var splitPoint = params.splitPoint;
88 var brightness_a, brightness_b;
89 var oo255 = 1.0 / 255.0;
91 // do some adjustments
92 fill *= 0.2;
93 brightness = (brightness - 1.0) * 0.75 + 1.0;
94 if (brightness < 1.0) {
95 brightness_a = brightness;
96 brightness_b = 0.0;
97 } else {
98 brightness_b = brightness - 1.0;
99 brightness_a = 1.0 - brightness_b;
101 contrast = contrast * 0.5;
102 contrast = (contrast - 0.5) * 0.75 + 0.5;
103 temperature = (temperature / 2000.0) * 0.1;
104 if (temperature > 0.0) temperature *= 2.0;
105 splitPoint = ((splitPoint + 1.0) * 0.5);
107 // apply to pixels
108 var sz = imageData.width * imageData.height;
109 var data = imageData.data;
110 for (var j = 0; j < sz; j++) {
111 var r = data[j*4+0] * oo255;
112 var g = data[j*4+1] * oo255;
113 var b = data[j*4+2] * oo255;
114 // convert RGB to YIQ
115 // this is a less than ideal colorspace;
116 // HSL would probably be better, but more expensive
117 var y = 0.299 * r + 0.587 * g + 0.114 * b;
118 var i = 0.596 * r - 0.275 * g - 0.321 * b;
119 var q = 0.212 * r - 0.523 * g + 0.311 * b;
120 i = i + temperature;
121 q = q - temperature;
122 i = i * saturation;
123 q = q * saturation;
124 y = (1.0 + blackPoint) * y - blackPoint;
125 y = y + fill;
126 y = y * brightness_a + brightness_b;
127 y = FastGain(contrast, Clamp(y));
129 if (y < splitPoint) {
130 q = q + (shadowsHue * shadowsSaturation) * (splitPoint - y);
131 } else {
132 i = i + (highlightsHue * highlightsSaturation) * (y - splitPoint);
135 // convert back to RGB for display
136 r = y + 0.956 * i + 0.621 * q;
137 g = y - 0.272 * i - 0.647 * q;
138 b = y - 1.105 * i + 1.702 * q;
140 // clamping is "free" as part of the ImageData object
141 data[j*4+0] = r * 255.0;
142 data[j*4+1] = g * 255.0;
143 data[j*4+2] = b * 255.0;
148 // UI code
151 var gFullCanvas = null;
152 var gFullContext = null;
153 var gFullImage = null;
154 var gDisplayCanvas = null;
155 var gDisplayContext = null;
156 var gZoomPoint = null;
157 var gDisplaySize = null;
158 var gZoomSize = [600, 600];
159 var gMouseStart = null;
160 var gMouseOrig = [0, 0];
161 var gDirty = true;
163 // If true, apply image correction to the original
164 // source image before scaling down; if false,
165 // scale down first.
166 var gCorrectBefore = false;
168 var gParams = null;
169 var gIgnoreChanges = true;
171 function OnSliderChanged() {
172 if (gIgnoreChanges)
173 return;
175 gDirty = true;
177 gParams = {};
179 // The values will come in as 0.0 .. 1.0; some params want
180 // a different range.
181 var ranges = {
182 "saturation": [0, 2],
183 "contrast": [0, 2],
184 "brightness": [0, 2],
185 "temperature": [-2000, 2000],
186 "splitPoint": [-1, 1]
189 $(".slider").each(function(index, e) {
190 var val = Math.floor($(e).slider("value")) / 1000.0;
191 var id = e.getAttribute("id");
192 if (id in ranges)
193 val = val * (ranges[id][1] - ranges[id][0]) + ranges[id][0];
194 gParams[id] = val;
197 Redisplay();
200 function ClampZoomPointToTranslation() {
201 var tx = gZoomPoint[0] - gZoomSize[0]/2;
202 var ty = gZoomPoint[1] - gZoomSize[1]/2;
203 tx = Math.max(0, tx);
204 ty = Math.max(0, ty);
206 if (tx + gZoomSize[0] > gFullImage.width)
207 tx = gFullImage.width - gZoomSize[0];
208 if (ty + gZoomSize[1] > gFullImage.height)
209 ty = gFullImage.height - gZoomSize[1];
210 return [tx, ty];
213 function Redisplay() {
214 if (!gParams)
215 return;
217 var angle =
218 (gParams.angle*2.0 - 1.0) * 90.0 +
219 (gParams.fineangle*2.0 - 1.0) * 2.0;
221 angle = Math.max(-90, Math.min(90, angle));
222 angle = (angle * Math.PI) / 180.0;
224 var processTime;
225 var processWidth, processHeight;
227 var t0 = (new Date()).getTime();
229 // Render the image with rotation; we only need to render
230 // if we're either correcting just the portion that's visible,
231 // or if we're correcting the full thing and the sliders have been
232 // changed. Otherwise, what's in the full canvas is already corrected
233 // and correct.
234 if ((gCorrectBefore && gDirty) ||
235 !gCorrectBefore)
237 gFullContext.save();
238 gFullContext.translate(Math.floor(gFullImage.width / 2), Math.floor(gFullImage.height / 2));
239 gFullContext.rotate(angle);
240 gFullContext.globalCompositeOperation = "copy";
241 gFullContext.drawImage(gFullImage,
242 -Math.floor(gFullImage.width / 2),
243 -Math.floor(gFullImage.height / 2));
244 gFullContext.restore();
247 function FullToDisplay() {
248 gDisplayContext.save();
249 if (gZoomPoint) {
250 var pt = ClampZoomPointToTranslation();
252 gDisplayContext.translate(-pt[0], -pt[1]);
253 } else {
254 gDisplayContext.translate(0, 0);
255 var ratio = gDisplaySize[0] / gFullCanvas.width;
256 gDisplayContext.scale(ratio, ratio);
259 gDisplayContext.globalCompositeOperation = "copy";
260 gDisplayContext.drawImage(gFullCanvas, 0, 0);
261 gDisplayContext.restore();
264 function ProcessCanvas(cx, canvas) {
265 var ts = (new Date()).getTime();
267 var data = cx.getImageData(0, 0, canvas.width, canvas.height);
268 ProcessImageData(data, gParams);
269 cx.putImageData(data, 0, 0);
271 processWidth = canvas.width;
272 processHeight = canvas.height;
274 processTime = (new Date()).getTime() - ts;
277 if (gCorrectBefore) {
278 if (gDirty) {
279 ProcessCanvas(gFullContext, gFullCanvas);
280 } else {
281 processTime = -1;
283 gDirty = false;
284 FullToDisplay();
285 } else {
286 FullToDisplay();
287 ProcessCanvas(gDisplayContext, gDisplayCanvas);
290 var t3 = (new Date()).getTime();
292 if (processTime != -1) {
293 $("#log")[0].innerHTML = "<p>" +
294 "Size: " + processWidth + "x" + processHeight + " (" + (processWidth*processHeight) + " pixels)<br>" +
295 "Process: " + processTime + "ms" + " Total: " + (t3-t0) + "ms<br>" +
296 "Throughput: " + Math.floor((processWidth*processHeight) / (processTime / 1000.0)) + " pixels per second<br>" +
297 "FPS: " + (Math.floor((1000.0 / (t3-t0)) * 100) / 100) + "<br>" +
298 "</p>";
299 } else {
300 $("#log")[0].innerHTML = "<p>(No stats when zoomed and no processing done)</p>";
304 function ZoomToPoint(x, y) {
305 if (gZoomSize[0] > gFullImage.width ||
306 gZoomSize[1] > gFullImage.height)
307 return;
309 var r = gDisplaySize[0] / gFullCanvas.width;
311 gDisplayCanvas.width = gZoomSize[0];
312 gDisplayCanvas.height = gZoomSize[1];
313 gZoomPoint = [x/r, y/r];
314 $("#canvas").removeClass("canzoomin").addClass("cangrab");
315 Redisplay();
318 function ZoomReset() {
319 gDisplayCanvas.width = gDisplaySize[0];
320 gDisplayCanvas.height = gDisplaySize[1];
321 gZoomPoint = null;
322 $("#canvas").removeClass("canzoomout cangrab isgrabbing").addClass("canzoomin");
323 Redisplay();
326 function LoadImage(url) {
327 if (!gFullCanvas)
328 gFullCanvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
329 if (!gDisplayCanvas)
330 gDisplayCanvas = $("#canvas")[0];
332 var img = new Image();
333 img.onload = function() {
334 var w = img.width;
335 var h = img.height;
337 gFullImage = img;
339 gFullCanvas.width = w;
340 gFullCanvas.height = h;
341 gFullContext = gFullCanvas.getContext("2d");
343 // XXX use the actual size of the visible region, so that
344 // we rescale along with the window
345 var dim = 600;
346 if (Math.max(w,h) > dim) {
347 var scale = dim / Math.max(w,h);
348 w *= scale;
349 h *= scale;
352 gDisplayCanvas.width = Math.floor(w);
353 gDisplayCanvas.height = Math.floor(h);
354 gDisplaySize = [ Math.floor(w), Math.floor(h) ];
355 gDisplayContext = gDisplayCanvas.getContext("2d");
357 $("#canvas").removeClass("canzoomin canzoomout cangrab isgrabbing");
359 if (gZoomSize[0] <= gFullImage.width &&
360 gZoomSize[1] <= gFullImage.height)
362 $("#canvas").addClass("canzoomin");
365 OnSliderChanged();
367 //img.src = "foo.jpg";
368 //img.src = "Nina6.jpg";
369 img.src = url ? url : "sunspots.jpg";
372 function SetupDnD() {
373 $("#imagedisplay").bind({
374 dragenter: function(e) {
375 $("#imagedisplay").addClass("indrag");
376 return false;
379 dragover: function(e) {
380 return false;
383 dragleave: function(e) {
384 $("#imagedisplay").removeClass("indrag");
385 return false;
388 drop: function(e) {
389 e = e.originalEvent;
390 var dt = e.dataTransfer;
391 var files = dt.files;
393 if (files.length > 0) {
394 var file = files[0];
395 var reader = new FileReader();
396 reader.onload = function(e) { LoadImage(e.target.result); };
397 reader.readAsDataURL(file);
400 $("#imagedisplay").removeClass("indrag");
401 return false;
406 function SetupZoomClick() {
407 $("#canvas").bind({
408 click: function(e) {
409 if (gZoomPoint)
410 return true;
412 var bounds = $("#canvas")[0].getBoundingClientRect();
413 var x = e.clientX - bounds.left;
414 var y = e.clientY - bounds.top;
416 ZoomToPoint(x, y);
417 return false;
420 mousedown: function(e) {
421 if (!gZoomPoint)
422 return true;
424 $("#canvas").addClass("isgrabbing");
426 gMouseOrig[0] = gZoomPoint[0];
427 gMouseOrig[1] = gZoomPoint[1];
428 gMouseStart = [ e.clientX, e.clientY ];
430 return false;
433 mouseup: function(e) {
434 if (!gZoomPoint || !gMouseStart)
435 return true;
436 $("#canvas").removeClass("isgrabbing");
438 gZoomPoint = ClampZoomPointToTranslation();
440 gZoomPoint[0] += gZoomSize[0]/2;
441 gZoomPoint[1] += gZoomSize[1]/2;
443 gMouseStart = null;
444 return false;
447 mousemove: function(e) {
448 if (!gZoomPoint || !gMouseStart)
449 return true;
451 gZoomPoint[0] = gMouseOrig[0] + (gMouseStart[0] - e.clientX);
452 gZoomPoint[1] = gMouseOrig[1] + (gMouseStart[1] - e.clientY);
453 Redisplay();
455 return false;
461 function CheckboxToggled(skipRedisplay) {
462 gCorrectBefore = $("#correct_before")[0].checked ? true : false;
464 if (!skipRedisplay)
465 Redisplay();
468 function ResetSliders() {
469 gIgnoreChanges = true;
471 $(".slider").each(function(index, e) { $(e).slider("value", 500); });
472 $("#blackPoint").slider("value", 0);
473 $("#fill").slider("value", 0);
474 $("#shadowsSaturation").slider("value", 0);
475 $("#highlightsSaturation").slider("value", 0);
477 gIgnoreChanges = false;
480 function DoReset() {
481 ResetSliders();
482 ZoomReset();
483 OnSliderChanged();
486 function DoRedisplay() {
487 Redisplay();
490 // Speed test: run 10 processings, report in thousands-of-pixels-per-second
491 function Benchmark() {
492 var times = [];
494 var width = gFullCanvas.width;
495 var height = gFullCanvas.height;
497 $("#benchmark-status")[0].innerHTML = "Resetting...";
499 ResetSliders();
501 setTimeout(RunOneTiming, 0);
503 function RunOneTiming() {
505 $("#benchmark-status")[0].innerHTML = "Running... " + (times.length + 1);
507 // reset to original image
508 gFullContext.save();
509 gFullContext.translate(Math.floor(gFullImage.width / 2), Math.floor(gFullImage.height / 2));
510 gFullContext.globalCompositeOperation = "copy";
511 gFullContext.drawImage(gFullImage,
512 -Math.floor(gFullImage.width / 2),
513 -Math.floor(gFullImage.height / 2));
514 gFullContext.restore();
516 // time the processing
517 var start = (new Date()).getTime();
518 var data = gFullContext.getImageData(0, 0, width, height);
519 ProcessImageData(data, gParams);
520 gFullContext.putImageData(data, 0, 0);
521 var end = (new Date()).getTime();
522 times.push(end - start);
524 if (times.length < 5) {
525 setTimeout(RunOneTiming, 0);
526 } else {
527 displayResults();
532 function displayResults() {
533 var totalTime = times.reduce(function(p, c) { return p + c; });
534 var totalPixels = height * width * times.length;
535 var MPixelsPerSec = totalPixels / totalTime / 1000;
536 $("#benchmark-status")[0].innerHTML = "Complete: " + MPixelsPerSec.toFixed(2) + " megapixels/sec";
537 $("#benchmark-ua")[0].innerHTML = navigator.userAgent;
541 function SetBackground(n) {
542 $("body").removeClass("blackbg whitebg graybg");
544 switch (n) {
545 case 0: // black
546 $("body").addClass("blackbg");
547 break;
548 case 1: // gray
549 $("body").addClass("graybg");
550 break;
551 case 2: // white
552 $("body").addClass("whitebg");
553 break;
557 $(function() {
558 $(".slider").slider({
559 orientation: 'horizontal',
560 range: "min",
561 max: 1000,
562 value: 500,
563 slide: OnSliderChanged,
564 change: OnSliderChanged
566 ResetSliders();
567 SetupDnD();
568 SetupZoomClick();
569 CheckboxToggled(true);
570 LoadImage();