compaa 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Rakefile +10 -2
- data/bin/compaa +6 -0
- data/compaa.gemspec +3 -0
- data/lib/assets/blender.js +59 -0
- data/lib/assets/compaa.js +138 -140
- data/lib/assets/context_blender.js +51 -0
- data/lib/assets/index.haml +4 -4
- data/lib/compaa/rack_app.rb +6 -3
- data/lib/compaa/version.rb +1 -1
- data/mock_app.rb +24 -15
- data/spec/generated_image_spec.rb +1 -1
- data/spec/integration/integration_spec.rb +1 -1
- data/spec/javascripts/BlenderSpec.js +26 -0
- data/spec/javascripts/support/expected.png +0 -0
- data/spec/javascripts/support/generated.png +0 -0
- data/spec/javascripts/support/imagediff.js +382 -0
- data/spec/javascripts/support/jasmine.yml +4 -4
- data/spec/javascripts/support/one.jpg +0 -0
- data/spec/javascripts/support/reference.png +0 -0
- data/spec/javascripts/support/three.png +0 -0
- data/spec/javascripts/support/two.jpg +0 -0
- metadata +48 -8
- data/lib/assets/jquery-2.0.0.js +0 -8755
- data/lib/assets/underscore.js +0 -1227
- data/spec/javascripts/CompaaSpec.js +0 -81
- data/spec/javascripts/support/jasmine-jquery.js +0 -658
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c8d165574630697b7fc7b420eef9892d182f145
|
4
|
+
data.tar.gz: 68192e078d30afd1805e444e72bff910e056a5f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03094bac2f80369957d8e5c1a2e365e74c9c9c25461e5709bd3644b6ece33cc98900be3718e1908316e71d56f7f957104cb07eef2746d6008e9d2069d02d84a2
|
7
|
+
data.tar.gz: c5d07e05502f92836fc34a9449c2c708f4a911cae5ec3c17279d135ac92cef9ca5f87e81372ba913050492c2d75d691e2457f486f240418895ad9840706145f1
|
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -8,6 +8,10 @@ load 'jasmine/tasks/jasmine.rake'
|
|
8
8
|
|
9
9
|
Bundler::GemHelper.install_tasks
|
10
10
|
|
11
|
+
task :start_mock do
|
12
|
+
require_relative 'mock_app'
|
13
|
+
end
|
14
|
+
|
11
15
|
namespace :spec do
|
12
16
|
Rake::TestTask.new :units do |t|
|
13
17
|
t.libs << 'spec'
|
@@ -23,11 +27,15 @@ namespace :spec do
|
|
23
27
|
t.colors = true
|
24
28
|
t.keep_on_error = true
|
25
29
|
end
|
30
|
+
|
31
|
+
task :js => :start_mock
|
26
32
|
end
|
27
33
|
|
28
|
-
task
|
34
|
+
task 'jasmine:require' => :start_mock
|
35
|
+
|
36
|
+
task :spec => ['spec:units', 'jasmine:ci', 'spec:integration']
|
29
37
|
|
30
|
-
task :default =>
|
38
|
+
task :default => :spec
|
31
39
|
|
32
40
|
task :demo do
|
33
41
|
require 'compaa'
|
data/bin/compaa
CHANGED
data/compaa.gemspec
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
1
2
|
require 'compaa/version'
|
2
3
|
|
3
4
|
Gem::Specification.new do |gem|
|
@@ -30,4 +31,6 @@ Gem::Specification.new do |gem|
|
|
30
31
|
|
31
32
|
gem.add_dependency 'haml'
|
32
33
|
gem.add_dependency 'rack'
|
34
|
+
gem.add_dependency 'launchy'
|
35
|
+
gem.add_dependency 'coffee-script'
|
33
36
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
(function() {
|
2
|
+
var supportsBlending = window.navigator.userAgent.indexOf('Firefox/2') !== -1,
|
3
|
+
firstImage = new Image(),
|
4
|
+
secondImage = new Image(),
|
5
|
+
differenceCanvas = document.createElement('canvas');
|
6
|
+
|
7
|
+
function resize(subject, width, height) {
|
8
|
+
subject.width = width;
|
9
|
+
subject.height = height;
|
10
|
+
};
|
11
|
+
|
12
|
+
function makeItBlend(imageOne, imageTwo, callback) {
|
13
|
+
firstImage = imageOne;
|
14
|
+
secondImage = imageTwo;
|
15
|
+
|
16
|
+
setBlenderWidth();
|
17
|
+
drawCanvas();
|
18
|
+
callback(differenceCanvas);
|
19
|
+
};
|
20
|
+
|
21
|
+
function setBlenderWidth() {
|
22
|
+
resize(differenceCanvas, secondImage.width, secondImage.height);
|
23
|
+
};
|
24
|
+
|
25
|
+
function drawCanvas() {
|
26
|
+
supportsBlending ? blendForRealz() : blendFallback();
|
27
|
+
};
|
28
|
+
|
29
|
+
function blendForRealz() {
|
30
|
+
var context;
|
31
|
+
|
32
|
+
context = differenceCanvas.getContext('2d');
|
33
|
+
context.globalCompositeOperation = 'difference';
|
34
|
+
context.drawImage(secondImage, 0, 0);
|
35
|
+
context.drawImage(firstImage, 0, 0);
|
36
|
+
};
|
37
|
+
|
38
|
+
function blendFallback() {
|
39
|
+
var canvas, over, under;
|
40
|
+
|
41
|
+
canvas = document.createElement('canvas');
|
42
|
+
resize(canvas, firstImage.width, firstImage.height);
|
43
|
+
|
44
|
+
over = canvas.getContext('2d');
|
45
|
+
under = differenceCanvas.getContext('2d');
|
46
|
+
|
47
|
+
over.clearRect(0, 0, over.canvas.width, over.canvas.height);
|
48
|
+
under.clearRect(0, 0, under.canvas.width, under.canvas.height);
|
49
|
+
|
50
|
+
over.drawImage(secondImage, 0, 0);
|
51
|
+
under.drawImage(firstImage, 0, 0);
|
52
|
+
|
53
|
+
over.blendOnto(under, 'difference');
|
54
|
+
};
|
55
|
+
|
56
|
+
window.blender = {
|
57
|
+
makeItBlend: makeItBlend
|
58
|
+
};
|
59
|
+
})();
|
data/lib/assets/compaa.js
CHANGED
@@ -1,153 +1,151 @@
|
|
1
|
-
|
2
|
-
imageTypes
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
1
|
+
(function() {
|
2
|
+
var imageTypes = ['difference', 'animation', 'referenceImage', 'generatedImage'],
|
3
|
+
compaaHost = '',
|
4
|
+
artifacts = [],
|
5
|
+
generatedImage = new Image(),
|
6
|
+
referenceImage = new Image();
|
7
|
+
|
8
|
+
|
9
|
+
function init() {
|
10
|
+
attachClickHandlers();
|
11
|
+
|
12
|
+
storeArtifacts(function() {
|
13
|
+
paintThePicture();
|
14
|
+
show('difference');
|
15
|
+
});
|
16
|
+
};
|
17
|
+
|
18
|
+
function storeArtifacts(callback) {
|
19
|
+
var xhr;
|
20
|
+
|
21
|
+
xhr = new XMLHttpRequest();
|
22
|
+
xhr.open('GET', compaaHost + '/artifacts.json', true);
|
23
|
+
xhr.onload = function() {
|
24
|
+
artifacts = JSON.parse(xhr.responseText).artifacts;
|
25
|
+
callback();
|
26
|
+
};
|
27
|
+
xhr.send();
|
28
|
+
};
|
29
|
+
|
30
|
+
function attachClickHandlers() {
|
31
|
+
attachButtonClickHandlers();
|
32
|
+
attachAcceptClickHandler();
|
33
|
+
attachRejectClickHandler();
|
34
|
+
};
|
35
|
+
|
36
|
+
function replaceCanvasWith(newCanvas) {
|
37
|
+
newCanvas.id = 'difference';
|
38
|
+
document.body.replaceChild(newCanvas, document.getElementById('difference'));
|
39
|
+
};
|
40
|
+
|
41
|
+
function paintThePicture() {
|
42
|
+
generatedImage.onload = function() {
|
43
|
+
referenceImage.onload = function() {
|
44
|
+
window.blender.makeItBlend(generatedImage, referenceImage, function(blendedImage) {
|
45
|
+
replaceCanvasWith(blendedImage);
|
46
|
+
setDifferenceImage();
|
47
|
+
setGeneratedImage();
|
48
|
+
setReferenceImage();
|
49
|
+
});
|
50
|
+
};
|
51
|
+
|
52
|
+
referenceImage.onerror = function() {
|
53
|
+
setGeneratedImage();
|
54
|
+
setReferenceImage();
|
55
|
+
disableOtherButtons();
|
56
|
+
show('generatedImage');
|
57
|
+
};
|
58
|
+
|
59
|
+
referenceImage.src = referenceImagePath();
|
60
|
+
};
|
61
|
+
|
62
|
+
generatedImage.src = generatedImagePath();
|
63
|
+
};
|
64
|
+
|
65
|
+
function disableOtherButtons() {
|
66
|
+
imageTypes.map(function(element) {
|
67
|
+
return document.getElementById(element + 'Button');
|
68
|
+
}).forEach(function(element) {
|
69
|
+
element.className = element.className + ' disabled';
|
70
|
+
});
|
71
|
+
};
|
72
|
+
|
73
|
+
function show(mode) {
|
74
|
+
imageTypes.forEach(function(type) {
|
75
|
+
document.getElementById(type).style.display = 'none';
|
35
76
|
});
|
36
77
|
|
37
|
-
|
38
|
-
}
|
39
|
-
|
40
|
-
attachButtonClickHandlers
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
}.bind(this));
|
46
|
-
}.bind(this));
|
47
|
-
},
|
48
|
-
|
49
|
-
supportsBlending: function() {
|
50
|
-
return window.navigator.userAgent.indexOf('Firefox/2') !== -1;
|
51
|
-
},
|
52
|
-
|
53
|
-
drawDifferenceCanvas: function() {
|
54
|
-
var generatedImage = new Image(),
|
55
|
-
referenceImage = new Image(),
|
56
|
-
context = document.getElementById('difference').getContext('2d');
|
57
|
-
|
58
|
-
$([referenceImage, generatedImage]).error(function() {
|
59
|
-
$('#difference').attr('width', 0).attr('height', 0);
|
78
|
+
document.getElementById(mode).style.display = 'inline';
|
79
|
+
};
|
80
|
+
|
81
|
+
function attachButtonClickHandlers() {
|
82
|
+
imageTypes.forEach(function(element) {
|
83
|
+
document.getElementById(element + 'Button').onclick = function() {
|
84
|
+
show(element);
|
85
|
+
};
|
60
86
|
});
|
87
|
+
};
|
88
|
+
|
89
|
+
function acceptImage() {
|
90
|
+
var url, xhr;
|
91
|
+
|
92
|
+
url = compaaHost + '/screenshots?filepath=' + generatedImagePath();
|
93
|
+
xhr = new XMLHttpRequest();
|
94
|
+
xhr.open('POST', url, true);
|
95
|
+
xhr.onload = moveToNextArtifact;
|
96
|
+
xhr.send();
|
97
|
+
};
|
98
|
+
|
99
|
+
function moveToNextArtifact() {
|
100
|
+
artifacts.shift();
|
101
|
+
if (currentArtifact()) {
|
102
|
+
paintThePicture();
|
103
|
+
} else {
|
104
|
+
endGame();
|
105
|
+
}
|
106
|
+
};
|
61
107
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
.attr('width', referenceImage.width)
|
66
|
-
.attr('height', referenceImage.height);
|
108
|
+
function attachAcceptClickHandler() {
|
109
|
+
document.getElementById('accept').onclick = acceptImage;
|
110
|
+
};
|
67
111
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
context.globalAlpha = 0.5;
|
72
|
-
}
|
112
|
+
function attachRejectClickHandler() {
|
113
|
+
document.getElementById('reject').onclick = moveToNextArtifact;
|
114
|
+
};
|
73
115
|
|
74
|
-
|
75
|
-
|
76
|
-
|
116
|
+
function setGeneratedImage() {
|
117
|
+
document.getElementById('generatedImage').src = generatedImagePath();
|
118
|
+
};
|
77
119
|
|
78
|
-
|
79
|
-
|
120
|
+
function setReferenceImage() {
|
121
|
+
document.getElementById('referenceImage').src = referenceImagePath();
|
122
|
+
};
|
80
123
|
|
81
|
-
|
82
|
-
|
124
|
+
function setDifferenceImage() {
|
125
|
+
document.getElementById('animation').src = differenceGifPath();
|
126
|
+
};
|
83
127
|
|
84
|
-
|
85
|
-
|
128
|
+
function differenceGifPath() {
|
129
|
+
return referenceImagePath().replace('reference_screenshots', 'differences_in_screenshots_this_run') + '_difference.gif';
|
130
|
+
};
|
86
131
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
},
|
132
|
+
function referenceImagePath() {
|
133
|
+
return currentArtifact();
|
134
|
+
};
|
91
135
|
|
92
|
-
|
93
|
-
|
94
|
-
}
|
136
|
+
function generatedImagePath() {
|
137
|
+
return referenceImagePath().replace('reference_screenshots', 'screenshots_generated_this_run');
|
138
|
+
};
|
95
139
|
|
96
|
-
|
97
|
-
|
140
|
+
function currentArtifact() {
|
141
|
+
return artifacts[0];
|
142
|
+
};
|
98
143
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
$('#accept').click(function(e) {
|
108
|
-
e.preventDefault();
|
109
|
-
this.acceptImage();
|
110
|
-
}.bind(this));
|
111
|
-
},
|
112
|
-
|
113
|
-
attachRejectClickHandler: function() {
|
114
|
-
$('#reject').click(function(e) {
|
115
|
-
e.preventDefault();
|
116
|
-
this.rejectImage();
|
117
|
-
}.bind(this));
|
118
|
-
},
|
119
|
-
|
120
|
-
setGeneratedImage: function() {
|
121
|
-
document.getElementById('generatedImage').src = this.generatedImagePath();
|
122
|
-
},
|
123
|
-
|
124
|
-
setReferenceImage: function() {
|
125
|
-
document.getElementById('referenceImage').src = this.referenceImagePath();
|
126
|
-
},
|
127
|
-
|
128
|
-
setDifferenceImage: function() {
|
129
|
-
document.getElementById('animation').src = this.differenceGifPath();
|
130
|
-
},
|
131
|
-
|
132
|
-
differenceGifPath: function() {
|
133
|
-
return this.referenceImagePath()
|
134
|
-
.replace('reference_screenshots', 'differences_in_screenshots_this_run') + '_difference.gif';
|
135
|
-
},
|
136
|
-
|
137
|
-
referenceImagePath: function() {
|
138
|
-
return this.currentArtifact();
|
139
|
-
},
|
140
|
-
|
141
|
-
generatedImagePath: function() {
|
142
|
-
return this.referenceImagePath()
|
143
|
-
.replace('reference_screenshots', 'screenshots_generated_this_run');
|
144
|
-
},
|
145
|
-
|
146
|
-
currentArtifact: function() {
|
147
|
-
return this.artifacts[0];
|
148
|
-
},
|
149
|
-
|
150
|
-
endGame: function() {
|
151
|
-
$('body').replaceWith('<h1>Done!</h1>');
|
152
|
-
}
|
153
|
-
};
|
144
|
+
function endGame() {
|
145
|
+
document.body.innerHTML = '<h1>Done!</h1>';
|
146
|
+
};
|
147
|
+
|
148
|
+
window.Compaa = {
|
149
|
+
init: init
|
150
|
+
};
|
151
|
+
})();
|
@@ -0,0 +1,51 @@
|
|
1
|
+
(function() {
|
2
|
+
function inGroupsOf(groupSize, collection, callback) {
|
3
|
+
for (var i = 0; i < collection.length; i += groupSize) {
|
4
|
+
callback(collection.slice(i, i + groupSize));
|
5
|
+
}
|
6
|
+
};
|
7
|
+
|
8
|
+
function range(n) {
|
9
|
+
var result = []
|
10
|
+
for (var i = 0; i < n; i++) result.push(i);
|
11
|
+
return result;
|
12
|
+
};
|
13
|
+
|
14
|
+
CanvasRenderingContext2D.prototype.blendOnto = function(destinationContext, blendMode) {
|
15
|
+
var width = Math.min(this.canvas.width, destinationContext.canvas.width),
|
16
|
+
height = Math.min(this.canvas.height, destinationContext.canvas.height);
|
17
|
+
|
18
|
+
var source = this.getImageData(0, 0, width, height),
|
19
|
+
destination = destinationContext.getImageData(0, 0, width, height);
|
20
|
+
|
21
|
+
var sourceData = source.data,
|
22
|
+
destinationData = destination.data;
|
23
|
+
|
24
|
+
inGroupsOf(4, range(destinationData.length), function(pixels) {
|
25
|
+
var redPixel = pixels[0],
|
26
|
+
greenPixel = pixels[1],
|
27
|
+
bluePixel = pixels[2],
|
28
|
+
alphaPixel = pixels[3];
|
29
|
+
|
30
|
+
|
31
|
+
var sourceAlpha = sourceData[alphaPixel] / 255;
|
32
|
+
var destinationAlpha = destinationData[alphaPixel] / 255;
|
33
|
+
var destinationAlpha2 = sourceAlpha + destinationAlpha - sourceAlpha * destinationAlpha;
|
34
|
+
|
35
|
+
var sourceRedAlpha = sourceData[redPixel] / 255 * sourceAlpha;
|
36
|
+
var destinationRedAlpha = destinationData[redPixel] / 255 * destinationAlpha;
|
37
|
+
var sourceGreenAlpha = sourceData[greenPixel] / 255 * sourceAlpha;
|
38
|
+
var destinationGreenAlpha = destinationData[greenPixel] / 255 * destinationAlpha;
|
39
|
+
var sourceBlueAlpha = sourceData[bluePixel] / 255 * sourceAlpha;
|
40
|
+
var destinationBlueAlpha = destinationData[bluePixel] / 255 * destinationAlpha;
|
41
|
+
var demultiply = 255 / destinationAlpha2;
|
42
|
+
|
43
|
+
destinationData[alphaPixel] = destinationAlpha2 * 255;
|
44
|
+
destinationData[redPixel] = (sourceRedAlpha + destinationRedAlpha - 2 * Math.min(sourceRedAlpha * destinationAlpha, destinationRedAlpha * sourceAlpha)) * demultiply;
|
45
|
+
destinationData[greenPixel] = (sourceGreenAlpha + destinationGreenAlpha - 2 * Math.min(sourceGreenAlpha * destinationAlpha, destinationGreenAlpha * sourceAlpha)) * demultiply;
|
46
|
+
destinationData[bluePixel] = (sourceBlueAlpha + destinationBlueAlpha - 2 * Math.min(sourceBlueAlpha * destinationAlpha, destinationBlueAlpha * sourceAlpha)) * demultiply;
|
47
|
+
});
|
48
|
+
|
49
|
+
destinationContext.putImageData(destination, 0, 0);
|
50
|
+
};
|
51
|
+
})();
|