fix: blend the canvas background over black

The opacity slider is documented as a blend over black, and the preview
image does that. The editor composited the image over #1a1a1a instead.
This commit is contained in:
by-ae
2026-11-02 07:21:23 +10:00
parent 7163ad24de
commit 8d998926f9
3 changed files with 32 additions and 1 deletions
+11
View File
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
backgroundPlateShown,
captionText,
cleanPalette,
elementToRegion,
@@ -236,6 +237,16 @@ describe("overlapping regions", () => {
});
});
describe("background image plate", () => {
it("is black only while a reference image is actually drawn", () => {
assert.equal(backgroundPlateShown("/view?filename=bg.png", 0.5), true);
assert.equal(backgroundPlateShown("/view?filename=bg.png", 1), true);
assert.equal(backgroundPlateShown("/view?filename=bg.png", 0), false);
assert.equal(backgroundPlateShown("", 0.5), false);
assert.equal(backgroundPlateShown(null, 0.5), false);
});
});
describe("color palettes", () => {
it("uppercases #RRGGBB, expands #RGB, and drops values Ideogram rejects", () => {
assert.deepEqual(
+9
View File
@@ -228,6 +228,15 @@ export function cleanPalette(colors, limit) {
return out;
}
/**
* The reference image is drawn with CSS opacity. The black plate is what
* that opacity blends over, matching the preview. Hidden when there is no
* image, so the empty canvas stays the editor gray.
*/
export function backgroundPlateShown(url, opacity) {
return Boolean(url) && Number(opacity) > 0;
}
/** Lowercase #rrggbb for input[type=color]. Anything else is unusable there. */
export function swatchHex(color) {
const hex = normalizeHex(color);
+12 -1
View File
@@ -8,6 +8,7 @@ import {
normalizeStoreName, savedPresetCollision,
} from "./ideogram4_shared.js";
import {
backgroundPlateShown,
captionText, cleanPalette, dimsFromAspect, elementToRegion, ideogramResolutionWarning,
parseCaptionText,
snapLayoutDim,
@@ -129,6 +130,7 @@ function injectStyle() {
s.textContent = `
.byae-ideo-wrap { display:flex; flex-direction:column; overflow:hidden; position:relative; pointer-events:auto; gap:4px; width:100%; box-sizing:border-box; }
.byae-ideo-canvas-wrap { position:relative; flex:0 0 auto; width:100%; border-radius:4px; overflow:hidden; background:#1a1a1a; }
.byae-ideo-bg-plate { position:absolute; inset:0; background:#000; pointer-events:none; display:none; }
.byae-ideo-bg { position:absolute; inset:0; width:100%; height:100%; object-fit:fill; pointer-events:none; display:none; }
.byae-ideo-canvas { cursor:crosshair; display:block; width:100%; height:auto; position:relative; z-index:1; background:transparent; border-radius:4px; outline:none; }
.byae-ideo-bar { display:flex; align-items:center; gap:6px; font:11px sans-serif; color:#aaa; user-select:none; padding:0 2px; flex:0 0 auto; flex-wrap:wrap; width:100%; box-sizing:border-box; }
@@ -753,6 +755,8 @@ app.registerExtension({
const canvasWrap = document.createElement("div");
canvasWrap.className = "byae-ideo-canvas-wrap";
const bgPlate = document.createElement("div");
bgPlate.className = "byae-ideo-bg-plate";
const bgImgEl = document.createElement("img");
bgImgEl.className = "byae-ideo-bg";
bgImgEl.alt = "";
@@ -766,6 +770,7 @@ app.registerExtension({
const canvasEl = document.createElement("canvas");
canvasEl.className = "byae-ideo-canvas";
canvasEl.tabIndex = 0;
canvasWrap.appendChild(bgPlate);
canvasWrap.appendChild(bgImgEl);
canvasWrap.appendChild(canvasEl);
let ctx = canvasEl.getContext("2d");
@@ -2233,11 +2238,16 @@ app.registerExtension({
}
function syncBgDisplay() {
const host = getEditorHost();
const { bgImgEl: bg } = getLiveCanvas();
const plate = host?.querySelector?.(".byae-ideo-bg-plate");
const url = node._bgDisplayUrl || "";
const op = getBgOpacity();
if (!bg) return;
if (url && op > 0) {
// Opacity blends the image with whatever is painted behind it. The
// plate is black so that matches the preview, which composites onto black.
const show = backgroundPlateShown(url, op);
if (show) {
if (bg.dataset.byaeSrc !== url) {
bg.dataset.byaeSrc = url;
bg.src = url;
@@ -2249,6 +2259,7 @@ app.registerExtension({
bg.removeAttribute("src");
bg.style.display = "none";
}
if (plate) plate.style.display = show ? "block" : "none";
}
function applyBgDisplay(displayUrl) {