From 9bbf0382664bfd707231fe3db18e7164d3226f2f Mon Sep 17 00:00:00 2001 From: David Manthey Date: Mon, 1 Mar 2021 09:08:58 -0500 Subject: [PATCH] Support mix-blend-mode compositing in screenshots. There are limitations to the current screenshot function. If layers are composited with mix-blend-mode, svg filters, or some other options, these are not applied. mix-blend-mode support is added here; other limitations remain. --- .travis.yml | 2 +- CHANGELOG.md | 8 ++++++++ src/map.js | 13 +++++++++---- tests/cases/map.js | 18 +++++++++++++++++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 81476fb67a..25fb64a47c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ sudo: required dist: xenial node_js: - - 14 + - 12 addons: # version 55.0 - 57.x have an issue with screenshots. diff --git a/CHANGELOG.md b/CHANGELOG.md index 027e358259..e74be01006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/map.js b/src/map.js index 027a94f62d..2cde6fe8e7 100644 --- a/src/map.js +++ b/src/map.js @@ -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')); }); }); } @@ -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')); }); }); }); @@ -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) { + 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(') { @@ -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'; } /** diff --git a/tests/cases/map.js b/tests/cases/map.js index e4897e0de7..4e5c5960b2 100644 --- a/tests/cases/map.js +++ b/tests/cases/map.js @@ -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); @@ -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)');