将滤色器添加到图像的黑暗部分,并在图像的亮部添加另一个滤镜?
Add color filter to dark part of image and another filter to light part of the image?
我的挑战是为图像的暗部添加滤色器,并为图像的亮部添加另一个滤色器。实现这样的效果https://imgur.com/a/cGmJbs9
我正在使用具有 globalCompositeOperation 效果的 canvas,但我只能应用一个滤镜而不会影响另一个滤镜。
ctx.drawImage(image, 0, 0, 380, 540);
ctx.globalCompositeOperation = 'darken';
ctx.fillStyle = overlayFillColor;
ctx.fillRect(0, 0, 380, 540);
这非常适用于基于 globalCompositeOperation 将滤色器应用于暗区或亮区,但如果我添加另一个滤镜,它也会更改前一个滤镜的颜色。
有什么想法吗?
谢谢艾尔斯
有一个很好的 SVG 滤镜组件,它可以将亮度映射到 alpha:<feColorMatrix type="luminanceToAlpha"/>
由于我们可以在 canvas 中使用 SVG 滤镜,这使我们能够将暗区与亮区分开,并使用合成而不是混合。
这样,您输入的颜色将被保留。
(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
-->
请注意,希望我们在不久的将来会有 CanvasFilters 对象,这将使 canvas 的 SVG 过滤器更易于使用,并且可以在 Workers 中访问(它们目前不是...)。
因此,对于未来的(或现在在 Canary 上启用 web-features 标志的),这看起来像:
// 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>
我的挑战是为图像的暗部添加滤色器,并为图像的亮部添加另一个滤色器。实现这样的效果https://imgur.com/a/cGmJbs9
我正在使用具有 globalCompositeOperation 效果的 canvas,但我只能应用一个滤镜而不会影响另一个滤镜。
ctx.drawImage(image, 0, 0, 380, 540);
ctx.globalCompositeOperation = 'darken';
ctx.fillStyle = overlayFillColor;
ctx.fillRect(0, 0, 380, 540);
这非常适用于基于 globalCompositeOperation 将滤色器应用于暗区或亮区,但如果我添加另一个滤镜,它也会更改前一个滤镜的颜色。
有什么想法吗?
谢谢艾尔斯
有一个很好的 SVG 滤镜组件,它可以将亮度映射到 alpha:<feColorMatrix type="luminanceToAlpha"/>
由于我们可以在 canvas 中使用 SVG 滤镜,这使我们能够将暗区与亮区分开,并使用合成而不是混合。
这样,您输入的颜色将被保留。
(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
-->
请注意,希望我们在不久的将来会有 CanvasFilters 对象,这将使 canvas 的 SVG 过滤器更易于使用,并且可以在 Workers 中访问(它们目前不是...)。 因此,对于未来的(或现在在 Canary 上启用 web-features 标志的),这看起来像:
// 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>