sampleSnapShotBottomColor accepted a frame aspect but cached by URL alone, so a second aspect would have read the first one's result. Every surface renders the capture in the same frame, so the parameter is gone and the per-URL cache is correct by construction.
Unified · Split
| @@ -107,7 +107,7 @@ export function coverCrop( · 106 unmodified lines | ||
| 107 | 107 | return { x: 0, y: (height - cropHeight) / 2, width, height: cropHeight }; |
| 108 | 108 | } |
| 109 | 109 | |
| 110 | −function readBottomBand(image: HTMLImageElement, frameAspect: number): RgbColor | null { | |
| 110 | +function readBottomBand(image: HTMLImageElement): RgbColor | null { | |
| 111 | 111 | const { naturalWidth: width, naturalHeight: height } = image; |
| 112 | 112 | if (width === 0 || height === 0) return null; |
| 113 | 113 | const canvas = document.createElement("canvas"); |
| @@ -115,7 +115,7 @@ function readBottomBand(image: HTMLImageElement, frameAspect: number): RgbColor · 1 unmodified line | ||
| 115 | 115 | canvas.height = SAMPLE_HEIGHT; |
| 116 | 116 | const context = canvas.getContext("2d", { willReadFrequently: true }); |
| 117 | 117 | if (context === null) return null; |
| 118 | − const crop = coverCrop(width, height, frameAspect); | |
| 118 | + const crop = coverCrop(width, height, SNAP_SHOT_FRAME_ASPECT); | |
| 119 | 119 | const bandHeight = Math.max(1, crop.height * SAMPLE_BAND); |
| 120 | 120 | // Drawing the band into a tiny canvas lets the rasterizer do the averaging. |
| 121 | 121 | context.drawImage( |
| @@ -139,22 +139,18 @@ function readBottomBand(image: HTMLImageElement, frameAspect: number): RgbColor · 17 unmodified lines | ||
| 139 | 139 | |
| 140 | 140 | /** |
| 141 | 141 | * Average color of the bottom band of the region the thumbnail frame shows, or |
| 142 | − * null when the image cannot be loaded or read. Results are memoized per URL | |
| 143 | − * so the surfaces that show the same capture decode it once. | |
| 142 | + * null when the image cannot be loaded or read. Every surface renders the | |
| 143 | + * capture in the same frame, so results are memoized per URL and the surfaces | |
| 144 | + * that show the same capture decode it once. | |
| 144 | 145 | */ |
| 145 | −export function sampleSnapShotBottomColor( | |
| 146 | − src: string, | |
| 147 | − frameAspect = SNAP_SHOT_FRAME_ASPECT, | |
| 148 | −): Promise<RgbColor | null> { | |
| 146 | +export function sampleSnapShotBottomColor(src: string): Promise<RgbColor | null> { | |
| 149 | 147 | const cached = sampleCache.get(src); |
| 150 | 148 | if (cached) return cached; |
| 151 | 149 | const pending = new Promise<RgbColor | null>((resolve) => { |
| 152 | 150 | const image = new Image(); |
| 153 | 151 | image.crossOrigin = "anonymous"; |
| 154 | 152 | image.decoding = "async"; |
| 155 | − image.addEventListener("load", () => resolve(readBottomBand(image, frameAspect)), { | |
| 156 | − once: true, | |
| 157 | − }); | |
| 153 | + image.addEventListener("load", () => resolve(readBottomBand(image)), { once: true }); | |
| 158 | 154 | image.addEventListener("error", () => resolve(null), { once: true }); |
| 159 | 155 | image.src = src; |
| 160 | 156 | }); |