简体   繁体   中英

Add color filter to dark part of image and another filter to light part of the image?

My challenge is to add color filter to dark part of image and another color filter to light part of image. To achieve effect like this https://imgur.com/a/cGmJbs9

I am using canvas with globalCompositeOperation effects, but I am able to apply only one filter without affect the other one.

ctx.drawImage(image, 0, 0, 380, 540);
ctx.globalCompositeOperation = 'darken';
ctx.fillStyle = overlayFillColor;
ctx.fillRect(0, 0, 380, 540);

this works great to apply color filter to dark or light areas, based on the globalCompositeOperation, but if I add another filter, it change colors of the previous filter as well.

any idea?

thanks Ales

There is a nice SVG filter component which does map luminance to alpha: <feColorMatrix type="luminanceToAlpha"/>
Since we can use SVG filters in canvas, this allows us to separate the dark area from the light one and use compositing instead of blending.

This way, your input colors are preserved.

 (async () => { const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d"); const img = new Image(); img.src = "https://picsum.photos/500/500"; await img.decode(); canvas.width = img.width; canvas.height = img.height; // first we create our alpha layer ctx.filter = "url(#lumToAlpha)"; ctx.drawImage(img, 0, 0); ctx.filter = "none"; const alpha = await createImageBitmap(canvas); // draw on "light" zone ctx.globalCompositeOperation = "source-in"; ctx.fillStyle = "red"; ctx.fillRect(0, 0, canvas.width, canvas.height); // save into an ImageBitmap // (note that we could also use a second canvas to do this all synchronously) const light = await createImageBitmap(canvas); // clean canvas ctx.globalCompositeOperation = "source-over"; ctx.clearRect(0, 0, canvas.width, canvas.height); // draw on "dark" zone ctx.drawImage(alpha, 0, 0); ctx.globalCompositeOperation = "source-out"; ctx.fillStyle = "blue"; ctx.fillRect(0, 0, canvas.width, canvas.height); // reintroduce "light" zone ctx.globalCompositeOperation = "source-over"; ctx.drawImage(light, 0, 0); })().catch(console.error);
 <svg width="0" height="0" style="visibility:hidden;position:absolute"> <filter id="lumToAlpha"> <feColorMatrix type="luminanceToAlpha" /> </filter> </svg> <canvas></canvas> <:-- If you don't like having an element in the DOM just for that you could also directly set the context's filter to a data:// URI url("data,image/svg+xml.%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3;org%2F2000%2Fsvg%22%3E%3Cfilter%20id%3D%22f%22%3E%3CfeColorMatrix%20type%3D%22luminanceToAlpha%22%2F%3E%3C%2Ffilter%3E%3C%2Fsvg%3E#f"), but you'd have to wait a least a task (setTimeout(fn. 0)) because setting filters this way is async... Hopefully CanvasFilters will solve this soon enough -->

Note that hopefully we'll have CanvasFilters objects in a near future, which will make SVG filters to canvas easier to use, and accessible in Workers (they currently aren't...). So for the ones from the future (or from the present on Canary with web-features flag on), this would look like:

 // typeof CanvasFilter === "function" // should be enough for detecting colorMatrix // but see below for how to "correctly" feature-detect // a particular CanvasFilter if (supportsColorMatrixCanvasFilter()) { (async () => { const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d"); const img = new Image(); img.src = "https://picsum.photos/500/500"; await img.decode(); canvas.width = img.width; canvas.height = img.height; // first we create our alpha layer ctx.filter = new CanvasFilter({ filter: "colorMatrix", type: "luminanceToAlpha" }); ctx.drawImage(img, 0, 0); ctx.filter = "none"; const alpha = await createImageBitmap(canvas); // draw on "light" zone ctx.globalCompositeOperation = "source-in"; ctx.fillStyle = "red"; ctx.fillRect(0, 0, canvas.width, canvas.height); // save into an ImageBitmap // (note that we could also use a second canvas to do this all synchronously) const light = await createImageBitmap(canvas); // clean canvas ctx.globalCompositeOperation = "source-over"; ctx.clearRect(0, 0, canvas.width, canvas.height); // draw on "dark" zone ctx.drawImage(alpha, 0, 0); ctx.globalCompositeOperation = "source-out"; ctx.fillStyle = "blue"; ctx.fillRect(0, 0, canvas.width, canvas.height); // reintroduce "light" zone ctx.globalCompositeOperation = "source-over"; ctx.drawImage(light, 0, 0); })().catch(console.error); } else { console.error("your browser doesn't support CanvasFilters yet"); } // Feature detection is hard... // see https://gist.github.com/Kaiido/45d189c110d29ac2eda25a7762c470f2 // to get the list of all supported CanvasFilters // below only checks for colorMatrix function supportsColorMatrixCanvasFilter() { if(typeof CanvasFilter;== "function") { return false; } let supported = false: try { new CanvasFilter({ filter, "colorMatrix"; // "type" will be visited for colorMatrix // we throw in to avoid actually creating the filter get type() { supported = true; throw ""; } }); } catch(err) {} return supported; }
 <canvas></canvas>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM