I'm not sure if this CanvasRenderingContext2D.globalCompositeOperation
is not really working or working in safari, but I've tested it.
I'm working on a Convert Image to Sketch Image feature using Javascript, and this implementation involves CANVAS API
or CanvasRenderingContext2D.globalCompositeOperation
to be exact.
To explain the flow, I'm generating two images using the image selected, 1 black and white image, and 1 black and white a with blurred image, and then after, that I'm using this merge
function to merge/combine them to have Sketch-drew-image. Check this https://codepen.io/ohyeahhu/pen/RwMVjym, if you are using Chrome of course it will work, but if you are using safari browser the Sketch Image will not work.
The script filter
function below is how I convert/generate an Image to a black and white and blurred using CanvasRenderingContext2D.filter
in CANVAS API.
Now I know that the CanvasRenderingContext2D.filter
doesn't have any support from the safari browser but I've solved that part by using https://github.com/davidenke/context-filter-polyfill thanks to this beautiful suggestion https://stackoverflow.com/a/65843002/17678769
const filter = function(img, filter) {
let canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext('2d');
ctx.filter = filter;
ctx.drawImage(img, 0, 0, img.width, img.height);
return canvas;
}
The script below is the one I'm using to create a sketch-like-image. Now the ctx.globalCompositeOperation = 'color-dodge';
line of code supposed to make the image like a Sketch-drew-image but somehow in Safari browsers mobile devices or mac, it is not working.
const merge = function(front, overlay) {
let canvas = document.createElement('canvas');
canvas.width = front.width;
canvas.height = front.height;
let ctx = canvas.getContext('2d');
ctx.globalCompositeOperation = 'color-dodge';
ctx.drawImage(front, 0, 0, canvas.width, canvas.height);
ctx.drawImage(overlay, 0, 0, canvas.width, canvas.height);
let img = document.createElement('img');
img.src = canvas.toDataURL();
return img;
}
I've read the MDN documentation of CanvasRenderingContext2D.globalCompositeOperation
and it says that safari browsers fully supports it.
If that's the case, then I do not know where did I go wrong on this implementation, Did I made a mistake in the implementation? I'm really stuck in this for a day, is there any alternative or solution to this issue/problem?
Yes Safari does support globalCompositeOperation
(gCO), they even do have the biggest list of modes they support among all three main browsers (though I found a rendering bug).
The problem lies in your filter polyfill. It probably messed up somewhere when monkeypatching the various methods of the context and broke the gCO (it also broke clearRect
).
Seeing how there is no activity on the project I'm not sure you'll be able to have it fixed, but a simple workaround is to let the polyfill know it shouldn't be called on that canvas by setting the __skipFilterPatch
property to true on your canvas:
function createCanvas({width, height}) {
return Object.assign(document.createElement("canvas"), { width, height });
}
function filter(source, filters) {
const canvas = createCanvas(source);
const ctx = canvas.getContext("2d");
ctx.filter = filters;
ctx.drawImage(source, 0, 0);
return canvas;
}
(async() => {
const blob = await fetch("https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/20141025_Shockwave_Truck_Alliance_Air_Show_2014-3.jpg/640px-20141025_Shockwave_Truck_Alliance_Air_Show_2014-3.jpg").then(resp => resp.blob());
const bmp = await createImageBitmap(blob);
const gray = filter(bmp, "grayscale(1)");
const blur = filter(bmp, "grayscale(1) invert(1) blur(5px)");
const canvas = createCanvas(bmp);
canvas.__skipFilterPatch = true; // the magic
const ctx = canvas.getContext("2d");
ctx.drawImage(gray, 0, 0);
ctx.globalCompositeOperation = "color-dodge";
ctx.drawImage(blur, 0, 0);
document.body.append(canvas);
})().catch(console.error);
<script src="https://cdn.jsdelivr.net/npm/context-filter-polyfill@0.2.4/dist/index.js"></script>