Skip to content

Layer ResourceCache support ImageBItMap As a key #2524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 287 additions & 0 deletions packages/maptalks/src/core/ResourceCacheManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
import Browser from './Browser';
import { isSVG, isImageBitMap, isString, hasOwn } from './util';

type ResourceUrl = string | string[];

type ResourceCacheItem = {
image: ImageBitmap;
width: number;
height: number;
refCnt: number;
}

/**
* resouce key support ImageBitMap by Map
* this.resources.set(imagebitmap,cache);
* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map
*/


class ResourceCacheMap {
resources: Map<string | ImageBitmap, ResourceCacheItem>;

//@internal
_errors: Map<string | ImageBitmap, number>;

constructor() {
this.resources = new Map();
this._errors = new Map();
}


addResource(url: [string, number | string, number | string], img) {
const imgUrl = this._getImgUrl(url as any);
this.resources.set(imgUrl, {
image: img,
width: +url[1],
height: +url[2],
refCnt: 0
})
//is Image
if (img && img.width && img.height && !img.close && Browser.imageBitMap && !Browser.safari && !Browser.iosWeixin) {
if (img.src && isSVG(img.src)) {
return;
}
createImageBitmap(img).then(imageBitmap => {
if (!this.resources.has(imgUrl)) {
//removed
return;
}
this.resources.get(imgUrl).image = imageBitmap;
});
}
}

isResourceLoaded(url: ResourceUrl, checkSVG?: boolean) {
if (!url) {
return false;
}
const imgUrl = this._getImgUrl(url);
if (this._errors.has(imgUrl)) {
return true;
}
const img = this.resources.get(imgUrl);
if (!img) {
return false;
}
if (checkSVG && isSVG(url[0]) && (+url[1] > img.width || +url[2] > img.height)) {
return false;
}
return true;
}

login(url: string) {
const res = this.resources.get(url);
if (res) {
res.refCnt++;
}
}

logout(url: string) {
const res = this.resources.get(url);
if (res && res.refCnt-- <= 0) {
if (res.image && res.image.close) {
res.image.close();
}
this.resources.delete(url);
}
}

getImage(url: ResourceUrl) {
const imgUrl = this._getImgUrl(url);
if (!this.isResourceLoaded(url) || this._errors.has(imgUrl)) {
return null;
}
return this.resources.get(imgUrl).image;
}

markErrorResource(url: ResourceUrl) {
const imgUrl = this._getImgUrl(url);
this._errors.set(imgUrl, 1);
}

merge(res: ResourceCacheMap) {
if (!res) {
return this;
}
const otherResource = res.resources;
if (otherResource) {
otherResource.forEach((value, key) => {
const img = value;
this.addResource([key as string, img.width, img.height], img.image);
})
}
return this;
}

forEach(fn: (key: string | ImageBitmap, value: any) => void) {
if (!this.resources) {
return this;
}
this.resources.forEach((value, key) => {
fn(key, value);
})
return this;
}

//@internal
_getImgUrl(url: ResourceUrl) {
if (isImageBitMap(url)) {
return url;
}
let imgUrl;
if (Array.isArray(url)) {
imgUrl = url[0];
} else if (isString(url)) {
imgUrl = url;
}
if (!imgUrl) {
console.error(`get url key error,the url is:`, url);
}
return imgUrl;
}

remove() {
if (!this.resources) {
return this;
}
this.resources.forEach((value) => {
if (value && value.image && value.image.close) {
value.image.close();
}
})
this.resources.clear();
return this;
}
}


