From 861d9ffaf12f34344ceb4f9c5e811ca80b62fade Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 6 Jun 2023 15:37:17 +0800 Subject: [PATCH 01/24] Encapsulating PAL/ImageData --- cc.config.json | 12 +- cocos/2d/assets/sprite-frame.ts | 3 +- .../asset/asset-manager/download-dom-image.ts | 58 ------ cocos/asset/asset-manager/downloader.ts | 6 +- cocos/asset/assets/image-asset.jsb.ts | 98 ++------- cocos/asset/assets/image-asset.ts | 97 ++------- cocos/asset/assets/simple-texture.jsb.ts | 14 +- cocos/asset/assets/simple-texture.ts | 18 +- cocos/asset/assets/texture-cube.ts | 3 +- cocos/game/splash-screen.ts | 13 +- cocos/spine/lib/spine-core.d.ts | 6 +- cocos/tiledmap/tiled-map.ts | 12 +- .../openharmony/modules/SystemWindow.h | 2 +- pal/image/image-data.ts | 197 ++++++++++++++++++ pal/image/minigame/image.ts | 2 + pal/image/native/image.ts | 2 + pal/image/runtime/image.ts | 2 + pal/image/types.ts | 42 ++++ pal/image/web/image.ts | 135 ++++++++++++ tsconfig.json | 1 + 20 files changed, 454 insertions(+), 269 deletions(-) delete mode 100644 cocos/asset/asset-manager/download-dom-image.ts create mode 100644 pal/image/image-data.ts create mode 100644 pal/image/minigame/image.ts create mode 100644 pal/image/native/image.ts create mode 100644 pal/image/runtime/image.ts create mode 100644 pal/image/types.ts create mode 100644 pal/image/web/image.ts diff --git a/cc.config.json b/cc.config.json index ad5c1e89bc1..7182305ae68 100644 --- a/cc.config.json +++ b/cc.config.json @@ -304,7 +304,8 @@ "pal/input": "pal/input/web/index.ts", "pal/env": "pal/env/web/env.ts", "pal/pacer": "pal/pacer/pacer-web.ts", - "pal/wasm": "pal/wasm/wasm-web.ts" + "pal/wasm": "pal/wasm/wasm-web.ts", + "pal/image":"pal/image/image-data.ts" } }, { @@ -318,7 +319,8 @@ "pal/input": "pal/input/native/index.ts", "pal/env": "pal/env/native/env.ts", "pal/pacer": "pal/pacer/pacer-native.ts", - "pal/wasm": "pal/wasm/wasm-native.ts" + "pal/wasm": "pal/wasm/wasm-native.ts", + "pal/image":"pal/image/image-data.ts" } }, { @@ -332,7 +334,8 @@ "pal/input": "pal/input/minigame/index.ts", "pal/env": "pal/env/minigame/env.ts", "pal/pacer": "pal/pacer/pacer-minigame.ts", - "pal/wasm": "pal/wasm/wasm-minigame.ts" + "pal/wasm": "pal/wasm/wasm-minigame.ts", + "pal/image":"pal/image/image-data.ts" } }, { @@ -346,7 +349,8 @@ "pal/input": "pal/input/minigame/index.ts", "pal/env": "pal/env/runtime/env.ts", "pal/pacer": "pal/pacer/pacer-minigame.ts", - "pal/wasm": "pal/wasm/wasm-minigame.ts" + "pal/wasm": "pal/wasm/wasm-minigame.ts", + "pal/image":"pal/image/image-data.ts" } }, { diff --git a/cocos/2d/assets/sprite-frame.ts b/cocos/2d/assets/sprite-frame.ts index 6d841fce3c9..7e5ed42fb11 100644 --- a/cocos/2d/assets/sprite-frame.ts +++ b/cocos/2d/assets/sprite-frame.ts @@ -27,10 +27,11 @@ import { ccclass } from 'cc.decorator'; import { EDITOR, TEST, BUILD } from 'internal:constants'; +import { ImageSource } from 'pal/image/types'; import { Mat4, Rect, Size, Vec2, Vec3, Vec4, cclegacy, errorID, warnID, js } from '../../core'; import { Asset } from '../../asset/assets/asset'; import { TextureBase } from '../../asset/assets/texture-base'; -import { ImageAsset, ImageSource } from '../../asset/assets/image-asset'; +import { ImageAsset } from '../../asset/assets/image-asset'; import { Texture2D } from '../../asset/assets/texture-2d'; import { dynamicAtlasManager } from '../utils/dynamic-atlas/atlas-manager'; import { Mesh } from '../../3d/assets/mesh'; diff --git a/cocos/asset/asset-manager/download-dom-image.ts b/cocos/asset/asset-manager/download-dom-image.ts deleted file mode 100644 index d8025f159cb..00000000000 --- a/cocos/asset/asset-manager/download-dom-image.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd. - - http://www.cocos.com - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -import { XIAOMI } from 'internal:constants'; -import { getError } from '../../core'; -import { ccwindow } from '../../core/global-exports'; - -export default function downloadDomImage ( - url: string, - options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void), -): HTMLImageElement { - const img = new ccwindow.Image(); - - // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' - if (ccwindow.location.protocol !== 'file:' || XIAOMI) { - img.crossOrigin = 'anonymous'; - } - - function loadCallback () { - img.removeEventListener('load', loadCallback); - img.removeEventListener('error', errorCallback); - if (onComplete) { onComplete(null, img); } - } - - function errorCallback () { - img.removeEventListener('load', loadCallback); - img.removeEventListener('error', errorCallback); - if (onComplete) { onComplete(new Error(getError(4930, url))); } - } - - img.addEventListener('load', loadCallback); - img.addEventListener('error', errorCallback); - img.src = url; - return img; -} diff --git a/cocos/asset/asset-manager/downloader.ts b/cocos/asset/asset-manager/downloader.ts index e110cc084cd..276dddce88b 100644 --- a/cocos/asset/asset-manager/downloader.ts +++ b/cocos/asset/asset-manager/downloader.ts @@ -23,9 +23,9 @@ */ import { BUILD, EDITOR, EDITOR_NOT_IN_PREVIEW } from 'internal:constants'; +import { ImageData } from 'pal/image'; import { sys, js, misc, path, cclegacy } from '../../core'; import Cache from './cache'; -import downloadDomImage from './download-dom-image'; import downloadFile from './download-file'; import downloadScript from './download-script'; import { files } from './shared'; @@ -48,7 +48,7 @@ const REGEX = /^(?:\w+:\/\/|\.+\/).+/; const downloadImage = (url: string, options: Record, onComplete: ((err: Error | null, data?: any | null) => void)) => { // if createImageBitmap is valid, we can transform blob to ImageBitmap. Otherwise, just use HTMLImageElement to load - const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : downloadDomImage; + const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : ImageData.downloadImage; func(url, options, onComplete); }; @@ -245,7 +245,7 @@ export class Downloader { /** * @deprecated Since v3.7, this is an engine internal interface. You can easily implement the functionality of this API using HTMLImageElement. */ - public downloadDomImage = downloadDomImage; + public downloadDomImage = ImageData.downloadImage; /** * @deprecated Since v3.7, this is an engine internal interface. You can easily implement the functionality of this API using HTMLAudioElement. diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index fa68d4833a7..131981fb7f5 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -24,11 +24,13 @@ import { ALIPAY, XIAOMI, JSB, TEST, BAIDU, EDITOR } from 'internal:constants'; import { Format, FormatFeatureBit, deviceManager } from '../../gfx'; +import { ImageData } from 'pal/image'; import { PixelFormat } from './asset-enum'; import { sys, macro, warnID, cclegacy } from '../../core'; import { patch_cc_ImageAsset } from '../../native-binding/decorators'; import './asset'; import type { ImageAsset as JsbImageAsset } from './image-asset'; +import { ImageSource } from '../../../pal/image/types'; declare const jsb: any; @@ -36,35 +38,8 @@ export type ImageAsset = JsbImageAsset; export const ImageAsset: typeof JsbImageAsset = jsb.ImageAsset; const jsbWindow = jsb.window; -export interface IMemoryImageSource { - _data: ArrayBufferView | null; - _compressed: boolean; - width: number; - height: number; - format: number; - mipmapLevelDataSize?: number[]; -} - -export type ImageSource = HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap; - const extnames = ['.png', '.jpg', '.jpeg', '.bmp', '.webp', '.pvr', '.pkm', '.astc']; -function isImageBitmap (imageSource: any): boolean { - return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); -} - -function isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - if (ALIPAY || XIAOMI || BAIDU) { - // We're unable to grab the constructors of Alipay native image or canvas object. - return !('_data' in imageSource); - } - if (JSB && (imageSource as IMemoryImageSource)._compressed === true) { - return false; - } - - return imageSource instanceof jsbWindow.HTMLImageElement || imageSource instanceof jsbWindow.HTMLCanvasElement || isImageBitmap(imageSource); -} - // TODO: we mark imageAssetProto as type of any, because here we have many dynamic injected property @dumganhar const imageAssetProto: any = ImageAsset.prototype; @@ -72,30 +47,16 @@ imageAssetProto._ctor = function (nativeAsset?: ImageSource) { jsb.Asset.prototype._ctor.apply(this, arguments); this._width = 0; this._height = 0; - this._nativeData = { - _data: null, - width: 0, - height: 0, - format: 0, - _compressed: false, - mipmapLevelDataSize:[], - }; - - if (nativeAsset !== undefined) { - this.reset(nativeAsset); - } + this._imageData = new ImageData(nativeAsset); }; Object.defineProperty(imageAssetProto, '_nativeAsset', { configurable: true, enumerable: true, get () { - return this._nativeData; + return this._imageData; }, - set (value: ImageSource) { - if (!(value instanceof jsbWindow.HTMLElement) && !isImageBitmap(value)) { - (value as IMemoryImageSource).format = (value as IMemoryImageSource).format || this.format; - } + set (value: any) { this.reset(value); }, }); @@ -104,11 +65,7 @@ Object.defineProperty(imageAssetProto, 'data', { configurable: true, enumerable: true, get () { - if (this._nativeData && isNativeImage(this._nativeData)) { - return this._nativeData; - } - - return this._nativeData && this._nativeData._data; + return this._imageData.data; }, }); @@ -123,25 +80,19 @@ imageAssetProto._setRawAsset = function (filename: string, inLibrary = true) { // TODO: Property 'format' does not exist on type 'HTMLCanvasElement'. // imageAssetProto.reset = function (data: ImageSource) { imageAssetProto.reset = function (data: any) { - this._nativeData = data; - - if (!(data instanceof jsbWindow.HTMLElement)) { - if(data.format !== undefined) { - this.format = (data as any).format; - } + this._imageData.reset(data); + if (this._imageData.format != null) { + this._format = this._imageData.format; } this._syncDataToNative(); }; const superDestroy = jsb.Asset.prototype.destroy; imageAssetProto.destroy = function () { - if(this.data && this.data instanceof jsbWindow.HTMLImageElement) { - this.data.src = ''; + if (this._imageData.isHtmlElement()) { this._setRawAsset(''); - this.data.destroy(); - } else if (isImageBitmap(this.data)) { - this.data.close && this.data.close(); } + this._imageData.destroy(); return superDestroy.call(this); }; @@ -149,7 +100,7 @@ Object.defineProperty(imageAssetProto, 'width', { configurable: true, enumerable: true, get () { - return this._nativeData.width || this._width; + return this._imageData.width || this._width; } }); @@ -157,12 +108,12 @@ Object.defineProperty(imageAssetProto, 'height', { configurable: true, enumerable: true, get () { - return this._nativeData.height || this._height; + return this._imageData.height || this._height; } }); imageAssetProto._syncDataToNative = function () { - const data: any = this._nativeData; + const data: any = this._imageData; this._width = data.width; this._height = data.height; @@ -170,24 +121,9 @@ imageAssetProto._syncDataToNative = function () { this.setHeight(this._height); this.url = this.nativeUrl; - if (data instanceof jsbWindow.HTMLCanvasElement) { - this.setData(data._data.data); - } - else if (data instanceof jsbWindow.HTMLImageElement) { - this.setData(data._data); - if (data._mipmapLevelDataSize){ - this.setMipmapLevelDataSize(data._mipmapLevelDataSize); - } - } - else { - if(!this._nativeData._data){ - console.error(`[ImageAsset] setData bad argument ${this._nativeData}`); - return; - } - this.setData(this._nativeData._data); - if (this._nativeData.mipmapLevelDataSize) { - this.setMipmapLevelDataSize(this._nativeData.mipmapLevelDataSize); - } + this.setData(this._imageData.nativeData()); + if (data._mipmapLevelDataSize){ + this.setMipmapLevelDataSize(data._mipmapLevelDataSize); } }; diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 77fb065f482..c2bca997af1 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -25,12 +25,14 @@ // @ts-check import { ccclass, override } from 'cc.decorator'; import { EDITOR, ALIPAY, XIAOMI, JSB, TEST, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; +import { ImageData } from 'pal/image'; import { Device, Format, FormatFeatureBit, deviceManager } from '../../gfx'; import { Asset } from './asset'; import { PixelFormat } from './asset-enum'; import { warnID, macro, sys, cclegacy } from '../../core'; import { ccwindow } from '../../core/global-exports'; import { Enum } from '../../core/value-types/enum'; +import { IMemoryImageSource, ImageSource } from '../../../pal/image/types'; // Compress mipmap constants const COMPRESSED_HEADER_LENGTH = 4; @@ -134,46 +136,6 @@ function readBEUint16 (header, offset: number) { return (header[offset] << 8) | header[offset + 1]; } -/** - * @en Image source in memory - * @zh 内存图像源。 - */ -export interface IMemoryImageSource { - _data: ArrayBufferView | null; - _compressed: boolean; - width: number; - height: number; - format: number; - mipmapLevelDataSize?: number[]; -} - -/** - * @en The image source, can be HTML canvas, image type or image in memory data - * @zh 图像资源的原始图像源。可以来源于 HTML 元素也可以来源于内存。 - */ -export type ImageSource = HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap; - -function isImageBitmap (imageSource: any): imageSource is ImageBitmap { - return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); -} - -function fetchImageSource (imageSource: ImageSource) { - return '_data' in imageSource ? imageSource._data : imageSource; -} - -// 返回该图像源是否是平台提供的图像对象。 -function isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { - // We're unable to grab the constructors of Alipay native image or canvas object. - return !('_data' in imageSource); - } - if (JSB && (imageSource as IMemoryImageSource)._compressed === true) { - return false; - } - - return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || isImageBitmap(imageSource); -} - /** * @en Image Asset. The image resource stores the raw data of the image and you can use this resource to create any Texture resource. * @zh 图像资源。图像资源存储了图像的原始数据,你可以使用此资源来创建任意 [[TextureBase]] 资源。 @@ -523,14 +485,11 @@ export class ImageAsset extends Asset { @override get _nativeAsset () { // Maybe returned to pool in webgl. - return this._nativeData; + return this._imageData.data; } // TODO: Property 'format' does not exist on type 'ImageBitmap' // set _nativeAsset (value: ImageSource) { set _nativeAsset (value: any) { - if (!(value instanceof HTMLElement) && !isImageBitmap(value)) { - value.format = value.format || this._format; - } this.reset(value); } @@ -539,11 +498,7 @@ export class ImageAsset extends Asset { * @zh 此图像资源的图像数据。 */ get data () { - if (isNativeImage(this._nativeData)) { - return this._nativeData; - } - - return this._nativeData && this._nativeData._data; + return this._imageData.data; } /** @@ -551,7 +506,7 @@ export class ImageAsset extends Asset { * @zh 此图像资源的像素宽度。 */ get width () { - return this._nativeData.width || this._width; + return this._imageData.width || this._width; } /** @@ -559,7 +514,7 @@ export class ImageAsset extends Asset { * @zh 此图像资源的像素高度。 */ get height () { - return this._nativeData.height || this._height; + return this._imageData.height || this._height; } /** @@ -585,7 +540,7 @@ export class ImageAsset extends Asset { * @engineInternal */ get mipmapLevelDataSize (): number[] | undefined { - return (this._nativeData as IMemoryImageSource).mipmapLevelDataSize; + return this._imageData.mipmapLevelDataSize; } /** @@ -599,7 +554,7 @@ export class ImageAsset extends Asset { private static extnames = ['.png', '.jpg', '.jpeg', '.bmp', '.webp', '.pvr', '.pkm', '.astc']; - private _nativeData: ImageSource; + private _imageData: ImageData; private _exportedExts: string[] | null | undefined = undefined; @@ -612,22 +567,10 @@ export class ImageAsset extends Asset { constructor (nativeAsset?: ImageSource) { super(); - this._nativeData = { - _data: null, - width: 0, - height: 0, - format: 0, - _compressed: false, - mipmapLevelDataSize: [], - }; - if (EDITOR) { this._exportedExts = null; } - - if (nativeAsset !== undefined) { - this.reset(nativeAsset); - } + this._imageData = new ImageData(nativeAsset); } /** @@ -636,29 +579,17 @@ export class ImageAsset extends Asset { * @param data @en The new source. @zh 新的图片数据源。 */ public reset (data: ImageSource) { - if (isImageBitmap(data)) { - this._nativeData = data; - } else if (!(data instanceof HTMLElement)) { - // this._nativeData = Object.create(data); - this._nativeData = data; - this._format = data.format; - } else { - this._nativeData = data; + this._imageData.reset(data); + if (this._imageData.format != null) { + this._format = this._imageData.format; } } public destroy () { - if (this.data && this.data instanceof HTMLImageElement) { - this.data.src = ''; + if (this._imageData.isHtmlElement()) { this._setRawAsset(''); - // JSB element should destroy native data. - // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. - // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. - // issue: https://github.com/cocos/cocos-engine/issues/14646 - if (JSB) (this.data as any).destroy(); - } else if (isImageBitmap(this.data)) { - this.data?.close(); } + this._imageData.destroy(); return super.destroy(); } diff --git a/cocos/asset/assets/simple-texture.jsb.ts b/cocos/asset/assets/simple-texture.jsb.ts index d862d4ea849..4ecb374c063 100644 --- a/cocos/asset/assets/simple-texture.jsb.ts +++ b/cocos/asset/assets/simple-texture.jsb.ts @@ -27,6 +27,7 @@ import { js, macro, cclegacy } from '../../core'; import './texture-base'; import { patch_cc_SimpleTexture } from '../../native-binding/decorators'; import type { SimpleTexture as JsbSimpleTexture } from './simple-texture'; +import { ImageData } from 'pal/image'; declare const jsb: any; @@ -42,17 +43,8 @@ SimpleTexture.WrapMode = WrapMode; const simpleTextureProto = jsb.SimpleTexture.prototype; const oldUpdateDataFunc = simpleTextureProto.uploadData; simpleTextureProto.uploadData = function (source, level = 0, arrayIndex = 0) { - let data; - if (source instanceof jsbWindow.HTMLCanvasElement) { - // @ts-ignore - data = source.data; - } else if (source instanceof jsbWindow.HTMLImageElement) { - // @ts-ignore - data = source._data; - } else if (ArrayBuffer.isView(source)) { - data = source.buffer; - } - oldUpdateDataFunc.call(this, data, level, arrayIndex); + let imageData = new ImageData(source); + oldUpdateDataFunc.call(this, imageData.nativeData(), level, arrayIndex); }; simpleTextureProto._ctor = function () { diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index dfef7d0295f..1547f28efca 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -24,12 +24,14 @@ import { ccclass } from 'cc.decorator'; import { DEV } from 'internal:constants'; +import { ImageData, ImageSource } from 'pal/image'; import { TextureFlagBit, TextureUsageBit, API, Texture, TextureInfo, TextureViewInfo, Device, BufferTextureCopy } from '../../gfx'; import { assertID, error, js, macro, cclegacy } from '../../core'; import { Filter } from './asset-enum'; import { ImageAsset } from './image-asset'; import { TextureBase } from './texture-base'; import dependUtil from '../asset-manager/depend-util'; +import { IMemoryImageSource } from '../../../pal/image/types'; const _regions: BufferTextureCopy[] = [new BufferTextureCopy()]; @@ -140,11 +142,11 @@ export class SimpleTexture extends TextureBase { * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 * @param arrayIndex @en The array index. @zh 要上传的数组索引。 */ - public uploadData (source: HTMLCanvasElement | HTMLImageElement | ArrayBufferView | ImageBitmap, level = 0, arrayIndex = 0) { + public uploadData (source: HTMLCanvasElement | HTMLImageElement | ArrayBufferView | ImageBitmap | IMemoryImageSource, level = 0, arrayIndex = 0) { if (!this._gfxTexture || this._mipmapLevel <= level) { return; } - + const imageData = new ImageData(source); const gfxDevice = this._getGFXDevice(); if (!gfxDevice) { return; @@ -157,18 +159,18 @@ export class SimpleTexture extends TextureBase { region.texSubres.baseArrayLayer = arrayIndex; if (DEV) { - if (source instanceof HTMLElement) { - if (source.height > region.texExtent.height - || source.width > region.texExtent.width) { + if (imageData.isHtmlElement()) { + if (imageData.height > region.texExtent.height + || imageData.width > region.texExtent.width) { error(`Image source(${this.name}) bounds override.`); } } } - if (ArrayBuffer.isView(source)) { - gfxDevice.copyBuffersToTexture([source], this._gfxTexture, _regions); + if (ArrayBuffer.isView(imageData.data)) { + gfxDevice.copyBuffersToTexture([imageData.data], this._gfxTexture, _regions); } else { - gfxDevice.copyTexImagesToTexture([source], this._gfxTexture, _regions); + gfxDevice.copyTexImagesToTexture([imageData.data as TexImageSource], this._gfxTexture, _regions); } } diff --git a/cocos/asset/assets/texture-cube.ts b/cocos/asset/assets/texture-cube.ts index ffed22ecab4..c9b14f65174 100644 --- a/cocos/asset/assets/texture-cube.ts +++ b/cocos/asset/assets/texture-cube.ts @@ -266,9 +266,8 @@ export class TextureCube extends SimpleTexture { const layoutInfo = layout[j]; _forEachFace(faceAtlas, (face, faceIndex) => { ctx.clearRect(0, 0, imageAtlasAsset.width, imageAtlasAsset.height); - const drawImg = face.data as HTMLImageElement; // NOTE: on OH platform, drawImage only supports ImageBitmap and PixelMap type, so we mark drawImg as any. - ctx.drawImage(drawImg as any, 0, 0); + ctx.drawImage(face.data as any, 0, 0); const rawData = ctx.getImageData(layoutInfo.left, layoutInfo.top, layoutInfo.width, layoutInfo.height); const bufferAsset = new ImageAsset({ diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index 6ba7261726d..b3de8af0548 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -23,6 +23,7 @@ */ import { EDITOR, TAOBAO } from 'internal:constants'; +import { ImageData } from 'pal/image'; import { Material } from '../asset/assets/material'; import { clamp01, Mat4, Vec2, Settings, settings, sys, cclegacy, easing, preTransforms } from '../core'; import { @@ -70,11 +71,11 @@ export class SplashScreen { private isMobile = false; private bgMat!: Material; - private bgImage!: HTMLImageElement; + private bgImage!: ImageData; private bgTexture!: Texture; private logoMat!: Material; - private logoImage!: HTMLImageElement; + private logoImage!: ImageData; private logoTexture!: Texture; private watermarkMat!: Material; @@ -135,7 +136,7 @@ export class SplashScreen { this.initWaterMark(); const bgPromise = new Promise((resolve, reject) => { - this.bgImage = new ccwindow.Image(); + this.bgImage = new ImageData(); this.bgImage.onload = () => { this.initBG(); resolve(); @@ -146,7 +147,7 @@ export class SplashScreen { this.bgImage.src = this.settings.bgBase64; }); const logoPromise = new Promise((resolve, reject) => { - this.logoImage = new ccwindow.Image(); + this.logoImage = new ImageData(); this.logoImage.onload = () => { this.initLogo(); resolve(); @@ -340,7 +341,7 @@ export class SplashScreen { region.texExtent.width = this.bgImage.width; region.texExtent.height = this.bgImage.height; region.texExtent.depth = 1; - device.copyTexImagesToTexture([this.bgImage], this.bgTexture, [region]); + device.copyTexImagesToTexture([this.bgImage.data], this.bgTexture, [region]); } private initLogo () { @@ -375,7 +376,7 @@ export class SplashScreen { region.texExtent.width = this.logoImage.width; region.texExtent.height = this.logoImage.height; region.texExtent.depth = 1; - device.copyTexImagesToTexture([this.logoImage], this.logoTexture, [region]); + device.copyTexImagesToTexture([this.logoImage.data], this.logoTexture, [region]); const logoRatio = this.logoImage.width / this.logoImage.height; if (logoRatio < 1) { diff --git a/cocos/spine/lib/spine-core.d.ts b/cocos/spine/lib/spine-core.d.ts index 12b6de44d8c..473fae22fde 100644 --- a/cocos/spine/lib/spine-core.d.ts +++ b/cocos/spine/lib/spine-core.d.ts @@ -855,9 +855,9 @@ declare namespace spine { constructor(index: number, name: string, boneData: BoneData); } abstract class Texture { - protected _image: HTMLImageElement | ImageBitmap; - constructor(image: HTMLImageElement | ImageBitmap); - getImage(): HTMLImageElement | ImageBitmap; + protected _image: ImageData; + constructor(image: ImageData); + getImage(): ImageData; abstract setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void; abstract setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void; abstract dispose(): void; diff --git a/cocos/tiledmap/tiled-map.ts b/cocos/tiledmap/tiled-map.ts index 908bb0f4224..94d201c174c 100644 --- a/cocos/tiledmap/tiled-map.ts +++ b/cocos/tiledmap/tiled-map.ts @@ -598,19 +598,15 @@ export class TiledMap extends Component { this._buildLayerAndGroup(); if (this.cleanupImageCache) { this._textures.forEach((tex) => { - this.doCleanupImageCache(tex); + this.doCleanupImageCache(tex.texture); }); } } doCleanupImageCache (texture) { - if (texture._image instanceof HTMLImageElement) { - texture._image.src = ''; - if (JSB) texture._image.destroy(); - } else if (sys.hasFeature(sys.Feature.IMAGE_BITMAP) && texture._image instanceof ImageBitmap) { - if (texture._image.close) texture._image.close(); - } - texture._image = null; + // TODO:There is a bug here, if you uncomment, it will cause the display result to be incorrect. + // texture.image.destroy(); + // texture.image = null; } lateUpdate (dt: number) { diff --git a/native/cocos/platform/openharmony/modules/SystemWindow.h b/native/cocos/platform/openharmony/modules/SystemWindow.h index 41a1b37698f..77568045f89 100644 --- a/native/cocos/platform/openharmony/modules/SystemWindow.h +++ b/native/cocos/platform/openharmony/modules/SystemWindow.h @@ -60,4 +60,4 @@ class SystemWindow : public ISystemWindow { uint64_t _height{0}; }; -} // namespace cc \ No newline at end of file +} // namespace cc diff --git a/pal/image/image-data.ts b/pal/image/image-data.ts new file mode 100644 index 00000000000..4305543c56f --- /dev/null +++ b/pal/image/image-data.ts @@ -0,0 +1,197 @@ +import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; +import { IMemoryImageSource, ImageSource } from './types'; +import { sys } from '../../cocos/core/platform/sys'; +import { ccwindow } from '../../cocos/core/global-exports'; +import { getError } from '../../cocos/core'; + +export class ImageData { + private _imageSource: ImageSource; + + constructor (imageAsset?: ImageSource | ArrayBufferView) { + this._imageSource = { + _data: null, + width: 0, + height: 0, + format: 0, + _compressed: false, + mipmapLevelDataSize: [], + }; + //this._nativeData = data; + if (typeof imageAsset !== 'undefined') { + if (!ArrayBuffer.isView(imageAsset)) { + this.reset(imageAsset); + } else { + this._imageSource._data = imageAsset; + } + } else if (typeof imageAsset === 'undefined') { + this._imageSource = new ccwindow.Image(); + } + } + + public destroy () { + if (this.data && this.data instanceof HTMLImageElement) { + this.data.src = ''; + // this._setRawAsset(''); + // JSB element should destroy native data. + // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. + // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. + // issue: https://github.com/cocos/cocos-engine/issues/14646 + if (JSB) (this.data as any).destroy(); + } else if (this.isImageBitmap(this.data)) { + this.data?.close(); + } + } + + set data (value: any) { + this.reset(value); + } + + get data (): ImageSource | ArrayBufferView | null { + if (this._imageSource && this.isNativeImage(this._imageSource)) { + return this._imageSource; + } + + return this._imageSource && this._imageSource._data; + } + + public nativeData (): unknown { + if (JSB) { + if (this._imageSource instanceof HTMLCanvasElement) { + // @ts-ignore + return this._imageSource._data.data; + } else if (this._imageSource instanceof HTMLImageElement) { + // @ts-ignore + return this._imageSource._data; + } else if (ArrayBuffer.isView(this._imageSource)) { + return this._imageSource.buffer; + } + } + return this.data as any; + } + + set crossOrigin (string) { + (this._imageSource as HTMLImageElement).crossOrigin = 'anonymous'; + } + + set onload (cb) { + (this._imageSource as HTMLImageElement).onload = cb; + } + + set onerror (cb) { + (this._imageSource as HTMLImageElement).onerror = cb; + } + + set src (url: string) { + (this._imageSource as HTMLImageElement).src = url; + } + + get src (): string { + return (this._imageSource as HTMLImageElement).src; + } + + get width () { + return this._imageSource.width; + } + + set width (value) { + // @ts-ignore + this._imageSource.width = value; + } + + get height (): number { + if (!(this._imageSource instanceof ArrayBuffer)) { + return this._imageSource.height; + } + return 0; + } + + set height (value) { + // @ts-ignore + this._imageSource.height = value; + } + + get format (): number | null { + if (!(this._imageSource instanceof HTMLElement) && !this.isImageBitmap(this._imageSource) && !this.isArrayBuffer()) { + return this._imageSource.format; + } + return null; + } + + get compressed () { + return false; + } + + get mipmapLevelDataSize () { + return (this._imageSource as IMemoryImageSource).mipmapLevelDataSize; + } + + public reset (data: ImageSource) { + this._imageSource = data; + } + public isArrayBuffer () { + return ArrayBuffer.isView((this._imageSource as IMemoryImageSource)._data); + } + public isHtmlElement () { + return this._imageSource instanceof HTMLElement; + } + public isImageBitmap (imageSource: any): imageSource is ImageBitmap { + return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); + } + + // 返回该图像源是否是平台提供的图像对象。 + private isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { + if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { + // We're unable to grab the constructors of Alipay native image or canvas object. + return !('_data' in imageSource); + } + if (JSB && (imageSource as IMemoryImageSource)._compressed === true) { + return false; + } + + return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || this.isImageBitmap(imageSource); + } + + private fetchImageSource (imageSource: ImageSource) { + return '_data' in imageSource ? imageSource._data : imageSource; + } + + public addEventListener (name, cb) { + if (this.isHtmlElement()) { + (this.data as HTMLImageElement).addEventListener(name, cb); + } + } + + public removeEventListener (name, cb) { + if (this.isHtmlElement()) { + (this.data as HTMLImageElement).removeEventListener(name, cb); + } + } + + static downloadImage (url: string, + options: Record, + onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + const image = new ImageData(); + + // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' + if (ccwindow.location.protocol !== 'file:' || XIAOMI) { + image.crossOrigin = 'anonymous'; + } + + function loadCallback () { + image.removeEventListener('load', loadCallback); + if (onComplete) { onComplete(null, image.data as HTMLImageElement); } + image.removeEventListener('error', errorCallback); + } + + function errorCallback () { + image.removeEventListener('load', loadCallback); + image.removeEventListener('error', errorCallback); + if (onComplete) { onComplete(new Error(getError(4930, url))); } + } + + image.addEventListener('load', loadCallback); + image.addEventListener('error', errorCallback); + image.src = url; + return (image.data as HTMLImageElement); + } +} diff --git a/pal/image/minigame/image.ts b/pal/image/minigame/image.ts new file mode 100644 index 00000000000..c2103ffdbf9 --- /dev/null +++ b/pal/image/minigame/image.ts @@ -0,0 +1,2 @@ +export class Image { +} diff --git a/pal/image/native/image.ts b/pal/image/native/image.ts new file mode 100644 index 00000000000..c2103ffdbf9 --- /dev/null +++ b/pal/image/native/image.ts @@ -0,0 +1,2 @@ +export class Image { +} diff --git a/pal/image/runtime/image.ts b/pal/image/runtime/image.ts new file mode 100644 index 00000000000..c2103ffdbf9 --- /dev/null +++ b/pal/image/runtime/image.ts @@ -0,0 +1,2 @@ +export class Image { +} diff --git a/pal/image/types.ts b/pal/image/types.ts new file mode 100644 index 00000000000..df9fb062c81 --- /dev/null +++ b/pal/image/types.ts @@ -0,0 +1,42 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/** + * @en Image source in memory + * @zh 内存图像源。 + */ +export interface IMemoryImageSource { + _data: ArrayBufferView | null; + _compressed: boolean; + width: number; + height: number; + format: number; + mipmapLevelDataSize?: number[]; +} + +/** + * @en The image source, can be HTML canvas, image type or image in memory data + * @zh 图像资源的原始图像源。可以来源于 HTML 元素也可以来源于内存。 + */ +export type ImageSource = HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap; diff --git a/pal/image/web/image.ts b/pal/image/web/image.ts new file mode 100644 index 00000000000..035fca76e8a --- /dev/null +++ b/pal/image/web/image.ts @@ -0,0 +1,135 @@ +import { systemInfo } from 'pal/system-info'; +import { EDITOR, ALIPAY, XIAOMI, JSB, TEST, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; +import { IMemoryImageSource, ImageSource } from '../types'; +import { sys } from '../../../cocos/core/platform/sys'; +import { ccwindow } from '../../../cocos/core/global-exports'; +import { getError } from '../../../cocos/core'; + +export class Image { + private _nativeData: ImageSource; + + constructor (imageAsset?: ImageSource) { + this._nativeData = { + _data: null, + width: 0, + height: 0, + format: 0, + _compressed: false, + mipmapLevelDataSize: [], + }; + //this._nativeData = data; + if (imageAsset !== undefined) { + this.reset(imageAsset); + } + } + + public destroy () { + if (this.data && this.data instanceof HTMLImageElement) { + this.data.src = ''; + // this._setRawAsset(''); + // JSB element should destroy native data. + // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. + // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. + // issue: https://github.com/cocos/cocos-engine/issues/14646 + if (JSB) (this.data as any).destroy(); + } else if (this.isImageBitmap(this.data)) { + this.data?.close(); + } + } + + set data (value: any) { + this.reset(value); + } + + get data () { + if (this.isNativeImage(this._nativeData)) { + return this._nativeData; + } + + return this._nativeData && this._nativeData._data; + } + + get width () { + return this._nativeData.width; + } + + get height () { + return this._nativeData.height; + } + + get format () { + return (this._nativeData as IMemoryImageSource).format; + } + + get compressed () { + return false; + } + + get mipmapLevelDataSize () { + return (this._nativeData as IMemoryImageSource).mipmapLevelDataSize; + } + + static downloadDomImage (url: string, + options: Record, + onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)) { + const img = new HTMLImageElement(); + + // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' + if (ccwindow.location.protocol !== 'file:' || XIAOMI) { + img.crossOrigin = 'anonymous'; + } + + function loadCallback () { + img.removeEventListener('load', loadCallback); + img.removeEventListener('error', errorCallback); + if (onComplete) { onComplete(null, img); } + } + + function errorCallback () { + img.removeEventListener('load', loadCallback); + img.removeEventListener('error', errorCallback); + if (onComplete) { onComplete(new Error(getError(4930, url))); } + } + + img.addEventListener('load', loadCallback); + img.addEventListener('error', errorCallback); + img.src = url; + return img; + } + + public reset (data: ImageSource) { + this._nativeData = data; + // if (this.isImageBitmap(data)) { + // this._nativeData = data; + // } else if (!(data instanceof HTMLElement)) { + // // this._nativeData = Object.create(data); + // this._nativeData = data; + // } else { + // this._nativeData = data; + // } + } + + public isHtmlElement () { + return this._nativeData instanceof HTMLElement; + } + public isImageBitmap (imageSource: any): imageSource is ImageBitmap { + return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); + } + + // 返回该图像源是否是平台提供的图像对象。 + public isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { + if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { + // We're unable to grab the constructors of Alipay native image or canvas object. + return !('_data' in imageSource); + } + if (JSB && (imageSource as IMemoryImageSource)._compressed === true) { + return false; + } + + return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || this.isImageBitmap(imageSource); + } + + public fetchImageSource (imageSource: ImageSource) { + return '_data' in imageSource ? imageSource._data : imageSource; + } +} diff --git a/tsconfig.json b/tsconfig.json index cb1ed02d828..ba8f94470e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -46,6 +46,7 @@ "./@types/pal/env", "./@types/pal/pacer", "./@types/pal/wasm", + "./@types/pal/image", ] }, "include": [ From 51a2632e35695c5a3e69660aae30cc48450ae821 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 20 Jun 2023 10:08:25 +0800 Subject: [PATCH 02/24] Delete unused files --- pal/image/minigame/image.ts | 2 - pal/image/native/image.ts | 2 - pal/image/runtime/image.ts | 2 - pal/image/web/image.ts | 135 ------------------------------------ 4 files changed, 141 deletions(-) delete mode 100644 pal/image/minigame/image.ts delete mode 100644 pal/image/native/image.ts delete mode 100644 pal/image/runtime/image.ts delete mode 100644 pal/image/web/image.ts diff --git a/pal/image/minigame/image.ts b/pal/image/minigame/image.ts deleted file mode 100644 index c2103ffdbf9..00000000000 --- a/pal/image/minigame/image.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class Image { -} diff --git a/pal/image/native/image.ts b/pal/image/native/image.ts deleted file mode 100644 index c2103ffdbf9..00000000000 --- a/pal/image/native/image.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class Image { -} diff --git a/pal/image/runtime/image.ts b/pal/image/runtime/image.ts deleted file mode 100644 index c2103ffdbf9..00000000000 --- a/pal/image/runtime/image.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class Image { -} diff --git a/pal/image/web/image.ts b/pal/image/web/image.ts deleted file mode 100644 index 035fca76e8a..00000000000 --- a/pal/image/web/image.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { systemInfo } from 'pal/system-info'; -import { EDITOR, ALIPAY, XIAOMI, JSB, TEST, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; -import { IMemoryImageSource, ImageSource } from '../types'; -import { sys } from '../../../cocos/core/platform/sys'; -import { ccwindow } from '../../../cocos/core/global-exports'; -import { getError } from '../../../cocos/core'; - -export class Image { - private _nativeData: ImageSource; - - constructor (imageAsset?: ImageSource) { - this._nativeData = { - _data: null, - width: 0, - height: 0, - format: 0, - _compressed: false, - mipmapLevelDataSize: [], - }; - //this._nativeData = data; - if (imageAsset !== undefined) { - this.reset(imageAsset); - } - } - - public destroy () { - if (this.data && this.data instanceof HTMLImageElement) { - this.data.src = ''; - // this._setRawAsset(''); - // JSB element should destroy native data. - // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. - // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. - // issue: https://github.com/cocos/cocos-engine/issues/14646 - if (JSB) (this.data as any).destroy(); - } else if (this.isImageBitmap(this.data)) { - this.data?.close(); - } - } - - set data (value: any) { - this.reset(value); - } - - get data () { - if (this.isNativeImage(this._nativeData)) { - return this._nativeData; - } - - return this._nativeData && this._nativeData._data; - } - - get width () { - return this._nativeData.width; - } - - get height () { - return this._nativeData.height; - } - - get format () { - return (this._nativeData as IMemoryImageSource).format; - } - - get compressed () { - return false; - } - - get mipmapLevelDataSize () { - return (this._nativeData as IMemoryImageSource).mipmapLevelDataSize; - } - - static downloadDomImage (url: string, - options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)) { - const img = new HTMLImageElement(); - - // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' - if (ccwindow.location.protocol !== 'file:' || XIAOMI) { - img.crossOrigin = 'anonymous'; - } - - function loadCallback () { - img.removeEventListener('load', loadCallback); - img.removeEventListener('error', errorCallback); - if (onComplete) { onComplete(null, img); } - } - - function errorCallback () { - img.removeEventListener('load', loadCallback); - img.removeEventListener('error', errorCallback); - if (onComplete) { onComplete(new Error(getError(4930, url))); } - } - - img.addEventListener('load', loadCallback); - img.addEventListener('error', errorCallback); - img.src = url; - return img; - } - - public reset (data: ImageSource) { - this._nativeData = data; - // if (this.isImageBitmap(data)) { - // this._nativeData = data; - // } else if (!(data instanceof HTMLElement)) { - // // this._nativeData = Object.create(data); - // this._nativeData = data; - // } else { - // this._nativeData = data; - // } - } - - public isHtmlElement () { - return this._nativeData instanceof HTMLElement; - } - public isImageBitmap (imageSource: any): imageSource is ImageBitmap { - return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); - } - - // 返回该图像源是否是平台提供的图像对象。 - public isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { - // We're unable to grab the constructors of Alipay native image or canvas object. - return !('_data' in imageSource); - } - if (JSB && (imageSource as IMemoryImageSource)._compressed === true) { - return false; - } - - return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || this.isImageBitmap(imageSource); - } - - public fetchImageSource (imageSource: ImageSource) { - return '_data' in imageSource ? imageSource._data : imageSource; - } -} From 8c48d24436e3ddba258e7baf16ec48d96e2b90ac Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 20 Jun 2023 10:13:53 +0800 Subject: [PATCH 03/24] Add PAL/Image declaration file --- @types/pal/image.d.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 @types/pal/image.d.ts diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts new file mode 100644 index 00000000000..538aee205fb --- /dev/null +++ b/@types/pal/image.d.ts @@ -0,0 +1,29 @@ +declare module 'pal/image' { + type ImageSource = import('pal/image/types').ImageSource; + export class ImageData { + constructor (imageAsset?: ImageSource | ArrayBufferView); + //constructor (content: string, manifestRoot: string); + destroy (): void; + get src (): string; + set src(url: string); + set data(value: any); + get data(): ImageSource | ArrayBufferView; + set width(value: any); + get width(): number; + get height(): number; + set height(value: any); + get format(): number; + get compressed(): number; + get mipmapLevelDataSize(): number[] | undefined; + static downloadImage ( + url: string, + options: Record, + onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void), + ): HTMLImageElement; + onload: ((ev: Event) => any) | null; + onerror: ((ev: Event) => any) | null; + nativeData (): unknown; + isHtmlElement(): boolean; + reset(data: ImageSource): void; + } +} From 0215a1c48b83ceb9f33980c5c1bf400580198e9f Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 20 Jun 2023 14:51:39 +0800 Subject: [PATCH 04/24] Different platform implementations of ImageData --- @types/pal/image.d.ts | 1 + cc.config.json | 8 +-- .../skinned-mesh-batch-renderer.ts | 2 +- cocos/asset/asset-manager/builtin-res-mgr.ts | 3 +- cocos/asset/asset-manager/parser.ts | 3 +- cocos/asset/assets/simple-texture.ts | 3 +- cocos/game/splash-screen.ts | 4 +- .../{image-data.ts => base-image-data.ts} | 60 +---------------- pal/image/minigame/image-data.ts | 47 ++++++++++++++ pal/image/native/image-data.ts | 64 +++++++++++++++++++ pal/image/web/image-data.ts | 38 +++++++++++ 11 files changed, 165 insertions(+), 68 deletions(-) rename pal/image/{image-data.ts => base-image-data.ts} (59%) create mode 100644 pal/image/minigame/image-data.ts create mode 100644 pal/image/native/image-data.ts create mode 100644 pal/image/web/image-data.ts diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 538aee205fb..04445c1f3e2 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -1,5 +1,6 @@ declare module 'pal/image' { type ImageSource = import('pal/image/types').ImageSource; + type IMemoryImageSource = import('pal/image/types').IMemoryImageSource; export class ImageData { constructor (imageAsset?: ImageSource | ArrayBufferView); //constructor (content: string, manifestRoot: string); diff --git a/cc.config.json b/cc.config.json index 7182305ae68..17ef1a7f61b 100644 --- a/cc.config.json +++ b/cc.config.json @@ -305,7 +305,7 @@ "pal/env": "pal/env/web/env.ts", "pal/pacer": "pal/pacer/pacer-web.ts", "pal/wasm": "pal/wasm/wasm-web.ts", - "pal/image":"pal/image/image-data.ts" + "pal/image":"pal/image/web/image-data.ts" } }, { @@ -320,7 +320,7 @@ "pal/env": "pal/env/native/env.ts", "pal/pacer": "pal/pacer/pacer-native.ts", "pal/wasm": "pal/wasm/wasm-native.ts", - "pal/image":"pal/image/image-data.ts" + "pal/image":"pal/image/native/image-data.ts" } }, { @@ -335,7 +335,7 @@ "pal/env": "pal/env/minigame/env.ts", "pal/pacer": "pal/pacer/pacer-minigame.ts", "pal/wasm": "pal/wasm/wasm-minigame.ts", - "pal/image":"pal/image/image-data.ts" + "pal/image":"pal/image/minigame/image-data.ts" } }, { @@ -350,7 +350,7 @@ "pal/env": "pal/env/runtime/env.ts", "pal/pacer": "pal/pacer/pacer-minigame.ts", "pal/wasm": "pal/wasm/wasm-minigame.ts", - "pal/image":"pal/image/image-data.ts" + "pal/image":"pal/image/minigame/image-data.ts" } }, { diff --git a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts index bf8f4a08bb2..547e6746ea6 100644 --- a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts +++ b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts @@ -469,7 +469,7 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { region.texExtent.height = unit.size.y * this.atlasSize; const { data } = partial.image; if (!ArrayBuffer.isView(data)) { - texImages.push(data); + texImages.push(data as TexImageSource); texImageRegions.push(region); } else { texBuffers.push(data); diff --git a/cocos/asset/asset-manager/builtin-res-mgr.ts b/cocos/asset/asset-manager/builtin-res-mgr.ts index b24284e03b3..7976c8744b9 100644 --- a/cocos/asset/asset-manager/builtin-res-mgr.ts +++ b/cocos/asset/asset-manager/builtin-res-mgr.ts @@ -23,8 +23,9 @@ */ import { EDITOR, EDITOR_NOT_IN_PREVIEW, TEST } from 'internal:constants'; +import { ImageSource } from 'pal/image'; import { Asset } from '../assets/asset'; -import { ImageAsset, ImageSource } from '../assets/image-asset'; +import { ImageAsset } from '../assets/image-asset'; import { SpriteFrame } from '../../2d/assets/sprite-frame'; import { Texture2D } from '../assets/texture-2d'; import { TextureCube } from '../assets/texture-cube'; diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index 4982f766cb3..ca76bc92c05 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -22,7 +22,8 @@ THE SOFTWARE. */ -import { ImageAsset, IMemoryImageSource } from '../assets/image-asset'; +import { IMemoryImageSource } from 'pal/image'; +import { ImageAsset } from '../assets/image-asset'; import { js } from '../../core'; import Cache from './cache'; import deserialize from './deserialize'; diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index 1547f28efca..73cc85d4322 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -24,14 +24,13 @@ import { ccclass } from 'cc.decorator'; import { DEV } from 'internal:constants'; -import { ImageData, ImageSource } from 'pal/image'; +import { ImageData, IMemoryImageSource } from 'pal/image'; import { TextureFlagBit, TextureUsageBit, API, Texture, TextureInfo, TextureViewInfo, Device, BufferTextureCopy } from '../../gfx'; import { assertID, error, js, macro, cclegacy } from '../../core'; import { Filter } from './asset-enum'; import { ImageAsset } from './image-asset'; import { TextureBase } from './texture-base'; import dependUtil from '../asset-manager/depend-util'; -import { IMemoryImageSource } from '../../../pal/image/types'; const _regions: BufferTextureCopy[] = [new BufferTextureCopy()]; diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index b3de8af0548..527957976f1 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -341,7 +341,7 @@ export class SplashScreen { region.texExtent.width = this.bgImage.width; region.texExtent.height = this.bgImage.height; region.texExtent.depth = 1; - device.copyTexImagesToTexture([this.bgImage.data], this.bgTexture, [region]); + device.copyTexImagesToTexture([this.bgImage.data as TexImageSource], this.bgTexture, [region]); } private initLogo () { @@ -376,7 +376,7 @@ export class SplashScreen { region.texExtent.width = this.logoImage.width; region.texExtent.height = this.logoImage.height; region.texExtent.depth = 1; - device.copyTexImagesToTexture([this.logoImage.data], this.logoTexture, [region]); + device.copyTexImagesToTexture([this.logoImage.data as TexImageSource], this.logoTexture, [region]); const logoRatio = this.logoImage.width / this.logoImage.height; if (logoRatio < 1) { diff --git a/pal/image/image-data.ts b/pal/image/base-image-data.ts similarity index 59% rename from pal/image/image-data.ts rename to pal/image/base-image-data.ts index 4305543c56f..75741b95556 100644 --- a/pal/image/image-data.ts +++ b/pal/image/base-image-data.ts @@ -1,11 +1,9 @@ -import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { IMemoryImageSource, ImageSource } from './types'; import { sys } from '../../cocos/core/platform/sys'; import { ccwindow } from '../../cocos/core/global-exports'; -import { getError } from '../../cocos/core'; -export class ImageData { - private _imageSource: ImageSource; +export class BaseImageData { + protected _imageSource: ImageSource; constructor (imageAsset?: ImageSource | ArrayBufferView) { this._imageSource = { @@ -32,11 +30,6 @@ export class ImageData { if (this.data && this.data instanceof HTMLImageElement) { this.data.src = ''; // this._setRawAsset(''); - // JSB element should destroy native data. - // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. - // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. - // issue: https://github.com/cocos/cocos-engine/issues/14646 - if (JSB) (this.data as any).destroy(); } else if (this.isImageBitmap(this.data)) { this.data?.close(); } @@ -55,17 +48,6 @@ export class ImageData { } public nativeData (): unknown { - if (JSB) { - if (this._imageSource instanceof HTMLCanvasElement) { - // @ts-ignore - return this._imageSource._data.data; - } else if (this._imageSource instanceof HTMLImageElement) { - // @ts-ignore - return this._imageSource._data; - } else if (ArrayBuffer.isView(this._imageSource)) { - return this._imageSource.buffer; - } - } return this.data as any; } @@ -139,15 +121,7 @@ export class ImageData { } // 返回该图像源是否是平台提供的图像对象。 - private isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { - // We're unable to grab the constructors of Alipay native image or canvas object. - return !('_data' in imageSource); - } - if (JSB && (imageSource as IMemoryImageSource)._compressed === true) { - return false; - } - + protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || this.isImageBitmap(imageSource); } @@ -166,32 +140,4 @@ export class ImageData { (this.data as HTMLImageElement).removeEventListener(name, cb); } } - - static downloadImage (url: string, - options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { - const image = new ImageData(); - - // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' - if (ccwindow.location.protocol !== 'file:' || XIAOMI) { - image.crossOrigin = 'anonymous'; - } - - function loadCallback () { - image.removeEventListener('load', loadCallback); - if (onComplete) { onComplete(null, image.data as HTMLImageElement); } - image.removeEventListener('error', errorCallback); - } - - function errorCallback () { - image.removeEventListener('load', loadCallback); - image.removeEventListener('error', errorCallback); - if (onComplete) { onComplete(new Error(getError(4930, url))); } - } - - image.addEventListener('load', loadCallback); - image.addEventListener('error', errorCallback); - image.src = url; - return (image.data as HTMLImageElement); - } } diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts new file mode 100644 index 00000000000..29121926ce8 --- /dev/null +++ b/pal/image/minigame/image-data.ts @@ -0,0 +1,47 @@ +import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; +import { BaseImageData } from '../base-image-data'; +import { ImageSource } from '../types'; +import { ccwindow } from '../../../cocos/core/global-exports'; +import { getError } from '../../../cocos/core'; + +export class ImageData extends BaseImageData { + protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { + if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { + // We're unable to grab the constructors of Alipay native image or canvas object. + return !('_data' in imageSource); + } + return super.isNativeImage(imageSource); + } + + set src (url: string) { + (this._imageSource as HTMLImageElement).src = url; + } + + static downloadImage (url: string, + options: Record, + onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + const image = new ImageData(); + + // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' + if (ccwindow.location.protocol !== 'file:' || XIAOMI) { + image.crossOrigin = 'anonymous'; + } + + function loadCallback () { + image.removeEventListener('load', loadCallback); + if (onComplete) { onComplete(null, image.data as HTMLImageElement); } + image.removeEventListener('error', errorCallback); + } + + function errorCallback () { + image.removeEventListener('load', loadCallback); + image.removeEventListener('error', errorCallback); + if (onComplete) { onComplete(new Error(getError(4930, url))); } + } + + image.addEventListener('load', loadCallback); + image.addEventListener('error', errorCallback); + image.src = url; + return (image.data as HTMLImageElement); + } +} diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts new file mode 100644 index 00000000000..3a38070783a --- /dev/null +++ b/pal/image/native/image-data.ts @@ -0,0 +1,64 @@ +import { BaseImageData } from '../base-image-data'; +import { ImageSource, IMemoryImageSource } from '../types'; +import { ccwindow } from '../../../cocos/core/global-exports'; +import { getError } from '../../../cocos/core'; + +export class ImageData extends BaseImageData { + public destroy () { + if (this.data && this.data instanceof HTMLImageElement) { + // JSB element should destroy native data. + // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. + // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. + // issue: https://github.com/cocos/cocos-engine/issues/14646 + (this.data as any).destroy(); + } + super.destroy(); + } + public nativeData (): unknown { + if (this._imageSource instanceof HTMLCanvasElement) { + // @ts-ignore + return this._imageSource._data.data; + } else if (this._imageSource instanceof HTMLImageElement) { + // @ts-ignore + return this._imageSource._data; + } else if (ArrayBuffer.isView(this._imageSource)) { + return this._imageSource.buffer; + } + return super.nativeData(); + } + + protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { + if ((imageSource as IMemoryImageSource)._compressed === true) { + return false; + } + return super.isNativeImage(imageSource); + } + + static downloadImage (url: string, + options: Record, + onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + const image = new ImageData(); + + // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' + if (ccwindow.location.protocol !== 'file:') { + image.crossOrigin = 'anonymous'; + } + + function loadCallback () { + image.removeEventListener('load', loadCallback); + if (onComplete) { onComplete(null, image.data as HTMLImageElement); } + image.removeEventListener('error', errorCallback); + } + + function errorCallback () { + image.removeEventListener('load', loadCallback); + image.removeEventListener('error', errorCallback); + if (onComplete) { onComplete(new Error(getError(4930, url))); } + } + + image.addEventListener('load', loadCallback); + image.addEventListener('error', errorCallback); + image.src = url; + return (image.data as HTMLImageElement); + } +} diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts new file mode 100644 index 00000000000..ede17987c4e --- /dev/null +++ b/pal/image/web/image-data.ts @@ -0,0 +1,38 @@ +import { BaseImageData } from '../base-image-data'; +import { ccwindow } from '../../../cocos/core/global-exports'; +import { getError } from '../../../cocos/core'; + +export class ImageData extends BaseImageData { + public nativeData (): unknown { + // TODO:Get raw image data + return null; + } + + static downloadImage (url: string, + options: Record, + onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + const image = new ImageData(); + + // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' + if (ccwindow.location.protocol !== 'file:') { + image.crossOrigin = 'anonymous'; + } + + function loadCallback () { + image.removeEventListener('load', loadCallback); + if (onComplete) { onComplete(null, image.data as HTMLImageElement); } + image.removeEventListener('error', errorCallback); + } + + function errorCallback () { + image.removeEventListener('load', loadCallback); + image.removeEventListener('error', errorCallback); + if (onComplete) { onComplete(new Error(getError(4930, url))); } + } + + image.addEventListener('load', loadCallback); + image.addEventListener('error', errorCallback); + image.src = url; + return (image.data as HTMLImageElement); + } +} From b8408b6a7d6c9fa53cded058cf91568586ed0113 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 20 Jun 2023 16:25:30 +0800 Subject: [PATCH 05/24] Add copyright information --- pal/image/base-image-data.ts | 23 +++++++++++++++++++++++ pal/image/minigame/image-data.ts | 23 +++++++++++++++++++++++ pal/image/native/image-data.ts | 23 +++++++++++++++++++++++ pal/image/web/image-data.ts | 23 +++++++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 75741b95556..ebe11bed0d2 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -1,3 +1,26 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ import { IMemoryImageSource, ImageSource } from './types'; import { sys } from '../../cocos/core/platform/sys'; import { ccwindow } from '../../cocos/core/global-exports'; diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index 29121926ce8..d5f943eca6c 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -1,3 +1,26 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { BaseImageData } from '../base-image-data'; import { ImageSource } from '../types'; diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 3a38070783a..1a4e488e88f 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -1,3 +1,26 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ import { BaseImageData } from '../base-image-data'; import { ImageSource, IMemoryImageSource } from '../types'; import { ccwindow } from '../../../cocos/core/global-exports'; diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index ede17987c4e..95944e48d88 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -1,3 +1,26 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; From 5c34e5a06209f8e39b95bff50af1eb3b7551415a Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Wed, 21 Jun 2023 08:40:26 +0800 Subject: [PATCH 06/24] fix CI --- tests/init.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/init.ts b/tests/init.ts index 15d492a8966..4189ea40f6f 100644 --- a/tests/init.ts +++ b/tests/init.ts @@ -61,6 +61,12 @@ jest.mock( { virtual: true, }, ); +jest.mock( + 'pal/image', + () => jest.requireActual('../pal/image/web/image-data'), + { virtual: true, }, +); + // Mock external wasm module here [ 'external:emscripten/bullet/bullet.wasm', From 641808c71e9bd921fbc53cd7c7c42f876a64f44c Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 27 Jun 2023 14:19:03 +0800 Subject: [PATCH 07/24] 1. Adding the copyImagesToTexture interface; 2. Modify the dependency of download on HTMLImageElement. --- @types/pal/image.d.ts | 77 ++++++++++++++++++++++---- cocos/2d/assembler/label/font-utils.ts | 2 +- cocos/2d/assets/sprite-frame.ts | 2 +- cocos/2d/utils/dynamic-atlas/atlas.ts | 2 +- cocos/asset/asset-manager/factory.ts | 4 +- cocos/asset/asset-manager/parser.ts | 9 +-- cocos/asset/assets/image-asset.jsb.ts | 2 +- cocos/asset/assets/image-asset.ts | 12 +++- cocos/asset/assets/simple-texture.ts | 17 ++---- cocos/gfx/base/device.ts | 10 ++++ cocos/gfx/empty/empty-device.ts | 2 + cocos/gfx/webgl/webgl-device.ts | 18 ++++++ cocos/gfx/webgl2/webgl2-device.ts | 18 ++++++ cocos/gfx/webgpu/webgpu-define.ts | 9 +++ pal/image/base-image-data.ts | 54 +++++++++--------- pal/image/minigame/image-data.ts | 21 +++---- pal/image/native/image-data.ts | 22 +++----- pal/image/web/image-data.ts | 59 ++++++++++++++------ platforms/native/engine/jsb-gfx.js | 11 ++++ 19 files changed, 244 insertions(+), 107 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 04445c1f3e2..40d735f108c 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -1,30 +1,87 @@ declare module 'pal/image' { type ImageSource = import('pal/image/types').ImageSource; type IMemoryImageSource = import('pal/image/types').IMemoryImageSource; + type PixelFormat = import('cocos/asset/assets/asset-enum').PixelFormat; export class ImageData { constructor (imageAsset?: ImageSource | ArrayBufferView); - //constructor (content: string, manifestRoot: string); + /** + * Destroy resources. + */ destroy (): void; + + /** + * Get the url of the image(when source is HTMLImageElement). + */ get src (): string; + /** + * Set the url of the image(when source is HTMLImageElement). + */ set src(url: string); - set data(value: any); + /** + * Set image data source. + * @param value Image data source. + */ + set data(value: ImageSource | ArrayBufferView); + /** + * Get image data source. + * @param value Image data source. + */ get data(): ImageSource | ArrayBufferView; - set width(value: any); + /** + * Set image width(when source is IMemoryImageSource). + */ + set width(value: number); + /** + * Get image width. + */ get width(): number; + /** + * Set image width(when source is IMemoryImageSource). + */ + set height(value: number); + /** + * Get image height. + */ get height(): number; - set height(value: any); - get format(): number; - get compressed(): number; + /** + * Get image format. + */ + get format(): PixelFormat; + /** + * Get if the image is compressed. + */ + get compressed(): boolean; get mipmapLevelDataSize(): number[] | undefined; + /** + * Download images from the server. + */ static downloadImage ( url: string, options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void), - ): HTMLImageElement; - onload: ((ev: Event) => any) | null; - onerror: ((ev: Event) => any) | null; + onComplete: ((err: Error | null, data?: ImageSource | null) => void), + ): ImageData; + + /** + * Image load success callback(when the image is HTMLImageElement). + */ + onload: ((ev: Event) => void) | null; + /** + * Image load error callback(when the image is HTMLImageElement). + */ + onerror: ((ev: Event) => void) | null; + + /** + * Get image data, in web platform is image source(like data interface), in native platform is image raw data. + */ nativeData (): unknown; + /** + * Determine if it is an HTMLElement element. + */ isHtmlElement(): boolean; + /** + * Set image data source. + * @param data Image data source(Not contain IMemoryImageSource). + */ reset(data: ImageSource): void; } } diff --git a/cocos/2d/assembler/label/font-utils.ts b/cocos/2d/assembler/label/font-utils.ts index 1034240d05f..9f9a7246a3e 100644 --- a/cocos/2d/assembler/label/font-utils.ts +++ b/cocos/2d/assembler/label/font-utils.ts @@ -260,7 +260,7 @@ export class LetterRenderTexture extends Texture2D { region.texOffset.y = y; region.texExtent.width = image.width; region.texExtent.height = image.height; - gfxDevice.copyTexImagesToTexture([image.data as HTMLCanvasElement], gfxTexture, [region]); + gfxDevice.copyImagesToTexture([image], gfxTexture, [region]); } } diff --git a/cocos/2d/assets/sprite-frame.ts b/cocos/2d/assets/sprite-frame.ts index 7e5ed42fb11..60cc27d91d2 100644 --- a/cocos/2d/assets/sprite-frame.ts +++ b/cocos/2d/assets/sprite-frame.ts @@ -27,7 +27,7 @@ import { ccclass } from 'cc.decorator'; import { EDITOR, TEST, BUILD } from 'internal:constants'; -import { ImageSource } from 'pal/image/types'; +import { ImageSource } from '../../../pal/image/types'; import { Mat4, Rect, Size, Vec2, Vec3, Vec4, cclegacy, errorID, warnID, js } from '../../core'; import { Asset } from '../../asset/assets/asset'; import { TextureBase } from '../../asset/assets/texture-base'; diff --git a/cocos/2d/utils/dynamic-atlas/atlas.ts b/cocos/2d/utils/dynamic-atlas/atlas.ts index dd8542545dc..b4c4431c222 100644 --- a/cocos/2d/utils/dynamic-atlas/atlas.ts +++ b/cocos/2d/utils/dynamic-atlas/atlas.ts @@ -259,6 +259,6 @@ export class DynamicAtlasTexture extends Texture2D { region.texOffset.y = y; region.texExtent.width = image.width; region.texExtent.height = image.height; - gfxDevice.copyTexImagesToTexture([image.data as HTMLCanvasElement], gfxTexture, [region]); + gfxDevice.copyImagesToTexture([image], gfxTexture, [region]); } } diff --git a/cocos/asset/asset-manager/factory.ts b/cocos/asset/asset-manager/factory.ts index 3d1cd620c52..cb2c3710df3 100644 --- a/cocos/asset/asset-manager/factory.ts +++ b/cocos/asset/asset-manager/factory.ts @@ -39,13 +39,13 @@ import { js } from '../../core'; export type CreateHandler = (id: string, data: any, options: Record, onComplete: ((err: Error | null, data?: Asset | Bundle | null) => void)) => void; -function createImageAsset (id: string, data: HTMLImageElement, options: Record, onComplete: ((err: Error | null, data?: ImageAsset | null) => void)) { +function createImageAsset (id: string, imageData: ImageData, options: Record, onComplete: ((err: Error | null, data?: ImageAsset | null) => void)) { let out: ImageAsset | null = null; let err: Error | null = null; try { out = new ImageAsset(); out._nativeUrl = id; - out._nativeAsset = data; + out._nativeAsset = imageData.data; } catch (e) { err = e as Error; } diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index ca76bc92c05..cf85aca43a3 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -22,7 +22,8 @@ THE SOFTWARE. */ -import { IMemoryImageSource } from 'pal/image'; +import { ImageData, ImageSource } from 'pal/image'; +import { IMemoryImageSource } from '../../../pal/image/types'; import { ImageAsset } from '../assets/image-asset'; import { js } from '../../core'; import Cache from './cache'; @@ -81,9 +82,9 @@ export class Parser { /** * @engineInternal */ - public parseImage (file: HTMLImageElement | Blob, options: Record, onComplete: ((err: Error | null, data?: HTMLImageElement | ImageBitmap | null) => void)) { - if (file instanceof HTMLImageElement) { - onComplete(null, file); + public parseImage (file: ImageData | Blob, options: Record, onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)) { + if (file instanceof ImageData) { + onComplete(null, file.data); return; } createImageBitmap(file, { premultiplyAlpha: 'none' }).then((result) => { diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index 131981fb7f5..efdfe3bb7c7 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -56,7 +56,7 @@ Object.defineProperty(imageAssetProto, '_nativeAsset', { get () { return this._imageData; }, - set (value: any) { + set (value: ImageSource) { this.reset(value); }, }); diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index c2bca997af1..9c14e990f8d 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -493,10 +493,18 @@ export class ImageAsset extends Asset { this.reset(value); } - /** + /* * @en Image data. * @zh 此图像资源的图像数据。 */ + get nativeData () { + return this._imageData.nativeData(); + } + + /** + * @en Image data source(include: HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView). + * @zh 图像的来源(包括:HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView)。 + */ get data () { return this._imageData.data; } @@ -564,7 +572,7 @@ export class ImageAsset extends Asset { private _height = 0; - constructor (nativeAsset?: ImageSource) { + constructor (nativeAsset?: ImageSource | ArrayBufferView) { super(); if (EDITOR) { diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index 73cc85d4322..f7b21bbd648 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -145,7 +145,7 @@ export class SimpleTexture extends TextureBase { if (!this._gfxTexture || this._mipmapLevel <= level) { return; } - const imageData = new ImageData(source); + const imageAsset = new ImageAsset(source); const gfxDevice = this._getGFXDevice(); if (!gfxDevice) { return; @@ -157,19 +157,10 @@ export class SimpleTexture extends TextureBase { region.texSubres.mipLevel = level; region.texSubres.baseArrayLayer = arrayIndex; - if (DEV) { - if (imageData.isHtmlElement()) { - if (imageData.height > region.texExtent.height - || imageData.width > region.texExtent.width) { - error(`Image source(${this.name}) bounds override.`); - } - } - } - - if (ArrayBuffer.isView(imageData.data)) { - gfxDevice.copyBuffersToTexture([imageData.data], this._gfxTexture, _regions); + if (ArrayBuffer.isView(imageAsset.data)) { + gfxDevice.copyBuffersToTexture([imageAsset.data], this._gfxTexture, _regions); } else { - gfxDevice.copyTexImagesToTexture([imageData.data as TexImageSource], this._gfxTexture, _regions); + gfxDevice.copyImagesToTexture([imageAsset], this._gfxTexture, _regions); } } diff --git a/cocos/gfx/base/device.ts b/cocos/gfx/base/device.ts index cb6c1541d20..dab58b6c3b2 100644 --- a/cocos/gfx/base/device.ts +++ b/cocos/gfx/base/device.ts @@ -47,6 +47,7 @@ import { GeneralBarrier } from './states/general-barrier'; import { TextureBarrier } from './states/texture-barrier'; import { BufferBarrier } from './states/buffer-barrier'; import { Swapchain } from './swapchain'; +import { ImageAsset } from '../../asset/assets'; /** * @en GFX Device. @@ -335,6 +336,15 @@ export abstract class Device { */ public abstract copyTexImagesToTexture (texImages: Readonly, texture: Texture, regions: Readonly): void; + /** + * @en Copy image assets to texture. + * @zh 拷贝图像资产到纹理。 + * @param imageAssets The image assets to be copied. + * @param texture The texture to copy to. + * @param regions The region descriptions. + */ + public abstract copyImagesToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly): void; + /** * @en Whether the device has specific feature. * @zh 是否具备特性。 diff --git a/cocos/gfx/empty/empty-device.ts b/cocos/gfx/empty/empty-device.ts index 8958073ebda..da27a6702a1 100644 --- a/cocos/gfx/empty/empty-device.ts +++ b/cocos/gfx/empty/empty-device.ts @@ -60,6 +60,7 @@ import { EmptyShader } from './empty-shader'; import { EmptySwapchain } from './empty-swapchain'; import { EmptyTexture } from './empty-texture'; import { debug, cclegacy } from '../../core'; +import { ImageAsset } from '../../asset/assets'; export class EmptyDevice extends Device { private _swapchain: EmptySwapchain | null = null; @@ -213,6 +214,7 @@ export class EmptyDevice extends Device { public copyBuffersToTexture (buffers: Readonly, texture: Texture, regions: Readonly) {} public copyTextureToBuffers (texture: Readonly, buffers: ArrayBufferView[], regions: Readonly) {} public copyTexImagesToTexture (texImages: Readonly, texture: Texture, regions: Readonly) {} + public copyImagesToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly) {} } cclegacy.EmptyDevice = EmptyDevice; diff --git a/cocos/gfx/webgl/webgl-device.ts b/cocos/gfx/webgl/webgl-device.ts index 440d08a16b5..674c1be6a34 100644 --- a/cocos/gfx/webgl/webgl-device.ts +++ b/cocos/gfx/webgl/webgl-device.ts @@ -65,6 +65,7 @@ import { debug } from '../../core'; import { Swapchain } from '../base/swapchain'; import { IWebGLExtensions, WebGLDeviceManager } from './webgl-define'; import { IWebGLBindingMapping } from './webgl-gpu-objects'; +import { ImageAsset } from '../../asset/assets'; export class WebGLDevice extends Device { get gl () { @@ -563,4 +564,21 @@ export class WebGLDevice extends Device { regions, ); } + + public copyImagesToTexture ( + imageAssets: Readonly, + texture: Texture, + regions: Readonly, + ) { + const texImages: TexImageSource[] = []; + imageAssets.forEach((item) => { + texImages.push(item.data as TexImageSource); + }); + WebGLCmdFuncCopyTexImagesToTexture( + this, + texImages, + (texture as WebGLTexture).gpuTexture, + regions, + ); + } } diff --git a/cocos/gfx/webgl2/webgl2-device.ts b/cocos/gfx/webgl2/webgl2-device.ts index c346230e5f4..ffdfa56b442 100644 --- a/cocos/gfx/webgl2/webgl2-device.ts +++ b/cocos/gfx/webgl2/webgl2-device.ts @@ -67,6 +67,7 @@ import { Swapchain } from '../base/swapchain'; import { IWebGL2Extensions, WebGL2DeviceManager } from './webgl2-define'; import { IWebGL2BindingMapping } from './webgl2-gpu-objects'; import { BrowserType, OS } from '../../../pal/system-info/enum-type'; +import { ImageAsset } from '../../asset/assets'; export class WebGL2Device extends Device { get gl () { @@ -630,4 +631,21 @@ export class WebGL2Device extends Device { regions, ); } + + public copyImagesToTexture ( + imageAssets: Readonly, + texture: Texture, + regions: Readonly, + ) { + const texImages: TexImageSource[] = []; + imageAssets.forEach((item) => { + texImages.push(item.data as TexImageSource); + }); + WebGL2CmdFuncCopyTexImagesToTexture( + this, + texImages, + (texture as WebGL2Texture).gpuTexture, + regions, + ); + } } diff --git a/cocos/gfx/webgpu/webgpu-define.ts b/cocos/gfx/webgpu/webgpu-define.ts index 73283fd0868..4b1c2805206 100644 --- a/cocos/gfx/webgpu/webgpu-define.ts +++ b/cocos/gfx/webgpu/webgpu-define.ts @@ -36,6 +36,7 @@ import { } from '../base/define'; import { ccwindow } from '../../core/global-exports'; +import { ImageAsset } from '../../asset/assets'; WEBGPU && promiseForWebGPUInstantiation.then(() => { @@ -197,6 +198,14 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); }; + Device.prototype.copyImagesToTexture = function (imageAssets: ImageAsset[], texture: typeof Texture, regions: BufferTextureCopy[]) { + const buffers: Uint8Array[] = []; + for (let i = 0; i < regions.length; i++) { + buffers.push(imageAssets[i].nativeData); + } + oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); + } + function seperateCombinedSamplerTexture (shaderSource: string) { // sampler and texture const samplerTexturArr = shaderSource.match(/(.*?)\(set = \d+, binding = \d+\) uniform(.*?)sampler\w* \w+;/g); diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index ebe11bed0d2..71b66debf28 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -21,6 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { PixelFormat } from '../../cocos/asset/assets/asset-enum'; import { IMemoryImageSource, ImageSource } from './types'; import { sys } from '../../cocos/core/platform/sys'; import { ccwindow } from '../../cocos/core/global-exports'; @@ -37,13 +38,8 @@ export class BaseImageData { _compressed: false, mipmapLevelDataSize: [], }; - //this._nativeData = data; if (typeof imageAsset !== 'undefined') { - if (!ArrayBuffer.isView(imageAsset)) { - this.reset(imageAsset); - } else { - this._imageSource._data = imageAsset; - } + this.data = imageAsset; } else if (typeof imageAsset === 'undefined') { this._imageSource = new ccwindow.Image(); } @@ -58,8 +54,12 @@ export class BaseImageData { } } - set data (value: any) { - this.reset(value); + set data (imageAsset: ImageSource | ArrayBufferView | null) { + if (imageAsset != null && !ArrayBuffer.isView(imageAsset)) { + this.reset(imageAsset); + } else { + (this._imageSource as IMemoryImageSource)._data = imageAsset; + } } get data (): ImageSource | ArrayBufferView | null { @@ -74,15 +74,15 @@ export class BaseImageData { return this.data as any; } - set crossOrigin (string) { - (this._imageSource as HTMLImageElement).crossOrigin = 'anonymous'; + set crossOrigin (cors: string) { + (this._imageSource as HTMLImageElement).crossOrigin = cors; } - set onload (cb) { + set onload (cb: (ev: Event) => void) { (this._imageSource as HTMLImageElement).onload = cb; } - set onerror (cb) { + set onerror (cb: (ev: Event | string) => void) { (this._imageSource as HTMLImageElement).onerror = cb; } @@ -98,9 +98,8 @@ export class BaseImageData { return this._imageSource.width; } - set width (value) { - // @ts-ignore - this._imageSource.width = value; + set width (value: number) { + (this._imageSource as IMemoryImageSource).width = value; } get height (): number { @@ -110,40 +109,41 @@ export class BaseImageData { return 0; } - set height (value) { - // @ts-ignore - this._imageSource.height = value; + set height (value: number) { + (this._imageSource as IMemoryImageSource).height = value; } - get format (): number | null { + get format (): PixelFormat | null { if (!(this._imageSource instanceof HTMLElement) && !this.isImageBitmap(this._imageSource) && !this.isArrayBuffer()) { return this._imageSource.format; } return null; } - get compressed () { + get compressed (): boolean { return false; } - get mipmapLevelDataSize () { + get mipmapLevelDataSize (): number[] | undefined { return (this._imageSource as IMemoryImageSource).mipmapLevelDataSize; } - public reset (data: ImageSource) { + public reset (data: ImageSource): void { this._imageSource = data; } - public isArrayBuffer () { + + public isArrayBuffer (): boolean { return ArrayBuffer.isView((this._imageSource as IMemoryImageSource)._data); } - public isHtmlElement () { + + public isHtmlElement (): boolean { return this._imageSource instanceof HTMLElement; } + public isImageBitmap (imageSource: any): imageSource is ImageBitmap { return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); } - // 返回该图像源是否是平台提供的图像对象。 protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || this.isImageBitmap(imageSource); } @@ -152,13 +152,13 @@ export class BaseImageData { return '_data' in imageSource ? imageSource._data : imageSource; } - public addEventListener (name, cb) { + protected addEventListener (name: string, cb: (ev: Event) => void): void { if (this.isHtmlElement()) { (this.data as HTMLImageElement).addEventListener(name, cb); } } - public removeEventListener (name, cb) { + protected removeEventListener (name: string, cb: (ev: Event) => void): void { if (this.isHtmlElement()) { (this.data as HTMLImageElement).removeEventListener(name, cb); } diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index d5f943eca6c..7c030e92f60 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -42,7 +42,7 @@ export class ImageData extends BaseImageData { static downloadImage (url: string, options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { const image = new ImageData(); // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' @@ -50,21 +50,14 @@ export class ImageData extends BaseImageData { image.crossOrigin = 'anonymous'; } - function loadCallback () { - image.removeEventListener('load', loadCallback); - if (onComplete) { onComplete(null, image.data as HTMLImageElement); } - image.removeEventListener('error', errorCallback); - } - - function errorCallback () { - image.removeEventListener('load', loadCallback); - image.removeEventListener('error', errorCallback); + image.onload = () => { + if (onComplete) { onComplete(null, image.data); } + }; + image.onerror = () => { if (onComplete) { onComplete(new Error(getError(4930, url))); } - } + }; - image.addEventListener('load', loadCallback); - image.addEventListener('error', errorCallback); image.src = url; - return (image.data as HTMLImageElement); + return image; } } diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 1a4e488e88f..27db0df4925 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -59,29 +59,21 @@ export class ImageData extends BaseImageData { static downloadImage (url: string, options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { const image = new ImageData(); - // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' if (ccwindow.location.protocol !== 'file:') { image.crossOrigin = 'anonymous'; } - function loadCallback () { - image.removeEventListener('load', loadCallback); - if (onComplete) { onComplete(null, image.data as HTMLImageElement); } - image.removeEventListener('error', errorCallback); - } - - function errorCallback () { - image.removeEventListener('load', loadCallback); - image.removeEventListener('error', errorCallback); + image.onload = () => { + if (onComplete) { onComplete(null, image.data); } + }; + image.onerror = () => { if (onComplete) { onComplete(new Error(getError(4930, url))); } - } + }; - image.addEventListener('load', loadCallback); - image.addEventListener('error', errorCallback); image.src = url; - return (image.data as HTMLImageElement); + return image; } } diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index 95944e48d88..eee2efc22b7 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -21,19 +21,53 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { ImageSource } from 'pal/image'; import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; export class ImageData extends BaseImageData { public nativeData (): unknown { - // TODO:Get raw image data - return null; + let data; + if ('getContext' in this._imageSource) { + const canvasElem = this._imageSource; + const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this._imageSource.width, this._imageSource.height); + const buff = imageData!.data.buffer; + let rawBuffer; + if ('buffer' in buff) { + // es-lint as any + data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); + } else { + rawBuffer = buff; + data = new Uint8Array(rawBuffer); + } + //buffers[i] = data; + } else if (this._imageSource instanceof HTMLImageElement || this._imageSource instanceof ImageBitmap) { + const img = this._imageSource; + const canvas = ccwindow.document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + ctx?.drawImage(img as any, 0, 0); + const imageData = ctx?.getImageData(0, 0, img.width, img.height); + const buff = imageData!.data.buffer; + let rawBuffer; + if ('buffer' in buff) { + // es-lint as any + data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); + } else { + rawBuffer = buff; + data = new Uint8Array(rawBuffer); + } + } else { + console.log('imageBmp copy not impled!'); + } + return this.data; } static downloadImage (url: string, options: Record, - onComplete: ((err: Error | null, data?: HTMLImageElement | null) => void)): HTMLImageElement { + onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { const image = new ImageData(); // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' @@ -41,21 +75,14 @@ export class ImageData extends BaseImageData { image.crossOrigin = 'anonymous'; } - function loadCallback () { - image.removeEventListener('load', loadCallback); - if (onComplete) { onComplete(null, image.data as HTMLImageElement); } - image.removeEventListener('error', errorCallback); - } - - function errorCallback () { - image.removeEventListener('load', loadCallback); - image.removeEventListener('error', errorCallback); + image.onload = () => { + if (onComplete) { onComplete(null, image.data); } + }; + image.onerror = () => { if (onComplete) { onComplete(new Error(getError(4930, url))); } - } + }; - image.addEventListener('load', loadCallback); - image.addEventListener('error', errorCallback); image.src = url; - return (image.data as HTMLImageElement); + return image; } } diff --git a/platforms/native/engine/jsb-gfx.js b/platforms/native/engine/jsb-gfx.js index 1fa6926dd41..21a055d7122 100644 --- a/platforms/native/engine/jsb-gfx.js +++ b/platforms/native/engine/jsb-gfx.js @@ -55,6 +55,17 @@ deviceProto.copyTexImagesToTexture = function (texImages, texture, regions) { oldCopyTexImagesToTextureFunc.call(this, images, texture, regions); }; +deviceProto.copyImagesToTexture = function (imageAssets, texture, regions) { + const images = []; + if (imageAssets) { + for (let i = 0; i < imageAssets.length; ++i) { + const image = imageAssets[i]; + images.push(image.nativeData); + } + } + oldCopyTexImagesToTextureFunc.call(this, images, texture, regions); +}; + const oldDeviceCreateSwapchainFunc = deviceProto.createSwapchain; deviceProto.createSwapchain = function (info) { // In openharmony, we need to get the window handle through the jsb interface From ace9911030d5c104a1cb72a0b4c7e815536c332a Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 27 Jun 2023 15:07:04 +0800 Subject: [PATCH 08/24] Rename 'downloadImage' to 'loadImage'. --- @types/pal/image.d.ts | 4 ++-- cocos/asset/asset-manager/downloader.ts | 2 +- pal/image/minigame/image-data.ts | 2 +- pal/image/native/image-data.ts | 2 +- pal/image/web/image-data.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 40d735f108c..22c81338336 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -36,7 +36,7 @@ declare module 'pal/image' { */ get width(): number; /** - * Set image width(when source is IMemoryImageSource). + * Set image height(when source is IMemoryImageSource). */ set height(value: number); /** @@ -55,7 +55,7 @@ declare module 'pal/image' { /** * Download images from the server. */ - static downloadImage ( + static loadImage ( url: string, options: Record, onComplete: ((err: Error | null, data?: ImageSource | null) => void), diff --git a/cocos/asset/asset-manager/downloader.ts b/cocos/asset/asset-manager/downloader.ts index 276dddce88b..fdfc21d7f7a 100644 --- a/cocos/asset/asset-manager/downloader.ts +++ b/cocos/asset/asset-manager/downloader.ts @@ -48,7 +48,7 @@ const REGEX = /^(?:\w+:\/\/|\.+\/).+/; const downloadImage = (url: string, options: Record, onComplete: ((err: Error | null, data?: any | null) => void)) => { // if createImageBitmap is valid, we can transform blob to ImageBitmap. Otherwise, just use HTMLImageElement to load - const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : ImageData.downloadImage; + const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : ImageData.loadImage; func(url, options, onComplete); }; diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index 7c030e92f60..3e72bc1fab1 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -40,7 +40,7 @@ export class ImageData extends BaseImageData { (this._imageSource as HTMLImageElement).src = url; } - static downloadImage (url: string, + static loadImage (url: string, options: Record, onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { const image = new ImageData(); diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 27db0df4925..3c4bce1a265 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -57,7 +57,7 @@ export class ImageData extends BaseImageData { return super.isNativeImage(imageSource); } - static downloadImage (url: string, + static loadImage (url: string, options: Record, onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { const image = new ImageData(); diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index eee2efc22b7..75d26432f6f 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -65,7 +65,7 @@ export class ImageData extends BaseImageData { return this.data; } - static downloadImage (url: string, + static loadImage (url: string, options: Record, onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { const image = new ImageData(); From 29f07a5990924978539e6c1ab80f952dfb021b92 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 27 Jun 2023 17:58:21 +0800 Subject: [PATCH 09/24] Fix ci error --- @types/pal/image.d.ts | 6 +++--- cocos/2d/assets/sprite-frame.ts | 2 +- cocos/asset/asset-manager/builtin-res-mgr.jsb.ts | 2 +- cocos/asset/asset-manager/downloader.ts | 2 +- cocos/asset/asset-manager/parser.ts | 3 +-- cocos/asset/assets/image-asset.jsb.ts | 3 +-- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 22c81338336..c5819b152e0 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -1,7 +1,7 @@ declare module 'pal/image' { - type ImageSource = import('pal/image/types').ImageSource; - type IMemoryImageSource = import('pal/image/types').IMemoryImageSource; - type PixelFormat = import('cocos/asset/assets/asset-enum').PixelFormat; + export type ImageSource = import('pal/image/types').ImageSource; + export type IMemoryImageSource = import('pal/image/types').IMemoryImageSource; + export type PixelFormat = import('cocos/asset/assets/asset-enum').PixelFormat; export class ImageData { constructor (imageAsset?: ImageSource | ArrayBufferView); /** diff --git a/cocos/2d/assets/sprite-frame.ts b/cocos/2d/assets/sprite-frame.ts index 60cc27d91d2..6f6ff3f23c5 100644 --- a/cocos/2d/assets/sprite-frame.ts +++ b/cocos/2d/assets/sprite-frame.ts @@ -27,7 +27,7 @@ import { ccclass } from 'cc.decorator'; import { EDITOR, TEST, BUILD } from 'internal:constants'; -import { ImageSource } from '../../../pal/image/types'; +import { ImageSource } from 'pal/image'; import { Mat4, Rect, Size, Vec2, Vec3, Vec4, cclegacy, errorID, warnID, js } from '../../core'; import { Asset } from '../../asset/assets/asset'; import { TextureBase } from '../../asset/assets/texture-base'; diff --git a/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts b/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts index 582fe6717b8..667e3a2f836 100644 --- a/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts +++ b/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts @@ -24,7 +24,7 @@ import { TEST, EDITOR, EDITOR_NOT_IN_PREVIEW } from 'internal:constants'; import { SpriteFrame } from '../../2d/assets/sprite-frame'; -import type { ImageSource } from '../assets/image-asset'; +import type { ImageSource } from 'pal/image'; import assetManager from '../asset-manager/asset-manager'; import { BuiltinBundleName } from '../asset-manager/shared'; import Bundle from '../asset-manager/bundle'; diff --git a/cocos/asset/asset-manager/downloader.ts b/cocos/asset/asset-manager/downloader.ts index fdfc21d7f7a..3b02ed10190 100644 --- a/cocos/asset/asset-manager/downloader.ts +++ b/cocos/asset/asset-manager/downloader.ts @@ -245,7 +245,7 @@ export class Downloader { /** * @deprecated Since v3.7, this is an engine internal interface. You can easily implement the functionality of this API using HTMLImageElement. */ - public downloadDomImage = ImageData.downloadImage; + public downloadDomImage = ImageData.loadImage; /** * @deprecated Since v3.7, this is an engine internal interface. You can easily implement the functionality of this API using HTMLAudioElement. diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index cf85aca43a3..d25747d3adf 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -22,8 +22,7 @@ THE SOFTWARE. */ -import { ImageData, ImageSource } from 'pal/image'; -import { IMemoryImageSource } from '../../../pal/image/types'; +import { ImageData, ImageSource, IMemoryImageSource } from 'pal/image'; import { ImageAsset } from '../assets/image-asset'; import { js } from '../../core'; import Cache from './cache'; diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index efdfe3bb7c7..2317ab548d2 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -24,13 +24,12 @@ import { ALIPAY, XIAOMI, JSB, TEST, BAIDU, EDITOR } from 'internal:constants'; import { Format, FormatFeatureBit, deviceManager } from '../../gfx'; -import { ImageData } from 'pal/image'; +import { ImageData,ImageSource } from 'pal/image'; import { PixelFormat } from './asset-enum'; import { sys, macro, warnID, cclegacy } from '../../core'; import { patch_cc_ImageAsset } from '../../native-binding/decorators'; import './asset'; import type { ImageAsset as JsbImageAsset } from './image-asset'; -import { ImageSource } from '../../../pal/image/types'; declare const jsb: any; From 97a06456042fbba0b8607bc24043f8bedd63365c Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Thu, 29 Jun 2023 08:22:40 +0800 Subject: [PATCH 10/24] Fix ci warning --- cocos/2d/assets/sprite-frame.ts | 2 +- .../asset-manager/builtin-res-mgr.jsb.ts | 2 +- cocos/asset/asset-manager/builtin-res-mgr.ts | 2 +- cocos/asset/asset-manager/parser.ts | 3 ++- cocos/asset/assets/image-asset.jsb.ts | 3 ++- cocos/asset/assets/image-asset.ts | 10 ++++++- cocos/asset/assets/simple-texture.ts | 3 ++- cocos/game/splash-screen.ts | 26 ++++++++++--------- pal/image/base-image-data.ts | 4 +++ pal/image/native/image-data.ts | 2 +- 10 files changed, 37 insertions(+), 20 deletions(-) diff --git a/cocos/2d/assets/sprite-frame.ts b/cocos/2d/assets/sprite-frame.ts index 6f6ff3f23c5..60cc27d91d2 100644 --- a/cocos/2d/assets/sprite-frame.ts +++ b/cocos/2d/assets/sprite-frame.ts @@ -27,7 +27,7 @@ import { ccclass } from 'cc.decorator'; import { EDITOR, TEST, BUILD } from 'internal:constants'; -import { ImageSource } from 'pal/image'; +import { ImageSource } from '../../../pal/image/types'; import { Mat4, Rect, Size, Vec2, Vec3, Vec4, cclegacy, errorID, warnID, js } from '../../core'; import { Asset } from '../../asset/assets/asset'; import { TextureBase } from '../../asset/assets/texture-base'; diff --git a/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts b/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts index 667e3a2f836..70495fcd06b 100644 --- a/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts +++ b/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts @@ -24,7 +24,7 @@ import { TEST, EDITOR, EDITOR_NOT_IN_PREVIEW } from 'internal:constants'; import { SpriteFrame } from '../../2d/assets/sprite-frame'; -import type { ImageSource } from 'pal/image'; +import { ImageSource } from '../../../pal/image/types'; import assetManager from '../asset-manager/asset-manager'; import { BuiltinBundleName } from '../asset-manager/shared'; import Bundle from '../asset-manager/bundle'; diff --git a/cocos/asset/asset-manager/builtin-res-mgr.ts b/cocos/asset/asset-manager/builtin-res-mgr.ts index 7976c8744b9..08ab550d20e 100644 --- a/cocos/asset/asset-manager/builtin-res-mgr.ts +++ b/cocos/asset/asset-manager/builtin-res-mgr.ts @@ -23,7 +23,7 @@ */ import { EDITOR, EDITOR_NOT_IN_PREVIEW, TEST } from 'internal:constants'; -import { ImageSource } from 'pal/image'; +import { ImageSource } from '../../../pal/image/types'; import { Asset } from '../assets/asset'; import { ImageAsset } from '../assets/image-asset'; import { SpriteFrame } from '../../2d/assets/sprite-frame'; diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index d25747d3adf..f6f525ad2c2 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -22,7 +22,8 @@ THE SOFTWARE. */ -import { ImageData, ImageSource, IMemoryImageSource } from 'pal/image'; +import { ImageData } from 'pal/image'; +import { ImageSource, IMemoryImageSource } from '../../../pal/image/types'; import { ImageAsset } from '../assets/image-asset'; import { js } from '../../core'; import Cache from './cache'; diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index 2317ab548d2..80ea73a42b4 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -24,7 +24,8 @@ import { ALIPAY, XIAOMI, JSB, TEST, BAIDU, EDITOR } from 'internal:constants'; import { Format, FormatFeatureBit, deviceManager } from '../../gfx'; -import { ImageData,ImageSource } from 'pal/image'; +import { ImageData } from 'pal/image'; +import { ImageSource } from '../../../pal/image/types'; import { PixelFormat } from './asset-enum'; import { sys, macro, warnID, cclegacy } from '../../core'; import { patch_cc_ImageAsset } from '../../native-binding/decorators'; diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 9c14e990f8d..f7d376b124f 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -26,13 +26,13 @@ import { ccclass, override } from 'cc.decorator'; import { EDITOR, ALIPAY, XIAOMI, JSB, TEST, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { ImageData } from 'pal/image'; +import { ImageSource, IMemoryImageSource } from '../../../pal/image/types'; import { Device, Format, FormatFeatureBit, deviceManager } from '../../gfx'; import { Asset } from './asset'; import { PixelFormat } from './asset-enum'; import { warnID, macro, sys, cclegacy } from '../../core'; import { ccwindow } from '../../core/global-exports'; import { Enum } from '../../core/value-types/enum'; -import { IMemoryImageSource, ImageSource } from '../../../pal/image/types'; // Compress mipmap constants const COMPRESSED_HEADER_LENGTH = 4; @@ -509,6 +509,14 @@ export class ImageAsset extends Asset { return this._imageData.data; } + /** + * @en Image data manager. + * @zh 图像的数据管理。 + */ + get imageData () { + return this._imageData; + } + /** * @en The pixel width of the image. * @zh 此图像资源的像素宽度。 diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index f7b21bbd648..ff88ecc83fb 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -24,7 +24,8 @@ import { ccclass } from 'cc.decorator'; import { DEV } from 'internal:constants'; -import { ImageData, IMemoryImageSource } from 'pal/image'; +import { IMemoryImageSource } from '../../../pal/image/types'; + import { TextureFlagBit, TextureUsageBit, API, Texture, TextureInfo, TextureViewInfo, Device, BufferTextureCopy } from '../../gfx'; import { assertID, error, js, macro, cclegacy } from '../../core'; import { Filter } from './asset-enum'; diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index 527957976f1..ae43f970eb2 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -35,6 +35,7 @@ import { PipelineStateManager } from '../rendering'; import { SetIndex } from '../rendering/define'; import { ccwindow, legacyCC } from '../core/global-exports'; import { XREye } from '../xr/xr-enums'; +import { ImageAsset } from '../asset/assets'; const v2_0 = new Vec2(); type SplashEffectType = 'default' | 'custom' | 'off'; @@ -71,11 +72,11 @@ export class SplashScreen { private isMobile = false; private bgMat!: Material; - private bgImage!: ImageData; + private bgImage!: ImageAsset; private bgTexture!: Texture; private logoMat!: Material; - private logoImage!: ImageData; + private logoImage!: ImageAsset; private logoTexture!: Texture; private watermarkMat!: Material; @@ -136,26 +137,26 @@ export class SplashScreen { this.initWaterMark(); const bgPromise = new Promise((resolve, reject) => { - this.bgImage = new ImageData(); - this.bgImage.onload = () => { + this.bgImage = new ImageAsset(); + this.bgImage.imageData.onload = () => { this.initBG(); resolve(); }; - this.bgImage.onerror = () => { + this.bgImage.imageData.onerror = () => { reject(); }; - this.bgImage.src = this.settings.bgBase64; + this.bgImage.imageData.src = this.settings.bgBase64; }); const logoPromise = new Promise((resolve, reject) => { - this.logoImage = new ImageData(); - this.logoImage.onload = () => { + this.logoImage = new ImageAsset(); + this.logoImage.imageData.onload = () => { this.initLogo(); resolve(); }; - this.logoImage.onerror = () => { + this.logoImage.imageData.onerror = () => { reject(); }; - this.logoImage.src = this.settings.base64src; + this.logoImage.imageData.src = this.settings.base64src; }); return Promise.all([bgPromise, logoPromise]); } @@ -341,7 +342,8 @@ export class SplashScreen { region.texExtent.width = this.bgImage.width; region.texExtent.height = this.bgImage.height; region.texExtent.depth = 1; - device.copyTexImagesToTexture([this.bgImage.data as TexImageSource], this.bgTexture, [region]); + + device.copyImagesToTexture([this.bgImage], this.bgTexture, [region]); } private initLogo () { @@ -376,7 +378,7 @@ export class SplashScreen { region.texExtent.width = this.logoImage.width; region.texExtent.height = this.logoImage.height; region.texExtent.depth = 1; - device.copyTexImagesToTexture([this.logoImage.data as TexImageSource], this.logoTexture, [region]); + device.copyImagesToTexture([this.logoImage], this.logoTexture, [region]); const logoRatio = this.logoImage.width / this.logoImage.height; if (logoRatio < 1) { diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 71b66debf28..69547e33244 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -74,6 +74,10 @@ export class BaseImageData { return this.data as any; } + public acceptAnonymousCORS () { + (this._imageSource as HTMLImageElement).crossOrigin = 'anonymous'; + } + set crossOrigin (cors: string) { (this._imageSource as HTMLImageElement).crossOrigin = cors; } diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 3c4bce1a265..01fb0674f59 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -22,7 +22,7 @@ THE SOFTWARE. */ import { BaseImageData } from '../base-image-data'; -import { ImageSource, IMemoryImageSource } from '../types'; +import type { ImageSource, IMemoryImageSource } from '../types'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; From 36524acc25e6e7e2d60bd620d2d9d47d1ee4b35b Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Thu, 29 Jun 2023 16:42:12 +0800 Subject: [PATCH 11/24] Rename 'nativeData' to 'rawData' --- @types/pal/image.d.ts | 4 ++-- cocos/asset/asset-manager/parser.ts | 1 + cocos/asset/assets/image-asset.jsb.ts | 6 +++--- cocos/asset/assets/image-asset.ts | 12 ++++++------ cocos/asset/assets/simple-texture.jsb.ts | 2 +- cocos/asset/assets/texture-2d.ts | 8 +++++++- cocos/gfx/webgpu/webgpu-define.ts | 2 +- pal/env/web/env.ts | 2 +- pal/image/base-image-data.ts | 2 +- pal/image/native/image-data.ts | 4 ++-- pal/image/web/image-data.ts | 2 +- platforms/native/engine/jsb-gfx.js | 2 +- 12 files changed, 27 insertions(+), 20 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index c5819b152e0..1943b42869b 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -71,9 +71,9 @@ declare module 'pal/image' { onerror: ((ev: Event) => void) | null; /** - * Get image data, in web platform is image source(like data interface), in native platform is image raw data. + * Get raw image data, in web platform is image source(like data interface), in native platform is image raw data. */ - nativeData (): unknown; + rawData (): unknown; /** * Determine if it is an HTMLElement element. */ diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index e3fc60f7aee..f0477c29b6d 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -85,6 +85,7 @@ export class Parser { public parseImage (file: ImageData | Blob, options: Record, onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): void { if (file instanceof ImageData) { onComplete(null, file.data); + return; } createImageBitmap(file, { premultiplyAlpha: 'none' }).then((result) => { onComplete(null, result); diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index c3e76b813bc..e1bd724ca10 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -69,11 +69,11 @@ Object.defineProperty(imageAssetProto, 'data', { }, }); -Object.defineProperty(imageAssetProto, 'nativeData', { +Object.defineProperty(imageAssetProto, 'rawData', { configurable: true, enumerable: true, get () { - return this._imageData.nativeData(); + return this._imageData.rawData(); }, }); @@ -137,7 +137,7 @@ imageAssetProto._syncDataToNative = function () { this.setHeight(this._height); this.url = this.nativeUrl; - this.setData(this._imageData.nativeData()); + this.setData(this._imageData.rawData()); if (data._mipmapLevelDataSize){ this.setMipmapLevelDataSize(data._mipmapLevelDataSize); } diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 38b724cd537..24d10d4abab 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -494,11 +494,11 @@ export class ImageAsset extends Asset { } /* - * @en Image data. - * @zh 此图像资源的图像数据。 + * @en Raw image data. + * @zh 原始图像数据。 */ - get nativeData (): any { - return this._imageData.nativeData(); + get rawData (): any { + return this._imageData.rawData(); } /** @@ -510,8 +510,8 @@ export class ImageAsset extends Asset { } /** - * @en Image data manager. - * @zh 图像的数据管理。 + * @en Image data object. + * @zh 图像的数据对象。 */ get imageData (): ImageData { return this._imageData; diff --git a/cocos/asset/assets/simple-texture.jsb.ts b/cocos/asset/assets/simple-texture.jsb.ts index 4ecb374c063..93f281d4e58 100644 --- a/cocos/asset/assets/simple-texture.jsb.ts +++ b/cocos/asset/assets/simple-texture.jsb.ts @@ -44,7 +44,7 @@ const simpleTextureProto = jsb.SimpleTexture.prototype; const oldUpdateDataFunc = simpleTextureProto.uploadData; simpleTextureProto.uploadData = function (source, level = 0, arrayIndex = 0) { let imageData = new ImageData(source); - oldUpdateDataFunc.call(this, imageData.nativeData(), level, arrayIndex); + oldUpdateDataFunc.call(this, imageData.rawData(), level, arrayIndex); }; simpleTextureProto._ctor = function () { diff --git a/cocos/asset/assets/texture-2d.ts b/cocos/asset/assets/texture-2d.ts index e795d6da063..3e0035a831e 100644 --- a/cocos/asset/assets/texture-2d.ts +++ b/cocos/asset/assets/texture-2d.ts @@ -247,7 +247,13 @@ export class Texture2D extends SimpleTexture { * @deprecated Please use [[ImageAsset.data]] instead */ public getHtmlElementObj (): HTMLCanvasElement | HTMLImageElement | null { - return (this._mipmaps[0] && (this._mipmaps[0].data instanceof HTMLElement)) ? this._mipmaps[0].data : null; + if (!this._mipmaps[0]) { + return null; + } + if (this._mipmaps[0].data instanceof HTMLCanvasElement || this._mipmaps[0].data instanceof HTMLImageElement) { + return this._mipmaps[0].data; + } + return null; } /** diff --git a/cocos/gfx/webgpu/webgpu-define.ts b/cocos/gfx/webgpu/webgpu-define.ts index 4b1c2805206..4e1d58044e6 100644 --- a/cocos/gfx/webgpu/webgpu-define.ts +++ b/cocos/gfx/webgpu/webgpu-define.ts @@ -201,7 +201,7 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { Device.prototype.copyImagesToTexture = function (imageAssets: ImageAsset[], texture: typeof Texture, regions: BufferTextureCopy[]) { const buffers: Uint8Array[] = []; for (let i = 0; i < regions.length; i++) { - buffers.push(imageAssets[i].nativeData); + buffers.push(imageAssets[i].rawData); } oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); } diff --git a/pal/env/web/env.ts b/pal/env/web/env.ts index ff35915bbcb..0147f07057b 100644 --- a/pal/env/web/env.ts +++ b/pal/env/web/env.ts @@ -27,7 +27,7 @@ export function findCanvas (): { frame: HTMLDivElement, container: HTMLDivElemen const container = document.querySelector('#Cocos3dGameContainer') as HTMLDivElement; const canvas = document.querySelector('#GameCanvas') as HTMLCanvasElement; - return { frame, container, canvas }; + return { frame, canvas, container }; } export function loadJsFile (path: string): Promise { diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 62523c3a820..375c4274be3 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -70,7 +70,7 @@ export class BaseImageData { return this._imageSource && this._imageSource._data; } - public nativeData (): unknown { + public rawData (): unknown { return this.data as any; } diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 35e02877d5a..3a0ee2e9789 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -37,7 +37,7 @@ export class ImageData extends BaseImageData { } super.destroy(); } - public nativeData (): unknown { + public rawData (): unknown { if (this._imageSource instanceof HTMLCanvasElement) { // @ts-ignore return this._imageSource._data.data; @@ -47,7 +47,7 @@ export class ImageData extends BaseImageData { } else if (ArrayBuffer.isView(this._imageSource)) { return this._imageSource.buffer; } - return super.nativeData(); + return super.rawData(); } protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index f0c481db5aa..8fb8773ff30 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -27,7 +27,7 @@ import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; export class ImageData extends BaseImageData { - public nativeData (): unknown { + public rawData (): unknown { let data; if ('getContext' in this._imageSource) { const canvasElem = this._imageSource; diff --git a/platforms/native/engine/jsb-gfx.js b/platforms/native/engine/jsb-gfx.js index 21a055d7122..de92befde19 100644 --- a/platforms/native/engine/jsb-gfx.js +++ b/platforms/native/engine/jsb-gfx.js @@ -60,7 +60,7 @@ deviceProto.copyImagesToTexture = function (imageAssets, texture, regions) { if (imageAssets) { for (let i = 0; i < imageAssets.length; ++i) { const image = imageAssets[i]; - images.push(image.nativeData); + images.push(image.rawData); } } oldCopyTexImagesToTextureFunc.call(this, images, texture, regions); From 6981e2aad4e483a05d1b06ebdd881ed8ddf45132 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 3 Jul 2023 15:21:08 +0800 Subject: [PATCH 12/24] Optimizing the implementation of loadiamge. --- @types/pal/image.d.ts | 17 ++-------- cocos/asset/asset-manager/downloader.ts | 5 +-- cocos/asset/asset-manager/parser.ts | 4 +-- cocos/asset/assets/image-asset.jsb.ts | 14 ++------ cocos/asset/assets/image-asset.ts | 12 ++----- cocos/asset/assets/simple-texture.jsb.ts | 2 +- cocos/asset/assets/simple-texture.ts | 4 +-- cocos/game/splash-screen.ts | 24 +++++++------- cocos/gfx/empty/empty-device.ts | 2 +- cocos/gfx/webgl/webgl-device.ts | 2 +- cocos/gfx/webgl2/webgl2-device.ts | 2 +- pal/image/base-image-data.ts | 14 +------- pal/image/minigame/image-data.ts | 37 ++++++++++----------- pal/image/native/image-data.ts | 42 ++++++++++++------------ pal/image/web/image-data.ts | 33 +++++++++---------- 15 files changed, 85 insertions(+), 129 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 1943b42869b..590718cf7df 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -55,25 +55,12 @@ declare module 'pal/image' { /** * Download images from the server. */ - static loadImage ( - url: string, - options: Record, - onComplete: ((err: Error | null, data?: ImageSource | null) => void), - ): ImageData; - - /** - * Image load success callback(when the image is HTMLImageElement). - */ - onload: ((ev: Event) => void) | null; - /** - * Image load error callback(when the image is HTMLImageElement). - */ - onerror: ((ev: Event) => void) | null; + static loadImage (url: string): Promise /** * Get raw image data, in web platform is image source(like data interface), in native platform is image raw data. */ - rawData (): unknown; + getRawData (): unknown; /** * Determine if it is an HTMLElement element. */ diff --git a/cocos/asset/asset-manager/downloader.ts b/cocos/asset/asset-manager/downloader.ts index 28a9ded0041..84593cf3ded 100644 --- a/cocos/asset/asset-manager/downloader.ts +++ b/cocos/asset/asset-manager/downloader.ts @@ -32,6 +32,7 @@ import { files } from './shared'; import { retry, RetryFunction, urlAppendTimestamp } from './utilities'; import { IConfigOption } from './config'; import { CCON, parseCCONJson, decodeCCONBinary } from '../../serialization/ccon'; +import downloadDomImage from './download-dom-image'; export type DownloadHandler = (url: string, options: Record, onComplete: ((err: Error | null, data?: any | null) => void)) => void; @@ -48,7 +49,7 @@ const REGEX = /^(?:\w+:\/\/|\.+\/).+/; const downloadImage = (url: string, options: Record, onComplete: ((err: Error | null, data?: any | null) => void)): void => { // if createImageBitmap is valid, we can transform blob to ImageBitmap. Otherwise, just use HTMLImageElement to load - const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : ImageData.loadImage; + const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : downloadDomImage; func(url, options, onComplete); }; @@ -245,7 +246,7 @@ export class Downloader { /** * @deprecated Since v3.7, this is an engine internal interface. You can easily implement the functionality of this API using HTMLImageElement. */ - public downloadDomImage = ImageData.loadImage; + public downloadDomImage = downloadDomImage; /** * @deprecated Since v3.7, this is an engine internal interface. You can easily implement the functionality of this API using HTMLAudioElement. diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index f0477c29b6d..a5267f62ba5 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -87,9 +87,9 @@ export class Parser { onComplete(null, file.data); return; } - createImageBitmap(file, { premultiplyAlpha: 'none' }).then((result) => { + createImageBitmap(file, { premultiplyAlpha: 'none' }).then((result): void => { onComplete(null, result); - }, (err) => { + }, (err): void => { onComplete(err, null); }); } diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index e1bd724ca10..d507d0fb4b7 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -54,7 +54,7 @@ Object.defineProperty(imageAssetProto, '_nativeAsset', { configurable: true, enumerable: true, get () { - return this._imageData; + return this._imageData.data; }, set (value: ImageSource) { this.reset(value); @@ -73,15 +73,7 @@ Object.defineProperty(imageAssetProto, 'rawData', { configurable: true, enumerable: true, get () { - return this._imageData.rawData(); - }, -}); - -Object.defineProperty(imageAssetProto, 'imageData', { - configurable: true, - enumerable: true, - get () { - return this._imageData; + return this._imageData.getRawData(); }, }); @@ -137,7 +129,7 @@ imageAssetProto._syncDataToNative = function () { this.setHeight(this._height); this.url = this.nativeUrl; - this.setData(this._imageData.rawData()); + this.setData(this._imageData.getRawData()); if (data._mipmapLevelDataSize){ this.setMipmapLevelDataSize(data._mipmapLevelDataSize); } diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 24d10d4abab..353ee5efbbd 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -498,25 +498,17 @@ export class ImageAsset extends Asset { * @zh 原始图像数据。 */ get rawData (): any { - return this._imageData.rawData(); + return this._imageData.getRawData(); } /** * @en Image data source(include: HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView). * @zh 图像的来源(包括:HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView)。 */ - get data (): any { + get data (): ImageSource | ArrayBufferView | null { return this._imageData.data; } - /** - * @en Image data object. - * @zh 图像的数据对象。 - */ - get imageData (): ImageData { - return this._imageData; - } - /** * @en The pixel width of the image. * @zh 此图像资源的像素宽度。 diff --git a/cocos/asset/assets/simple-texture.jsb.ts b/cocos/asset/assets/simple-texture.jsb.ts index 93f281d4e58..a6a927a8ad1 100644 --- a/cocos/asset/assets/simple-texture.jsb.ts +++ b/cocos/asset/assets/simple-texture.jsb.ts @@ -44,7 +44,7 @@ const simpleTextureProto = jsb.SimpleTexture.prototype; const oldUpdateDataFunc = simpleTextureProto.uploadData; simpleTextureProto.uploadData = function (source, level = 0, arrayIndex = 0) { let imageData = new ImageData(source); - oldUpdateDataFunc.call(this, imageData.rawData(), level, arrayIndex); + oldUpdateDataFunc.call(this, imageData.getRawData(), level, arrayIndex); }; simpleTextureProto._ctor = function () { diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index 80ed090bbf6..fb433a77c72 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -24,7 +24,7 @@ import { ccclass } from 'cc.decorator'; import { DEV } from 'internal:constants'; -import { IMemoryImageSource } from '../../../pal/image/types'; +import { ImageSource } from '../../../pal/image/types'; import { TextureFlagBit, TextureUsageBit, API, Texture, TextureInfo, TextureViewInfo, Device, BufferTextureCopy } from '../../gfx'; import { assertID, error, js, macro, cclegacy } from '../../core'; @@ -142,7 +142,7 @@ export class SimpleTexture extends TextureBase { * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 * @param arrayIndex @en The array index. @zh 要上传的数组索引。 */ - public uploadData (source: HTMLCanvasElement | HTMLImageElement | ArrayBufferView | ImageBitmap | IMemoryImageSource, level = 0, arrayIndex = 0): void { + public uploadData (source: ImageSource | ArrayBufferView, level = 0, arrayIndex = 0): void { if (!this._gfxTexture || this._mipmapLevel <= level) { return; } diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index 82e5bbcdeca..9e458309c86 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -23,7 +23,7 @@ */ import { EDITOR, TAOBAO } from 'internal:constants'; -import { ImageData } from 'pal/image'; +import { ImageData, ImageSource } from 'pal/image'; import { Material } from '../asset/assets/material'; import { clamp01, Mat4, Vec2, Settings, settings, sys, cclegacy, easing, preTransforms } from '../core'; import { @@ -137,26 +137,24 @@ export class SplashScreen { this.initWaterMark(); const bgPromise = new Promise((resolve, reject): void => { - this.bgImage = new ImageAsset(); - this.bgImage.imageData.onload = (): void => { + ImageData.loadImage(this.settings.bgBase64).then((imageData) => { + this.bgImage = new ImageAsset(); + this.bgImage._nativeAsset = imageData.data; this.initBG(); resolve(); - }; - this.bgImage.imageData.onerror = (): void => { + }).catch((err) => { reject(); - }; - this.bgImage.imageData.src = this.settings.bgBase64; + }); }); const logoPromise = new Promise((resolve, reject) => { - this.logoImage = new ImageAsset(); - this.logoImage.imageData.onload = (): void => { + ImageData.loadImage(this.settings.base64src).then((imageData) => { + this.logoImage = new ImageAsset(); + this.logoImage._nativeAsset = imageData.data; this.initLogo(); resolve(); - }; - this.logoImage.imageData.onerror = (): void => { + }).catch((err) => { reject(); - }; - this.logoImage.imageData.src = this.settings.base64src; + }); }); return Promise.all([bgPromise, logoPromise]); } diff --git a/cocos/gfx/empty/empty-device.ts b/cocos/gfx/empty/empty-device.ts index bbef3af5c3c..ea455892a90 100644 --- a/cocos/gfx/empty/empty-device.ts +++ b/cocos/gfx/empty/empty-device.ts @@ -214,7 +214,7 @@ export class EmptyDevice extends Device { public copyBuffersToTexture (buffers: Readonly, texture: Texture, regions: Readonly): void {} public copyTextureToBuffers (texture: Readonly, buffers: ArrayBufferView[], regions: Readonly): void {} public copyTexImagesToTexture (texImages: Readonly, texture: Texture, regions: Readonly): void {} - public copyImagesToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly) {} + public copyImagesToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly): void {} } cclegacy.EmptyDevice = EmptyDevice; diff --git a/cocos/gfx/webgl/webgl-device.ts b/cocos/gfx/webgl/webgl-device.ts index d453132786b..080560d5fb9 100644 --- a/cocos/gfx/webgl/webgl-device.ts +++ b/cocos/gfx/webgl/webgl-device.ts @@ -570,7 +570,7 @@ export class WebGLDevice extends Device { imageAssets: Readonly, texture: Texture, regions: Readonly, - ) { + ): void { const texImages: TexImageSource[] = []; imageAssets.forEach((item) => { texImages.push(item.data as TexImageSource); diff --git a/cocos/gfx/webgl2/webgl2-device.ts b/cocos/gfx/webgl2/webgl2-device.ts index 9b29bc03f1a..0d8f3646186 100644 --- a/cocos/gfx/webgl2/webgl2-device.ts +++ b/cocos/gfx/webgl2/webgl2-device.ts @@ -637,7 +637,7 @@ export class WebGL2Device extends Device { imageAssets: Readonly, texture: Texture, regions: Readonly, - ) { + ): void { const texImages: TexImageSource[] = []; imageAssets.forEach((item) => { texImages.push(item.data as TexImageSource); diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 375c4274be3..38cd3481c9b 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -70,7 +70,7 @@ export class BaseImageData { return this._imageSource && this._imageSource._data; } - public rawData (): unknown { + public getRawData (): unknown { return this.data as any; } @@ -155,16 +155,4 @@ export class BaseImageData { private fetchImageSource (imageSource: ImageSource): any { return '_data' in imageSource ? imageSource._data : imageSource; } - - protected addEventListener (name: string, cb: (ev: Event) => void): void { - if (this.isHtmlElement()) { - (this.data as HTMLImageElement).addEventListener(name, cb); - } - } - - protected removeEventListener (name: string, cb: (ev: Event) => void): void { - if (this.isHtmlElement()) { - (this.data as HTMLImageElement).removeEventListener(name, cb); - } - } } diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index 619db005d10..42dd5e85821 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -40,24 +40,23 @@ export class ImageData extends BaseImageData { (this._imageSource as HTMLImageElement).src = url; } - static loadImage (url: string, - options: Record, - onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { - const image = new ImageData(); - - // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' - if (ccwindow.location.protocol !== 'file:' || XIAOMI) { - image.crossOrigin = 'anonymous'; - } - - image.onload = (): void => { - if (onComplete) { onComplete(null, image.data); } - }; - image.onerror = (): void => { - if (onComplete) { onComplete(new Error(getError(4930, url))); } - }; - - image.src = url; - return image; + static loadImage (url: string): Promise { + return new Promise((resolve, reject) => { + const image = new ImageData(); + + if (ccwindow.location.protocol !== 'file:' || XIAOMI) { + image.crossOrigin = 'anonymous'; + } + + image.onload = (): void => { + resolve(image); + }; + image.onerror = (): void => { + reject(new Error(getError(4930, url))); + }; + + image.src = url; + return image; + }); } } diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 3a0ee2e9789..aba0ec1fcdf 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -37,17 +37,17 @@ export class ImageData extends BaseImageData { } super.destroy(); } - public rawData (): unknown { + + public getRawData (): unknown { + // TODO(qgh):Need to remove implementations such as HTMLImageElement and use a simpler image class. if (this._imageSource instanceof HTMLCanvasElement) { - // @ts-ignore - return this._imageSource._data.data; + return (this._imageSource as any)._data.data; } else if (this._imageSource instanceof HTMLImageElement) { - // @ts-ignore - return this._imageSource._data; + return (this._imageSource as any)._data; } else if (ArrayBuffer.isView(this._imageSource)) { return this._imageSource.buffer; } - return super.rawData(); + return super.getRawData(); } protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { @@ -57,23 +57,23 @@ export class ImageData extends BaseImageData { return super.isNativeImage(imageSource); } - static loadImage (url: string, - options: Record, - onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { - const image = new ImageData(); + static loadImage (url: string): Promise { + return new Promise((resolve, reject) => { + const image = new ImageData(); - if (ccwindow.location.protocol !== 'file:') { - image.crossOrigin = 'anonymous'; - } + if (ccwindow.location.protocol !== 'file:') { + image.crossOrigin = 'anonymous'; + } - image.onload = (): void => { - if (onComplete) { onComplete(null, image.data); } - }; - image.onerror = (): void => { - if (onComplete) { onComplete(new Error(getError(4930, url))); } - }; + image.onload = (): void => { + resolve(image); + }; + image.onerror = (): void => { + reject(new Error(getError(4930, url))); + }; - image.src = url; - return image; + image.src = url; + return image; + }); } } diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index 8fb8773ff30..7c21102970d 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -27,7 +27,7 @@ import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; export class ImageData extends BaseImageData { - public rawData (): unknown { + public getRawData (): unknown { let data; if ('getContext' in this._imageSource) { const canvasElem = this._imageSource; @@ -66,24 +66,23 @@ export class ImageData extends BaseImageData { return this.data; } - static loadImage (url: string, - options: Record, - onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): ImageData { - const image = new ImageData(); + static loadImage (url: string): Promise { + return new Promise((resolve, reject) => { + const image = new ImageData(); - // NOTE: on xiaomi platform, we need to force setting img.crossOrigin as 'anonymous' - if (ccwindow.location.protocol !== 'file:') { - image.crossOrigin = 'anonymous'; - } + if (ccwindow.location.protocol !== 'file:') { + image.crossOrigin = 'anonymous'; + } - image.onload = (): void => { - if (onComplete) { onComplete(null, image.data); } - }; - image.onerror = (): void => { - if (onComplete) { onComplete(new Error(getError(4930, url))); } - }; + image.onload = (): void => { + resolve(image); + }; + image.onerror = (): void => { + reject(new Error(getError(4930, url))); + }; - image.src = url; - return image; + image.src = url; + return image; + }); } } From ea77a5d8d8bf05b545f854576ac067cf88f32e4b Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 3 Jul 2023 17:06:00 +0800 Subject: [PATCH 13/24] Submit download-dom-image.ts. --- .../asset/asset-manager/download-dom-image.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 cocos/asset/asset-manager/download-dom-image.ts diff --git a/cocos/asset/asset-manager/download-dom-image.ts b/cocos/asset/asset-manager/download-dom-image.ts new file mode 100644 index 00000000000..902d2e19447 --- /dev/null +++ b/cocos/asset/asset-manager/download-dom-image.ts @@ -0,0 +1,35 @@ +/* + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd. + http://www.cocos.com + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { ImageData } from 'pal/image'; +import { ImageSource } from '../../../pal/image/types'; + +export default function downloadDomImage ( + url: string, + options: Record, + onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void), +): void { + ImageData.loadImage(url).then((imageData) => { + onComplete(null, imageData.data); + }).catch((err) => { + onComplete(err); + }); +} From d9a979fd703ab25d1800cdb4a012260b79fbfcb1 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 4 Jul 2023 10:28:55 +0800 Subject: [PATCH 14/24] Modify comments --- @types/pal/image.d.ts | 32 ++++----- editor/assets/primitives.fbx.meta | 66 ++++++------------- .../assets/tools/parsed-effect-info.json.meta | 2 +- 3 files changed, 31 insertions(+), 69 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 590718cf7df..6eb5b163d79 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -10,39 +10,29 @@ declare module 'pal/image' { destroy (): void; /** - * Get the url of the image(when source is HTMLImageElement). + * Get and set the url of the image(when source is HTMLImageElement). */ get src (): string; - /** - * Set the url of the image(when source is HTMLImageElement). - */ set src(url: string); /** - * Set image data source. - * @param value Image data source. - */ - set data(value: ImageSource | ArrayBufferView); - /** - * Get image data source. + * Get and set image data source. * @param value Image data source. */ get data(): ImageSource | ArrayBufferView; + set data(value: ImageSource | ArrayBufferView); + /** - * Set image width(when source is IMemoryImageSource). - */ - set width(value: number); - /** - * Get image width. + * Get and set image width(when source is IMemoryImageSource). */ get width(): number; + set width(value: number); + /** - * Set image height(when source is IMemoryImageSource). - */ - set height(value: number); - /** - * Get image height. + * Get and set image height(when source is IMemoryImageSource). */ get height(): number; + set height(value: number); + /** * Get image format. */ @@ -53,7 +43,7 @@ declare module 'pal/image' { get compressed(): boolean; get mipmapLevelDataSize(): number[] | undefined; /** - * Download images from the server. + * Load image via local url or web url. */ static loadImage (url: string): Promise diff --git a/editor/assets/primitives.fbx.meta b/editor/assets/primitives.fbx.meta index 12676f8a302..abc533a3bbb 100644 --- a/editor/assets/primitives.fbx.meta +++ b/editor/assets/primitives.fbx.meta @@ -1,5 +1,5 @@ { - "ver": "2.3.8", + "ver": "2.3.5", "importer": "fbx", "imported": true, "uuid": "1263d74c-8167-4928-91a6-4e2672411f47", @@ -11,7 +11,7 @@ "displayName": "", "id": "17020", "name": "sphere.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -19,8 +19,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 4, - "triangleCount": 2380 + "gltfIndex": 4 } }, "801ec": { @@ -29,7 +28,7 @@ "displayName": "", "id": "801ec", "name": "capsule.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -37,8 +36,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 0, - "triangleCount": 2048 + "gltfIndex": 0 } }, "2e76e": { @@ -47,7 +45,7 @@ "displayName": "", "id": "2e76e", "name": "plane.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -55,8 +53,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 1, - "triangleCount": 200 + "gltfIndex": 1 } }, "38fd2": { @@ -65,7 +62,7 @@ "displayName": "", "id": "38fd2", "name": "cone.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -73,8 +70,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 2, - "triangleCount": 72 + "gltfIndex": 2 } }, "40ece": { @@ -83,7 +79,7 @@ "displayName": "", "id": "40ece", "name": "torus.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -91,8 +87,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 3, - "triangleCount": 2048 + "gltfIndex": 3 } }, "fc873": { @@ -101,7 +96,7 @@ "displayName": "", "id": "fc873", "name": "quad.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -109,8 +104,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 5, - "triangleCount": 2 + "gltfIndex": 5 } }, "8abdc": { @@ -119,7 +113,7 @@ "displayName": "", "id": "8abdc", "name": "cylinder.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -127,8 +121,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 6, - "triangleCount": 128 + "gltfIndex": 6 } }, "a804a": { @@ -137,7 +130,7 @@ "displayName": "", "id": "a804a", "name": "box.mesh", - "ver": "1.1.1", + "ver": "1.1.0", "imported": true, "files": [ ".bin", @@ -145,8 +138,7 @@ ], "subMetas": {}, "userData": { - "gltfIndex": 7, - "triangleCount": 12 + "gltfIndex": 7 } }, "8d883": { @@ -171,7 +163,7 @@ "displayName": "", "id": "aae0f", "name": "primitives.prefab", - "ver": "1.0.13", + "ver": "1.0.12", "imported": true, "files": [ ".json" @@ -225,27 +217,7 @@ "legacyFbxImporter": false, "fbx": { "preferLocalTimeSpan": false, - "smartMaterialEnabled": false, - "matchMeshNames": false - }, - "mountAllAnimationsOnPrefab": true, - "lods": { - "enable": false, - "hasBuiltinLOD": false, - "options": [ - { - "screenRatio": 0.25, - "faceCount": 1 - }, - { - "screenRatio": 0.125, - "faceCount": 0.25 - }, - { - "screenRatio": 0.01, - "faceCount": 0.1 - } - ] + "smartMaterialEnabled": false } } } diff --git a/editor/assets/tools/parsed-effect-info.json.meta b/editor/assets/tools/parsed-effect-info.json.meta index 7fc88514ec8..96005b50796 100644 --- a/editor/assets/tools/parsed-effect-info.json.meta +++ b/editor/assets/tools/parsed-effect-info.json.meta @@ -1,5 +1,5 @@ { - "ver": "2.0.0", + "ver": "1.0.0", "importer": "json", "imported": true, "uuid": "e6914174-8ecd-4c99-8e9b-9773a9d368df", From 2d67b2d6c7ed604c4937eefc5f697b762e205398 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Tue, 4 Jul 2023 11:08:28 +0800 Subject: [PATCH 15/24] Modify comments --- @types/pal/image.d.ts | 17 +++++++++-------- cocos/tiledmap/tiled-map.ts | 2 +- scripts/native-pack-tool/package-lock.json | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index 6eb5b163d79..bb1aa87de87 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -9,11 +9,6 @@ declare module 'pal/image' { */ destroy (): void; - /** - * Get and set the url of the image(when source is HTMLImageElement). - */ - get src (): string; - set src(url: string); /** * Get and set image data source. * @param value Image data source. @@ -22,19 +17,25 @@ declare module 'pal/image' { set data(value: ImageSource | ArrayBufferView); /** - * Get and set image width(when source is IMemoryImageSource). + * Get image width. */ get width(): number; + /** + * Set image width(when source is IMemoryImageSource). + */ set width(value: number); /** - * Get and set image height(when source is IMemoryImageSource). + * Get image height. */ get height(): number; + /** + * Set image height(when source is IMemoryImageSource). + */ set height(value: number); /** - * Get image format. + * Get image format(when source is IMemoryImageSource). */ get format(): PixelFormat; /** diff --git a/cocos/tiledmap/tiled-map.ts b/cocos/tiledmap/tiled-map.ts index fef7610ce1d..73f2de6433c 100644 --- a/cocos/tiledmap/tiled-map.ts +++ b/cocos/tiledmap/tiled-map.ts @@ -604,7 +604,7 @@ export class TiledMap extends Component { } doCleanupImageCache (texture): void { - // TODO:There is a bug here, if you uncomment, it will cause the display result to be incorrect. + // TODO:There is a bug here, if we uncomment, it will cause the display result to be incorrect. // texture.image.destroy(); // texture.image = null; } diff --git a/scripts/native-pack-tool/package-lock.json b/scripts/native-pack-tool/package-lock.json index cd0b13025a2..43a55b0c26c 100644 --- a/scripts/native-pack-tool/package-lock.json +++ b/scripts/native-pack-tool/package-lock.json @@ -746,7 +746,7 @@ }, "json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonfile": { From c74993c3a0441f228de7b14a131132ed3ffe447b Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 31 Jul 2023 09:17:04 +0800 Subject: [PATCH 16/24] Optimize pal/image implementation --- @types/jsb.d.ts | 4 + @types/pal/image.d.ts | 36 ++--- .../asset/asset-manager/download-dom-image.ts | 4 +- cocos/asset/asset-manager/downloader.ts | 3 +- cocos/asset/asset-manager/factory.ts | 3 +- cocos/asset/asset-manager/parser.ts | 6 +- cocos/asset/assets/image-asset.jsb.ts | 28 ++-- cocos/asset/assets/image-asset.ts | 49 ++++--- cocos/asset/assets/simple-texture.ts | 20 +-- cocos/asset/assets/texture-cube.ts | 2 +- cocos/game/splash-screen.ts | 8 +- cocos/gfx/webgl/webgl-device.ts | 29 +++- cocos/gfx/webgl2/webgl2-device.ts | 31 ++-- pal/image/base-image-data.ts | 134 +++++------------- pal/image/minigame/image-data.ts | 58 ++++++-- pal/image/native/image-data.ts | 83 ++++++----- pal/image/types.ts | 2 + pal/image/web/image-data.ts | 37 ++--- 18 files changed, 282 insertions(+), 255 deletions(-) diff --git a/@types/jsb.d.ts b/@types/jsb.d.ts index 2c6347e1994..89131a7e4a4 100644 --- a/@types/jsb.d.ts +++ b/@types/jsb.d.ts @@ -339,6 +339,10 @@ declare namespace jsb { setVerifyCallback (verifyCallback: (path: string, asset: ManifestAsset) => boolean): void; setEventCallback (eventCallback: (event: EventAssetsManager) => void): void; } + + export class JSBNativeDataHolder { + + } } declare namespace ns { diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index bb1aa87de87..f637a2ded90 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -1,7 +1,9 @@ declare module 'pal/image' { + export type ImageSource = import('pal/image/types').ImageSource; export type IMemoryImageSource = import('pal/image/types').IMemoryImageSource; export type PixelFormat = import('cocos/asset/assets/asset-enum').PixelFormat; + export type RawDataType = import('pal/image/types').RawDataType; export class ImageData { constructor (imageAsset?: ImageSource | ArrayBufferView); /** @@ -13,53 +15,33 @@ declare module 'pal/image' { * Get and set image data source. * @param value Image data source. */ - get data(): ImageSource | ArrayBufferView; - set data(value: ImageSource | ArrayBufferView); + get source(): ImageSource; + set source(value: ImageSource); /** * Get image width. */ get width(): number; - /** - * Set image width(when source is IMemoryImageSource). - */ - set width(value: number); /** * Get image height. */ get height(): number; - /** - * Set image height(when source is IMemoryImageSource). - */ - set height(value: number); - /** - * Get image format(when source is IMemoryImageSource). - */ - get format(): PixelFormat; - /** - * Get if the image is compressed. - */ - get compressed(): boolean; - get mipmapLevelDataSize(): number[] | undefined; /** * Load image via local url or web url. */ - static loadImage (url: string): Promise + static loadImage (urlAndBase64: string): Promise /** * Get raw image data, in web platform is image source(like data interface), in native platform is image raw data. */ - getRawData (): unknown; - /** - * Determine if it is an HTMLElement element. - */ - isHtmlElement(): boolean; + getRawData (): RawDataType | null; + /** * Set image data source. - * @param data Image data source(Not contain IMemoryImageSource). + * @param data Image data source. */ - reset(data: ImageSource): void; + reset(data?: ImageSource | ArrayBufferView): void; } } diff --git a/cocos/asset/asset-manager/download-dom-image.ts b/cocos/asset/asset-manager/download-dom-image.ts index 902d2e19447..a75c6307ae4 100644 --- a/cocos/asset/asset-manager/download-dom-image.ts +++ b/cocos/asset/asset-manager/download-dom-image.ts @@ -25,10 +25,10 @@ import { ImageSource } from '../../../pal/image/types'; export default function downloadDomImage ( url: string, options: Record, - onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void), + onComplete: ((err: Error | null, data?: ImageData | null) => void), ): void { ImageData.loadImage(url).then((imageData) => { - onComplete(null, imageData.data); + onComplete(null, imageData); }).catch((err) => { onComplete(err); }); diff --git a/cocos/asset/asset-manager/downloader.ts b/cocos/asset/asset-manager/downloader.ts index 84593cf3ded..b7afa4579af 100644 --- a/cocos/asset/asset-manager/downloader.ts +++ b/cocos/asset/asset-manager/downloader.ts @@ -23,7 +23,6 @@ */ import { BUILD, EDITOR, EDITOR_NOT_IN_PREVIEW } from 'internal:constants'; -import { ImageData } from 'pal/image'; import { sys, js, misc, path, cclegacy } from '../../core'; import Cache from './cache'; import downloadFile from './download-file'; @@ -48,7 +47,7 @@ interface IDownloadRequest { const REGEX = /^(?:\w+:\/\/|\.+\/).+/; const downloadImage = (url: string, options: Record, onComplete: ((err: Error | null, data?: any | null) => void)): void => { - // if createImageBitmap is valid, we can transform blob to ImageBitmap. Otherwise, just use HTMLImageElement to load + // if createImageBitmap is valid, we can transform blob to ImageBitmap. Otherwise, just use ImageData to load const func = sys.hasFeature(sys.Feature.IMAGE_BITMAP) && cclegacy.assetManager.allowImageBitmap ? downloadBlob : downloadDomImage; func(url, options, onComplete); }; diff --git a/cocos/asset/asset-manager/factory.ts b/cocos/asset/asset-manager/factory.ts index c71963a815c..05e64e00857 100644 --- a/cocos/asset/asset-manager/factory.ts +++ b/cocos/asset/asset-manager/factory.ts @@ -23,6 +23,7 @@ */ import { EDITOR } from 'internal:constants'; +import { ImageData } from 'pal/image'; import { ImageAsset } from '../assets/image-asset'; import JsonAsset from '../assets/json-asset'; import { TextAsset } from '../assets/text-asset'; @@ -45,7 +46,7 @@ function createImageAsset (id: string, imageData: ImageData, options: Record, onComplete: ((err: Error | null, data?: ImageSource | ArrayBufferView | null) => void)): void { + public parseImage (file: ImageData | Blob, options: Record, onComplete: ((err: Error | null, data?: ImageData | null) => void)): void { if (file instanceof ImageData) { - onComplete(null, file.data); + onComplete(null, file); return; } createImageBitmap(file, { premultiplyAlpha: 'none' }).then((result): void => { - onComplete(null, result); + onComplete(null, new ImageData(result)); }, (err): void => { onComplete(err, null); }); diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index d507d0fb4b7..1efacb78fa5 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -54,10 +54,15 @@ Object.defineProperty(imageAssetProto, '_nativeAsset', { configurable: true, enumerable: true, get () { - return this._imageData.data; + return this._imageData.source; }, set (value: ImageSource) { - this.reset(value); + if (value instanceof ImageData) { + this._imageData = value; + this._syncDataToNative(); + } else { + this.reset(value); + } }, }); @@ -65,7 +70,11 @@ Object.defineProperty(imageAssetProto, 'data', { configurable: true, enumerable: true, get () { - return this._imageData.data; + if ('_data' in this._imageData.source) { + return this._imageData.getRawData() as ArrayBufferView; + } else { + return this._imageData.source; + } }, }); @@ -87,19 +96,20 @@ imageAssetProto._setRawAsset = function (filename: string, inLibrary = true) { // TODO: Property 'format' does not exist on type 'HTMLCanvasElement'. // imageAssetProto.reset = function (data: ImageSource) { -imageAssetProto.reset = function (data: any) { +imageAssetProto.reset = function (data: ImageSource | ArrayBufferView) { this._imageData.reset(data); - if (this._imageData.format != null) { - this._format = this._imageData.format; + if ('_data' in data) { + const format = data.format; + if (format != null) { + this._format = format; + } } this._syncDataToNative(); }; const superDestroy = jsb.Asset.prototype.destroy; imageAssetProto.destroy = function () { - if (this._imageData.isHtmlElement()) { - this._setRawAsset(''); - } + this._setRawAsset(''); this._imageData.destroy(); return superDestroy.call(this); }; diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 353ee5efbbd..eef6b0cd997 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -26,7 +26,7 @@ import { ccclass, override } from 'cc.decorator'; import { EDITOR, ALIPAY, XIAOMI, JSB, TEST, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { ImageData } from 'pal/image'; -import { ImageSource, IMemoryImageSource } from '../../../pal/image/types'; +import { ImageSource, IMemoryImageSource, RawDataType } from '../../../pal/image/types'; import { Device, Format, FormatFeatureBit, deviceManager } from '../../gfx'; import { Asset } from './asset'; import { PixelFormat } from './asset-enum'; @@ -485,20 +485,16 @@ export class ImageAsset extends Asset { @override get _nativeAsset (): any { // Maybe returned to pool in webgl. - return this._imageData.data; + return this._imageData.source; } // TODO: Property 'format' does not exist on type 'ImageBitmap' // set _nativeAsset (value: ImageSource) { set _nativeAsset (value: any) { - this.reset(value); - } - - /* - * @en Raw image data. - * @zh 原始图像数据。 - */ - get rawData (): any { - return this._imageData.getRawData(); + if (value instanceof ImageData) { + this._imageData = value; + } else { + this.reset(value as ImageSource); + } } /** @@ -506,7 +502,19 @@ export class ImageAsset extends Asset { * @zh 图像的来源(包括:HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView)。 */ get data (): ImageSource | ArrayBufferView | null { - return this._imageData.data; + if ('_data' in this._imageData.source) { + return this._imageData.getRawData() as ArrayBufferView; + } else { + return this._imageData.source; + } + } + + /* + * @en Raw image data. + * @zh 原始图像数据。 + */ + get rawData (): RawDataType | null { + return this._imageData.getRawData(); } /** @@ -530,7 +538,7 @@ export class ImageAsset extends Asset { * @zh 此图像资源的像素格式。 */ get format (): PixelFormat { - return this._format; + return (this.data as IMemoryImageSource).format; } /** @@ -548,7 +556,7 @@ export class ImageAsset extends Asset { * @engineInternal */ get mipmapLevelDataSize (): number[] | undefined { - return this._imageData.mipmapLevelDataSize; + return (this.data as IMemoryImageSource).mipmapLevelDataSize; } /** @@ -572,7 +580,7 @@ export class ImageAsset extends Asset { private _height = 0; - constructor (nativeAsset?: ImageSource | ArrayBufferView) { + constructor (nativeAsset?: ImageSource) { super(); if (EDITOR) { @@ -588,15 +596,16 @@ export class ImageAsset extends Asset { */ public reset (data: ImageSource): void { this._imageData.reset(data); - if (this._imageData.format != null) { - this._format = this._imageData.format; + if ('_data' in data) { + const format = data.format; + if (format != null) { + this._format = format; + } } } public destroy (): boolean { - if (this._imageData.isHtmlElement()) { - this._setRawAsset(''); - } + this._setRawAsset(''); this._imageData.destroy(); return super.destroy(); } diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index fb433a77c72..c651411205f 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -142,11 +142,11 @@ export class SimpleTexture extends TextureBase { * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 * @param arrayIndex @en The array index. @zh 要上传的数组索引。 */ - public uploadData (source: ImageSource | ArrayBufferView, level = 0, arrayIndex = 0): void { + public uploadData (source: ImageSource | ArrayBufferView | ImageAsset, level = 0, arrayIndex = 0): void { if (!this._gfxTexture || this._mipmapLevel <= level) { return; } - const imageAsset = new ImageAsset(source); + const gfxDevice = this._getGFXDevice(); if (!gfxDevice) { return; @@ -157,10 +157,15 @@ export class SimpleTexture extends TextureBase { region.texExtent.height = this._textureHeight >> level; region.texSubres.mipLevel = level; region.texSubres.baseArrayLayer = arrayIndex; - - if (ArrayBuffer.isView(imageAsset.data)) { - gfxDevice.copyBuffersToTexture([imageAsset.data], this._gfxTexture, _regions); + if (ArrayBuffer.isView(source)) { + gfxDevice.copyBuffersToTexture([source], this._gfxTexture, _regions); } else { + let imageAsset; + if (source instanceof ImageAsset) { + imageAsset = source; + } else { + imageAsset = new ImageAsset(source); + } gfxDevice.copyImagesToTexture([imageAsset], this._gfxTexture, _regions); } } @@ -169,11 +174,10 @@ export class SimpleTexture extends TextureBase { * @engineInternal */ protected _assignImage (image: ImageAsset, level: number, arrayIndex?: number): void { - const data = image.data; - if (!data) { + if (!image.data) { return; } - this.uploadData(data, level, arrayIndex); + this.uploadData(image, level, arrayIndex); this._checkTextureLoaded(); if (macro.CLEANUP_IMAGE_CACHE) { diff --git a/cocos/asset/assets/texture-cube.ts b/cocos/asset/assets/texture-cube.ts index 741c3be23f8..d53fb5bf998 100644 --- a/cocos/asset/assets/texture-cube.ts +++ b/cocos/asset/assets/texture-cube.ts @@ -578,7 +578,7 @@ export class TextureCube extends SimpleTexture { height: face.height, format: face.format, }); - tex.uploadData(face.data!); + tex.uploadData(face); for (let i = 0; i < layout.length; i++) { const layoutInfo = layout[i]; diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index 9e458309c86..f96ab9023a0 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -139,21 +139,21 @@ export class SplashScreen { const bgPromise = new Promise((resolve, reject): void => { ImageData.loadImage(this.settings.bgBase64).then((imageData) => { this.bgImage = new ImageAsset(); - this.bgImage._nativeAsset = imageData.data; + this.bgImage._nativeAsset = imageData; this.initBG(); resolve(); }).catch((err) => { - reject(); + reject(err); }); }); const logoPromise = new Promise((resolve, reject) => { ImageData.loadImage(this.settings.base64src).then((imageData) => { this.logoImage = new ImageAsset(); - this.logoImage._nativeAsset = imageData.data; + this.logoImage._nativeAsset = imageData; this.initLogo(); resolve(); }).catch((err) => { - reject(); + reject(err); }); }); return Promise.all([bgPromise, logoPromise]); diff --git a/cocos/gfx/webgl/webgl-device.ts b/cocos/gfx/webgl/webgl-device.ts index 080560d5fb9..30b3c3b08d5 100644 --- a/cocos/gfx/webgl/webgl-device.ts +++ b/cocos/gfx/webgl/webgl-device.ts @@ -572,14 +572,29 @@ export class WebGLDevice extends Device { regions: Readonly, ): void { const texImages: TexImageSource[] = []; + const buffers: ArrayBufferView[] = []; imageAssets.forEach((item) => { - texImages.push(item.data as TexImageSource); + if (ArrayBuffer.isView(item.data)) { + buffers.push(item.data); + } else { + texImages.push(item.data as TexImageSource); + } }); - WebGLCmdFuncCopyTexImagesToTexture( - this, - texImages, - (texture as WebGLTexture).gpuTexture, - regions, - ); + if (texImages.length > 0) { + WebGLCmdFuncCopyTexImagesToTexture( + this, + texImages, + (texture as WebGLTexture).gpuTexture, + regions, + ); + } + if (buffers.length > 0) { + WebGLCmdFuncCopyBuffersToTexture( + this, + buffers, + (texture as WebGLTexture).gpuTexture, + regions, + ); + } } } diff --git a/cocos/gfx/webgl2/webgl2-device.ts b/cocos/gfx/webgl2/webgl2-device.ts index 0d8f3646186..99c0e2f33f0 100644 --- a/cocos/gfx/webgl2/webgl2-device.ts +++ b/cocos/gfx/webgl2/webgl2-device.ts @@ -637,16 +637,31 @@ export class WebGL2Device extends Device { imageAssets: Readonly, texture: Texture, regions: Readonly, - ): void { + ): void { const texImages: TexImageSource[] = []; + const buffers: ArrayBufferView[] = []; imageAssets.forEach((item) => { - texImages.push(item.data as TexImageSource); + if (ArrayBuffer.isView(item.data)) { + buffers.push(item.data); + } else { + texImages.push(item.data as TexImageSource); + } }); - WebGL2CmdFuncCopyTexImagesToTexture( - this, - texImages, - (texture as WebGL2Texture).gpuTexture, - regions, - ); + if (texImages.length > 0) { + WebGL2CmdFuncCopyTexImagesToTexture( + this, + texImages, + (texture as WebGL2Texture).gpuTexture, + regions, + ); + } + if (buffers.length > 0) { + WebGL2CmdFuncCopyBuffersToTexture( + this, + buffers, + (texture as WebGL2Texture).gpuTexture, + regions, + ); + } } } diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 38cd3481c9b..c67e586e7e3 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -21,16 +21,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { PixelFormat } from '../../cocos/asset/assets/asset-enum'; import { IMemoryImageSource, ImageSource } from './types'; import { sys } from '../../cocos/core/platform/sys'; -import { ccwindow } from '../../cocos/core/global-exports'; export class BaseImageData { - protected _imageSource: ImageSource; + // TODO(qgh):Designed for compatibility, may be removed in the future. + protected _source: ImageSource; constructor (imageAsset?: ImageSource | ArrayBufferView) { - this._imageSource = { + this._source = { _data: null, width: 0, height: 0, @@ -39,120 +38,59 @@ export class BaseImageData { mipmapLevelDataSize: [], }; if (typeof imageAsset !== 'undefined') { - this.data = imageAsset; - } else if (typeof imageAsset === 'undefined') { - this._imageSource = new ccwindow.Image(); + if (!ArrayBuffer.isView(imageAsset)) { + this._source = imageAsset; + } else { + this._source._data = imageAsset; + } } } public destroy (): void { - if (this.data && this.data instanceof HTMLImageElement) { - this.data.src = ''; - // this._setRawAsset(''); - } else if (this.isImageBitmap(this.data)) { - this.data?.close(); + if (this.source && this.source instanceof HTMLImageElement) { + this.source.src = ''; + } else if (this.isImageBitmap(this.source)) { + this.source?.close(); } } - set data (imageAsset: ImageSource | ArrayBufferView | null) { - if (imageAsset != null && !ArrayBuffer.isView(imageAsset)) { + set source (imageAsset: ImageSource | null) { + if (imageAsset != null) { this.reset(imageAsset); - } else { - (this._imageSource as IMemoryImageSource)._data = imageAsset; } } - get data (): ImageSource | ArrayBufferView | null { - if (this._imageSource && this.isNativeImage(this._imageSource)) { - return this._imageSource; - } - - return this._imageSource && this._imageSource._data; - } - - public getRawData (): unknown { - return this.data as any; - } - - public acceptAnonymousCORS (): void { - (this._imageSource as HTMLImageElement).crossOrigin = 'anonymous'; - } - - set crossOrigin (cors: string) { - (this._imageSource as HTMLImageElement).crossOrigin = cors; - } - - set onload (cb: (ev: Event) => void) { - (this._imageSource as HTMLImageElement).onload = cb; - } - - set onerror (cb: (ev: Event | string) => void) { - (this._imageSource as HTMLImageElement).onerror = cb; - } - - set src (url: string) { - (this._imageSource as HTMLImageElement).src = url; - } - - get src (): string { - return (this._imageSource as HTMLImageElement).src; + get source (): ImageSource { + return this._source; } get width (): number { - return this._imageSource.width; - } - - set width (value: number) { - (this._imageSource as IMemoryImageSource).width = value; + return this._source.width; } get height (): number { - if (!(this._imageSource instanceof ArrayBuffer)) { - return this._imageSource.height; + return this._source.height; + } + + public reset (data?: ImageSource | ArrayBufferView): void { + if (data != null) { + if (!ArrayBuffer.isView(data)) { + this._source = data; + } else { + this._source = { + _data: null, + width: 0, + height: 0, + format: 0, + _compressed: false, + mipmapLevelDataSize: [], + }; + this._source._data = data; + } } - return 0; } - set height (value: number) { - (this._imageSource as IMemoryImageSource).height = value; - } - - get format (): PixelFormat | null { - if (!(this._imageSource instanceof HTMLElement) && !this.isImageBitmap(this._imageSource) && !this.isArrayBuffer()) { - return this._imageSource.format; - } - return null; - } - - get compressed (): boolean { - return false; - } - - get mipmapLevelDataSize (): number[] | undefined { - return (this._imageSource as IMemoryImageSource).mipmapLevelDataSize; - } - - public reset (data: ImageSource): void { - this._imageSource = data; - } - - public isArrayBuffer (): boolean { - return ArrayBuffer.isView((this._imageSource as IMemoryImageSource)._data); - } - - public isHtmlElement (): boolean { - return this._imageSource instanceof HTMLElement; - } - - public isImageBitmap (imageSource: any): imageSource is ImageBitmap { + private isImageBitmap (imageSource: any): imageSource is ImageBitmap { return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); } - - protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - return imageSource instanceof HTMLImageElement || imageSource instanceof HTMLCanvasElement || this.isImageBitmap(imageSource); - } - - private fetchImageSource (imageSource: ImageSource): any { - return '_data' in imageSource ? imageSource._data : imageSource; - } } diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index 42dd5e85821..8d63e310381 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -23,40 +23,72 @@ */ import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { BaseImageData } from '../base-image-data'; -import { ImageSource } from '../types'; +import { ImageSource, RawDataType } from '../types'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; +export type ImageDataType = ArrayBufferView; export class ImageData extends BaseImageData { - protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - if (ALIPAY || TAOBAO || TAOBAO_MINIGAME || XIAOMI || BAIDU || WECHAT_MINI_PROGRAM) { - // We're unable to grab the constructors of Alipay native image or canvas object. - return !('_data' in imageSource); + public getRawData (): RawDataType | null { + if (this.source == null) { + return null; } - return super.isNativeImage(imageSource); - } - - set src (url: string) { - (this._imageSource as HTMLImageElement).src = url; + let data: ArrayBufferView | null = null; + if ('_data' in this.source) { + data = this.source._data; + } else if ('getContext' in this.source) { + const canvasElem = this.source; + const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this.source.width, this.source.height); + const buff = imageData!.data.buffer; + let rawBuffer; + if ('buffer' in buff) { + // es-lint as any + data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); + } else { + rawBuffer = buff; + data = new Uint8Array(rawBuffer); + } + } else if (this.source instanceof HTMLImageElement || this.source instanceof ImageBitmap) { + const img = this.source; + const canvas = ccwindow.document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + ctx?.drawImage(img as any, 0, 0); + const imageData = ctx?.getImageData(0, 0, img.width, img.height); + const buff = imageData!.data.buffer; + let rawBuffer; + if ('buffer' in buff) { + // es-lint as any + data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); + } else { + rawBuffer = buff; + data = new Uint8Array(rawBuffer); + } + } else { + // eslint-disable-next-line no-console + console.log('imageBmp copy not impled!'); + } + return data; } static loadImage (url: string): Promise { return new Promise((resolve, reject) => { - const image = new ImageData(); + const image = new ccwindow.Image(); if (ccwindow.location.protocol !== 'file:' || XIAOMI) { image.crossOrigin = 'anonymous'; } image.onload = (): void => { - resolve(image); + const imageData = new ImageData(image); + resolve(imageData); }; image.onerror = (): void => { reject(new Error(getError(4930, url))); }; image.src = url; - return image; }); } } diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index aba0ec1fcdf..30062bc9249 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -22,58 +22,69 @@ THE SOFTWARE. */ import { BaseImageData } from '../base-image-data'; -import type { ImageSource, IMemoryImageSource } from '../types'; -import { ccwindow } from '../../../cocos/core/global-exports'; +import { ImageSource, IMemoryImageSource, RawDataType } from '../types'; import { getError } from '../../../cocos/core'; +declare const jsb: any; + export class ImageData extends BaseImageData { + protected _rawData: RawDataType | null = null; + constructor (imageAsset?: ImageSource | ArrayBufferView) { + super(imageAsset); + this.reset(imageAsset); + } + public destroy (): void { - if (this.data && this.data instanceof HTMLImageElement) { - // JSB element should destroy native data. - // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. - // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. - // issue: https://github.com/cocos/cocos-engine/issues/14646 - (this.data as any).destroy(); + if (this._rawData instanceof jsb.JSBNativeDataHolder) { + jsb.destroyImage(this._rawData); } + // if (this.imageSource && this.imageSource instanceof HTMLImageElement) { + // // JSB element should destroy native data. + // // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. + // // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. + // // issue: https://github.com/cocos/cocos-engine/issues/14646 + // (this.imageSource as any).destroy(); + // } super.destroy(); } - public getRawData (): unknown { - // TODO(qgh):Need to remove implementations such as HTMLImageElement and use a simpler image class. - if (this._imageSource instanceof HTMLCanvasElement) { - return (this._imageSource as any)._data.data; - } else if (this._imageSource instanceof HTMLImageElement) { - return (this._imageSource as any)._data; - } else if (ArrayBuffer.isView(this._imageSource)) { - return this._imageSource.buffer; - } - return super.getRawData(); + public getRawData (): RawDataType | null { + // TODO(qgh) :ImageBitmap without raw data. + return this._rawData; } - protected isNativeImage (imageSource: ImageSource): imageSource is (HTMLImageElement | HTMLCanvasElement | ImageBitmap) { - if ((imageSource as IMemoryImageSource)._compressed === true) { - return false; + public reset (imageSource?: ImageSource | ArrayBufferView): void { + if (imageSource == null) { + this._rawData = null; + return; } - return super.isNativeImage(imageSource); + // TODO(qgh):Need to remove implementations such as HTMLImageElement and use a simpler image class. + if (imageSource instanceof HTMLCanvasElement) { + this._rawData = (imageSource as any)._data.data; + } else if (imageSource instanceof HTMLImageElement) { + this._rawData = (imageSource as any).data; + } else if (ArrayBuffer.isView(imageSource)) { + this._rawData = imageSource; + } else if ('_data' in imageSource) { + this._rawData = imageSource._data; + } + super.reset(imageSource); } - static loadImage (url: string): Promise { + static loadImage (urlOrBase64: string): Promise { return new Promise((resolve, reject) => { const image = new ImageData(); - - if (ccwindow.location.protocol !== 'file:') { - image.crossOrigin = 'anonymous'; - } - - image.onload = (): void => { + jsb.loadImage(urlOrBase64, (info): void => { + if (!info) { + reject(new Error(getError(4930, urlOrBase64))); + return; + } + (image.source as IMemoryImageSource).width = info.width; + (image.source as IMemoryImageSource).height = info.height; + (image.source as IMemoryImageSource)._data = info.data; + image._rawData = info.data; resolve(image); - }; - image.onerror = (): void => { - reject(new Error(getError(4930, url))); - }; - - image.src = url; - return image; + }); }); } } diff --git a/pal/image/types.ts b/pal/image/types.ts index df9fb062c81..ec8eab23008 100644 --- a/pal/image/types.ts +++ b/pal/image/types.ts @@ -40,3 +40,5 @@ export interface IMemoryImageSource { * @zh 图像资源的原始图像源。可以来源于 HTML 元素也可以来源于内存。 */ export type ImageSource = HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap; + +export type RawDataType = ArrayBufferView | jsb.JSBNativeDataHolder; diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index 7c21102970d..78ff97e9e07 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -22,16 +22,22 @@ THE SOFTWARE. */ import { ImageSource } from 'pal/image'; +import { RawDataType } from '../types'; import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; export class ImageData extends BaseImageData { - public getRawData (): unknown { - let data; - if ('getContext' in this._imageSource) { - const canvasElem = this._imageSource; - const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this._imageSource.width, this._imageSource.height); + public getRawData (): RawDataType | null { + if (this.source == null) { + return null; + } + let data: ArrayBufferView | null = null; + if ('_data' in this.source) { + data = this.source._data; + } else if ('getContext' in this.source) { + const canvasElem = this.source; + const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this.source.width, this.source.height); const buff = imageData!.data.buffer; let rawBuffer; if ('buffer' in buff) { @@ -41,9 +47,8 @@ export class ImageData extends BaseImageData { rawBuffer = buff; data = new Uint8Array(rawBuffer); } - //buffers[i] = data; - } else if (this._imageSource instanceof HTMLImageElement || this._imageSource instanceof ImageBitmap) { - const img = this._imageSource; + } else if (this.source instanceof HTMLImageElement || this.source instanceof ImageBitmap) { + const img = this.source; const canvas = ccwindow.document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; @@ -53,7 +58,7 @@ export class ImageData extends BaseImageData { const buff = imageData!.data.buffer; let rawBuffer; if ('buffer' in buff) { - // es-lint as any + // es-lint as any data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); } else { rawBuffer = buff; @@ -63,26 +68,26 @@ export class ImageData extends BaseImageData { // eslint-disable-next-line no-console console.log('imageBmp copy not impled!'); } - return this.data; + return data; } - static loadImage (url: string): Promise { + static loadImage (urlOrBase64: string): Promise { return new Promise((resolve, reject) => { - const image = new ImageData(); + const image = new ccwindow.Image(); if (ccwindow.location.protocol !== 'file:') { image.crossOrigin = 'anonymous'; } image.onload = (): void => { - resolve(image); + const imageData = new ImageData(image); + resolve(imageData); }; image.onerror = (): void => { - reject(new Error(getError(4930, url))); + reject(new Error(getError(4930, urlOrBase64))); }; - image.src = url; - return image; + image.src = urlOrBase64; }); } } From 556caee6f19b7c95a005eb7c8f78d1e78a67455c Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Wed, 2 Aug 2023 18:34:34 +0800 Subject: [PATCH 17/24] Optimize the design and deprecate some of dom's interfaces. --- cocos/2d/assets/sprite-frame.ts | 16 ++- .../skinned-mesh-batch-renderer.ts | 17 +-- cocos/asset/asset-manager/builtin-res-mgr.ts | 14 +-- .../asset/asset-manager/download-dom-image.ts | 7 +- cocos/asset/asset-manager/parser.ts | 4 +- cocos/asset/assets/image-asset.jsb.ts | 26 ++--- cocos/asset/assets/image-asset.ts | 105 +++++++++++++----- cocos/asset/assets/simple-texture.jsb.ts | 15 ++- cocos/asset/assets/simple-texture.ts | 27 ++++- cocos/game/splash-screen.ts | 2 +- cocos/gfx/webgpu/webgpu-define.ts | 2 +- pal/env/web/env.ts | 2 +- pal/image/base-image-data.ts | 71 +++++++++--- pal/image/minigame/image-data.ts | 45 -------- pal/image/native/image-data.ts | 15 ++- pal/image/web/image-data.ts | 45 -------- platforms/native/engine/jsb-gfx.js | 2 +- 17 files changed, 222 insertions(+), 193 deletions(-) diff --git a/cocos/2d/assets/sprite-frame.ts b/cocos/2d/assets/sprite-frame.ts index 6042ef3d5b4..0ad83ebe717 100644 --- a/cocos/2d/assets/sprite-frame.ts +++ b/cocos/2d/assets/sprite-frame.ts @@ -27,7 +27,7 @@ import { ccclass } from 'cc.decorator'; import { EDITOR, TEST, BUILD } from 'internal:constants'; -import { ImageSource } from '../../../pal/image/types'; +import { IMemoryImageSource } from '../../../pal/image/types'; import { Mat4, Rect, Size, Vec2, Vec3, Vec4, cclegacy, errorID, warnID, js } from '../../core'; import { Asset } from '../../asset/assets/asset'; import { TextureBase } from '../../asset/assets/texture-base'; @@ -249,9 +249,19 @@ export class SpriteFrame extends Asset { * @param imageSourceOrImageAsset @en ImageAsset or ImageSource, ImageSource could be HTMLCanvasElement, HTMLImageElement, IMemoryImageSource. * @zh 图像资源或图像原始图像源,图像原始图像源支持 HTMLCanvasElement HTMLImageElement IMemoryImageSource 三种资源。 * @returns @en SpriteFrame asset. @zh 精灵资源。 + * @deprecated @en Recommended use of the ImageAsset object. @zh 推荐使用ImageAsset对象 */ - public static createWithImage (imageSourceOrImageAsset: ImageSource | ImageAsset): SpriteFrame { - const img = imageSourceOrImageAsset instanceof ImageAsset ? imageSourceOrImageAsset : new ImageAsset(imageSourceOrImageAsset); + public static createWithImage (imageSourceOrImageAsset: HTMLCanvasElement | HTMLImageElement | ImageBitmap): SpriteFrame; + /** + * @en Create a SpriteFrame object by an image asset or an native image asset. + * @zh 通过 Image 资源或者平台相关 Image 对象创建一个 SpriteFrame 资源。 + * @param imageSourceOrImageAsset @en ImageAsset or ImageSource, ImageSource could be HTMLCanvasElement, HTMLImageElement, IMemoryImageSource. + * @zh 图像资源或图像原始图像源,图像原始图像源支持 HTMLCanvasElement HTMLImageElement IMemoryImageSource 三种资源。 + * @returns @en SpriteFrame asset. @zh 精灵资源。 + */ + public static createWithImage (imageSourceOrImageAsset: ImageAsset | IMemoryImageSource): SpriteFrame; + public static createWithImage (imageSourceOrImageAsset: ImageAsset | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap): SpriteFrame { + const img = imageSourceOrImageAsset instanceof ImageAsset ? imageSourceOrImageAsset : new ImageAsset(imageSourceOrImageAsset as IMemoryImageSource); const tex = new Texture2D(); tex.image = img; const spf = new SpriteFrame(); diff --git a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts index 9e6b479bdf9..737334bca4d 100644 --- a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts +++ b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts @@ -36,6 +36,7 @@ import { CCString, Mat4, Vec2, Vec3, cclegacy, warn } from '../../core'; import { AttributeName, FormatInfos, Format, Type, Attribute, BufferTextureCopy } from '../../gfx'; import { mapBuffer, readBuffer, writeBuffer } from '../misc/buffer'; import { SkinnedMeshRenderer } from './skinned-mesh-renderer'; +import { ImageAsset } from '../../asset/assets'; const repeat = (n: number): number => n - Math.floor(n); const batch_id: Attribute = new Attribute(AttributeName.ATTR_BATCH_ID, Format.R32F); @@ -453,9 +454,8 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { } protected cookTextures (target: Texture2D, prop: string, passIdx: number): void { - const texImages: TexImageSource[] = []; + const texImages: ImageAsset[] = []; const texImageRegions: BufferTextureCopy[] = []; - const texBuffers: ArrayBufferView[] = []; const texBufferRegions: BufferTextureCopy[] = []; for (let u = 0; u < this.units.length; u++) { const unit = this.units[u]; @@ -467,20 +467,13 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { region.texOffset.y = unit.offset.y * this.atlasSize; region.texExtent.width = unit.size.x * this.atlasSize; region.texExtent.height = unit.size.y * this.atlasSize; - const { data } = partial.image; - if (!ArrayBuffer.isView(data)) { - texImages.push(data as TexImageSource); - texImageRegions.push(region); - } else { - texBuffers.push(data); - texBufferRegions.push(region); - } + texImages.push(partial.image); + texImageRegions.push(region); } } const gfxTex = target.getGFXTexture()!; const { device } = cclegacy.director.root!; - if (texBuffers.length > 0) { device.copyBuffersToTexture(texBuffers, gfxTex, texBufferRegions); } - if (texImages.length > 0) { device.copyTexImagesToTexture(texImages, gfxTex, texImageRegions); } + if (texImages.length > 0) { device.copyImagesToTexture(texImages, gfxTex, texImageRegions); } } protected createTexture (prop: string): Texture2D { diff --git a/cocos/asset/asset-manager/builtin-res-mgr.ts b/cocos/asset/asset-manager/builtin-res-mgr.ts index 0b55b095973..571f128165a 100644 --- a/cocos/asset/asset-manager/builtin-res-mgr.ts +++ b/cocos/asset/asset-manager/builtin-res-mgr.ts @@ -23,7 +23,7 @@ */ import { EDITOR, EDITOR_NOT_IN_PREVIEW, TEST } from 'internal:constants'; -import { ImageSource } from '../../../pal/image/types'; +import { IMemoryImageSource } from '../../../pal/image/types'; import { Asset } from '../assets/asset'; import { ImageAsset } from '../assets/image-asset'; import { SpriteFrame } from '../../2d/assets/sprite-frame'; @@ -120,7 +120,7 @@ export class BuiltinResMgr { offset += halfDefaultSize * numChannels; } - const blackMemImageSource: ImageSource = { + const blackMemImageSource: IMemoryImageSource = { width: len, height: len, _data: blackValueView, @@ -128,7 +128,7 @@ export class BuiltinResMgr { format: Texture2D.PixelFormat.RGBA8888, }; - const emptyMemImageSource: ImageSource = { + const emptyMemImageSource: IMemoryImageSource = { width: len, height: len, _data: emptyValueView, @@ -136,7 +136,7 @@ export class BuiltinResMgr { format: Texture2D.PixelFormat.RGBA8888, }; - const greyMemImageSource: ImageSource = { + const greyMemImageSource: IMemoryImageSource = { width: len, height: len, _data: greyValueView, @@ -144,7 +144,7 @@ export class BuiltinResMgr { format: Texture2D.PixelFormat.RGBA8888, }; - const whiteMemImageSource: ImageSource = { + const whiteMemImageSource: IMemoryImageSource = { width: len, height: len, _data: whiteValueView, @@ -152,7 +152,7 @@ export class BuiltinResMgr { format: Texture2D.PixelFormat.RGBA8888, }; - const normalMemImageSource: ImageSource = { + const normalMemImageSource: IMemoryImageSource = { width: len, height: len, _data: normalValueView, @@ -160,7 +160,7 @@ export class BuiltinResMgr { format: Texture2D.PixelFormat.RGBA8888, }; - const defaultMemImageSource: ImageSource = { + const defaultMemImageSource: IMemoryImageSource = { width: defaultSize, height: defaultSize, _data: defaultValueView, diff --git a/cocos/asset/asset-manager/download-dom-image.ts b/cocos/asset/asset-manager/download-dom-image.ts index a75c6307ae4..fb25c5bffb3 100644 --- a/cocos/asset/asset-manager/download-dom-image.ts +++ b/cocos/asset/asset-manager/download-dom-image.ts @@ -1,15 +1,19 @@ /* Copyright (c) 2013-2016 Chukong Technologies Inc. Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd. + http://www.cocos.com + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,7 +24,6 @@ */ import { ImageData } from 'pal/image'; -import { ImageSource } from '../../../pal/image/types'; export default function downloadDomImage ( url: string, @@ -29,7 +32,7 @@ export default function downloadDomImage ( ): void { ImageData.loadImage(url).then((imageData) => { onComplete(null, imageData); - }).catch((err) => { + }).catch((err: Error) => { onComplete(err); }); } diff --git a/cocos/asset/asset-manager/parser.ts b/cocos/asset/asset-manager/parser.ts index d29325bd158..6acf6f4fba4 100644 --- a/cocos/asset/asset-manager/parser.ts +++ b/cocos/asset/asset-manager/parser.ts @@ -23,7 +23,7 @@ */ import { ImageData } from 'pal/image'; -import { ImageSource, IMemoryImageSource } from '../../../pal/image/types'; +import { IMemoryImageSource } from '../../../pal/image/types'; import { ImageAsset } from '../assets/image-asset'; import { js, warn } from '../../core'; import Cache from './cache'; @@ -89,7 +89,7 @@ export class Parser { } createImageBitmap(file, { premultiplyAlpha: 'none' }).then((result): void => { onComplete(null, new ImageData(result)); - }, (err): void => { + }, (err: Error): void => { onComplete(err, null); }); } diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index 1efacb78fa5..87a99a00113 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -25,7 +25,7 @@ import { ALIPAY, XIAOMI, JSB, TEST, BAIDU, EDITOR } from 'internal:constants'; import { Format, FormatFeatureBit, deviceManager } from '../../gfx'; import { ImageData } from 'pal/image'; -import { ImageSource } from '../../../pal/image/types'; +import { IMemoryImageSource, ImageSource } from '../../../pal/image/types'; import { PixelFormat } from './asset-enum'; import { sys, macro, warnID, cclegacy, error } from '../../core'; import { patch_cc_ImageAsset } from '../../native-binding/decorators'; @@ -43,11 +43,15 @@ const extnames = ['.png', '.jpg', '.jpeg', '.bmp', '.webp', '.pvr', '.pkm', '.as // TODO: we mark imageAssetProto as type of any, because here we have many dynamic injected property @dumganhar const imageAssetProto: any = ImageAsset.prototype; -imageAssetProto._ctor = function (nativeAsset?: ImageSource) { +imageAssetProto._ctor = function (nativeAsset?: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { jsb.Asset.prototype._ctor.apply(this, arguments); this._width = 0; this._height = 0; - this._imageData = new ImageData(nativeAsset); + if (nativeAsset instanceof ImageData) { + this._imageData = nativeAsset; + } else { + this._imageData = new ImageData(nativeAsset); + } }; Object.defineProperty(imageAssetProto, '_nativeAsset', { @@ -56,7 +60,7 @@ Object.defineProperty(imageAssetProto, '_nativeAsset', { get () { return this._imageData.source; }, - set (value: ImageSource) { + set (value: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { if (value instanceof ImageData) { this._imageData = value; this._syncDataToNative(); @@ -67,18 +71,6 @@ Object.defineProperty(imageAssetProto, '_nativeAsset', { }); Object.defineProperty(imageAssetProto, 'data', { - configurable: true, - enumerable: true, - get () { - if ('_data' in this._imageData.source) { - return this._imageData.getRawData() as ArrayBufferView; - } else { - return this._imageData.source; - } - }, -}); - -Object.defineProperty(imageAssetProto, 'rawData', { configurable: true, enumerable: true, get () { @@ -96,7 +88,7 @@ imageAssetProto._setRawAsset = function (filename: string, inLibrary = true) { // TODO: Property 'format' does not exist on type 'HTMLCanvasElement'. // imageAssetProto.reset = function (data: ImageSource) { -imageAssetProto.reset = function (data: ImageSource | ArrayBufferView) { +imageAssetProto.reset = function (data: IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { this._imageData.reset(data); if ('_data' in data) { const format = data.format; diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index eef6b0cd997..954034cb2d4 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -27,7 +27,7 @@ import { ccclass, override } from 'cc.decorator'; import { EDITOR, ALIPAY, XIAOMI, JSB, TEST, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { ImageData } from 'pal/image'; import { ImageSource, IMemoryImageSource, RawDataType } from '../../../pal/image/types'; -import { Device, Format, FormatFeatureBit, deviceManager } from '../../gfx'; +import { Device, Format, FormatFeatureBit, deviceManager, API } from '../../gfx'; import { Asset } from './asset'; import { PixelFormat } from './asset-enum'; import { warnID, macro, sys, cclegacy, warn } from '../../core'; @@ -193,8 +193,12 @@ export class ImageAsset extends Asset { let dataOffset = fileHeaderLength; for (let i = 0; i < files.length; i++) { const file = files[i]; - outView.setUint32(COMPRESSED_HEADER_LENGTH + COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + i * COMPRESSED_MIPMAP_DATA_SIZE_LENGTH, - file.byteLength, true); //add file data size + outView.setUint32( + COMPRESSED_HEADER_LENGTH + COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + i * COMPRESSED_MIPMAP_DATA_SIZE_LENGTH, + file.byteLength, + + true, + ); //add file data size // Append compresssed file if (file instanceof ArrayBuffer) { @@ -267,8 +271,14 @@ export class ImageAsset extends Asset { * @param out @zh 压缩纹理输出。 * @engineInternal */ - public static parseCompressedTexture (file: ArrayBuffer | ArrayBufferView, levelIndex: number, - beginOffset: number, endOffset: number, type: number, out: IMemoryImageSource): void { + public static parseCompressedTexture ( + file: ArrayBuffer | ArrayBufferView, + levelIndex: number, + beginOffset: number, + endOffset: number, + type: number, + out: IMemoryImageSource, + ): void { switch (type) { case compressType.PVR: ImageAsset.parsePVRTexture(file, levelIndex, beginOffset, endOffset, out); @@ -293,8 +303,13 @@ export class ImageAsset extends Asset { * @param out @zh 压缩纹理输出。 * @engineInternal */ - public static parsePVRTexture (file: ArrayBuffer | ArrayBufferView, levelIndex: number, - beginOffset: number, endOffset: number, out: IMemoryImageSource): void { + public static parsePVRTexture ( + file: ArrayBuffer | ArrayBufferView, + levelIndex: number, + beginOffset: number, + endOffset: number, + out: IMemoryImageSource, + ): void { const buffer = file instanceof ArrayBuffer ? file : file.buffer; // Get a view of the arrayBuffer that represents the DDS header. const header = new Int32Array(buffer, beginOffset, PVR_HEADER_LENGTH); @@ -345,8 +360,13 @@ export class ImageAsset extends Asset { * @param out @zh 压缩纹理输出。 * @engineInternal */ - public static parsePKMTexture (file: ArrayBuffer | ArrayBufferView, levelIndex: number, - beginOffset: number, endOffset: number, out: IMemoryImageSource): void { + public static parsePKMTexture ( + file: ArrayBuffer | ArrayBufferView, + levelIndex: number, + beginOffset: number, + endOffset: number, + out: IMemoryImageSource, + ): void { const buffer = file instanceof ArrayBuffer ? file : file.buffer; const header = new Uint8Array(buffer, beginOffset, ETC_PKM_HEADER_LENGTH); const format = readBEUint16(header, ETC_PKM_FORMAT_OFFSET); @@ -379,8 +399,13 @@ export class ImageAsset extends Asset { * @param out @zh 压缩纹理输出。 * @engineInternal */ - public static parseASTCTexture (file: ArrayBuffer | ArrayBufferView, levelIndex: number, - beginOffset: number, endOffset: number, out: IMemoryImageSource): void { + public static parseASTCTexture ( + file: ArrayBuffer | ArrayBufferView, + levelIndex: number, + beginOffset: number, + endOffset: number, + out: IMemoryImageSource, + ): void { const buffer = file instanceof ArrayBuffer ? file : file.buffer; const header = new Uint8Array(buffer, beginOffset, ASTC_HEADER_LENGTH); @@ -493,7 +518,8 @@ export class ImageAsset extends Asset { if (value instanceof ImageData) { this._imageData = value; } else { - this.reset(value as ImageSource); + // This is a hack method, otherwise ts will just report an error. + this.reset(value as IMemoryImageSource); } } @@ -501,22 +527,18 @@ export class ImageAsset extends Asset { * @en Image data source(include: HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView). * @zh 图像的来源(包括:HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView)。 */ - get data (): ImageSource | ArrayBufferView | null { - if ('_data' in this._imageData.source) { - return this._imageData.getRawData() as ArrayBufferView; + get data (): ImageSource | RawDataType | null { + if (deviceManager.gfxDevice.gfxAPI === API.WEBGL || deviceManager.gfxDevice.gfxAPI === API.WEBGL2) { + if ('_data' in this._imageData.source) { + return this._imageData.getRawData(); + } else { + return this._imageData.source; + } } else { - return this._imageData.source; + return this._imageData.getRawData(); } } - /* - * @en Raw image data. - * @zh 原始图像数据。 - */ - get rawData (): RawDataType | null { - return this._imageData.getRawData(); - } - /** * @en The pixel width of the image. * @zh 此图像资源的像素宽度。 @@ -538,7 +560,7 @@ export class ImageAsset extends Asset { * @zh 此图像资源的像素格式。 */ get format (): PixelFormat { - return (this.data as IMemoryImageSource).format; + return (this._nativeAsset as IMemoryImageSource).format; } /** @@ -556,7 +578,7 @@ export class ImageAsset extends Asset { * @engineInternal */ get mipmapLevelDataSize (): number[] | undefined { - return (this.data as IMemoryImageSource).mipmapLevelDataSize; + return (this._nativeAsset as IMemoryImageSource).mipmapLevelDataSize; } /** @@ -580,21 +602,46 @@ export class ImageAsset extends Asset { private _height = 0; - constructor (nativeAsset?: ImageSource) { + /** + * @en Constructing an ImageSource object. + * @zh 构造ImageSource对象 + * @param data @en The image source. @zh 图像数据源。 + * @deprecated please use imagedata structure. + */ + constructor (nativeAsset: HTMLCanvasElement | HTMLImageElement | ImageBitmap); + /** + * @en Constructing an ImageSource object. + * @zh 构造ImageSource对象 + * @param data @en The image source. @zh 图像数据源。 + */ + constructor (nativeAsset?: ImageData | IMemoryImageSource); + constructor (nativeAsset?: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { super(); if (EDITOR) { this._exportedExts = null; } - this._imageData = new ImageData(nativeAsset); + if (nativeAsset instanceof ImageData) { + this._imageData = nativeAsset; + } else { + this._imageData = new ImageData(nativeAsset); + } } + /** + * @en Reset the source of the image asset. + * @zh 重置此图像资源使用的原始图像源。 + * @param data @en The new source. @zh 新的图片数据源。 + * @deprecated @en Recommended use of the IMemoryImageSource object. @zh 推荐使用IMemoryImageSource对象 + */ + public reset (data: HTMLCanvasElement | HTMLImageElement | ImageBitmap): void; /** * @en Reset the source of the image asset. * @zh 重置此图像资源使用的原始图像源。 * @param data @en The new source. @zh 新的图片数据源。 */ - public reset (data: ImageSource): void { + public reset (data: IMemoryImageSource): void; + public reset (data: IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap): void { this._imageData.reset(data); if ('_data' in data) { const format = data.format; diff --git a/cocos/asset/assets/simple-texture.jsb.ts b/cocos/asset/assets/simple-texture.jsb.ts index a6a927a8ad1..209f3de54b3 100644 --- a/cocos/asset/assets/simple-texture.jsb.ts +++ b/cocos/asset/assets/simple-texture.jsb.ts @@ -27,7 +27,7 @@ import { js, macro, cclegacy } from '../../core'; import './texture-base'; import { patch_cc_SimpleTexture } from '../../native-binding/decorators'; import type { SimpleTexture as JsbSimpleTexture } from './simple-texture'; -import { ImageData } from 'pal/image'; +import { ImageAsset } from './image-asset'; declare const jsb: any; @@ -43,8 +43,17 @@ SimpleTexture.WrapMode = WrapMode; const simpleTextureProto = jsb.SimpleTexture.prototype; const oldUpdateDataFunc = simpleTextureProto.uploadData; simpleTextureProto.uploadData = function (source, level = 0, arrayIndex = 0) { - let imageData = new ImageData(source); - oldUpdateDataFunc.call(this, imageData.getRawData(), level, arrayIndex); + if(ArrayBuffer.isView(source)) { + oldUpdateDataFunc.call(this, source, level, arrayIndex); + } else { + let imageAsset; + if (source instanceof ImageAsset) { + imageAsset = source; + } else { + imageAsset = new ImageAsset(source); + } + oldUpdateDataFunc.call(this, imageAsset.data, level, arrayIndex); + } }; simpleTextureProto._ctor = function () { diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index c651411205f..c5e5f4f8b3c 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -24,7 +24,7 @@ import { ccclass } from 'cc.decorator'; import { DEV } from 'internal:constants'; -import { ImageSource } from '../../../pal/image/types'; +import { IMemoryImageSource } from '../../../pal/image/types'; import { TextureFlagBit, TextureUsageBit, API, Texture, TextureInfo, TextureViewInfo, Device, BufferTextureCopy } from '../../gfx'; import { assertID, error, js, macro, cclegacy } from '../../core'; @@ -141,8 +141,28 @@ export class SimpleTexture extends TextureBase { * @param source @en The source image or image data. @zh 源图像或图像数据。 * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 * @param arrayIndex @en The array index. @zh 要上传的数组索引。 + * @deprecated since v3.9, please use `uploadData()` instead. */ - public uploadData (source: ImageSource | ArrayBufferView | ImageAsset, level = 0, arrayIndex = 0): void { + public uploadData (source: HTMLCanvasElement | HTMLImageElement | ImageBitmap, level, arrayIndex): void; + /** + * @en Upload data to the given mipmap level. + * The size of the image will affect how the mipmap is updated. + * - When the image is an ArrayBuffer, the size of the image must match the mipmap size. + * - If the image size matches the mipmap size, the mipmap data will be updated entirely. + * - If the image size is smaller than the mipmap size, the mipmap will be updated from top left corner. + * - If the image size is larger, an error will be raised + * @zh 上传图像数据到指定层级的 Mipmap 中。 + * 图像的尺寸影响 Mipmap 的更新范围: + * - 当图像是 `ArrayBuffer` 时,图像的尺寸必须和 Mipmap 的尺寸一致;否则, + * - 若图像的尺寸与 Mipmap 的尺寸相同,上传后整个 Mipmap 的数据将与图像数据一致; + * - 若图像的尺寸小于指定层级 Mipmap 的尺寸(不管是长或宽),则从贴图左上角开始,图像尺寸范围内的 Mipmap 会被更新; + * - 若图像的尺寸超出了指定层级 Mipmap 的尺寸(不管是长或宽),都将引起错误。 + * @param source @en The source image or image data. @zh 源图像或图像数据。 + * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 + * @param arrayIndex @en The array index. @zh 要上传的数组索引。 + */ + public uploadData (source: ImageAsset | IMemoryImageSource | ArrayBufferView, level, arrayIndex): void; + public uploadData (source: ImageAsset | IMemoryImageSource | ArrayBufferView | HTMLCanvasElement | HTMLImageElement | ImageBitmap, level = 0, arrayIndex = 0): void { if (!this._gfxTexture || this._mipmapLevel <= level) { return; } @@ -164,7 +184,8 @@ export class SimpleTexture extends TextureBase { if (source instanceof ImageAsset) { imageAsset = source; } else { - imageAsset = new ImageAsset(source); + // This is a hack method, otherwise ts will just report an error. + imageAsset = new ImageAsset(source as IMemoryImageSource); } gfxDevice.copyImagesToTexture([imageAsset], this._gfxTexture, _regions); } diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index f96ab9023a0..af39ca0beab 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -23,7 +23,7 @@ */ import { EDITOR, TAOBAO } from 'internal:constants'; -import { ImageData, ImageSource } from 'pal/image'; +import { ImageData } from 'pal/image'; import { Material } from '../asset/assets/material'; import { clamp01, Mat4, Vec2, Settings, settings, sys, cclegacy, easing, preTransforms } from '../core'; import { diff --git a/cocos/gfx/webgpu/webgpu-define.ts b/cocos/gfx/webgpu/webgpu-define.ts index 4e1d58044e6..22ff2e40d4e 100644 --- a/cocos/gfx/webgpu/webgpu-define.ts +++ b/cocos/gfx/webgpu/webgpu-define.ts @@ -201,7 +201,7 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { Device.prototype.copyImagesToTexture = function (imageAssets: ImageAsset[], texture: typeof Texture, regions: BufferTextureCopy[]) { const buffers: Uint8Array[] = []; for (let i = 0; i < regions.length; i++) { - buffers.push(imageAssets[i].rawData); + buffers.push(imageAssets[i].data); } oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); } diff --git a/pal/env/web/env.ts b/pal/env/web/env.ts index 4a8c625e2f5..e0aee6672a7 100644 --- a/pal/env/web/env.ts +++ b/pal/env/web/env.ts @@ -29,7 +29,7 @@ export function findCanvas (): { frame: HTMLDivElement, container: HTMLDivElemen const container = document.querySelector('#Cocos3dGameContainer') as HTMLDivElement; const canvas = document.querySelector('#GameCanvas') as HTMLCanvasElement; - return { frame, canvas, container }; + return { frame, container, canvas }; } export function loadJsFile (path: string): Promise { diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index c67e586e7e3..efe2de0eb7d 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -21,14 +21,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { IMemoryImageSource, ImageSource } from './types'; +import { RawDataType, ImageSource } from './types'; import { sys } from '../../cocos/core/platform/sys'; +import { deviceManager, API } from '../../cocos/gfx'; +import { ccwindow } from '../../cocos/core/global-exports'; export class BaseImageData { // TODO(qgh):Designed for compatibility, may be removed in the future. protected _source: ImageSource; - constructor (imageAsset?: ImageSource | ArrayBufferView) { + constructor (source?: ImageSource | ArrayBufferView) { this._source = { _data: null, width: 0, @@ -37,26 +39,26 @@ export class BaseImageData { _compressed: false, mipmapLevelDataSize: [], }; - if (typeof imageAsset !== 'undefined') { - if (!ArrayBuffer.isView(imageAsset)) { - this._source = imageAsset; + if (typeof source !== 'undefined') { + if (!ArrayBuffer.isView(source)) { + this._source = source; } else { - this._source._data = imageAsset; + this._source._data = source; } } } public destroy (): void { - if (this.source && this.source instanceof HTMLImageElement) { - this.source.src = ''; - } else if (this.isImageBitmap(this.source)) { - this.source?.close(); + if (this._source && this._source instanceof HTMLImageElement) { + this._source.src = ''; + } else if (this.isImageBitmap(this._source)) { + this._source?.close(); } } - set source (imageAsset: ImageSource | null) { - if (imageAsset != null) { - this.reset(imageAsset); + set source (source: ImageSource | null) { + if (source != null) { + this.reset(source); } } @@ -90,6 +92,49 @@ export class BaseImageData { } } + public getRawData (): RawDataType | null { + if (this._source == null) { + return null; + } + let data: ArrayBufferView | null = null; + if ('_data' in this._source) { + data = this._source._data; + } else if ('getContext' in this._source) { + const canvasElem = this._source; + const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this._source.width, this._source.height); + const buff = imageData!.data.buffer; + let rawBuffer; + if ('buffer' in buff) { + // es-lint as any + data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); + } else { + rawBuffer = buff; + data = new Uint8Array(rawBuffer); + } + } else if (this._source instanceof HTMLImageElement || this._source instanceof ImageBitmap) { + const img = this._source; + const canvas = ccwindow.document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const ctx = canvas.getContext('2d'); + ctx?.drawImage(img as any, 0, 0); + const imageData = ctx?.getImageData(0, 0, img.width, img.height); + const buff = imageData!.data.buffer; + let rawBuffer; + if ('buffer' in buff) { + // es-lint as any + data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); + } else { + rawBuffer = buff; + data = new Uint8Array(rawBuffer); + } + } else { + // eslint-disable-next-line no-console + console.log('imageBmp copy not impled!'); + } + return data; + } + private isImageBitmap (imageSource: any): imageSource is ImageBitmap { return !!(sys.hasFeature(sys.Feature.IMAGE_BITMAP) && imageSource instanceof ImageBitmap); } diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index 8d63e310381..46c45e63704 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -23,55 +23,10 @@ */ import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { BaseImageData } from '../base-image-data'; -import { ImageSource, RawDataType } from '../types'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; -export type ImageDataType = ArrayBufferView; export class ImageData extends BaseImageData { - public getRawData (): RawDataType | null { - if (this.source == null) { - return null; - } - let data: ArrayBufferView | null = null; - if ('_data' in this.source) { - data = this.source._data; - } else if ('getContext' in this.source) { - const canvasElem = this.source; - const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this.source.width, this.source.height); - const buff = imageData!.data.buffer; - let rawBuffer; - if ('buffer' in buff) { - // es-lint as any - data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); - } else { - rawBuffer = buff; - data = new Uint8Array(rawBuffer); - } - } else if (this.source instanceof HTMLImageElement || this.source instanceof ImageBitmap) { - const img = this.source; - const canvas = ccwindow.document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - ctx?.drawImage(img as any, 0, 0); - const imageData = ctx?.getImageData(0, 0, img.width, img.height); - const buff = imageData!.data.buffer; - let rawBuffer; - if ('buffer' in buff) { - // es-lint as any - data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); - } else { - rawBuffer = buff; - data = new Uint8Array(rawBuffer); - } - } else { - // eslint-disable-next-line no-console - console.log('imageBmp copy not impled!'); - } - return data; - } - static loadImage (url: string): Promise { return new Promise((resolve, reject) => { const image = new ccwindow.Image(); diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 30062bc9249..aad01e2b9e9 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -23,7 +23,7 @@ */ import { BaseImageData } from '../base-image-data'; import { ImageSource, IMemoryImageSource, RawDataType } from '../types'; -import { getError } from '../../../cocos/core'; +import { getError, override, assert } from '../../../cocos/core'; declare const jsb: any; @@ -34,25 +34,22 @@ export class ImageData extends BaseImageData { this.reset(imageAsset); } + @override public destroy (): void { if (this._rawData instanceof jsb.JSBNativeDataHolder) { + // JSB element should destroy native data. jsb.destroyImage(this._rawData); } - // if (this.imageSource && this.imageSource instanceof HTMLImageElement) { - // // JSB element should destroy native data. - // // TODO: Property 'destroy' does not exist on type 'HTMLImageElement'. - // // maybe we need a higher level implementation called `pal/image`, we provide `destroy` interface here. - // // issue: https://github.com/cocos/cocos-engine/issues/14646 - // (this.imageSource as any).destroy(); - // } super.destroy(); } + @override public getRawData (): RawDataType | null { // TODO(qgh) :ImageBitmap without raw data. return this._rawData; } + @override public reset (imageSource?: ImageSource | ArrayBufferView): void { if (imageSource == null) { this._rawData = null; @@ -67,6 +64,8 @@ export class ImageData extends BaseImageData { this._rawData = imageSource; } else if ('_data' in imageSource) { this._rawData = imageSource._data; + } else { + assert(false, 'ImageBitmap has no raw data!'); } super.reset(imageSource); } diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index 78ff97e9e07..9205d8a996b 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -21,56 +21,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { ImageSource } from 'pal/image'; -import { RawDataType } from '../types'; import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; import { getError } from '../../../cocos/core'; export class ImageData extends BaseImageData { - public getRawData (): RawDataType | null { - if (this.source == null) { - return null; - } - let data: ArrayBufferView | null = null; - if ('_data' in this.source) { - data = this.source._data; - } else if ('getContext' in this.source) { - const canvasElem = this.source; - const imageData = canvasElem.getContext('2d')?.getImageData(0, 0, this.source.width, this.source.height); - const buff = imageData!.data.buffer; - let rawBuffer; - if ('buffer' in buff) { - // es-lint as any - data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); - } else { - rawBuffer = buff; - data = new Uint8Array(rawBuffer); - } - } else if (this.source instanceof HTMLImageElement || this.source instanceof ImageBitmap) { - const img = this.source; - const canvas = ccwindow.document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - const ctx = canvas.getContext('2d'); - ctx?.drawImage(img as any, 0, 0); - const imageData = ctx?.getImageData(0, 0, img.width, img.height); - const buff = imageData!.data.buffer; - let rawBuffer; - if ('buffer' in buff) { - // es-lint as any - data = new Uint8Array((buff as any).buffer, (buff as any).byteOffset, (buff as any).byteLength); - } else { - rawBuffer = buff; - data = new Uint8Array(rawBuffer); - } - } else { - // eslint-disable-next-line no-console - console.log('imageBmp copy not impled!'); - } - return data; - } - static loadImage (urlOrBase64: string): Promise { return new Promise((resolve, reject) => { const image = new ccwindow.Image(); diff --git a/platforms/native/engine/jsb-gfx.js b/platforms/native/engine/jsb-gfx.js index de92befde19..bfbad608886 100644 --- a/platforms/native/engine/jsb-gfx.js +++ b/platforms/native/engine/jsb-gfx.js @@ -60,7 +60,7 @@ deviceProto.copyImagesToTexture = function (imageAssets, texture, regions) { if (imageAssets) { for (let i = 0; i < imageAssets.length; ++i) { const image = imageAssets[i]; - images.push(image.rawData); + images.push(image.data); } } oldCopyTexImagesToTextureFunc.call(this, images, texture, regions); From 7315c72c57b39122840c8f2cd48546cbe73708e6 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 7 Aug 2023 13:40:48 +0800 Subject: [PATCH 18/24] (1) Do not use console.log. (2) Modify the comment --- cocos/asset/asset-manager/builtin-res-mgr.jsb.ts | 4 ++-- cocos/asset/assets/image-asset.ts | 16 ++++++++-------- cocos/asset/assets/simple-texture.ts | 2 +- pal/image/base-image-data.ts | 5 ++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts b/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts index 70495fcd06b..a05df5cc4d6 100644 --- a/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts +++ b/cocos/asset/asset-manager/builtin-res-mgr.jsb.ts @@ -24,7 +24,7 @@ import { TEST, EDITOR, EDITOR_NOT_IN_PREVIEW } from 'internal:constants'; import { SpriteFrame } from '../../2d/assets/sprite-frame'; -import { ImageSource } from '../../../pal/image/types'; +import type { IMemoryImageSource } from '../../../pal/image/types'; import assetManager from '../asset-manager/asset-manager'; import { BuiltinBundleName } from '../asset-manager/shared'; import Bundle from '../asset-manager/bundle'; @@ -56,7 +56,7 @@ builtinResMgrProto.init = function () { blackValueView[offset + 3] = 255; } - const blackMemImageSource: ImageSource = { + const blackMemImageSource: IMemoryImageSource = { width: len, height: len, _data: blackValueView, diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 954034cb2d4..572722ad434 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -606,25 +606,25 @@ export class ImageAsset extends Asset { * @en Constructing an ImageSource object. * @zh 构造ImageSource对象 * @param data @en The image source. @zh 图像数据源。 - * @deprecated please use imagedata structure. + * @deprecated @en please use `constructor(imageSource?: ImageData | IMemoryImageSource)` instead. */ - constructor (nativeAsset: HTMLCanvasElement | HTMLImageElement | ImageBitmap); + constructor (imageSource: HTMLCanvasElement | HTMLImageElement | ImageBitmap); /** * @en Constructing an ImageSource object. * @zh 构造ImageSource对象 * @param data @en The image source. @zh 图像数据源。 */ - constructor (nativeAsset?: ImageData | IMemoryImageSource); - constructor (nativeAsset?: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { + constructor (imageSource?: ImageData | IMemoryImageSource); + constructor (imageSource?: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { super(); if (EDITOR) { this._exportedExts = null; } - if (nativeAsset instanceof ImageData) { - this._imageData = nativeAsset; + if (imageSource instanceof ImageData) { + this._imageData = imageSource; } else { - this._imageData = new ImageData(nativeAsset); + this._imageData = new ImageData(imageSource); } } @@ -632,7 +632,7 @@ export class ImageAsset extends Asset { * @en Reset the source of the image asset. * @zh 重置此图像资源使用的原始图像源。 * @param data @en The new source. @zh 新的图片数据源。 - * @deprecated @en Recommended use of the IMemoryImageSource object. @zh 推荐使用IMemoryImageSource对象 + * @deprecated @en please use `reset(data: IMemoryImageSource)` instead. @zh 请使用`reset(data: IMemoryImageSource)`替换 */ public reset (data: HTMLCanvasElement | HTMLImageElement | ImageBitmap): void; /** diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index c5e5f4f8b3c..7aee53344a9 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -141,7 +141,7 @@ export class SimpleTexture extends TextureBase { * @param source @en The source image or image data. @zh 源图像或图像数据。 * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 * @param arrayIndex @en The array index. @zh 要上传的数组索引。 - * @deprecated since v3.9, please use `uploadData()` instead. + * @deprecated since v3.9, please use `uploadData (source: ImageAsset | IMemoryImageSource | ArrayBufferView, level, arrayIndex)` instead. */ public uploadData (source: HTMLCanvasElement | HTMLImageElement | ImageBitmap, level, arrayIndex): void; /** diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index efe2de0eb7d..342eb9e8efb 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -23,8 +23,8 @@ */ import { RawDataType, ImageSource } from './types'; import { sys } from '../../cocos/core/platform/sys'; -import { deviceManager, API } from '../../cocos/gfx'; import { ccwindow } from '../../cocos/core/global-exports'; +import { assert } from '../../cocos/core/platform/debug'; export class BaseImageData { // TODO(qgh):Designed for compatibility, may be removed in the future. @@ -129,8 +129,7 @@ export class BaseImageData { data = new Uint8Array(rawBuffer); } } else { - // eslint-disable-next-line no-console - console.log('imageBmp copy not impled!'); + assert(false, 'ImageBitmap has no raw data!'); } return data; } From 8d4ce62626f83d593107d996f3d20d042130f424 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 7 Aug 2023 13:56:19 +0800 Subject: [PATCH 19/24] Modify the comment --- cocos/asset/assets/image-asset.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index 572722ad434..bd4801ef897 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -524,8 +524,16 @@ export class ImageAsset extends Asset { } /** - * @en Image data source(include: HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView). - * @zh 图像的来源(包括:HTMLCanvasElement | HTMLImageElement | IMemoryImageSource | ImageBitmap | ArrayBufferView)。 + * @en Return image data. + * If using a webgl backend: + * - Return source if source is HTMLCanvasElement | HTMLImageElement | ImageBitmap. + * - If source is IMemoryImageSource then return raw image data. + * Other rendering backends that return raw image data. + * @zh 返回图像数据。 + * 如果是webgl的渲染后端: + * - 如果source是HTMLCanvasElement | HTMLImageElement | ImageBitmap则直接返回source. + * - 如果source是IMemoryImageSource则返回原始图像数据. + * 其他的渲染后端,返回的都是原始图像数据。 */ get data (): ImageSource | RawDataType | null { if (deviceManager.gfxDevice.gfxAPI === API.WEBGL || deviceManager.gfxDevice.gfxAPI === API.WEBGL2) { From e7bc49f57d121cebd1cc8a3a14c703d8a139ea0f Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 7 Aug 2023 16:57:11 +0800 Subject: [PATCH 20/24] Remove empty lines --- cocos/asset/assets/image-asset.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index bd4801ef897..b9e440cfe4e 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -196,7 +196,6 @@ export class ImageAsset extends Asset { outView.setUint32( COMPRESSED_HEADER_LENGTH + COMPRESSED_MIPMAP_LEVEL_COUNT_LENGTH + i * COMPRESSED_MIPMAP_DATA_SIZE_LENGTH, file.byteLength, - true, ); //add file data size From 33eca300ccf0bd21323805d7f2861a8a858eae35 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Thu, 10 Aug 2023 10:15:23 +0800 Subject: [PATCH 21/24] gfx does not rely on imageasset, it relies on imagedata. --- @types/pal/image.d.ts | 12 +++++++----- cocos/2d/assembler/label/font-utils.ts | 4 ++-- cocos/2d/utils/dynamic-atlas/atlas.ts | 2 +- .../skinned-mesh-batch-renderer.ts | 8 ++++---- cocos/asset/assets/image-asset.jsb.ts | 12 ++++++++++-- cocos/asset/assets/image-asset.ts | 12 ++++++++++-- cocos/asset/assets/simple-texture.ts | 9 +++++---- cocos/core/platform/index.ts | 2 ++ cocos/game/splash-screen.ts | 4 ++-- cocos/gfx/base/device.ts | 8 ++++---- cocos/gfx/empty/empty-device.ts | 3 ++- cocos/gfx/webgl/webgl-device.ts | 12 ++++++------ cocos/gfx/webgl2/webgl2-device.ts | 13 ++++++------- cocos/gfx/webgpu/webgpu-define.ts | 6 +++--- pal/image/base-image-data.ts | 5 +++-- pal/image/minigame/image-data.ts | 2 +- pal/image/native/image-data.ts | 8 +++++--- pal/image/web/image-data.ts | 4 ++-- platforms/native/engine/jsb-gfx.js | 8 ++++---- 19 files changed, 79 insertions(+), 55 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index f637a2ded90..cad77c7c6ea 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -11,9 +11,16 @@ declare module 'pal/image' { */ destroy (): void; + /** + * @en Get raw image data.Return `ImageBitmap` if source is `ImageBitmap`. + * @zh 获取原始图像数据。如果source是`ImageBitmap`类型,则返回ImageBitmap。 + */ + get data(): RawDataType | null; + /** * Get and set image data source. * @param value Image data source. + * @engineinternal */ get source(): ImageSource; set source(value: ImageSource); @@ -33,11 +40,6 @@ declare module 'pal/image' { */ static loadImage (urlAndBase64: string): Promise - /** - * Get raw image data, in web platform is image source(like data interface), in native platform is image raw data. - */ - getRawData (): RawDataType | null; - /** * Set image data source. * @param data Image data source. diff --git a/cocos/2d/assembler/label/font-utils.ts b/cocos/2d/assembler/label/font-utils.ts index 39c260195fe..e2e4b9288d5 100644 --- a/cocos/2d/assembler/label/font-utils.ts +++ b/cocos/2d/assembler/label/font-utils.ts @@ -23,7 +23,7 @@ */ import { FontAtlas } from '../../assets/bitmap-font'; -import { Color, macro, warn, warnID } from '../../../core'; +import { Color, macro, warn, warnID, ImageData } from '../../../core'; import { ImageAsset, Texture2D } from '../../../asset/assets'; import { PixelFormat } from '../../../asset/assets/asset-enum'; import { BufferTextureCopy } from '../../../gfx'; @@ -260,7 +260,7 @@ export class LetterRenderTexture extends Texture2D { region.texOffset.y = y; region.texExtent.width = image.width; region.texExtent.height = image.height; - gfxDevice.copyImagesToTexture([image], gfxTexture, [region]); + gfxDevice.copyImageDatasToTexture([image.imageData], gfxTexture, [region]); } } diff --git a/cocos/2d/utils/dynamic-atlas/atlas.ts b/cocos/2d/utils/dynamic-atlas/atlas.ts index c45bc881c98..176fda8b0ec 100644 --- a/cocos/2d/utils/dynamic-atlas/atlas.ts +++ b/cocos/2d/utils/dynamic-atlas/atlas.ts @@ -263,6 +263,6 @@ export class DynamicAtlasTexture extends Texture2D { region.texOffset.y = y; region.texExtent.width = image.width; region.texExtent.height = image.height; - gfxDevice.copyImagesToTexture([image], gfxTexture, [region]); + gfxDevice.copyImageDatasToTexture([image.imageData], gfxTexture, [region]); } } diff --git a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts index 737334bca4d..df57e47da51 100644 --- a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts +++ b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts @@ -26,6 +26,7 @@ import { EDITOR } from 'internal:constants'; import { ccclass, help, executeInEditMode, executionOrder, menu, tooltip, type, visible, override, serializable, editable, } from 'cc.decorator'; +import { ImageData } from 'pal/image'; import { getWorldTransformUntilRoot } from '../../animation/transform-utils'; import { Filter, PixelFormat } from '../../asset/assets/asset-enum'; import { Material } from '../../asset/assets/material'; @@ -36,7 +37,6 @@ import { CCString, Mat4, Vec2, Vec3, cclegacy, warn } from '../../core'; import { AttributeName, FormatInfos, Format, Type, Attribute, BufferTextureCopy } from '../../gfx'; import { mapBuffer, readBuffer, writeBuffer } from '../misc/buffer'; import { SkinnedMeshRenderer } from './skinned-mesh-renderer'; -import { ImageAsset } from '../../asset/assets'; const repeat = (n: number): number => n - Math.floor(n); const batch_id: Attribute = new Attribute(AttributeName.ATTR_BATCH_ID, Format.R32F); @@ -454,7 +454,7 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { } protected cookTextures (target: Texture2D, prop: string, passIdx: number): void { - const texImages: ImageAsset[] = []; + const texImageDatas: ImageData[] = []; const texImageRegions: BufferTextureCopy[] = []; const texBufferRegions: BufferTextureCopy[] = []; for (let u = 0; u < this.units.length; u++) { @@ -467,13 +467,13 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { region.texOffset.y = unit.offset.y * this.atlasSize; region.texExtent.width = unit.size.x * this.atlasSize; region.texExtent.height = unit.size.y * this.atlasSize; - texImages.push(partial.image); + texImageDatas.push(partial.image.imageData); texImageRegions.push(region); } } const gfxTex = target.getGFXTexture()!; const { device } = cclegacy.director.root!; - if (texImages.length > 0) { device.copyImagesToTexture(texImages, gfxTex, texImageRegions); } + if (texImageDatas.length > 0) { device.copyImageDatasToTexture(texImageDatas, gfxTex, texImageRegions); } } protected createTexture (prop: string): Texture2D { diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index 87a99a00113..46d8687d860 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -74,10 +74,18 @@ Object.defineProperty(imageAssetProto, 'data', { configurable: true, enumerable: true, get () { - return this._imageData.getRawData(); + return this._imageData.data; }, }); +Object.defineProperty(imageAssetProto, 'imageData', { + configurable: true, + enumerable: true, + get () { + return this._imageData; + } +}); + imageAssetProto._setRawAsset = function (filename: string, inLibrary = true) { if (inLibrary !== false) { this._native = filename || ''; @@ -131,7 +139,7 @@ imageAssetProto._syncDataToNative = function () { this.setHeight(this._height); this.url = this.nativeUrl; - this.setData(this._imageData.getRawData()); + this.setData(this._imageData.data); if (data._mipmapLevelDataSize){ this.setMipmapLevelDataSize(data._mipmapLevelDataSize); } diff --git a/cocos/asset/assets/image-asset.ts b/cocos/asset/assets/image-asset.ts index b9e440cfe4e..4df3d4a3922 100644 --- a/cocos/asset/assets/image-asset.ts +++ b/cocos/asset/assets/image-asset.ts @@ -537,15 +537,23 @@ export class ImageAsset extends Asset { get data (): ImageSource | RawDataType | null { if (deviceManager.gfxDevice.gfxAPI === API.WEBGL || deviceManager.gfxDevice.gfxAPI === API.WEBGL2) { if ('_data' in this._imageData.source) { - return this._imageData.getRawData(); + return this._imageData.data; } else { return this._imageData.source; } } else { - return this._imageData.getRawData(); + return this._imageData.data; } } + /** + * @en Return image data management object. + * @zh 返回图像数据管理对象。 + */ + get imageData (): ImageData { + return this._imageData; + } + /** * @en The pixel width of the image. * @zh 此图像资源的像素宽度。 diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index 7aee53344a9..367173b4587 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -24,6 +24,7 @@ import { ccclass } from 'cc.decorator'; import { DEV } from 'internal:constants'; +import { ImageData } from 'pal/image'; import { IMemoryImageSource } from '../../../pal/image/types'; import { TextureFlagBit, TextureUsageBit, API, Texture, TextureInfo, TextureViewInfo, Device, BufferTextureCopy } from '../../gfx'; @@ -180,14 +181,14 @@ export class SimpleTexture extends TextureBase { if (ArrayBuffer.isView(source)) { gfxDevice.copyBuffersToTexture([source], this._gfxTexture, _regions); } else { - let imageAsset; + let imageData; if (source instanceof ImageAsset) { - imageAsset = source; + imageData = source.imageData; } else { // This is a hack method, otherwise ts will just report an error. - imageAsset = new ImageAsset(source as IMemoryImageSource); + imageData = new ImageData(source as IMemoryImageSource); } - gfxDevice.copyImagesToTexture([imageAsset], this._gfxTexture, _regions); + gfxDevice.copyImageDatasToTexture([imageData], this._gfxTexture, _regions); } } diff --git a/cocos/core/platform/index.ts b/cocos/core/platform/index.ts index 9109c0d9f05..a7910ed4b9e 100644 --- a/cocos/core/platform/index.ts +++ b/cocos/core/platform/index.ts @@ -45,3 +45,5 @@ export { } from './debug'; export { screen } from './screen'; + +export { ImageData } from 'pal/image'; diff --git a/cocos/game/splash-screen.ts b/cocos/game/splash-screen.ts index 31d668a22b1..d6da631d1b8 100644 --- a/cocos/game/splash-screen.ts +++ b/cocos/game/splash-screen.ts @@ -381,7 +381,7 @@ export class SplashScreen { region.texExtent.height = this.bgImage.height; region.texExtent.depth = 1; - device.copyImagesToTexture([this.bgImage], this.bgTexture, [region]); + device.copyImageDatasToTexture([this.bgImage.imageData], this.bgTexture, [region]); } private initLogo (): void { @@ -416,7 +416,7 @@ export class SplashScreen { region.texExtent.width = this.logoImage.width; region.texExtent.height = this.logoImage.height; region.texExtent.depth = 1; - device.copyImagesToTexture([this.logoImage], this.logoTexture, [region]); + device.copyImageDatasToTexture([this.logoImage.imageData], this.logoTexture, [region]); const logoRatio = this.logoImage.width / this.logoImage.height; if (logoRatio < 1) { diff --git a/cocos/gfx/base/device.ts b/cocos/gfx/base/device.ts index a8320f90137..69368a73a27 100644 --- a/cocos/gfx/base/device.ts +++ b/cocos/gfx/base/device.ts @@ -22,6 +22,7 @@ THE SOFTWARE. */ +import { ImageData } from 'pal/image'; import { API, Feature, MemoryStatus, CommandBufferInfo, BufferInfo, BufferViewInfo, TextureInfo, TextureViewInfo, SamplerInfo, DescriptorSetInfo, @@ -47,7 +48,6 @@ import { GeneralBarrier } from './states/general-barrier'; import { TextureBarrier } from './states/texture-barrier'; import { BufferBarrier } from './states/buffer-barrier'; import { Swapchain } from './swapchain'; -import { ImageAsset } from '../../asset/assets'; /** * @en GFX Device. @@ -337,13 +337,13 @@ export abstract class Device { public abstract copyTexImagesToTexture (texImages: Readonly, texture: Texture, regions: Readonly): void; /** - * @en Copy image assets to texture. + * @en Copy image data to texture. * @zh 拷贝图像资产到纹理。 - * @param imageAssets The image assets to be copied. + * @param imageData The image data to be copied. * @param texture The texture to copy to. * @param regions The region descriptions. */ - public abstract copyImagesToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly): void; + public abstract copyImageDatasToTexture (imageData: Readonly, texture: Texture, regions: Readonly): void; /** * @en Whether the device has specific feature. diff --git a/cocos/gfx/empty/empty-device.ts b/cocos/gfx/empty/empty-device.ts index ea455892a90..efb6c9abf52 100644 --- a/cocos/gfx/empty/empty-device.ts +++ b/cocos/gfx/empty/empty-device.ts @@ -22,6 +22,7 @@ THE SOFTWARE. */ +import { ImageData } from 'pal/image'; import { DescriptorSet } from '../base/descriptor-set'; import { DescriptorSetLayout } from '../base/descriptor-set-layout'; import { PipelineLayout } from '../base/pipeline-layout'; @@ -214,7 +215,7 @@ export class EmptyDevice extends Device { public copyBuffersToTexture (buffers: Readonly, texture: Texture, regions: Readonly): void {} public copyTextureToBuffers (texture: Readonly, buffers: ArrayBufferView[], regions: Readonly): void {} public copyTexImagesToTexture (texImages: Readonly, texture: Texture, regions: Readonly): void {} - public copyImagesToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly): void {} + public copyImageDatasToTexture (imageAssets: Readonly, texture: Texture, regions: Readonly): void {} } cclegacy.EmptyDevice = EmptyDevice; diff --git a/cocos/gfx/webgl/webgl-device.ts b/cocos/gfx/webgl/webgl-device.ts index 30b3c3b08d5..55d57f11737 100644 --- a/cocos/gfx/webgl/webgl-device.ts +++ b/cocos/gfx/webgl/webgl-device.ts @@ -22,6 +22,7 @@ THE SOFTWARE. */ +import { ImageData } from 'pal/image'; import { DescriptorSet } from '../base/descriptor-set'; import { DescriptorSetLayout } from '../base/descriptor-set-layout'; import { PipelineLayout } from '../base/pipeline-layout'; @@ -66,7 +67,6 @@ import { Swapchain } from '../base/swapchain'; import { IWebGLExtensions, WebGLDeviceManager } from './webgl-define'; import { IWebGLBindingMapping, IWebGLBlitManager } from './webgl-gpu-objects'; import type { WebGLStateCache } from './webgl-state-cache'; -import { ImageAsset } from '../../asset/assets'; export class WebGLDevice extends Device { get gl (): WebGLRenderingContext { @@ -566,18 +566,18 @@ export class WebGLDevice extends Device { ); } - public copyImagesToTexture ( - imageAssets: Readonly, + public copyImageDatasToTexture ( + imageDatas: Readonly, texture: Texture, regions: Readonly, ): void { const texImages: TexImageSource[] = []; const buffers: ArrayBufferView[] = []; - imageAssets.forEach((item) => { - if (ArrayBuffer.isView(item.data)) { + imageDatas.forEach((item) => { + if ('_data' in item.source && ArrayBuffer.isView(item.data)) { buffers.push(item.data); } else { - texImages.push(item.data as TexImageSource); + texImages.push(item.source as TexImageSource); } }); if (texImages.length > 0) { diff --git a/cocos/gfx/webgl2/webgl2-device.ts b/cocos/gfx/webgl2/webgl2-device.ts index 99c0e2f33f0..6d68c15803b 100644 --- a/cocos/gfx/webgl2/webgl2-device.ts +++ b/cocos/gfx/webgl2/webgl2-device.ts @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - +import { ImageData } from 'pal/image'; import { systemInfo } from 'pal/system-info'; import { DescriptorSet } from '../base/descriptor-set'; import { DescriptorSetLayout } from '../base/descriptor-set-layout'; @@ -68,7 +68,6 @@ import { IWebGL2Extensions, WebGL2DeviceManager } from './webgl2-define'; import { IWebGL2BindingMapping, IWebGL2BlitManager } from './webgl2-gpu-objects'; import { BrowserType, OS } from '../../../pal/system-info/enum-type'; import type { WebGL2StateCache } from './webgl2-state-cache'; -import { ImageAsset } from '../../asset/assets'; export class WebGL2Device extends Device { get gl (): WebGL2RenderingContext { @@ -633,18 +632,18 @@ export class WebGL2Device extends Device { ); } - public copyImagesToTexture ( - imageAssets: Readonly, + public copyImageDatasToTexture ( + imageDatas: Readonly, texture: Texture, regions: Readonly, ): void { const texImages: TexImageSource[] = []; const buffers: ArrayBufferView[] = []; - imageAssets.forEach((item) => { - if (ArrayBuffer.isView(item.data)) { + imageDatas.forEach((item) => { + if ('_data' in item.source && ArrayBuffer.isView(item.data)) { buffers.push(item.data); } else { - texImages.push(item.data as TexImageSource); + texImages.push(item.source as TexImageSource); } }); if (texImages.length > 0) { diff --git a/cocos/gfx/webgpu/webgpu-define.ts b/cocos/gfx/webgpu/webgpu-define.ts index 22ff2e40d4e..a1486108987 100644 --- a/cocos/gfx/webgpu/webgpu-define.ts +++ b/cocos/gfx/webgpu/webgpu-define.ts @@ -36,7 +36,7 @@ import { } from '../base/define'; import { ccwindow } from '../../core/global-exports'; -import { ImageAsset } from '../../asset/assets'; +import { ImageData } from 'pal/image'; WEBGPU && promiseForWebGPUInstantiation.then(() => { @@ -198,10 +198,10 @@ WEBGPU && promiseForWebGPUInstantiation.then(() => { oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); }; - Device.prototype.copyImagesToTexture = function (imageAssets: ImageAsset[], texture: typeof Texture, regions: BufferTextureCopy[]) { + Device.prototype.copyImageDatasToTexture = function (imageDatas: ImageData[], texture: typeof Texture, regions: BufferTextureCopy[]) { const buffers: Uint8Array[] = []; for (let i = 0; i < regions.length; i++) { - buffers.push(imageAssets[i].data); + buffers.push(imageDatas[i].data); } oldDeviceCopyBuffersToTexture.call(this, buffers, texture, regions); } diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 342eb9e8efb..8791d3ef807 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -92,7 +92,7 @@ export class BaseImageData { } } - public getRawData (): RawDataType | null { + get data (): RawDataType | null { if (this._source == null) { return null; } @@ -129,7 +129,8 @@ export class BaseImageData { data = new Uint8Array(rawBuffer); } } else { - assert(false, 'ImageBitmap has no raw data!'); + //assert(false, 'ImageBitmap has no raw data!'); + return this.source; } return data; } diff --git a/pal/image/minigame/image-data.ts b/pal/image/minigame/image-data.ts index 46c45e63704..aaa36afc979 100644 --- a/pal/image/minigame/image-data.ts +++ b/pal/image/minigame/image-data.ts @@ -24,7 +24,7 @@ import { ALIPAY, XIAOMI, JSB, BAIDU, TAOBAO, TAOBAO_MINIGAME, WECHAT_MINI_PROGRAM } from 'internal:constants'; import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; -import { getError } from '../../../cocos/core'; +import { getError } from '../../../cocos/core/platform/debug'; export class ImageData extends BaseImageData { static loadImage (url: string): Promise { diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index aad01e2b9e9..3ec26de34e6 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -23,7 +23,8 @@ */ import { BaseImageData } from '../base-image-data'; import { ImageSource, IMemoryImageSource, RawDataType } from '../types'; -import { getError, override, assert } from '../../../cocos/core'; +import { getError, assert } from '../../../cocos/core/platform/debug'; +import { override } from '../../../cocos/core/data/decorators/override'; declare const jsb: any; @@ -44,7 +45,7 @@ export class ImageData extends BaseImageData { } @override - public getRawData (): RawDataType | null { + get data (): RawDataType | null { // TODO(qgh) :ImageBitmap without raw data. return this._rawData; } @@ -65,7 +66,8 @@ export class ImageData extends BaseImageData { } else if ('_data' in imageSource) { this._rawData = imageSource._data; } else { - assert(false, 'ImageBitmap has no raw data!'); + this._rawData = imageSource; + //assert(false, 'ImageBitmap has no raw data!'); } super.reset(imageSource); } diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index 9205d8a996b..51e57b123e6 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -23,7 +23,7 @@ */ import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; -import { getError } from '../../../cocos/core'; +// import { getError } from '../../../cocos/core/platform/debug'; export class ImageData extends BaseImageData { static loadImage (urlOrBase64: string): Promise { @@ -39,7 +39,7 @@ export class ImageData extends BaseImageData { resolve(imageData); }; image.onerror = (): void => { - reject(new Error(getError(4930, urlOrBase64))); + //reject(new Error(getError(4930, urlOrBase64))); }; image.src = urlOrBase64; diff --git a/platforms/native/engine/jsb-gfx.js b/platforms/native/engine/jsb-gfx.js index bfbad608886..d148cdd7a9c 100644 --- a/platforms/native/engine/jsb-gfx.js +++ b/platforms/native/engine/jsb-gfx.js @@ -55,11 +55,11 @@ deviceProto.copyTexImagesToTexture = function (texImages, texture, regions) { oldCopyTexImagesToTextureFunc.call(this, images, texture, regions); }; -deviceProto.copyImagesToTexture = function (imageAssets, texture, regions) { +deviceProto.copyImageDatasToTexture = function (imageDatas, texture, regions) { const images = []; - if (imageAssets) { - for (let i = 0; i < imageAssets.length; ++i) { - const image = imageAssets[i]; + if (imageDatas) { + for (let i = 0; i < imageDatas.length; ++i) { + const image = imageDatas[i]; images.push(image.data); } } From 43055bbe8af28565bd0fa29ae777108ed14ec6f8 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Thu, 10 Aug 2023 16:32:07 +0800 Subject: [PATCH 22/24] Remove redundant comments --- @types/pal/image.d.ts | 2 +- cocos/2d/assets/sprite-frame.ts | 15 ++++++----- .../skinned-mesh-batch-renderer.ts | 7 +++-- cocos/asset/assets/simple-texture.jsb.ts | 12 ++++----- cocos/asset/assets/simple-texture.ts | 26 ++++++++----------- cocos/asset/assets/texture-cube.ts | 2 +- pal/image/base-image-data.ts | 1 - pal/image/native/image-data.ts | 1 - pal/image/web/image-data.ts | 4 +-- 9 files changed, 32 insertions(+), 38 deletions(-) diff --git a/@types/pal/image.d.ts b/@types/pal/image.d.ts index cad77c7c6ea..d1236a6aea3 100644 --- a/@types/pal/image.d.ts +++ b/@types/pal/image.d.ts @@ -5,7 +5,7 @@ declare module 'pal/image' { export type PixelFormat = import('cocos/asset/assets/asset-enum').PixelFormat; export type RawDataType = import('pal/image/types').RawDataType; export class ImageData { - constructor (imageAsset?: ImageSource | ArrayBufferView); + constructor (source?: ImageSource | ArrayBufferView); /** * Destroy resources. */ diff --git a/cocos/2d/assets/sprite-frame.ts b/cocos/2d/assets/sprite-frame.ts index 0ad83ebe717..2eee8801e65 100644 --- a/cocos/2d/assets/sprite-frame.ts +++ b/cocos/2d/assets/sprite-frame.ts @@ -246,20 +246,21 @@ export class SpriteFrame extends Asset { /** * @en Create a SpriteFrame object by an image asset or an native image asset. * @zh 通过 Image 资源或者平台相关 Image 对象创建一个 SpriteFrame 资源。 - * @param imageSourceOrImageAsset @en ImageAsset or ImageSource, ImageSource could be HTMLCanvasElement, HTMLImageElement, IMemoryImageSource. - * @zh 图像资源或图像原始图像源,图像原始图像源支持 HTMLCanvasElement HTMLImageElement IMemoryImageSource 三种资源。 + * @param imageSource @en ImageSource could be HTMLCanvasElement, HTMLImageElement, ImageBitmap. + * @zh 图像原始图像源支持 HTMLCanvasElement HTMLImageElement ImageBitmap 三种资源。 * @returns @en SpriteFrame asset. @zh 精灵资源。 - * @deprecated @en Recommended use of the ImageAsset object. @zh 推荐使用ImageAsset对象 + * @deprecated @en Please use `createWithImage (memoryImageSourceOrImageAsset: ImageAsset | IMemoryImageSource)` instead. + * @zh 请使用`createWithImage (memoryImageSourceOrImageAsset: ImageAsset | IMemoryImageSource)`代替。 */ - public static createWithImage (imageSourceOrImageAsset: HTMLCanvasElement | HTMLImageElement | ImageBitmap): SpriteFrame; + public static createWithImage (imageSource: HTMLCanvasElement | HTMLImageElement | ImageBitmap): SpriteFrame; /** * @en Create a SpriteFrame object by an image asset or an native image asset. * @zh 通过 Image 资源或者平台相关 Image 对象创建一个 SpriteFrame 资源。 - * @param imageSourceOrImageAsset @en ImageAsset or ImageSource, ImageSource could be HTMLCanvasElement, HTMLImageElement, IMemoryImageSource. - * @zh 图像资源或图像原始图像源,图像原始图像源支持 HTMLCanvasElement HTMLImageElement IMemoryImageSource 三种资源。 + * @param memoryImageSourceOrImageAsset @en ImageAsset or IMemoryImageSource. + * @zh 图像资产或IMemoryImageSource。 * @returns @en SpriteFrame asset. @zh 精灵资源。 */ - public static createWithImage (imageSourceOrImageAsset: ImageAsset | IMemoryImageSource): SpriteFrame; + public static createWithImage (memoryImageSourceOrImageAsset: ImageAsset | IMemoryImageSource): SpriteFrame; public static createWithImage (imageSourceOrImageAsset: ImageAsset | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap): SpriteFrame { const img = imageSourceOrImageAsset instanceof ImageAsset ? imageSourceOrImageAsset : new ImageAsset(imageSourceOrImageAsset as IMemoryImageSource); const tex = new Texture2D(); diff --git a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts index df57e47da51..c5b916501a1 100644 --- a/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts +++ b/cocos/3d/skinned-mesh-renderer/skinned-mesh-batch-renderer.ts @@ -455,8 +455,7 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { protected cookTextures (target: Texture2D, prop: string, passIdx: number): void { const texImageDatas: ImageData[] = []; - const texImageRegions: BufferTextureCopy[] = []; - const texBufferRegions: BufferTextureCopy[] = []; + const texRegions: BufferTextureCopy[] = []; for (let u = 0; u < this.units.length; u++) { const unit = this.units[u]; if (!unit.material) { continue; } @@ -468,12 +467,12 @@ export class SkinnedMeshBatchRenderer extends SkinnedMeshRenderer { region.texExtent.width = unit.size.x * this.atlasSize; region.texExtent.height = unit.size.y * this.atlasSize; texImageDatas.push(partial.image.imageData); - texImageRegions.push(region); + texRegions.push(region); } } const gfxTex = target.getGFXTexture()!; const { device } = cclegacy.director.root!; - if (texImageDatas.length > 0) { device.copyImageDatasToTexture(texImageDatas, gfxTex, texImageRegions); } + if (texImageDatas.length > 0) { device.copyImageDatasToTexture(texImageDatas, gfxTex, texRegions); } } protected createTexture (prop: string): Texture2D { diff --git a/cocos/asset/assets/simple-texture.jsb.ts b/cocos/asset/assets/simple-texture.jsb.ts index 209f3de54b3..34510757211 100644 --- a/cocos/asset/assets/simple-texture.jsb.ts +++ b/cocos/asset/assets/simple-texture.jsb.ts @@ -27,7 +27,7 @@ import { js, macro, cclegacy } from '../../core'; import './texture-base'; import { patch_cc_SimpleTexture } from '../../native-binding/decorators'; import type { SimpleTexture as JsbSimpleTexture } from './simple-texture'; -import { ImageAsset } from './image-asset'; +import { ImageData } from 'pal/image'; declare const jsb: any; @@ -46,13 +46,13 @@ simpleTextureProto.uploadData = function (source, level = 0, arrayIndex = 0) { if(ArrayBuffer.isView(source)) { oldUpdateDataFunc.call(this, source, level, arrayIndex); } else { - let imageAsset; - if (source instanceof ImageAsset) { - imageAsset = source; + let imageData; + if (source instanceof ImageData) { + imageData = source; } else { - imageAsset = new ImageAsset(source); + imageData = new ImageData(source); } - oldUpdateDataFunc.call(this, imageAsset.data, level, arrayIndex); + oldUpdateDataFunc.call(this, imageData.data, level, arrayIndex); } }; diff --git a/cocos/asset/assets/simple-texture.ts b/cocos/asset/assets/simple-texture.ts index 367173b4587..377b1d2a7ef 100644 --- a/cocos/asset/assets/simple-texture.ts +++ b/cocos/asset/assets/simple-texture.ts @@ -144,7 +144,7 @@ export class SimpleTexture extends TextureBase { * @param arrayIndex @en The array index. @zh 要上传的数组索引。 * @deprecated since v3.9, please use `uploadData (source: ImageAsset | IMemoryImageSource | ArrayBufferView, level, arrayIndex)` instead. */ - public uploadData (source: HTMLCanvasElement | HTMLImageElement | ImageBitmap, level, arrayIndex): void; + public uploadData (source: HTMLCanvasElement | HTMLImageElement | ImageBitmap, level?: number, arrayIndex?: number): void; /** * @en Upload data to the given mipmap level. * The size of the image will affect how the mipmap is updated. @@ -158,12 +158,12 @@ export class SimpleTexture extends TextureBase { * - 若图像的尺寸与 Mipmap 的尺寸相同,上传后整个 Mipmap 的数据将与图像数据一致; * - 若图像的尺寸小于指定层级 Mipmap 的尺寸(不管是长或宽),则从贴图左上角开始,图像尺寸范围内的 Mipmap 会被更新; * - 若图像的尺寸超出了指定层级 Mipmap 的尺寸(不管是长或宽),都将引起错误。 - * @param source @en The source image or image data. @zh 源图像或图像数据。 + * @param source @en The ImageData,IMemoryImageSource or ArrayBufferView. @zh 图像管理对象、内存图像数据或ArrayBufferView。 * @param level @en Mipmap level to upload the image to. @zh 要上传的 mipmap 层级。 * @param arrayIndex @en The array index. @zh 要上传的数组索引。 */ - public uploadData (source: ImageAsset | IMemoryImageSource | ArrayBufferView, level, arrayIndex): void; - public uploadData (source: ImageAsset | IMemoryImageSource | ArrayBufferView | HTMLCanvasElement | HTMLImageElement | ImageBitmap, level = 0, arrayIndex = 0): void { + public uploadData (source: ImageData | IMemoryImageSource | ArrayBufferView, level?: number, arrayIndex?: number): void; + public uploadData (source: ImageData | IMemoryImageSource | ArrayBufferView | HTMLCanvasElement | HTMLImageElement | ImageBitmap, level: number = 0, arrayIndex: number = 0): void { if (!this._gfxTexture || this._mipmapLevel <= level) { return; } @@ -178,18 +178,14 @@ export class SimpleTexture extends TextureBase { region.texExtent.height = this._textureHeight >> level; region.texSubres.mipLevel = level; region.texSubres.baseArrayLayer = arrayIndex; - if (ArrayBuffer.isView(source)) { - gfxDevice.copyBuffersToTexture([source], this._gfxTexture, _regions); + let imageData; + if (source instanceof ImageData) { + imageData = source; } else { - let imageData; - if (source instanceof ImageAsset) { - imageData = source.imageData; - } else { - // This is a hack method, otherwise ts will just report an error. - imageData = new ImageData(source as IMemoryImageSource); - } - gfxDevice.copyImageDatasToTexture([imageData], this._gfxTexture, _regions); + // This is a hack method, otherwise ts will just report an error. + imageData = new ImageData(source as IMemoryImageSource); } + gfxDevice.copyImageDatasToTexture([imageData], this._gfxTexture, _regions); } /** @@ -199,7 +195,7 @@ export class SimpleTexture extends TextureBase { if (!image.data) { return; } - this.uploadData(image, level, arrayIndex); + this.uploadData(image.imageData, level, arrayIndex); this._checkTextureLoaded(); if (macro.CLEANUP_IMAGE_CACHE) { diff --git a/cocos/asset/assets/texture-cube.ts b/cocos/asset/assets/texture-cube.ts index d53fb5bf998..a0645e2a047 100644 --- a/cocos/asset/assets/texture-cube.ts +++ b/cocos/asset/assets/texture-cube.ts @@ -578,7 +578,7 @@ export class TextureCube extends SimpleTexture { height: face.height, format: face.format, }); - tex.uploadData(face); + tex.uploadData(face.imageData); for (let i = 0; i < layout.length; i++) { const layoutInfo = layout[i]; diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 8791d3ef807..16fcf207bda 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -129,7 +129,6 @@ export class BaseImageData { data = new Uint8Array(rawBuffer); } } else { - //assert(false, 'ImageBitmap has no raw data!'); return this.source; } return data; diff --git a/pal/image/native/image-data.ts b/pal/image/native/image-data.ts index 3ec26de34e6..2500c84dd08 100644 --- a/pal/image/native/image-data.ts +++ b/pal/image/native/image-data.ts @@ -67,7 +67,6 @@ export class ImageData extends BaseImageData { this._rawData = imageSource._data; } else { this._rawData = imageSource; - //assert(false, 'ImageBitmap has no raw data!'); } super.reset(imageSource); } diff --git a/pal/image/web/image-data.ts b/pal/image/web/image-data.ts index 51e57b123e6..47d0547fa1a 100644 --- a/pal/image/web/image-data.ts +++ b/pal/image/web/image-data.ts @@ -23,7 +23,7 @@ */ import { BaseImageData } from '../base-image-data'; import { ccwindow } from '../../../cocos/core/global-exports'; -// import { getError } from '../../../cocos/core/platform/debug'; +import { getError } from '../../../cocos/core/platform/debug'; export class ImageData extends BaseImageData { static loadImage (urlOrBase64: string): Promise { @@ -39,7 +39,7 @@ export class ImageData extends BaseImageData { resolve(imageData); }; image.onerror = (): void => { - //reject(new Error(getError(4930, urlOrBase64))); + reject(new Error(getError(4930, urlOrBase64))); }; image.src = urlOrBase64; From 4771e1abd32dbef2f01685ab79b515fabb2959a6 Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Fri, 11 Aug 2023 13:37:44 +0800 Subject: [PATCH 23/24] Fix unit test test failures --- pal/image/base-image-data.ts | 2 +- tests/asset-manager/asset-manager.test.ts | 24 +++++++++++++---------- tests/asset-manager/loader.test.ts | 7 ++++--- tests/asset-manager/mock-downloader.ts | 6 +++--- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pal/image/base-image-data.ts b/pal/image/base-image-data.ts index 16fcf207bda..7d27b68b7a7 100644 --- a/pal/image/base-image-data.ts +++ b/pal/image/base-image-data.ts @@ -111,7 +111,7 @@ export class BaseImageData { rawBuffer = buff; data = new Uint8Array(rawBuffer); } - } else if (this._source instanceof HTMLImageElement || this._source instanceof ImageBitmap) { + } else if (this._source instanceof HTMLImageElement || this.isImageBitmap(this._source)) { const img = this._source; const canvas = ccwindow.document.createElement('canvas'); canvas.width = img.width; diff --git a/tests/asset-manager/asset-manager.test.ts b/tests/asset-manager/asset-manager.test.ts index 705aff8861e..a7b9a2c83fc 100644 --- a/tests/asset-manager/asset-manager.test.ts +++ b/tests/asset-manager/asset-manager.test.ts @@ -3,6 +3,7 @@ import { ImageAsset } from "../../cocos/asset/assets"; import { AssetManager, assetManager, loader, resources } from "../../cocos/asset/asset-manager"; import { js } from "../../cocos/core"; import { TestSprite } from "./common-class"; +import { IMemoryImageSource, ImageData } from "pal/image"; describe('asset-manager', function () { const assetDir = './tests/fixtures'; @@ -57,7 +58,7 @@ describe('asset-manager', function () { assetManager.loadAny(resources, { __requestType__: 'url'}, function (finish, total, item) { if (item.uuid === image1) { - expect(item.content).toBeInstanceOf(Image); + expect(item.content).toBeInstanceOf(ImageData); } else if (item.uuid === json1) { expect(item.content.width).toBe(89); @@ -70,7 +71,7 @@ describe('asset-manager', function () { } }, function (err, assets) { expect(assets.length).toBe(3); - expect(assets[0]).toBeInstanceOf(Image); + expect(assets[0]).toBeInstanceOf(ImageData); expect(assets[1].width).toBe(89); expect(assets[2]._native).toBe('YouKnowEverything'); expect(assetManager.assets.has(image1)).toBeFalsy(); @@ -85,14 +86,14 @@ describe('asset-manager', function () { assetManager.loadAny({ url: image1 }, function (completedCount, totalCount, item) { if (item.uuid === image1) { - expect(item.content).toBeInstanceOf(Image); + expect(item.content).toBeInstanceOf(ImageData); } else { fail('should not load an unknown url'); } }, function (error, image) { expect(error).toBeFalsy(); - expect(image).toBeInstanceOf(Image); + expect(image).toBeInstanceOf(ImageData); expect(assetManager.assets.has(image1)).toBeFalsy(); done(); }); @@ -112,7 +113,7 @@ describe('asset-manager', function () { const progressCallback = jest.fn(function (completedCount, totalCount, item) { if (item.uuid === image) { - expect(item.content).toBeInstanceOf(Image); + expect(item.content).toBeInstanceOf(ImageData); } else if (item.uuid === font.url) { expect(item.content).toBe('Thonburi_LABEL'); @@ -124,7 +125,7 @@ describe('asset-manager', function () { assetManager.loadAny(resources, { __requestType__: 'url' }, progressCallback, function (error, assets) { expect(assets.length).toBe(2); - expect(assets[0]).toBeInstanceOf(Image); + expect(assets[0]).toBeInstanceOf(ImageData); expect(assets[1]).toBe('Thonburi_LABEL'); expect(progressCallback).toBeCalledTimes(total); done(); @@ -136,8 +137,8 @@ describe('asset-manager', function () { const image2 = assetDir + '/button.png?url=http://.../2'; assetManager.loadAny({url: image1, ext: '.png' }, function (error, image1) { assetManager.loadAny({url: image2, ext: '.png' }, function (error, image2) { - expect(image1).toBeInstanceOf(Image);; - expect(image2).toBeInstanceOf(Image);; + expect(image1).toBeInstanceOf(ImageData);; + expect(image2).toBeInstanceOf(ImageData);; expect(image1 !== image2).toBeTruthy(); done(); }); @@ -147,8 +148,11 @@ describe('asset-manager', function () { test('Loading remote image', function (done) { const image = assetDir + '/button.png'; assetManager.loadRemote(image, function (error, texture) { - expect(texture).toBeInstanceOf(ImageAsset);; - expect(texture._nativeAsset).toBeInstanceOf(Image);; + expect(texture).toBeInstanceOf(ImageAsset); + // texture._nativeAsset should return `IMemoryImageSource`. + // But we can't use toBeInstanceOf. Because IMemoryImageSource is an interface. + console.log(texture._nativeAsset); + expect('_data' in texture._nativeAsset).toBeTruthy(); expect(texture.refCount === 0).toBeTruthy(); done(); }); diff --git a/tests/asset-manager/loader.test.ts b/tests/asset-manager/loader.test.ts index 53fe1c84434..590c87b5ccd 100644 --- a/tests/asset-manager/loader.test.ts +++ b/tests/asset-manager/loader.test.ts @@ -1,5 +1,6 @@ import { assetManager, loader } from "../../cocos/asset/asset-manager"; import { ImageAsset } from "../../cocos/asset/assets/image-asset"; +import { ImageData } from "pal/image"; describe('Loader', () => { const assetDir = './tests/fixtures'; @@ -18,7 +19,7 @@ describe('Loader', () => { loader.load(resources, function (completedCount, totalCount, item) { if (item.uuid === image1) { - expect(item.content).toBeInstanceOf(Image); + expect(item.content).toBeInstanceOf(ImageData); } else if (item.uuid === json1) { expect(item.content.width).toBe(89); @@ -43,7 +44,7 @@ describe('Loader', () => { loader.load(image1, function (completedCount, totalCount, item) { if (item.uuid === image1) { - expect(item.content).toBeInstanceOf(Image); + expect(item.content).toBeInstanceOf(ImageData); } else { fail('should not load an unknown url'); @@ -152,7 +153,7 @@ describe('Loader', () => { const progressCallback = jest.fn(function (completedCount, totalCount, item) { if (item.uuid === image) { - expect(item.content).toBeInstanceOf(Image); + expect(item.content).toBeInstanceOf(ImageData); } else if (item.uuid === font.url) { expect(item.content).toBe('Thonburi_LABEL'); diff --git a/tests/asset-manager/mock-downloader.ts b/tests/asset-manager/mock-downloader.ts index ce9e261a634..43620d4a41c 100644 --- a/tests/asset-manager/mock-downloader.ts +++ b/tests/asset-manager/mock-downloader.ts @@ -1,6 +1,7 @@ import downloader from '../../cocos/asset/asset-manager/downloader'; import * as fs from 'fs'; import { PNG } from 'pngjs'; +import { ImageData } from '../../exports/base'; function downloadArrayBuffer (url, option, cb) { fs.readFile(url, null, (err, data) => { @@ -41,11 +42,10 @@ function downloadImage (url: string, options: any, cb) { if (err) { cb(err); } else { - var image = new Image(); var png = new PNG(); png.on('metadata', (meta) => { - image.width = meta.width; - image.height = meta.height; + // Not caring about the parsed image data, a fake test data is assigned here. + var image = new ImageData({with:meta.width, height:meta.height, _data: '123'}); cb(err, image); }); png.parse(data); From 2941de29ea114afdc92d50d978a58c3f0c21c65c Mon Sep 17 00:00:00 2001 From: qiuguohua Date: Mon, 14 Aug 2023 10:29:08 +0800 Subject: [PATCH 24/24] Change the name of the imageAsset parameter to imageSource. --- cocos/asset/assets/image-asset.jsb.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cocos/asset/assets/image-asset.jsb.ts b/cocos/asset/assets/image-asset.jsb.ts index 46d8687d860..e9f3a3c33f6 100644 --- a/cocos/asset/assets/image-asset.jsb.ts +++ b/cocos/asset/assets/image-asset.jsb.ts @@ -43,14 +43,14 @@ const extnames = ['.png', '.jpg', '.jpeg', '.bmp', '.webp', '.pvr', '.pkm', '.as // TODO: we mark imageAssetProto as type of any, because here we have many dynamic injected property @dumganhar const imageAssetProto: any = ImageAsset.prototype; -imageAssetProto._ctor = function (nativeAsset?: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { +imageAssetProto._ctor = function (imageSource?: ImageData | IMemoryImageSource | HTMLCanvasElement | HTMLImageElement | ImageBitmap) { jsb.Asset.prototype._ctor.apply(this, arguments); this._width = 0; this._height = 0; - if (nativeAsset instanceof ImageData) { - this._imageData = nativeAsset; + if (imageSource instanceof ImageData) { + this._imageData = imageSource; } else { - this._imageData = new ImageData(nativeAsset); + this._imageData = new ImageData(imageSource); } };