Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support mix-blend-mode compositing in screenshots. #1074

Merged
merged 2 commits into from
Mar 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sudo: required
dist: xenial

node_js:
- 14
- 12

addons:
# version 55.0 - 57.x have an issue with screenshots.
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Change Log

## Unreleased

### Improvements
- Screenshots now handle mix-blend-mode settings on top level divs (#1074)

### Bug Fixes
- Fixed an issue with affine transforms and polygons (#1073)

## Version 0.20.0

### Features
Expand Down
13 changes: 9 additions & 4 deletions src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -1820,13 +1820,13 @@ var map = function (arg) {
if (layer.renderer() && layer.renderer().api() === 'webgl') {
layer.renderer()._renderFrame();
}
drawLayerImageToContext(context, opacity, canvasElem, canvasElem[0]);
drawLayerImageToContext(context, opacity, canvasElem, canvasElem[0], layer.node().css('mix-blend-mode'));
});
});
if ((layer.node().children().not('canvas').length || !layer.node().children().length) && (!layer.renderer() || layer.renderer().api() !== 'webgl')) {
defer = defer.then(function () {
return util.htmlToImage(layer.node(), 1).done(function (img) {
drawLayerImageToContext(context, 1, $([]), img);
drawLayerImageToContext(context, 1, $([]), img, layer.node().css('mix-blend-mode'));
});
});
}
Expand All @@ -1846,7 +1846,7 @@ var map = function (arg) {
var attrElem = $(this);
defer = defer.then(function () {
return util.htmlToImage(attrElem, 1).done(function (img) {
drawLayerImageToContext(context, 1, $([]), img);
drawLayerImageToContext(context, 1, $([]), img, attrElem.css('mix-blend-mode'));
});
});
});
Expand Down Expand Up @@ -1963,10 +1963,14 @@ var map = function (arg) {
* @param {number} opacity The opacity in the range [0, 1].
* @param {object} elem A jQuery element that might have a transform.
* @param {HTMLImageObject} img The image or canvas to draw to the canvas.
* @param {string} [mixBlendMode] the mix-blend-mode used to add this layer.
* @private
*/
function drawLayerImageToContext(context, opacity, elem, img) {
function drawLayerImageToContext(context, opacity, elem, img, mixBlendMode) {
context.globalAlpha = opacity;
if (mixBlendMode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be not possible to just have the user pass any one type of globalCompositeOperation (source in/out, over etc?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, any of the globalCompositeOperation values are supported. They are called mix-blend-mode in css and globalCompositeOperation in canvas.

context.globalCompositeOperation = mixBlendMode;
}
var transform = elem.css('transform');
// if the canvas is being transformed, apply the same transformation
if (transform && transform.substr(0, 7) === 'matrix(') {
Expand All @@ -1975,6 +1979,7 @@ var map = function (arg) {
context.setTransform(1, 0, 0, 1, 0, 0);
}
context.drawImage(img, 0, 0);
context.globalCompositeOperation = 'source-over';
}

/**
Expand Down
18 changes: 17 additions & 1 deletion tests/cases/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,6 @@ describe('geo.core.map', function () {
});
});
it('partial opacity', function (done) {
// making a layer transparent is as good as not asking for it
layer2.opacity(0.5);
m.screenshot().then(function (result) {
expect(result).not.toEqual(ss.basic);
Expand All @@ -853,6 +852,23 @@ describe('geo.core.map', function () {
// These tests won't work in PhantomJS. See
// https://bugs.webkit.org/show_bug.cgi?id=17352, also 29305 and 129172.
if (!isPhantomJS()) {
it('mix-blend-mode multiply', function (done) {
layer2.node().css('mix-blend-mode', 'multiply');
m.screenshot().then(function (result) {
expect(result).not.toEqual(ss.basic);
expect(result).not.toEqual(ss.onelayer);
layer2.node().css('mix-blend-mode', 'initial');
done();
});
});
it('mix-blend-mode normal', function (done) {
layer2.node().css('mix-blend-mode', 'normal');
m.screenshot().then(function (result) {
expect(result).toEqual(ss.basic);
layer2.node().css('mix-blend-mode', 'initial');
done();
});
});
it('layer background', function (done) {
var layer3 = m.createLayer('ui');
layer3.node().css('background-image', 'url(/data/tilefancy.png)');
Expand Down
2 changes: 1 addition & 1 deletion tests/gl-cases/webglContour.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe('webglContour', function () {
}
myMap.draw();

imageTest.imageTest(imageName, null, 0.0015, done, myMap.onIdle, 0, 2);
imageTest.imageTest(imageName, null, 0.0015, done, myMap.onIdle, 5000, 2);
});
}

Expand Down
8 changes: 4 additions & 4 deletions tests/headed-cases/blog-lines.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ describe('blog-lines example', function () {

it('basic', function (done) {
$('#map').attr('src', '/examples/blog-lines/index.html?mode=select');
imageTest.imageTest('exampleBlogLines', '#map', 0.0015, done, ready, 500, 2, '.leaflet-pane');
}, 10000);
imageTest.imageTest('exampleBlogLines', '#map', 0.0015, done, ready, 5000, 2, '.leaflet-pane');
}, 20000);
it('round line cap', function (done) {
$('#map')[0].contentWindow.scrollTo(0, 130);
base$ = $('iframe#map')[0].contentWindow.jQuery;
base$('#feature').val('linecap-round').trigger('change');
imageTest.imageTest('exampleBlogLinesRoundCap', '#map', 0.0015, done, ready, 500, 2, '.mapboxgl-canvas');
imageTest.imageTest('exampleBlogLinesRoundCap', '#map', 0.0015, done, ready, 5000, 2, '.mapboxgl-canvas');
}, 20000);
it('10,000 lines in geojs', function (done) {
// remove previous contents to ensure we detect new contents
Expand All @@ -62,6 +62,6 @@ describe('blog-lines example', function () {
// this permits a large delta to pass on CI. It visually is rendered
// correctly, though with seemingly different aliasing choices by the
// renderer
imageTest.imageTest('exampleBlogLines10k', '#map', 0.04, done, null, 1000, 2, '.geojs-map.ready');
imageTest.imageTest('exampleBlogLines10k', '#map', 0.04, done, null, 5000, 2, '.geojs-map.ready');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this increase to 5000?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Travis was timing out on this. I don't know why, but increasing it allowed the tests to pass. It is like they are using a different mesa library than before that is slower, but that is just speculation.

}, 10000);
});