gpt4 book ai didi

javascript - 为什么将 blob 转换为数据 URI 会导致与直接数据 URI 方法不同的 URI?

转载 作者:行者123 更新时间:2023-12-03 20:45:09 30 4
gpt4 key购买 nike

我正在做一个实验,我发现将 Canvas 转换为 blob,然后转换为数据 URI 会导致与直接从 Canvas 获取数据 URI 不同的 URI。打开时的内容在两个 URI 上几乎相同。
使用 blob 方法时,如何获得与直接数据 URI 方法相同的 URI 结果?

let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let text = "Bufferoverrun";

ctx.textBaseline = "top";
ctx.font = "16px 'Arial'";
ctx.textBaseline = "alphabetic";
ctx.rotate(.05);
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText(text, 2, 15);
ctx.fillStyle = "rgba(102, 200, 0, 0.7)";
ctx.fillText(text, 4, 17);
ctx.shadowBlur = 10;
ctx.shadowColor = "blue";
ctx.fillRect(-20, 10, 234, 5);

const blobToBase64 = blob => {
const reader = new FileReader();
reader.readAsDataURL(blob);
return new Promise(resolve => {
reader.onloadend = () => {
resolve(reader.result);
};
});
};

canvas.toBlob(async function (result) {
let blobToURL = await blobToBase64(result)
if (blobToURL != canvas.toDataURL()) {
console.log("Data mismatch");
} else {
console.log("Match")
}
})

我查看了 Chrome 的 Blink 内部结构,但找不到任何可以解释更改的内容。
Canvas Element Source Code - Blink

最佳答案

当前的区别在于 toBlobtoDataURL 如何不同地处理颜色空间转换。toBlob 在某些情况下可能会保留当前的颜色空间并将其包含在生成的 Blob 中,toDataURL 永远不会,并且当 toBlob 这样做时,它使用其他路径进行转换。
这里是他们为 toDataURL 进行转换的地方,这里是 toBlob

有趣的是,Chrome 将在再次将两个文件重新绘制到 Canvas 上时从这两个文件生成相同的位图,但是如果您将两个结果保存到磁盘并在像 Firefox 一样检查源颜色空间的浏览器中绘制它们,您将看到它们实际上是不同的。

请注意,这可能不是您会发现两种方法之间差异的唯一地方。
例如,我还注意到 toBlob 会受到 Canvas 加速与否的影响(下面的演示需要 chrome://flags/#enable-experimental-web-platform-features ):

const test = (accelerated) => {
const HW = accelerated ? "accelerated" : "not-accelerated"
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d', {
willReadFrequently: !accelerated
});
let text = "Bufferoverrun";

ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText(text, 2, 15);
ctx.fillStyle = "rgba(102, 200, 0, 0.7)";
ctx.shadowBlur = 10;
ctx.shadowColor = "blue";
ctx.fillRect(-20, 10, 234, 5);

canvas.toBlob(async function(result) {
let blobToURL = await blobToBase64(result)
const data = canvas.toDataURL();
if (blobToURL != data) {
console.log(HW, "Data mismatch");
} else {
console.log(HW, "Match")
}
});

function blobToBase64(blob) {
const reader = new FileReader();
reader.readAsDataURL(blob);
return new Promise(resolve => {
reader.onloadend = () => {
resolve(reader.result);
};
});
}
};
test(true);
test(false);


而且我应该注意到 你不应该期望这些方法无论如何都返回相同的值 。规范中没有任何要求,恰恰相反,人们可以预期,由于 toBlob 是异步的并并行执行其编码,因此与同步 toDataURL 相比,它的编码器将使用更慢但质量更好的选项。
但是,如果对于您自己的情况,您希望通过两种方法获得相同的结果,您可以通过禁用硬件加速 ( chrome://settings/?search=hardware acceleration) 来强制软件渲染,它们将产生相同的结果。

从评论中 OP 解释说,它们实际上是在一个 Web 扩展中,并且实际上处理的是一个 OffscreenCanvas,它没有 toDataURL 方法。
首先,Firefox 中的 在 ChromeContexts(网络扩展)可用的 2D 上下文上有一个 demote method,它应该允许强制使用软件渲染器 但 Chrome 没有实现此功能
现在关于工作案例中的 OffscreenCanvas,一种解决方法是从 HTMLCanvasElement 上的主线程调用 toDataURL(),我们从中获取了 OffscreenCanvas:

const worker = new Worker( worker_url );
const workercanvas = document.createElement( "canvas" );
const offscreen = workercanvas.transferControlToOffscreen();
worker.postMessage(offscreen, [offscreen]);
worker.onmessage = (evt) => {
const fromworker = workercanvas.toDataURL();
const frommain = getDataURLFromMain();
console.log( fromworker === frommain ? "Match" : "Data mismatch" );
};
<script>
// boiler plate prepare scripts content for SO's StackSnippet
const drawing_ops = `
const ctx = canvas.getContext("2d");
const text = "Bufferoverrun";

ctx.textBaseline = "top";
ctx.font = "16px 'Arial'";
ctx.textBaseline = "alphabetic";
ctx.rotate(.05);
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText(text, 2, 15);
ctx.fillStyle = "rgba(102, 200, 0, 0.7)";
ctx.fillText(text, 4, 17);
ctx.shadowBlur = 10;
ctx.shadowColor = "blue";
ctx.fillRect(-20, 10, 234, 5);
`;
const getDataURLFromMain = new Function(`
const canvas = document.createElement("canvas");
${ drawing_ops }
return canvas.toDataURL();
`);
const worker_script = new Blob( [ `
onmessage = (evt) => {
const canvas = evt.data;
${ drawing_ops }
if( ctx.commit ) { // might require WebPlatformFeatures flags
ctx.commit();
postMessage("");
}
else {
requestAnimationFrame(() => postMessage(""));
}
};` ] );
const worker_url = URL.createObjectURL( worker_script );
</script>

Also available as a glitch 因为它可能更清楚。

关于javascript - 为什么将 blob 转换为数据 URI 会导致与直接数据 URI 方法不同的 URI?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66071736/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com