2008-02-13 11:36:03 +03:00
<?xml version="1.0" encoding="UTF-8"?>
2014-02-17 04:05:09 +04:00
<!-- - * - Mode: HTML; tab - width: 2; indent - tabs - mode: nil; - * - -->
<!-- vim: set shiftwidth=2 tabstop=2 autoindent expandtab: -->
2012-05-21 15:12:37 +04:00
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
2008-02-14 00:58:28 +03:00
<!--
Features to add:
2010-05-13 16:19:50 +04:00
* make the left and right parts of the viewer independently scrollable
2008-02-14 00:58:28 +03:00
* make the test list filterable
** default to only showing unexpecteds
* add other ways to highlight differences other than circling?
* add zoom/pan to images
* Add ability to load log via XMLHttpRequest (also triggered via URL param)
* color the test list based on pass/fail and expected/unexpected/random/skip
* ability to load multiple logs ?
** rename them by clicking on the name and editing
** turn the test list into a collapsing tree view
** move log loading into popup from viewer UI
-->
2011-07-15 22:08:00 +04:00
<!DOCTYPE html>
2008-02-14 00:58:28 +03:00
< html lang = "en-US" xml:lang = "en-US" xmlns = "http://www.w3.org/1999/xhtml" >
2008-02-13 11:36:03 +03:00
< head >
2011-01-24 07:21:40 +03:00
< title > Reftest analyzer< / title >
< style type = "text/css" > < ! [ C D A T A [
2008-02-13 11:36:03 +03:00
2011-04-30 09:32:44 +04:00
html, body { margin: 0; }
html { padding: 0; }
body { padding: 4px; }
2011-02-02 19:40:17 +03:00
2012-07-28 14:31:26 +04:00
#pixelarea, #itemlist, #images { position: absolute; }
2011-04-30 09:32:44 +04:00
#itemlist, #images { overflow: auto; }
#pixelarea { top: 0; left: 0; width: 320px; height: 84px; overflow: visible }
2011-02-02 19:40:17 +03:00
#itemlist { top: 84px; left: 0; width: 320px; bottom: 0; }
#images { top: 0; bottom: 0; left: 320px; right: 0; }
2011-01-24 07:21:40 +03:00
#leftpane { width: 320px; }
#images { position: fixed; top: 10px; left: 340px; }
2008-02-13 11:36:03 +03:00
2011-01-24 07:21:40 +03:00
form#imgcontrols { margin: 0; display: block; }
2008-02-13 11:36:03 +03:00
2011-01-24 07:21:40 +03:00
#itemlist > table { border-collapse: collapse; }
#itemlist > table > tbody > tr > td { border: 1px solid; padding: 1px; }
2008-02-13 11:36:03 +03:00
2011-01-24 07:21:40 +03:00
/*
#itemlist > table > tbody > tr.pass > td.url { background: lime; }
#itemlist > table > tbody > tr.fail > td.url { background: red; }
*/
2008-02-13 11:36:03 +03:00
2011-02-02 19:40:17 +03:00
#magnification > svg { display: block; width: 84px; height: 84px; }
#pixelinfo { font: small sans-serif; position: absolute; width: 200px; left: 84px; }
#pixelinfo table { border-collapse: collapse; }
#pixelinfo table th { white-space: nowrap; text-align: left; padding: 0; }
#pixelinfo table td { font-family: monospace; padding: 0 0 0 0.25em; }
2011-01-24 07:21:40 +03:00
#pixelhint { display: inline; color: #88f; cursor: help; }
#pixelhint > * { display: none; position: absolute; margin: 8px 0 0 8px; padding: 4px; width: 400px; background: #ffa; color: black; box-shadow: 3px 3px 2px #888; z-index: 1; }
#pixelhint:hover { color: #000; }
#pixelhint:hover > * { display: block; }
#pixelhint p { margin: 0; }
#pixelhint p + p { margin-top: 1em; }
2011-01-24 07:21:40 +03:00
]]>< / style >
< script type = "text/javascript" > < ! [ C D A T A [
2008-02-13 11:36:03 +03:00
var XLINK_NS = "http://www.w3.org/1999/xlink";
var SVG_NS = "http://www.w3.org/2000/svg";
2012-05-30 17:59:42 +04:00
var IMAGE_NOT_AVAILABLE = "data:image/png;base64,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";
2008-02-13 11:36:03 +03:00
var gPhases = null;
2008-02-13 12:45:54 +03:00
var gIDCache = {};
2011-01-24 07:21:40 +03:00
var gMagPixPaths = []; // 2D array of array-of-two < path > objects used in the pixel magnifier
var gMagWidth = 5; // number of zoomed in pixels to show horizontally
var gMagHeight = 5; // number of zoomed in pixels to show vertically
var gMagZoom = 16; // size of the zoomed in pixels
var gImage1Data; // ImageData object for the reference image
var gImage2Data; // ImageData object for the test output image
var gFlashingPixels = []; // array of < path > objects that should be flashed due to pixel color mismatch
2014-09-03 22:37:22 +04:00
var gParams;
2011-01-24 07:21:40 +03:00
2008-02-13 12:45:54 +03:00
function ID(id) {
2011-01-24 07:21:40 +03:00
if (!(id in gIDCache))
gIDCache[id] = document.getElementById(id);
return gIDCache[id];
2008-02-13 12:45:54 +03:00
}
2008-02-13 11:36:03 +03:00
2011-04-30 09:33:15 +04:00
function hash_parameters() {
var result = { };
var params = window.location.hash.substr(1).split(/[&; ]/);
for (var i = 0; i < params.length ; i + + ) {
var parts = params[i].split("=");
result[parts[0]] = unescape(unescape(parts[1]));
}
return result;
}
2008-02-13 11:36:03 +03:00
function load() {
2011-01-24 07:21:40 +03:00
gPhases = [ ID("entry"), ID("loading"), ID("viewer") ];
2011-01-24 07:21:40 +03:00
build_mag();
2014-09-03 22:37:22 +04:00
gParams = hash_parameters();
if (gParams.log) {
ID("logentry").value = gParams.log;
2011-04-30 09:33:15 +04:00
log_pasted();
2014-09-03 22:37:22 +04:00
} else if (gParams.logurl) {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState === 4) {
ID("logentry").value = req.responseText;
log_pasted();
}
};
req.open('GET', gParams.logurl, true);
req.send();
2011-04-30 09:33:15 +04:00
}
2012-01-31 17:18:09 +04:00
window.addEventListener('keypress', maybe_load_image, false);
2012-05-30 17:59:42 +04:00
ID("image1").addEventListener('error', image_load_error, false);
ID("image2").addEventListener('error', image_load_error, false);
}
function image_load_error(e) {
e.target.setAttributeNS(XLINK_NS, "xlink:href", IMAGE_NOT_AVAILABLE);
2011-01-24 07:21:40 +03:00
}
function build_mag() {
var mag = ID("mag");
var r = document.createElementNS(SVG_NS, "rect");
r.setAttribute("x", gMagZoom * -gMagWidth / 2);
r.setAttribute("y", gMagZoom * -gMagHeight / 2);
r.setAttribute("width", gMagZoom * gMagWidth);
r.setAttribute("height", gMagZoom * gMagHeight);
mag.appendChild(r);
mag.setAttribute("transform", "translate(" + (gMagZoom * (gMagWidth / 2) + 1) + "," + (gMagZoom * (gMagHeight / 2) + 1) + ")");
for (var x = 0; x < gMagWidth ; x + + ) {
gMagPixPaths[x] = [];
for (var y = 0; y < gMagHeight ; y + + ) {
var p1 = document.createElementNS(SVG_NS, "path");
p1.setAttribute("d", "M" + ((x - gMagWidth / 2) + 1) * gMagZoom + "," + (y - gMagHeight / 2) * gMagZoom + "h" + -gMagZoom + "v" + gMagZoom);
p1.setAttribute("stroke", "black");
p1.setAttribute("stroke-width", "1px");
p1.setAttribute("fill", "#aaa");
var p2 = document.createElementNS(SVG_NS, "path");
p2.setAttribute("d", "M" + ((x - gMagWidth / 2) + 1) * gMagZoom + "," + (y - gMagHeight / 2) * gMagZoom + "v" + gMagZoom + "h" + -gMagZoom);
p2.setAttribute("stroke", "black");
p2.setAttribute("stroke-width", "1px");
p2.setAttribute("fill", "#888");
mag.appendChild(p1);
mag.appendChild(p2);
gMagPixPaths[x][y] = [p1, p2];
}
}
var flashedOn = false;
setInterval(function() {
flashedOn = !flashedOn;
flash_pixels(flashedOn);
}, 500);
2008-02-13 11:36:03 +03:00
}
function show_phase(phaseid) {
2011-01-24 07:21:40 +03:00
for (var i in gPhases) {
var phase = gPhases[i];
phase.style.display = (phase.id == phaseid) ? "" : "none";
}
2008-02-13 11:36:03 +03:00
2011-01-24 07:21:40 +03:00
if (phase == "viewer")
ID("images").style.display = "none";
2008-02-13 11:36:03 +03:00
}
function fileentry_changed() {
2011-01-24 07:21:40 +03:00
show_phase("loading");
var input = ID("fileentry");
var files = input.files;
if (files.length > 0) {
// Only handle the first file; don't handle multiple selection.
// The parts of the log we care about are ASCII-only. Since we
// can ignore lines we don't care about, best to read in as
// iso-8859-1, which guarantees we don't get decoding errors.
2011-06-07 21:03:02 +04:00
var fileReader = new FileReader();
fileReader.onload = function(e) {
var log = null;
log = e.target.result;
2014-02-17 04:05:09 +04:00
2011-06-07 21:03:02 +04:00
if (log)
process_log(log);
else
show_phase("entry");
}
fileReader.readAsText(files[0], "iso-8859-1");
2011-01-24 07:21:40 +03:00
}
// So the user can process the same filename again (after
// overwriting the log), clear the value on the form input so we
// will always get an onchange event.
input.value = "";
2008-02-13 11:36:03 +03:00
}
function log_pasted() {
2011-01-24 07:21:40 +03:00
show_phase("loading");
var entry = ID("logentry");
var log = entry.value;
entry.value = "";
process_log(log);
2008-02-13 11:36:03 +03:00
}
var gTestItems;
function process_log(contents) {
2011-01-24 07:21:40 +03:00
var lines = contents.split(/[\r\n]+/);
gTestItems = [];
for (var j in lines) {
var line = lines[j];
2014-05-28 23:07:43 +04:00
// Ignore duplicated output in logcat.
if (line.match(/^I\/Gecko/))
continue;
2014-01-25 14:03:54 +04:00
var match = line.match(/^.*?REFTEST (.*)$/);
2011-01-24 07:21:40 +03:00
if (!match)
continue;
line = match[1];
2011-11-19 07:57:29 +04:00
match = line.match(/^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL|TEST-DEBUG-INFO)(\(EXPECTED RANDOM\)|) \| ([^\|]+) \|(.*)/);
2011-01-24 07:21:40 +03:00
if (match) {
var state = match[1];
var random = match[2];
var url = match[3];
2014-02-17 04:05:09 +04:00
var extra = match[4];
2011-01-24 07:21:40 +03:00
gTestItems.push(
{
2011-11-19 07:57:29 +04:00
pass: !state.match(/DEBUG-INFO$|FAIL$/),
2011-01-24 07:21:40 +03:00
// only one of the following three should ever be true
unexpected: !!state.match(/^TEST-UNEXPECTED/),
random: (random == "(EXPECTED RANDOM)"),
skip: (extra == " (SKIP)"),
url: url,
2014-05-28 23:07:45 +04:00
images: [],
imageLabels: []
2011-01-24 07:21:40 +03:00
});
continue;
}
2014-10-26 03:15:25 +03:00
match = line.match(/^(?: |)IMAGE ([^:]*): (data:.*)$/);
2011-01-24 07:21:40 +03:00
if (match) {
var item = gTestItems[gTestItems.length - 1];
2014-05-28 23:07:45 +04:00
item.images.push(match[2]);
item.imageLabels.push(match[1]);
2011-01-24 07:21:40 +03:00
}
}
build_viewer();
2008-02-13 11:36:03 +03:00
}
function build_viewer() {
2011-01-24 07:21:40 +03:00
if (gTestItems.length == 0) {
show_phase("entry");
return;
}
var cell = ID("itemlist");
while (cell.childNodes.length > 0)
cell.removeChild(cell.childNodes[cell.childNodes.length - 1]);
var table = document.createElement("table");
var tbody = document.createElement("tbody");
table.appendChild(tbody);
for (var i in gTestItems) {
var item = gTestItems[i];
2014-09-03 22:37:22 +04:00
// optional url filter for only showing unexpected results
if (parseInt(gParams.only_show_unexpected) & & !item.unexpected)
continue;
// XXX regardless skip expected pass items until we have filtering UI
2011-01-24 07:21:40 +03:00
if (item.pass & & !item.unexpected)
continue;
var tr = document.createElement("tr");
var rowclass = item.pass ? "pass" : "fail";
var td;
var text;
td = document.createElement("td");
text = "";
if (item.unexpected) { text += "!"; rowclass += " unexpected"; }
if (item.random) { text += "R"; rowclass += " random"; }
if (item.skip) { text += "S"; rowclass += " skip"; }
td.appendChild(document.createTextNode(text));
tr.appendChild(td);
td = document.createElement("td");
td.className = "url";
// Only display part of URL after "/mozilla/".
var match = item.url.match(/\/mozilla\/(.*)/);
text = document.createTextNode(match ? match[1] : item.url);
if (item.images.length > 0) {
var a = document.createElement("a");
a.href = "javascript:show_images(" + i + ")";
a.appendChild(text);
td.appendChild(a);
} else {
td.appendChild(text);
}
tr.appendChild(td);
tbody.appendChild(tr);
}
cell.appendChild(table);
show_phase("viewer");
2008-02-13 11:36:03 +03:00
}
2011-01-24 07:21:40 +03:00
function get_image_data(src, whenReady) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement("canvas");
2014-10-22 13:48:36 +04:00
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
2011-01-24 07:21:40 +03:00
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
2014-10-22 13:48:36 +04:00
whenReady(ctx.getImageData(0, 0, img.naturalWidth, img.naturalHeight));
2011-01-24 07:21:40 +03:00
};
img.src = src;
}
2014-10-22 13:48:36 +04:00
function sync_svg_size(imageData) {
// We need the size of the 'svg' and its 'image' elements to match the size
// of the ImageData objects that we're going to read pixels from or else our
// magnify() function will be very broken.
ID("svg").setAttribute("width", imageData.width);
ID("svg").setAttribute("height", imageData.height);
}
2008-02-13 11:36:03 +03:00
function show_images(i) {
2011-01-24 07:21:40 +03:00
var item = gTestItems[i];
var cell = ID("images");
ID("image1").style.display = "";
ID("image2").style.display = "none";
ID("diffrect").style.display = "none";
ID("imgcontrols").reset();
ID("image1").setAttributeNS(XLINK_NS, "xlink:href", item.images[0]);
// Making the href be #image1 doesn't seem to work
ID("feimage1").setAttributeNS(XLINK_NS, "xlink:href", item.images[0]);
if (item.images.length == 1) {
ID("imgcontrols").style.display = "none";
} else {
ID("imgcontrols").style.display = "";
ID("image2").setAttributeNS(XLINK_NS, "xlink:href", item.images[1]);
// Making the href be #image2 doesn't seem to work
ID("feimage2").setAttributeNS(XLINK_NS, "xlink:href", item.images[1]);
2014-05-28 23:07:45 +04:00
ID("label1").textContent = 'Image ' + item.imageLabels[0];
ID("label2").textContent = 'Image ' + item.imageLabels[1];
2011-01-24 07:21:40 +03:00
}
cell.style.display = "";
2011-01-24 07:21:40 +03:00
2014-10-22 13:48:36 +04:00
get_image_data(item.images[0], function(data) { gImage1Data = data; sync_svg_size(gImage1Data); });
2011-01-24 07:21:40 +03:00
get_image_data(item.images[1], function(data) { gImage2Data = data });
2008-02-13 11:36:03 +03:00
}
function show_image(i) {
2011-01-24 07:21:40 +03:00
if (i == 1) {
ID("image1").style.display = "";
ID("image2").style.display = "none";
} else {
ID("image1").style.display = "none";
ID("image2").style.display = "";
}
2008-02-13 11:36:03 +03:00
}
2012-01-31 17:18:09 +04:00
function maybe_load_image(event) {
switch (event.charCode) {
case 49: // "1" key
document.getElementById("radio1").checked = true;
show_image(1);
break;
case 50: // "2" key
document.getElementById("radio2").checked = true;
show_image(2);
break;
}
}
2008-02-13 11:36:03 +03:00
function show_differences(cb) {
2011-01-24 07:21:40 +03:00
ID("diffrect").style.display = cb.checked ? "" : "none";
2008-02-13 11:36:03 +03:00
}
2011-01-24 07:21:40 +03:00
function flash_pixels(on) {
var stroke = on ? "red" : "black";
var strokeWidth = on ? "2px" : "1px";
for (var i = 0; i < gFlashingPixels.length ; i + + ) {
gFlashingPixels[i].setAttribute("stroke", stroke);
gFlashingPixels[i].setAttribute("stroke-width", strokeWidth);
}
}
function cursor_point(evt) {
var m = evt.target.getScreenCTM().inverse();
var p = ID("svg").createSVGPoint();
p.x = evt.clientX;
p.y = evt.clientY;
p = p.matrixTransform(m);
return { x: Math.floor(p.x), y: Math.floor(p.y) };
}
function hex2(i) {
return (i < 16 ? " 0 " : " " ) + i . toString ( 16 ) ;
}
function canvas_pixel_as_hex(data, x, y) {
var offset = (y * data.width + x) * 4;
var r = data.data[offset];
var g = data.data[offset + 1];
var b = data.data[offset + 2];
return "#" + hex2(r) + hex2(g) + hex2(b);
}
function hex_as_rgb(hex) {
return "rgb(" + [parseInt(hex.substring(1, 3), 16), parseInt(hex.substring(3, 5), 16), parseInt(hex.substring(5, 7), 16)] + ")";
}
function magnify(evt) {
var { x: x, y: y } = cursor_point(evt);
var centerPixelColor1, centerPixelColor2;
var dx_lo = -Math.floor(gMagWidth / 2);
var dx_hi = Math.floor(gMagWidth / 2);
var dy_lo = -Math.floor(gMagHeight / 2);
var dy_hi = Math.floor(gMagHeight / 2);
flash_pixels(false);
gFlashingPixels = [];
for (var j = dy_lo; j < = dy_hi; j++) {
for (var i = dx_lo; i < = dx_hi; i++) {
var px = x + i;
var py = y + j;
var p1 = gMagPixPaths[i + dx_hi][j + dy_hi][0];
var p2 = gMagPixPaths[i + dx_hi][j + dy_hi][1];
2014-10-22 13:48:36 +04:00
// Here we just use the dimensions of gImage1Data since we expect test
// and reference to have the same dimensions.
if (px < 0 | | py < 0 | | px > = gImage1Data.width || py >= gImage1Data.height) {
2011-01-24 07:21:40 +03:00
p1.setAttribute("fill", "#aaa");
p2.setAttribute("fill", "#888");
} else {
var color1 = canvas_pixel_as_hex(gImage1Data, x + i, y + j);
var color2 = canvas_pixel_as_hex(gImage2Data, x + i, y + j);
p1.setAttribute("fill", color1);
p2.setAttribute("fill", color2);
if (color1 != color2) {
gFlashingPixels.push(p1, p2);
p1.parentNode.appendChild(p1);
p2.parentNode.appendChild(p2);
}
if (i == 0 & & j == 0) {
centerPixelColor1 = color1;
centerPixelColor2 = color2;
}
}
}
}
flash_pixels(true);
show_pixelinfo(x, y, centerPixelColor1, hex_as_rgb(centerPixelColor1), centerPixelColor2, hex_as_rgb(centerPixelColor2));
}
function show_pixelinfo(x, y, pix1rgb, pix1hex, pix2rgb, pix2hex) {
var pixelinfo = ID("pixelinfo");
ID("coords").textContent = [x, y];
ID("pix1hex").textContent = pix1hex;
ID("pix1rgb").textContent = pix1rgb;
ID("pix2hex").textContent = pix2hex;
ID("pix2rgb").textContent = pix2rgb;
}
2011-01-24 07:21:40 +03:00
]]>< / script >
2008-02-13 11:36:03 +03:00
< / head >
< body onload = "load()" >
< div id = "entry" >
< h1 > Reftest analyzer: load reftest log< / h1 >
< p > Either paste your log into this textarea:< br / >
2011-01-24 07:21:40 +03:00
< textarea cols = "80" rows = "10" id = "logentry" / > < br / >
2008-02-13 11:36:03 +03:00
< input type = "button" value = "Process pasted log" onclick = "log_pasted()" / > < / p >
< p > ... or load it from a file:< br / >
< input type = "file" id = "fileentry" onchange = "fileentry_changed()" / >
< / p >
< / div >
< div id = "loading" style = "display:none" > Loading log...< / div >
2008-03-06 10:36:20 +03:00
< div id = "viewer" style = "display:none" >
2011-02-02 19:40:17 +03:00
< div id = "pixelarea" >
2011-01-24 07:21:40 +03:00
< div id = "pixelinfo" >
< table >
< tbody >
< tr > < th > Pixel at:< / th > < td colspan = "2" id = "coords" / > < / tr >
< tr > < th > Image 1:< / th > < td id = "pix1rgb" > < / td > < td id = "pix1hex" > < / td > < / tr >
< tr > < th > Image 2:< / th > < td id = "pix2rgb" > < / td > < td id = "pix2hex" > < / td > < / tr >
< / tbody >
< / table >
< div >
< div id = "pixelhint" > ★
< div >
< p > Move the mouse over the reftest image on the right to show
magnified pixels on the left. The color information above is for
the pixel centered in the magnified view.< / p >
< p > Image 1 is shown in the upper triangle of each pixel and Image 2
is shown in the lower triangle.< / p >
< / div >
< / div >
< / div >
< / div >
< div id = "magnification" >
2011-02-02 19:40:17 +03:00
< svg xmlns = "http://www.w3.org/2000/svg" width = "84" height = "84" shape-rendering = "optimizeSpeed" >
2011-01-24 07:21:40 +03:00
< g id = "mag" / >
< / svg >
< / div >
< / div >
2011-02-02 19:40:17 +03:00
< div id = "itemlist" > < / div >
2008-03-06 10:36:20 +03:00
< div id = "images" style = "display:none" >
< form id = "imgcontrols" >
2014-05-28 23:07:45 +04:00
< input id = "radio1" type = "radio" name = "which" value = "0" onchange = "show_image(1)" checked = "checked" / > < label id = "label1" title = "1" for = "radio1" > Image 1< / label >
< input id = "radio2" type = "radio" name = "which" value = "1" onchange = "show_image(2)" / > < label id = "label2" title = "2" for = "radio2" > Image 2< / label >
2008-03-06 10:36:20 +03:00
< label > < input type = "checkbox" onchange = "show_differences(this)" / > Circle differences< / label >
< / form >
2014-10-22 13:48:36 +04:00
< svg xmlns = "http://www.w3.org/2000/svg" xmlns:xlink = "http://www.w3.org/1999/xlink" version = "1.1" width = "800" height = "1000" id = "svg" >
2008-03-06 10:36:20 +03:00
< defs >
2011-01-24 07:21:40 +03:00
<!-- use sRGB to avoid loss of data -->
< filter id = "showDifferences" x = "0%" y = "0%" width = "100%" height = "100%"
style="color-interpolation-filters: sRGB">
< feImage id = "feimage1" result = "img1" xlink:href = "#image1" / >
< feImage id = "feimage2" result = "img2" xlink:href = "#image2" / >
<!-- inv1 and inv2 are the images with RGB inverted -->
< feComponentTransfer result = "inv1" in = "img1" >
< feFuncR type = "linear" slope = "-1" intercept = "1" / >
< feFuncG type = "linear" slope = "-1" intercept = "1" / >
< feFuncB type = "linear" slope = "-1" intercept = "1" / >
< / feComponentTransfer >
< feComponentTransfer result = "inv2" in = "img2" >
< feFuncR type = "linear" slope = "-1" intercept = "1" / >
< feFuncG type = "linear" slope = "-1" intercept = "1" / >
< feFuncB type = "linear" slope = "-1" intercept = "1" / >
< / feComponentTransfer >
<!-- w1 will have non - white pixels anywhere that img2
is brighter than img1, and w2 for the reverse.
It would be nice not to have to go through these
intermediate states, but feComposite
type="arithmetic" can't transform the RGB channels
and leave the alpha channel untouched. -->
< feComposite result = "w1" in = "img1" in2 = "inv2" operator = "arithmetic" k2 = "1" k3 = "1" / >
< feComposite result = "w2" in = "img2" in2 = "inv1" operator = "arithmetic" k2 = "1" k3 = "1" / >
<!-- c1 will have non - black pixels anywhere that img2
is brighter than img1, and c2 for the reverse -->
< feComponentTransfer result = "c1" in = "w1" >
< feFuncR type = "linear" slope = "-1" intercept = "1" / >
< feFuncG type = "linear" slope = "-1" intercept = "1" / >
< feFuncB type = "linear" slope = "-1" intercept = "1" / >
< / feComponentTransfer >
< feComponentTransfer result = "c2" in = "w2" >
< feFuncR type = "linear" slope = "-1" intercept = "1" / >
< feFuncG type = "linear" slope = "-1" intercept = "1" / >
< feFuncB type = "linear" slope = "-1" intercept = "1" / >
< / feComponentTransfer >
<!-- c will be nonblack (and fully on) for every pixel+component where there are differences -->
< feComposite result = "c" in = "c1" in2 = "c2" operator = "arithmetic" k2 = "255" k3 = "255" / >
<!-- a will be opaque for every pixel with differences and transparent for all others -->
< feColorMatrix result = "a" type = "matrix" values = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0" / >
2012-11-28 13:04:13 +04:00
2011-01-24 07:21:40 +03:00
<!-- a, dilated by 1 pixel -->
< feMorphology result = "dila1" in = "a" operator = "dilate" radius = "1" / >
2012-11-28 13:04:13 +04:00
<!-- a, dilated by 2 pixels -->
< feMorphology result = "dila2" in = "dila1" operator = "dilate" radius = "1" / >
<!-- all the pixels in the 2 - pixel dilation of a but not in the 1 - pixel dilation, to highlight the diffs -->
< feComposite result = "highlight" in = "dila2" in2 = "dila1" operator = "out" / >
2011-01-24 07:21:40 +03:00
< feFlood result = "red" flood-color = "red" / >
< feComposite result = "redhighlight" in = "red" in2 = "highlight" operator = "in" / >
< feFlood result = "black" flood-color = "black" flood-opacity = "0.5" / >
< feMerge >
< feMergeNode in = "black" / >
< feMergeNode in = "redhighlight" / >
< / feMerge >
< / filter >
2008-03-06 10:36:20 +03:00
< / defs >
2011-01-24 07:21:40 +03:00
< g onmousemove = "magnify(evt)" >
< image x = "0" y = "0" width = "100%" height = "100%" id = "image1" / >
< image x = "0" y = "0" width = "100%" height = "100%" id = "image2" / >
< / g >
< rect id = "diffrect" filter = "url(#showDifferences)" pointer-events = "none" x = "0" y = "0" width = "100%" height = "100%" / >
2008-03-06 10:36:20 +03:00
< / svg >
< / div >
< / div >
2008-02-13 11:36:03 +03:00
< / body >
< / html >