export class ResourceCache {
resources: any;

//@internal
_errors: any;

constructor() {
this.resources = {};
this._errors = {};
}

addResource(url: [string, number | string, number | string], img) {
this.resources[url[0]] = {
image: img,
width: +url[1],
height: +url[2],
refCnt: 0
};
if (img && img.width && img.height && !img.close && Browser.imageBitMap && !Browser.safari && !Browser.iosWeixin) {
if (img.src && isSVG(img.src)) {
return;
}
createImageBitmap(img).then(imageBitmap => {
if (!this.resources[url[0]]) {
//removed
return;
}
this.resources[url[0]].image = imageBitmap;
});
}
}

isResourceLoaded(url: ResourceUrl, checkSVG?: boolean) {
if (!url) {
return false;
}
const imgUrl = this._getImgUrl(url);
if (this._errors[imgUrl]) {
return true;
}
const img = this.resources[imgUrl];
if (!img) {
return false;
}
if (checkSVG && isSVG(url[0]) && (+url[1] > img.width || +url[2] > img.height)) {
return false;
}
return true;
}

login(url: string) {
const res = this.resources[url];
if (res) {
res.refCnt++;
}
}

logout(url: string) {
const res = this.resources[url];
if (res && res.refCnt-- <= 0) {
if (res.image && res.image.close) {
res.image.close();
}
delete this.resources[url];
}
}

getImage(url: ResourceUrl) {
const imgUrl = this._getImgUrl(url);
if (!this.isResourceLoaded(url) || this._errors[imgUrl]) {
return null;
}
return this.resources[imgUrl].image;
}

markErrorResource(url: ResourceUrl) {
this._errors[this._getImgUrl(url)] = 1;
}

merge(res: any) {
if (!res) {
return this;
}
for (const p in res.resources) {
const img = res.resources[p];
this.addResource([p, img.width, img.height], img.image);
}
return this;
}

forEach(fn: (key: string, value: any) => void) {
if (!this.resources) {
return this;
}
for (const p in this.resources) {
if (hasOwn(this.resources, p)) {
fn(p, this.resources[p]);
}
}
return this;
}

//@internal
_getImgUrl(url: ResourceUrl) {
if (!Array.isArray(url)) {
return url;
}
return url[0];
}

remove() {
for (const p in this.resources) {
const res = this.resources[p];
if (res && res.image && res.image.close) {
// close bitmap
res.image.close();
}
}
this.resources = {};
}
}

// Dynamically obtain resource cache instances
export function getResouceCacheInstance(): ResourceCache {
if (Browser.decodeImageInWorker) {
return new ResourceCacheMap() as ResourceCache;
}
return new ResourceCache();
}
2 changes: 1 addition & 1 deletion packages/maptalks/src/core/util/draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PointExtent from '../../geo/PointExtent';
import { isGradient } from './style';
import { isNumber } from './common';
import Canvas from '../Canvas';
import { ResourceCache } from '../../renderer/layer/LayerAbstractRenderer'
import { BBOX } from './bbox';
import { ResourceCache } from '../ResourceCacheManager';

