fix: better handling png image size for scale < 2 (#6688)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2024-09-23 18:24:40 +07:00 committed by GitHub
parent 0763624688
commit 21a5f07870
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 38 additions and 24 deletions

View File

@ -27,6 +27,7 @@
import { getFileUrl } from '../file' import { getFileUrl } from '../file'
import { getPreviewType, previewTypes } from '../filetypes' import { getPreviewType, previewTypes } from '../filetypes'
import { imageSizeToRatio } from '../image'
import { BlobMetadata, FilePreviewExtension } from '../types' import { BlobMetadata, FilePreviewExtension } from '../types'
export let file: Ref<Blob> export let file: Ref<Blob>
@ -68,8 +69,8 @@
return return
} }
const pR: number = mD?.pixelRatio ?? 1 const pR: number = mD?.pixelRatio ?? 1
const fWidth: number = mD.originalWidth / pR const fWidth: number = imageSizeToRatio(mD.originalWidth, pR)
const fHeight: number = mD.originalHeight / pR const fHeight: number = imageSizeToRatio(mD.originalHeight, pR)
let mHeight: number = 0 let mHeight: number = 0
let scale: number = 1 let scale: number = 1
if (fWidth > pWidth) { if (fWidth > pWidth) {

View File

@ -15,6 +15,11 @@
import extract from 'png-chunks-extract' import extract from 'png-chunks-extract'
export function imageSizeToRatio (width: number, pixelRatio: number): number {
// consider pixel ratio < 2 as non retina and display them in original size
return pixelRatio < 2 ? width : Math.round(width / pixelRatio)
}
export async function getImageSize (file: Blob): Promise<{ width: number, height: number, pixelRatio: number }> { export async function getImageSize (file: Blob): Promise<{ width: number, height: number, pixelRatio: number }> {
const size = isPng(file) ? await getPngImageSize(file) : undefined const size = isPng(file) ? await getPngImageSize(file) : undefined
@ -65,20 +70,18 @@ async function getPngImageSize (file: Blob): Promise<{ width: number, height: nu
const idhrData = parseIHDR(new DataView(iHDRChunk.data.buffer)) const idhrData = parseIHDR(new DataView(iHDRChunk.data.buffer))
const physData = parsePhys(new DataView(pHYsChunk.data.buffer)) const physData = parsePhys(new DataView(pHYsChunk.data.buffer))
if (physData.unit === 0 && physData.ppux === physData.ppuy) { // Assuming pixels are square
const pixelRatio = Math.round(physData.ppux / 2834.5) // http://www.libpng.org/pub/png/spec/1.2/PNG-Decoders.html#D.Pixel-dimensions
return { const pixelRatio = Math.round(physData.ppux * 0.0254) / 72
width: idhrData.width, return {
height: idhrData.height, width: idhrData.width,
pixelRatio height: idhrData.height,
} pixelRatio
} }
} catch (err) { } catch (err) {
console.error(err) console.error(err)
return undefined return undefined
} }
return undefined
} }
// See http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html // See http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html

View File

@ -17,7 +17,7 @@
<script lang="ts"> <script lang="ts">
import { type Space, type Class, type CollaborativeDoc, type Doc, type Ref } from '@hcengineering/core' import { type Space, type Class, type CollaborativeDoc, type Doc, type Ref } from '@hcengineering/core'
import { IntlString, translate } from '@hcengineering/platform' import { IntlString, translate } from '@hcengineering/platform'
import { getFileUrl, getImageSize } from '@hcengineering/presentation' import { getFileUrl, getImageSize, imageSizeToRatio } from '@hcengineering/presentation'
import { markupToJSON } from '@hcengineering/text' import { markupToJSON } from '@hcengineering/text'
import { import {
AnySvelteComponent, AnySvelteComponent,
@ -297,7 +297,7 @@
type: 'image', type: 'image',
attrs: { attrs: {
'file-id': attached.file, 'file-id': attached.file,
width: Math.round(size.width / size.pixelRatio) width: imageSizeToRatio(size.width, size.pixelRatio)
} }
}, },
{ {

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { Markup } from '@hcengineering/core' import { Markup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform' import { IntlString } from '@hcengineering/platform'
import presentation, { MessageViewer, getFileUrl, getImageSize } from '@hcengineering/presentation' import presentation, { MessageViewer, getFileUrl, getImageSize, imageSizeToRatio } from '@hcengineering/presentation'
import { EmptyMarkup } from '@hcengineering/text' import { EmptyMarkup } from '@hcengineering/text'
import textEditor, { RefAction } from '@hcengineering/text-editor' import textEditor, { RefAction } from '@hcengineering/text-editor'
import { import {
@ -257,7 +257,7 @@
type: 'image', type: 'image',
attrs: { attrs: {
'file-id': attached.file, 'file-id': attached.file,
width: Math.round(size.width / size.pixelRatio) width: imageSizeToRatio(size.width, size.pixelRatio)
} }
}, },
{ {

View File

@ -153,12 +153,15 @@ export const ImageExtension = ImageNode.extend<ImageOptions>({
const fileName = node.attrs.alt ?? '' const fileName = node.attrs.alt ?? ''
const fileType = node.attrs['data-file-type'] ?? 'image/*' const fileType = node.attrs['data-file-type'] ?? 'image/*'
const metadata = node.attrs.width !== undefined ? { originalWidth: node.attrs.width } : {}
showPopup( showPopup(
FilePreviewPopup, FilePreviewPopup,
{ {
file: fileId, file: fileId,
name: fileName, name: fileName,
contentType: fileType, contentType: fileType,
metadata,
fullSize: true, fullSize: true,
showIcon: false showIcon: false
}, },

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
// //
import { setPlatformStatus, unknownError } from '@hcengineering/platform' import { setPlatformStatus, unknownError } from '@hcengineering/platform'
import { getImageSize } from '@hcengineering/presentation' import { imageSizeToRatio, getImageSize } from '@hcengineering/presentation'
import { Extension } from '@tiptap/core' import { Extension } from '@tiptap/core'
import { Plugin, PluginKey } from '@tiptap/pm/state' import { Plugin, PluginKey } from '@tiptap/pm/state'
import { type EditorView } from '@tiptap/pm/view' import { type EditorView } from '@tiptap/pm/view'
@ -41,6 +41,8 @@ function getType (type: string): 'image' | 'other' {
* @public * @public
*/ */
export const ImageUploadExtension = Extension.create<ImageUploadOptions>({ export const ImageUploadExtension = Extension.create<ImageUploadOptions>({
name: 'image-upload',
addOptions () { addOptions () {
return { return {
getFileUrl: () => '' getFileUrl: () => ''
@ -157,7 +159,7 @@ async function handleImageUpload (
src: url, src: url,
alt: file.name, alt: file.name,
title: file.name, title: file.name,
width: Math.round(size.width / size.pixelRatio) width: imageSizeToRatio(size.width, size.pixelRatio)
}) })
const transaction = view.state.tr.insert(pos?.pos ?? 0, node) const transaction = view.state.tr.insert(pos?.pos ?? 0, node)

View File

@ -14,7 +14,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import { type Blob, type Ref } from '@hcengineering/core' import { type Blob, type Ref } from '@hcengineering/core'
import { getBlobRef, type BlobMetadata } from '@hcengineering/presentation' import { getBlobRef, imageSizeToRatio, type BlobMetadata } from '@hcengineering/presentation'
import { Loading } from '@hcengineering/ui' import { Loading } from '@hcengineering/ui'
export let value: Ref<Blob> export let value: Ref<Blob>
@ -22,15 +22,20 @@
export let metadata: BlobMetadata | undefined export let metadata: BlobMetadata | undefined
export let fit: boolean = false export let fit: boolean = false
$: p = getBlobRef(value, name) $: originalWidth = metadata?.originalWidth
$: width = metadata?.originalWidth ? `min(${metadata.originalWidth / metadata?.pixelRatio ?? 1}px, 100%)` : '100%' $: originalHeight = metadata?.originalHeight
$: height = metadata?.originalHeight $: pixelRatio = metadata?.pixelRatio ?? 1
? `min(${metadata.originalHeight / metadata?.pixelRatio ?? 1}px, ${fit ? '100%' : '80vh'})`
: '100%' $: imageWidth = originalWidth != null ? imageSizeToRatio(originalWidth, pixelRatio) : undefined
$: imageHeight = originalHeight != null ? imageSizeToRatio(originalHeight, pixelRatio) : undefined
$: width = imageWidth != null ? `min(${imageWidth}px, 100%)` : '100%'
$: height = imageHeight != null ? `min(${imageHeight}px, ${fit ? '100%' : '80vh'})` : '100%'
let loading = true let loading = true
</script> </script>
{#await p then blobRef} {#await getBlobRef(value, name) then blobRef}
{#if loading} {#if loading}
<div class="flex-center w-full h-full clear-mins"> <div class="flex-center w-full h-full clear-mins">
<Loading /> <Loading />