| |
| |
| |
| |
| |
| |
|
|
| import { morph } from 'lib/morph'; |
|
|
| class UpscalePreview extends HTMLElement { |
| #ctx = null; |
|
|
| connectedCallback() { |
| this.classList.add('upscale-preview'); |
| this.#render(); |
| } |
|
|
| |
| |
| |
| |
| showDimmedPreview(image, outW, outH) { |
| this.style.setProperty('--ar', `${outW} / ${outH}`); |
| this.style.setProperty('--ar-num', `${outW / outH}`); |
| this.style.setProperty('--natural-w', `${outW}px`); |
| this.#render(); |
| this.style.display = 'block'; |
|
|
| const canvas = this.querySelector('canvas'); |
| canvas.width = outW; |
| canvas.height = outH; |
| const ctx = canvas.getContext('2d'); |
| ctx.imageSmoothingEnabled = false; |
| ctx.drawImage(image, 0, 0, outW, outH); |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.45)'; |
| ctx.fillRect(0, 0, outW, outH); |
| this.#ctx = ctx; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| drawTile(tileInfo, { opacity = 1 } = {}) { |
| if (!this.#ctx) return; |
| const { x, y, w, h } = tileInfo.crop ?? { |
| x: tileInfo.outX, y: tileInfo.outY, w: tileInfo.outW, h: tileInfo.outH, |
| }; |
| const needsAlpha = opacity < 1; |
| if (needsAlpha) { this.#ctx.save(); this.#ctx.globalAlpha = opacity; } |
| this.#ctx.drawImage(tileInfo.canvas, x, y, w, h, x, y, w, h); |
| if (needsAlpha) this.#ctx.restore(); |
| } |
|
|
| hide() { |
| this.style.display = 'none'; |
| this.style.removeProperty('--ar'); |
| this.style.removeProperty('--ar-num'); |
| this.style.removeProperty('--natural-w'); |
| const canvas = this.querySelector('canvas'); |
| if (canvas) { canvas.width = 0; canvas.height = 0; } |
| this.#ctx = null; |
| } |
|
|
| #render() { |
| morph(this, ` |
| <style> |
| .upscale-preview { display: none; position: relative; } |
| .upscale-preview:not(.expanded) { |
| width: 100%; |
| max-width: 100%; |
| aspect-ratio: var(--ar, auto); |
| margin-inline: auto; |
| } |
| .upscale-preview.expanded { |
| height: calc(100vh - 1rem); |
| width: calc((100vh - 1rem) * var(--ar-num, 1)); |
| max-width: none; |
| margin-inline: auto; |
| } |
| /* native-size: canvas at its natural pixel dimensions, centered in |
| a workspace-sized scroll container. Mirrors the compare-slider. */ |
| .upscale-preview.native-size { |
| width: 100%; |
| max-width: 100%; |
| height: calc(100vh - 1rem); |
| max-height: calc(100vh - 1rem); |
| overflow: auto; |
| display: flex; |
| margin-inline: auto; |
| } |
| .upscale-preview.native-size canvas { |
| margin: auto; |
| width: auto; |
| height: auto; |
| max-width: none; |
| flex: 0 0 auto; |
| } |
| .upscale-preview canvas { |
| display: block; |
| width: 100%; |
| height: auto; |
| max-width: 100%; |
| border: 1px solid var(--pico-muted-border-color, #333); |
| border-radius: var(--pico-border-radius, 4px); |
| background: #000; |
| } |
| </style> |
| <canvas></canvas> |
| `); |
| } |
| } |
|
|
| customElements.define('upscale-preview', UpscalePreview); |
|
|