export function drawImageMarker(ctx: CanvasRenderingContext2D, image, point, symbol) {
let w = symbol && symbol['markerWidth'];
Expand Down
2 changes: 1 addition & 1 deletion packages/maptalks/src/core/util/marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Point from '../../geo/Point';
import PointExtent from '../../geo/PointExtent';
import Size from '../../geo/Size';
import { type MarkerType } from './draw'
import { ResourceCache } from '../../renderer/layer/LayerAbstractRenderer'
import { ResourceCache } from '../ResourceCacheManager';

export const DEFAULT_MARKER_SYMBOLS = {
markerWidth: 10,
Expand Down
6 changes: 3 additions & 3 deletions packages/maptalks/src/geometry/ext/Geometry.Drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import Handler from '../../handler/Handler';
import Geometry from '../Geometry';
import DragHandler from '../../handler/Drag';
import { ConnectorLine } from '../ConnectorLine';
import { ResourceCache } from '../../renderer/layer/LayerAbstractRenderer';
import Point from '../../geo/Point';
import Coordinate from '../../geo/Coordinate';
import { getResouceCacheInstance } from '../../core/ResourceCacheManager';

const DRAG_STAGE_LAYER_ID = INTERNAL_LAYER_PREFIX + '_drag_stage';

Expand Down Expand Up @@ -193,7 +193,7 @@ class GeometryDragHandler extends Handler {
});
map.addLayer(this._dragStageLayer);
//copy resources to avoid repeat resource loading.
const resources = new ResourceCache();
const resources = getResouceCacheInstance();
resources.merge(layer._getRenderer().resources);
this._dragStageLayer._getRenderer().resources = resources;
}
Expand Down Expand Up @@ -415,7 +415,7 @@ class GeometryDragHandler extends Handler {
delete this._shadowConnectors;
}
if (this._dragStageLayer) {
this._dragStageLayer._getRenderer().resources = new ResourceCache();
this._dragStageLayer._getRenderer().resources = getResouceCacheInstance();
this._dragStageLayer.remove();
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/maptalks/src/layer/ImageLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import Browser from '../core/Browser';
import Point from '../geo/Point';
import ImageGLRenderable from '../renderer/layer/ImageGLRenderable';
import CanvasRenderer from '../renderer/layer/CanvasRenderer';
import { ResourceCache } from '../renderer/layer/LayerAbstractRenderer';
import Extent from '../geo/Extent';
import Layer, { LayerOptionsType } from './Layer';
import { PointExtent } from '../geo';
import { getResouceCacheInstance } from '../core/ResourceCacheManager';


/**
Expand Down Expand Up @@ -152,7 +152,7 @@ export class ImageLayerCanvasRenderer extends CanvasRenderer {
let urls = layer._imageData.map(img => [img.url, null, null]);
if (this.resources) {
const unloaded = [];
const resources = new ResourceCache();
const resources = getResouceCacheInstance();
urls.forEach(url => {
if (this.resources.isResourceLoaded(url)) {
const img = this.resources.getImage(url);
Expand Down
4 changes: 2 additions & 2 deletions packages/maptalks/src/renderer/edit/EditHandle.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Eventable from '../../core/Eventable';
import Class from '../../core/Class';
import Point, { type PointJson } from '../../geo/Point';
import { ResourceCache } from '../layer/LayerAbstractRenderer';
import { drawVectorMarker } from '../../core/util/draw';
import { isNil } from '../../core/util/';
import { getSymbolHash } from '../../core/util';
Expand All @@ -10,8 +9,9 @@ import DragHandler from '../../handler/Drag';
import { BBOX, bufferBBOX, getDefaultBBOX } from '../../core/util/bbox';
import type Map from '../../map/Map';
import type GeometryEditor from '../../geometry/editor/GeometryEditor';
import { getResouceCacheInstance } from '../../core/ResourceCacheManager';

const resources = new ResourceCache();
const resources = getResouceCacheInstance();
let prevX, prevY;

type EventParams = any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PointExtent from '../../geo/PointExtent';
import { BBOX, getDefaultBBOX, resetBBOX, setBBOX, validateBBOX } from '../../core/util/bbox';
import Painter from './Painter';
import Extent from '../../geo/Extent';
import { ResourceCache } from '../layer/LayerAbstractRenderer';
import { Geometries } from '../../geometry'
import { ResourceCache } from '../../core/ResourceCacheManager';

const TEMP_EXTENT = new PointExtent();

Expand Down
2 changes: 1 addition & 1 deletion packages/maptalks/src/renderer/geometry/Painter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { BBOX, getDefaultBBOX, resetBBOX, setBBOX, validateBBOX } from '../../co
import Map from '../../map/Map'
import { DebugSymbolizer } from './symbolizers';
import Extent from '../../geo/Extent';
import { ResourceCache } from '../layer/LayerAbstractRenderer';
import type { WithUndef } from '../../types/typings';
import { Geometries } from '../../geometry'
import { ResourceCache } from '../../core/ResourceCacheManager';

//registered symbolizers
//the latter will paint at the last
Expand Down
Loading