Skip to content

Commit d0f9047

Browse files
authoredDec 31, 2024··
Fix image size for svg (#1186)
1 parent 66e6e74 commit d0f9047

File tree

3 files changed

+44
-23
lines changed

3 files changed

+44
-23
lines changed
 

‎spx-gui/src/components/editor/preview/stage-viewer/SpriteNode.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type { Image, ImageConfig } from 'konva/lib/shapes/Image'
1414
import type { Action } from '@/models/project'
1515
import { LeftRight, RotationStyle, headingToLeftRight, leftRightToHeading, type Sprite } from '@/models/sprite'
1616
import type { Size } from '@/models/common'
17-
import { nomalizeDegree, round } from '@/utils/utils'
17+
import { nomalizeDegree, round, useAsyncComputed } from '@/utils/utils'
1818
import { useFileImg } from '@/utils/file'
1919
import { useEditorCtx } from '../../EditorContextProvider.vue'
2020
import { getNodeId } from './node'
@@ -30,6 +30,7 @@ const editorCtx = useEditorCtx()
3030
const costume = computed(() => props.sprite.defaultCostume)
3131
const bitmapResolution = computed(() => costume.value?.bitmapResolution ?? 1)
3232
const [image] = useFileImg(() => costume.value?.img)
33+
const rawSize = useAsyncComputed(async () => costume.value?.getRawSize() ?? null)
3334
3435
const nodeId = computed(() => getNodeId(props.sprite))
3536
@@ -71,6 +72,8 @@ const config = computed<ImageConfig>(() => {
7172
const config = {
7273
nodeId: nodeId.value,
7374
image: image.value ?? undefined,
75+
width: rawSize.value?.width ?? 0,
76+
height: rawSize.value?.height ?? 0,
7477
draggable: true,
7578
offsetX: 0,
7679
offsetY: 0,

‎spx-gui/src/models/common/file.ts

+37-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
import { markRaw } from 'vue'
88
import { getMimeFromExt } from '@/utils/file'
99
import { extname } from '@/utils/path'
10-
import type { Disposer } from '@/utils/disposable'
10+
import { Disposable, type Disposer } from '@/utils/disposable'
1111
import { Cancelled } from '@/utils/exception'
12+
import type { Size } from '.'
1213

1314
export type Options = {
1415
/** MIME type of file */
@@ -121,6 +122,41 @@ export async function toConfig(file: File) {
121122
return JSON.parse(text) as unknown
122123
}
123124

125+
async function getSVGImageSize(svgFile: File) {
126+
const svgText = await toText(svgFile)
127+
const parser = new DOMParser()
128+
const svg = parser.parseFromString(svgText, 'image/svg+xml').documentElement
129+
if (!(svg instanceof SVGSVGElement)) throw new Error(`invalid svg: ${svgFile.name}`)
130+
// Keep consistent with spx, for details see https://github.com/goplus/spx/blob/15b2e572746f3aaea519c2d9c0027188b50b62c8/internal/svgr/svg.go#L39
131+
const { width, height } = svg.viewBox.baseVal
132+
return { width, height }
133+
}
134+
135+
async function getBitmapImageSize(bitmapImgFile: File) {
136+
const d = new Disposable()
137+
const imgUrl = await bitmapImgFile.url((fn) => d.addDisposer(fn))
138+
return new Promise<Size>((resolve, reject) => {
139+
const img = new window.Image()
140+
img.src = imgUrl
141+
img.onload = () => {
142+
resolve({
143+
width: img.width,
144+
height: img.height
145+
})
146+
}
147+
img.onerror = (e) => {
148+
reject(new Error(`load image failed: ${e.toString()}`))
149+
}
150+
}).finally(() => {
151+
d.dispose()
152+
})
153+
}
154+
155+
export async function getImageSize(file: File) {
156+
if (file.type === 'image/svg+xml') return getSVGImageSize(file)
157+
return getBitmapImageSize(file)
158+
}
159+
124160
export function listDirs(
125161
files: { [path: string]: unknown },
126162
/** path of parent dir to do list, with no tailing slash */

‎spx-gui/src/models/costume.ts

+3-21
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import { nanoid } from 'nanoid'
33

44
import { extname, resolve } from '@/utils/path'
55
import { adaptImg } from '@/utils/spx'
6-
import { Disposable } from '@/utils/disposable'
7-
import { File, type Files } from './common/file'
8-
import type { Size } from './common'
6+
import { File, type Files, getImageSize } from './common/file'
97
import { getCostumeName, validateCostumeName } from './common/asset-name'
108
import type { Sprite } from './sprite'
119
import type { Animation } from './animation'
@@ -71,24 +69,8 @@ export class Costume {
7169
this.bitmapResolution = bitmapResolution
7270
}
7371

74-
private async getRawSize() {
75-
const d = new Disposable()
76-
const imgUrl = await this.img.url((fn) => d.addDisposer(fn))
77-
return new Promise<Size>((resolve, reject) => {
78-
const img = new window.Image()
79-
img.src = imgUrl
80-
img.onload = () => {
81-
resolve({
82-
width: img.width,
83-
height: img.height
84-
})
85-
}
86-
img.onerror = (e) => {
87-
reject(new Error(`load image failed: ${e.toString()}`))
88-
}
89-
}).finally(() => {
90-
d.dispose()
91-
})
72+
async getRawSize() {
73+
return getImageSize(this.img)
9274
}
9375

9476
async getSize() {

0 commit comments

Comments
 (0)
Please sign in to comment.