diff --git a/webui/src/components/display/blobs/FragmentViewer.svelte b/webui/src/components/display/blobs/FragmentViewer.svelte index 17b1241..eb2d49d 100644 --- a/webui/src/components/display/blobs/FragmentViewer.svelte +++ b/webui/src/components/display/blobs/FragmentViewer.svelte @@ -39,6 +39,7 @@ width: 100%; display: flex; justify-content: center; + min-height: 0; } img { diff --git a/webui/src/components/display/blobs/ImageViewer.svelte b/webui/src/components/display/blobs/ImageViewer.svelte index 61343eb..e5bc33e 100644 --- a/webui/src/components/display/blobs/ImageViewer.svelte +++ b/webui/src/components/display/blobs/ImageViewer.svelte @@ -204,10 +204,10 @@ min-height: 0; max-height: 100%; } - } - .preview-image { - margin: auto; + .preview-image { + margin: auto; + } } .r6o-editor { diff --git a/webui/src/util/fragments/xywh.ts b/webui/src/util/fragments/xywh.ts index 0bf1ca3..8f4f47b 100644 --- a/webui/src/util/fragments/xywh.ts +++ b/webui/src/util/fragments/xywh.ts @@ -43,24 +43,20 @@ export function xywh(mediaItem: HTMLImageElement | HTMLVideoElement) { } /** - * Applies the media fragment when the image has loaded. We need the image's - * original width and height. + * Applies the media fragment when the image has loaded. */ function addImageLoadListener(mediaFragment: MediaFragment) { - // Base64-encoded transparent 1x1 pixel GIF - const TRANSPARENT_GIF = - "data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="; const mediaItem = mediaFragment.mediaItem; - const onload = function () { - // Prevent onload firing when the 1x1 pixel GIF loads; but still react when `src` - // is changed programatically. - if (mediaItem.src !== TRANSPARENT_GIF) { + // Prevent onload firing when the fragment loads; but still react when `src` + // is changed programatically. + let lastSrc: string; + function onload() { + if (mediaItem.src !== lastSrc) { // Required on reloads because of size calculations. - mediaItem.style.cssText = ""; applyFragment(mediaFragment); - mediaItem.src = TRANSPARENT_GIF; + lastSrc = mediaItem.src; } - }; + } mediaItem.addEventListener("load", onload); } @@ -86,34 +82,26 @@ function addVideoLoadListener(mediaFragment: MediaFragment) { * 2D transformation according to the fragment's x and y values. */ function applyFragment(fragment: MediaFragment) { - let x: string, y: string, w: string, h: string; - const originalWidth = - fragment.mediaType === "img" - ? fragment.mediaItem.width - : fragment.mediaItem.videoWidth; - const originalHeight = - fragment.mediaType === "img" - ? fragment.mediaItem.height - : fragment.mediaItem.videoHeight; - // Unit is pixel: - if (fragment.unit === "pixel:") { - const scale = - fragment.mediaType === "img" - ? originalWidth / fragment.mediaItem.naturalWidth - : originalWidth / fragment.mediaItem.clientWidth; - w = fragment.w * scale + "px"; - h = fragment.h * scale + "px"; - x = "-" + fragment.x * scale + "px"; - y = "-" + fragment.y * scale + "px"; - // Unit is percent: - } else { - w = (originalWidth * fragment.w) / 100 + "px"; - h = (originalHeight * fragment.h) / 100 + "px"; - x = "-" + (originalWidth * fragment.x) / 100 + "px"; - y = "-" + (originalHeight * fragment.y) / 100 + "px"; - } // Media item is a video if (fragment.mediaType === "video") { + let x: string, y: string, w: string, h: string; + const originalWidth = fragment.mediaItem.videoWidth; + const originalHeight = fragment.mediaItem.videoHeight; + // Unit is pixel: + if (fragment.unit === "pixel:") { + const scale = originalWidth / fragment.mediaItem.clientWidth; + w = fragment.w * scale + "px"; + h = fragment.h * scale + "px"; + x = "-" + fragment.x * scale + "px"; + y = "-" + fragment.y * scale + "px"; + // Unit is percent: + } else { + w = (originalWidth * fragment.w) / 100 + "px"; + h = (originalHeight * fragment.h) / 100 + "px"; + x = "-" + (originalWidth * fragment.x) / 100 + "px"; + y = "-" + (originalHeight * fragment.y) / 100 + "px"; + } + const wrapper = document.createElement("div"); wrapper.style.cssText += "overflow:hidden;" + @@ -148,25 +136,23 @@ function applyFragment(fragment: MediaFragment) { } // Media item is an image } else { - fragment.mediaItem.style.cssText += - "width:" + - w + - ";" + - "height:" + - h + - ";" + - "background:url(" + - fragment.mediaItem.src + - ") " + // background-image - "no-repeat " + // background-repeat - x + - " " + - y + - "; " + // background-position - "background-size: " + - originalWidth + - "px " + - originalHeight + - "px;"; + let x: number, y: number, w: number, h: number; + if (fragment.unit === "pixel:") { + x = fragment.x; + y = fragment.y; + w = fragment.w; + h = fragment.h; + } else { + x = (fragment.x / 100) * fragment.mediaItem.naturalWidth; + y = (fragment.y / 100) * fragment.mediaItem.naturalHeight; + w = (fragment.w / 100) * fragment.mediaItem.naturalWidth; + h = (fragment.h / 100) * fragment.mediaItem.naturalHeight; + } + const canvas = document.createElement("canvas"); + canvas.width = w; + canvas.height = h; + const context = canvas.getContext("2d"); + context.drawImage(fragment.mediaItem, x, y, w, h, 0, 0, w, h); + fragment.mediaItem.src = canvas.toDataURL(); } }