maplibre-gl-layers 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  /*!
3
3
  * name: maplibre-gl-layers
4
- * version: 0.1.0
4
+ * version: 0.3.0
5
5
  * description: MapLibre's layer extension library enabling the display, movement, and modification of large numbers of dynamic sprite images
6
6
  * author: Kouji Matsui (@kekyo@mi.kekyo.net)
7
7
  * license: MIT
8
8
  * repository.url: https://github.com/kekyo/maplibre-gl-layers.git
9
- * git.commit.hash: 88b09a93779279947d9c6b1e3a7538d4d197dbcb
9
+ * git.commit.hash: fd90d1fd66e4ca9c0defa49e68ecaeb9c14fbd06
10
10
  */
11
11
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
12
12
  var maplibreGl$1 = { exports: {} };
@@ -21505,6 +21505,27 @@ const screenToClip = (x, y, drawingBufferWidth, drawingBufferHeight, pixelRatio)
21505
21505
  const clipY = 1 - deviceY / drawingBufferHeight * 2;
21506
21506
  return [clipX, clipY];
21507
21507
  };
21508
+ const clipToScreen = (clipPosition, drawingBufferWidth, drawingBufferHeight, pixelRatio) => {
21509
+ const [clipX, clipY, , clipW] = clipPosition;
21510
+ if (!Number.isFinite(clipW) || clipW === 0) {
21511
+ return null;
21512
+ }
21513
+ const invW = 1 / clipW;
21514
+ const ndcX = clipX * invW;
21515
+ const ndcY = clipY * invW;
21516
+ const deviceX = (ndcX + 1) * 0.5 * drawingBufferWidth;
21517
+ const deviceY = (1 - ndcY) * 0.5 * drawingBufferHeight;
21518
+ if (!Number.isFinite(deviceX) || !Number.isFinite(deviceY)) {
21519
+ return null;
21520
+ }
21521
+ if (!Number.isFinite(pixelRatio) || pixelRatio === 0) {
21522
+ return null;
21523
+ }
21524
+ return {
21525
+ x: deviceX / pixelRatio,
21526
+ y: deviceY / pixelRatio
21527
+ };
21528
+ };
21508
21529
  const TRIANGLE_INDICES = [0, 1, 2, 2, 1, 3];
21509
21530
  const UV_CORNERS = [
21510
21531
  [0, 0],
@@ -21664,6 +21685,7 @@ const calculateBillboardCornerScreenPositions = (params) => {
21664
21685
  return corners;
21665
21686
  };
21666
21687
  const calculateSurfaceCenterPosition = (params) => {
21688
+ var _a, _b;
21667
21689
  const {
21668
21690
  baseLngLat,
21669
21691
  imageWidth,
@@ -21674,8 +21696,38 @@ const calculateSurfaceCenterPosition = (params) => {
21674
21696
  totalRotateDeg,
21675
21697
  anchor,
21676
21698
  offset,
21677
- project
21699
+ project,
21700
+ projectToClipSpace,
21701
+ drawingBufferWidth,
21702
+ drawingBufferHeight,
21703
+ pixelRatio,
21704
+ altitudeMeters,
21705
+ resolveAnchorless = false
21678
21706
  } = params;
21707
+ const baseAltitude = Number.isFinite(altitudeMeters) ? altitudeMeters : (_a = baseLngLat.z) != null ? _a : 0;
21708
+ const hasClipProjection = typeof drawingBufferWidth === "number" && drawingBufferWidth > 0 && typeof drawingBufferHeight === "number" && drawingBufferHeight > 0 && typeof pixelRatio === "number" && Number.isFinite(pixelRatio) && pixelRatio !== 0 && typeof projectToClipSpace === "function";
21709
+ const projectPoint = (lngLat) => {
21710
+ var _a2;
21711
+ if (hasClipProjection && projectToClipSpace) {
21712
+ const clip = projectToClipSpace(
21713
+ lngLat.lng,
21714
+ lngLat.lat,
21715
+ (_a2 = lngLat.z) != null ? _a2 : baseAltitude
21716
+ );
21717
+ if (clip) {
21718
+ const screen = clipToScreen(
21719
+ clip,
21720
+ drawingBufferWidth,
21721
+ drawingBufferHeight,
21722
+ pixelRatio
21723
+ );
21724
+ if (screen) {
21725
+ return screen;
21726
+ }
21727
+ }
21728
+ }
21729
+ return project ? project(lngLat) : null;
21730
+ };
21679
21731
  const worldDims = calculateSurfaceWorldDimensions(
21680
21732
  imageWidth,
21681
21733
  imageHeight,
@@ -21700,12 +21752,31 @@ const calculateSurfaceCenterPosition = (params) => {
21700
21752
  totalEast,
21701
21753
  totalNorth
21702
21754
  );
21703
- const center = project(displaced);
21755
+ const center = projectPoint(displaced);
21756
+ let anchorlessCenter;
21757
+ let anchorlessDisplacement;
21758
+ let anchorlessLngLat;
21759
+ if (resolveAnchorless) {
21760
+ anchorlessDisplacement = {
21761
+ east: offsetMeters.east,
21762
+ north: offsetMeters.north
21763
+ };
21764
+ anchorlessLngLat = applySurfaceDisplacement(
21765
+ baseLngLat.lng,
21766
+ baseLngLat.lat,
21767
+ anchorlessDisplacement.east,
21768
+ anchorlessDisplacement.north
21769
+ );
21770
+ anchorlessCenter = (_b = projectPoint(anchorlessLngLat)) != null ? _b : null;
21771
+ }
21704
21772
  return {
21705
21773
  center,
21706
21774
  worldDimensions: worldDims,
21707
21775
  totalDisplacement: { east: totalEast, north: totalNorth },
21708
- displacedLngLat: displaced
21776
+ displacedLngLat: displaced,
21777
+ anchorlessCenter,
21778
+ anchorlessDisplacement,
21779
+ anchorlessLngLat
21709
21780
  };
21710
21781
  };
21711
21782
  const SURFACE_BASE_CORNERS = [
@@ -22324,6 +22395,16 @@ const createSpriteLayer = (options) => {
22324
22395
  let uniformTextureLocation = null;
22325
22396
  let uniformOpacityLocation = null;
22326
22397
  const images = /* @__PURE__ */ new Map();
22398
+ const queuedTextureIds = /* @__PURE__ */ new Set();
22399
+ const queueTextureUpload = (image) => {
22400
+ queuedTextureIds.add(image.id);
22401
+ };
22402
+ const cancelQueuedTextureUpload = (imageId) => {
22403
+ queuedTextureIds.delete(imageId);
22404
+ };
22405
+ const clearTextureQueue = () => {
22406
+ queuedTextureIds.clear();
22407
+ };
22327
22408
  const sprites = /* @__PURE__ */ new Map();
22328
22409
  const getImageState = (sprite, subLayer, order) => {
22329
22410
  var _a2;
@@ -22410,7 +22491,12 @@ const createSpriteLayer = (options) => {
22410
22491
  spriteMaxPixel,
22411
22492
  effectivePixelsPerMeter,
22412
22493
  images: images2,
22413
- mapInstance
22494
+ mapInstance,
22495
+ drawingBufferWidth,
22496
+ drawingBufferHeight,
22497
+ pixelRatio,
22498
+ clipContext,
22499
+ altitudeMeters
22414
22500
  } = params;
22415
22501
  const useResolvedAnchor = (_a2 = options2 == null ? void 0 : options2.useResolvedAnchor) != null ? _a2 : false;
22416
22502
  let spriteCache = originCenterCache.get(sprite.spriteId);
@@ -22479,6 +22565,7 @@ const createSpriteLayer = (options) => {
22479
22565
  // Otherwise use the sprite's own interpolated geographic location.
22480
22566
  { lng: sprite.currentLocation.lng, lat: sprite.currentLocation.lat }
22481
22567
  );
22568
+ const projectToClipSpace = clipContext ? (lng, lat, elevation) => projectLngLatToClipSpace(lng, lat, elevation, clipContext) : void 0;
22482
22569
  const surfacePlacement = calculateSurfaceCenterPosition({
22483
22570
  baseLngLat,
22484
22571
  imageWidth: imageResourceRef == null ? void 0 : imageResourceRef.width,
@@ -22489,33 +22576,21 @@ const createSpriteLayer = (options) => {
22489
22576
  totalRotateDeg: totalRotDeg,
22490
22577
  anchor: img.anchor,
22491
22578
  offset: img.offset,
22492
- project: (lngLat) => {
22579
+ projectToClipSpace,
22580
+ drawingBufferWidth,
22581
+ drawingBufferHeight,
22582
+ pixelRatio,
22583
+ altitudeMeters,
22584
+ resolveAnchorless: true,
22585
+ project: projectToClipSpace === void 0 ? (lngLat) => {
22493
22586
  const projectedPoint = mapInstance.project(lngLat);
22494
22587
  if (!projectedPoint) {
22495
22588
  return null;
22496
22589
  }
22497
22590
  return { x: projectedPoint.x, y: projectedPoint.y };
22498
- }
22591
+ } : void 0
22499
22592
  });
22500
- const anchorlessPlacement = calculateSurfaceCenterPosition({
22501
- baseLngLat,
22502
- imageWidth: imageResourceRef == null ? void 0 : imageResourceRef.width,
22503
- imageHeight: imageResourceRef == null ? void 0 : imageResourceRef.height,
22504
- baseMetersPerPixel,
22505
- imageScale: imageScaleLocal,
22506
- zoomScaleFactor,
22507
- totalRotateDeg: totalRotDeg,
22508
- anchor: void 0,
22509
- offset: img.offset,
22510
- project: (lngLat) => {
22511
- const projectedPoint = mapInstance.project(lngLat);
22512
- if (!projectedPoint) {
22513
- return null;
22514
- }
22515
- return { x: projectedPoint.x, y: projectedPoint.y };
22516
- }
22517
- });
22518
- const anchorlessCenter = (_g = anchorlessPlacement.center) != null ? _g : {
22593
+ const anchorlessCenter = (_g = surfacePlacement.anchorlessCenter) != null ? _g : {
22519
22594
  // If the anchorless placement could not be projected, fall back to the original screen position.
22520
22595
  x: baseX,
22521
22596
  y: baseY
@@ -22529,13 +22604,19 @@ const createSpriteLayer = (options) => {
22529
22604
  };
22530
22605
  const renderTargetEntries = [];
22531
22606
  const hitTestEntries = [];
22607
+ const ensureHitTestCorners = (imageEntry) => {
22608
+ if (!imageEntry.hitTestCorners) {
22609
+ imageEntry.hitTestCorners = [
22610
+ { x: 0, y: 0 },
22611
+ { x: 0, y: 0 },
22612
+ { x: 0, y: 0 },
22613
+ { x: 0, y: 0 }
22614
+ ];
22615
+ }
22616
+ return imageEntry.hitTestCorners;
22617
+ };
22532
22618
  const registerHitTestEntry = (spriteEntry, imageEntry, screenCorners) => {
22533
- const corners = [
22534
- { x: screenCorners[0].x, y: screenCorners[0].y },
22535
- { x: screenCorners[1].x, y: screenCorners[1].y },
22536
- { x: screenCorners[2].x, y: screenCorners[2].y },
22537
- { x: screenCorners[3].x, y: screenCorners[3].y }
22538
- ];
22619
+ const corners = screenCorners;
22539
22620
  let minX = corners[0].x;
22540
22621
  let maxX = corners[0].x;
22541
22622
  let minY = corners[0].y;
@@ -22663,13 +22744,19 @@ const createSpriteLayer = (options) => {
22663
22744
  if (!gl) {
22664
22745
  return;
22665
22746
  }
22747
+ if (queuedTextureIds.size === 0) {
22748
+ return;
22749
+ }
22666
22750
  const glContext = gl;
22667
- images.forEach((image) => {
22668
- if (!image.dirty || !image.bitmap) {
22751
+ queuedTextureIds.forEach((imageId) => {
22752
+ const image = images.get(imageId);
22753
+ queuedTextureIds.delete(imageId);
22754
+ if (!image || !image.bitmap) {
22669
22755
  return;
22670
22756
  }
22671
22757
  if (image.texture) {
22672
22758
  glContext.deleteTexture(image.texture);
22759
+ image.texture = void 0;
22673
22760
  }
22674
22761
  const texture = glContext.createTexture();
22675
22762
  if (!texture) {
@@ -22706,7 +22793,6 @@ const createSpriteLayer = (options) => {
22706
22793
  image.bitmap
22707
22794
  );
22708
22795
  image.texture = texture;
22709
- image.dirty = false;
22710
22796
  });
22711
22797
  };
22712
22798
  const scheduleRender = () => {
@@ -22733,6 +22819,17 @@ const createSpriteLayer = (options) => {
22733
22819
  });
22734
22820
  });
22735
22821
  });
22822
+ renderTargetEntries.sort((a, b) => {
22823
+ const aImage = a[1];
22824
+ const bImage = b[1];
22825
+ if (aImage.subLayer !== bImage.subLayer) {
22826
+ return aImage.subLayer - bImage.subLayer;
22827
+ }
22828
+ if (aImage.order !== bImage.order) {
22829
+ return aImage.order - bImage.order;
22830
+ }
22831
+ return aImage.imageId.localeCompare(bImage.imageId);
22832
+ });
22736
22833
  };
22737
22834
  const onAdd = (mapInstance, glContext) => {
22738
22835
  map = mapInstance;
@@ -22852,7 +22949,7 @@ const createSpriteLayer = (options) => {
22852
22949
  glContext.deleteTexture(image.texture);
22853
22950
  }
22854
22951
  image.texture = void 0;
22855
- image.dirty = true;
22952
+ queueTextureUpload(image);
22856
22953
  });
22857
22954
  if (vertexBuffer) {
22858
22955
  glContext.deleteBuffer(vertexBuffer);
@@ -22998,7 +23095,7 @@ const createSpriteLayer = (options) => {
22998
23095
  );
22999
23096
  glContext.uniform1i(uniformTextureLocation, 0);
23000
23097
  const drawSpriteImage = (spriteEntry, imageEntry, imageResource, originCenterCache2) => {
23001
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
23098
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
23002
23099
  let screenCornerBuffer = null;
23003
23100
  const anchor = (_a2 = imageEntry.anchor) != null ? _a2 : DEFAULT_ANCHOR;
23004
23101
  const offsetDef = (_b = imageEntry.offset) != null ? _b : DEFAULT_IMAGE_OFFSET;
@@ -23037,18 +23134,23 @@ const createSpriteLayer = (options) => {
23037
23134
  baseMetersPerPixel,
23038
23135
  spriteMinPixel,
23039
23136
  spriteMaxPixel,
23040
- effectivePixelsPerMeter
23137
+ effectivePixelsPerMeter,
23138
+ drawingBufferWidth,
23139
+ drawingBufferHeight,
23140
+ pixelRatio,
23141
+ clipContext,
23142
+ altitudeMeters: (_f = spriteEntry.currentLocation.z) != null ? _f : 0
23041
23143
  };
23042
23144
  let baseProjected = { x: projected.x, y: projected.y };
23043
23145
  if (imageEntry.originLocation !== void 0) {
23044
- const refImg = (_f = spriteEntry.images.get(imageEntry.originLocation.subLayer)) == null ? void 0 : _f.get(imageEntry.originLocation.order);
23146
+ const refImg = (_g = spriteEntry.images.get(imageEntry.originLocation.subLayer)) == null ? void 0 : _g.get(imageEntry.originLocation.order);
23045
23147
  if (refImg) {
23046
23148
  baseProjected = computeImageCenterXY(
23047
23149
  spriteEntry,
23048
23150
  refImg,
23049
23151
  centerParams,
23050
23152
  {
23051
- useResolvedAnchor: (_g = imageEntry.originLocation.useResolvedAnchor) != null ? _g : false
23153
+ useResolvedAnchor: (_h = imageEntry.originLocation.useResolvedAnchor) != null ? _h : false
23052
23154
  }
23053
23155
  );
23054
23156
  }
@@ -23074,10 +23176,15 @@ const createSpriteLayer = (options) => {
23074
23176
  totalRotateDeg,
23075
23177
  anchor,
23076
23178
  offset: offsetDef,
23077
- project: (lngLat) => {
23179
+ projectToClipSpace: (lng, lat, elevation) => projectLngLatToClipSpace(lng, lat, elevation, clipContext),
23180
+ drawingBufferWidth,
23181
+ drawingBufferHeight,
23182
+ pixelRatio,
23183
+ altitudeMeters: (_i = spriteEntry.currentLocation.z) != null ? _i : 0,
23184
+ project: !clipContext ? (lngLat) => {
23078
23185
  const result = mapInstance.project(lngLat);
23079
23186
  return result ? { x: result.x, y: result.y } : null;
23080
- }
23187
+ } : void 0
23081
23188
  });
23082
23189
  if (!surfaceCenter.center) {
23083
23190
  return;
@@ -23093,7 +23200,7 @@ const createSpriteLayer = (options) => {
23093
23200
  totalRotateDeg,
23094
23201
  offsetMeters
23095
23202
  });
23096
- const screenCornersLocal = new Array(4);
23203
+ const hitTestCorners = ensureHitTestCorners(imageEntry);
23097
23204
  let bufferOffset = 0;
23098
23205
  for (const index of TRIANGLE_INDICES) {
23099
23206
  const displacement = cornerDisplacements[index];
@@ -23107,25 +23214,24 @@ const createSpriteLayer = (options) => {
23107
23214
  displaced.lng,
23108
23215
  displaced.lat,
23109
23216
  // Default altitude to zero when sprites lack explicit elevation.
23110
- (_h = spriteEntry.currentLocation.z) != null ? _h : 0,
23217
+ (_j = spriteEntry.currentLocation.z) != null ? _j : 0,
23111
23218
  clipContext
23112
23219
  );
23113
23220
  if (!clipPosition) {
23114
23221
  return;
23115
23222
  }
23116
- const projectedCorner = mapInstance.project({
23117
- lng: displaced.lng,
23118
- lat: displaced.lat,
23119
- // Mirror the same altitude assumption when projecting back to screen space.
23120
- z: (_i = spriteEntry.currentLocation.z) != null ? _i : 0
23121
- });
23122
- if (!projectedCorner) {
23223
+ const screenCorner = clipToScreen(
23224
+ clipPosition,
23225
+ drawingBufferWidth,
23226
+ drawingBufferHeight,
23227
+ pixelRatio
23228
+ );
23229
+ if (!screenCorner) {
23123
23230
  return;
23124
23231
  }
23125
- screenCornersLocal[index] = {
23126
- x: projectedCorner.x,
23127
- y: projectedCorner.y
23128
- };
23232
+ const targetCorner = hitTestCorners[index];
23233
+ targetCorner.x = screenCorner.x;
23234
+ targetCorner.y = screenCorner.y;
23129
23235
  let [clipX, clipY, clipZ, clipW] = clipPosition;
23130
23236
  {
23131
23237
  const orderIndex = Math.min(imageEntry.order, ORDER_MAX - 1);
@@ -23145,7 +23251,7 @@ const createSpriteLayer = (options) => {
23145
23251
  QUAD_VERTEX_SCRATCH[bufferOffset++] = u;
23146
23252
  QUAD_VERTEX_SCRATCH[bufferOffset++] = v;
23147
23253
  }
23148
- screenCornerBuffer = screenCornersLocal;
23254
+ screenCornerBuffer = hitTestCorners;
23149
23255
  } else {
23150
23256
  const placement = calculateBillboardCenterPosition({
23151
23257
  base: baseProjected,
@@ -23169,6 +23275,7 @@ const createSpriteLayer = (options) => {
23169
23275
  anchor,
23170
23276
  totalRotateDeg
23171
23277
  });
23278
+ const hitTestCorners = ensureHitTestCorners(imageEntry);
23172
23279
  let bufferOffset = 0;
23173
23280
  for (const index of TRIANGLE_INDICES) {
23174
23281
  const corner = corners[index];
@@ -23186,13 +23293,13 @@ const createSpriteLayer = (options) => {
23186
23293
  QUAD_VERTEX_SCRATCH[bufferOffset++] = corner.u;
23187
23294
  QUAD_VERTEX_SCRATCH[bufferOffset++] = corner.v;
23188
23295
  }
23189
- screenCornerBuffer = [
23190
- // Preserve the resolved screen-space corners for hit testing below.
23191
- { x: corners[0].x, y: corners[0].y },
23192
- { x: corners[1].x, y: corners[1].y },
23193
- { x: corners[2].x, y: corners[2].y },
23194
- { x: corners[3].x, y: corners[3].y }
23195
- ];
23296
+ for (let i = 0; i < corners.length; i++) {
23297
+ const source = corners[i];
23298
+ const target = hitTestCorners[i];
23299
+ target.x = source.x;
23300
+ target.y = source.y;
23301
+ }
23302
+ screenCornerBuffer = hitTestCorners;
23196
23303
  }
23197
23304
  if (screenCornerBuffer && screenCornerBuffer.length === 4) {
23198
23305
  registerHitTestEntry(
@@ -23210,7 +23317,7 @@ const createSpriteLayer = (options) => {
23210
23317
  const originCenterCache = /* @__PURE__ */ new Map();
23211
23318
  const sortedSubLayerBuckets = buildSortedSubLayerBuckets(renderTargetEntries);
23212
23319
  const renderSortedBucket = (bucket) => {
23213
- var _a2, _b, _c, _d, _e;
23320
+ var _a2, _b, _c, _d, _e, _f;
23214
23321
  const itemsWithDepth = [];
23215
23322
  const projectToClipSpace = (lng, lat, elevation) => projectLngLatToClipSpace(lng, lat, elevation, clipContext);
23216
23323
  const unprojectPoint = ({ x, y }) => {
@@ -23256,10 +23363,15 @@ const createSpriteLayer = (options) => {
23256
23363
  baseMetersPerPixel,
23257
23364
  spriteMinPixel,
23258
23365
  spriteMaxPixel,
23259
- effectivePixelsPerMeter
23366
+ effectivePixelsPerMeter,
23367
+ drawingBufferWidth,
23368
+ drawingBufferHeight,
23369
+ pixelRatio,
23370
+ clipContext,
23371
+ altitudeMeters: (_a2 = spriteEntry.currentLocation.z) != null ? _a2 : 0
23260
23372
  };
23261
- const anchorResolved = (_a2 = imageEntry.anchor) != null ? _a2 : DEFAULT_ANCHOR;
23262
- const offsetResolved = (_b = imageEntry.offset) != null ? _b : DEFAULT_IMAGE_OFFSET;
23373
+ const anchorResolved = (_b = imageEntry.anchor) != null ? _b : DEFAULT_ANCHOR;
23374
+ const offsetResolved = (_c = imageEntry.offset) != null ? _c : DEFAULT_IMAGE_OFFSET;
23263
23375
  const depthCenter = computeImageCenterXY(
23264
23376
  spriteEntry,
23265
23377
  imageEntry,
@@ -23268,7 +23380,7 @@ const createSpriteLayer = (options) => {
23268
23380
  );
23269
23381
  let depthKey;
23270
23382
  if (imageEntry.mode === "surface") {
23271
- const imageScale = (_c = imageEntry.scale) != null ? _c : 1;
23383
+ const imageScale = (_d = imageEntry.scale) != null ? _d : 1;
23272
23384
  const worldDims = calculateSurfaceWorldDimensions(
23273
23385
  imageResource.width,
23274
23386
  imageResource.height,
@@ -23277,7 +23389,7 @@ const createSpriteLayer = (options) => {
23277
23389
  zoomScaleFactor
23278
23390
  );
23279
23391
  const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normaliseAngleDeg(
23280
- ((_d = imageEntry.resolvedBaseRotateDeg) != null ? _d : 0) + ((_e = imageEntry.rotateDeg) != null ? _e : 0)
23392
+ ((_e = imageEntry.resolvedBaseRotateDeg) != null ? _e : 0) + ((_f = imageEntry.rotateDeg) != null ? _f : 0)
23281
23393
  );
23282
23394
  const offsetMeters = calculateSurfaceOffsetMeters(
23283
23395
  offsetResolved,
@@ -23399,14 +23511,15 @@ const createSpriteLayer = (options) => {
23399
23511
  if (images.has(imageId)) {
23400
23512
  return false;
23401
23513
  }
23402
- images.set(imageId, {
23514
+ const image = {
23403
23515
  id: imageId,
23404
23516
  width: bitmap.width,
23405
23517
  height: bitmap.height,
23406
23518
  bitmap,
23407
- texture: void 0,
23408
- dirty: true
23409
- });
23519
+ texture: void 0
23520
+ };
23521
+ images.set(imageId, image);
23522
+ queueTextureUpload(image);
23410
23523
  ensureTextures();
23411
23524
  scheduleRender();
23412
23525
  return true;
@@ -23592,14 +23705,15 @@ const createSpriteLayer = (options) => {
23592
23705
  totalHeight,
23593
23706
  renderPixelRatio
23594
23707
  );
23595
- images.set(textGlyphId, {
23708
+ const image = {
23596
23709
  id: textGlyphId,
23597
23710
  width: totalWidth,
23598
23711
  height: totalHeight,
23599
23712
  bitmap,
23600
- texture: void 0,
23601
- dirty: true
23602
- });
23713
+ texture: void 0
23714
+ };
23715
+ images.set(textGlyphId, image);
23716
+ queueTextureUpload(image);
23603
23717
  ensureTextures();
23604
23718
  scheduleRender();
23605
23719
  return true;
@@ -23613,12 +23727,30 @@ const createSpriteLayer = (options) => {
23613
23727
  if (glContext && image.texture) {
23614
23728
  glContext.deleteTexture(image.texture);
23615
23729
  }
23730
+ cancelQueuedTextureUpload(imageId);
23616
23731
  images.delete(imageId);
23617
23732
  ensureRenderTargetEntries();
23618
23733
  scheduleRender();
23619
23734
  return true;
23620
23735
  };
23621
- const addSprite = (spriteId, init) => {
23736
+ const unregisterAllImages = () => {
23737
+ const glContext = gl;
23738
+ images.forEach((image) => {
23739
+ var _a2, _b;
23740
+ if (glContext && image.texture) {
23741
+ glContext.deleteTexture(image.texture);
23742
+ }
23743
+ if (image.bitmap) {
23744
+ (_b = (_a2 = image.bitmap).close) == null ? void 0 : _b.call(_a2);
23745
+ }
23746
+ });
23747
+ images.clear();
23748
+ clearTextureQueue();
23749
+ ensureRenderTargetEntries();
23750
+ scheduleRender();
23751
+ };
23752
+ const getAllImageIds = () => Array.from(images.keys());
23753
+ const addSpriteInternal = (spriteId, init) => {
23622
23754
  var _a2, _b, _c, _d;
23623
23755
  if (sprites.get(spriteId)) {
23624
23756
  return false;
@@ -23697,18 +23829,90 @@ const createSpriteLayer = (options) => {
23697
23829
  lastAutoRotationAngleDeg: 0
23698
23830
  };
23699
23831
  sprites.set(spriteId, spriteState);
23700
- ensureRenderTargetEntries();
23701
- scheduleRender();
23702
23832
  return true;
23703
23833
  };
23834
+ const resolveSpriteInitCollection = (collection) => {
23835
+ if (Array.isArray(collection)) {
23836
+ return collection.map((entry) => [
23837
+ entry.spriteId,
23838
+ entry
23839
+ ]);
23840
+ }
23841
+ return Object.entries(collection);
23842
+ };
23843
+ const addSprite = (spriteId, init) => {
23844
+ const isAdded = addSpriteInternal(spriteId, init);
23845
+ if (isAdded) {
23846
+ ensureRenderTargetEntries();
23847
+ scheduleRender();
23848
+ }
23849
+ return isAdded;
23850
+ };
23851
+ const addSprites = (collection) => {
23852
+ let addedCount = 0;
23853
+ for (const [spriteId, spriteInit] of resolveSpriteInitCollection(
23854
+ collection
23855
+ )) {
23856
+ if (addSpriteInternal(spriteId, spriteInit)) {
23857
+ addedCount++;
23858
+ }
23859
+ }
23860
+ if (addedCount > 0) {
23861
+ ensureRenderTargetEntries();
23862
+ scheduleRender();
23863
+ }
23864
+ return addedCount;
23865
+ };
23866
+ const removeSpriteInternal = (spriteId) => sprites.delete(spriteId);
23704
23867
  const removeSprite = (spriteId) => {
23705
- if (!sprites.delete(spriteId)) {
23868
+ const removed = removeSpriteInternal(spriteId);
23869
+ if (!removed) {
23706
23870
  return false;
23707
23871
  }
23708
23872
  ensureRenderTargetEntries();
23709
23873
  scheduleRender();
23710
23874
  return true;
23711
23875
  };
23876
+ const removeSprites = (spriteIds) => {
23877
+ let removedCount = 0;
23878
+ for (const spriteId of spriteIds) {
23879
+ if (removeSpriteInternal(spriteId)) {
23880
+ removedCount++;
23881
+ }
23882
+ }
23883
+ if (removedCount > 0) {
23884
+ ensureRenderTargetEntries();
23885
+ scheduleRender();
23886
+ }
23887
+ return removedCount;
23888
+ };
23889
+ const removeAllSprites = () => {
23890
+ const removedCount = sprites.size;
23891
+ if (removedCount === 0) {
23892
+ return 0;
23893
+ }
23894
+ sprites.clear();
23895
+ ensureRenderTargetEntries();
23896
+ scheduleRender();
23897
+ return removedCount;
23898
+ };
23899
+ const removeAllSpriteImages = (spriteId) => {
23900
+ const sprite = sprites.get(spriteId);
23901
+ if (!sprite) {
23902
+ return 0;
23903
+ }
23904
+ if (sprite.images.size === 0) {
23905
+ return 0;
23906
+ }
23907
+ let removedCount = 0;
23908
+ sprite.images.forEach((orderMap) => {
23909
+ removedCount += orderMap.size;
23910
+ });
23911
+ sprite.images.clear();
23912
+ ensureRenderTargetEntries();
23913
+ scheduleRender();
23914
+ return removedCount;
23915
+ };
23712
23916
  const getSpriteState = (spriteId) => {
23713
23917
  return sprites.get(spriteId);
23714
23918
  };
@@ -24061,6 +24265,124 @@ const createSpriteLayer = (options) => {
24061
24265
  }
24062
24266
  return updatedCount;
24063
24267
  };
24268
+ const mutateSprites = (sourceItems, mutator) => {
24269
+ if (sourceItems.length === 0) {
24270
+ return 0;
24271
+ }
24272
+ let changedCount = 0;
24273
+ let isRequiredRender = false;
24274
+ let currentSprite = void 0;
24275
+ let didMutateImages = false;
24276
+ const operationResult = {
24277
+ isUpdated: false
24278
+ };
24279
+ const updateObject = {
24280
+ getImageIndexMap: () => {
24281
+ const map2 = /* @__PURE__ */ new Map();
24282
+ currentSprite.images.forEach((inner, subLayer) => {
24283
+ map2.set(subLayer, new Set(inner.keys()));
24284
+ });
24285
+ return map2;
24286
+ },
24287
+ addImage: (subLayer, order, imageInit) => {
24288
+ const added = addSpriteImageInternal(
24289
+ currentSprite,
24290
+ subLayer,
24291
+ order,
24292
+ imageInit,
24293
+ operationResult
24294
+ );
24295
+ if (added) {
24296
+ didMutateImages = true;
24297
+ }
24298
+ return added;
24299
+ },
24300
+ updateImage: (subLayer, order, imageUpdate) => {
24301
+ const updated = updateSpriteImageInternal(
24302
+ currentSprite,
24303
+ subLayer,
24304
+ order,
24305
+ imageUpdate,
24306
+ operationResult
24307
+ );
24308
+ if (updated) {
24309
+ didMutateImages = true;
24310
+ }
24311
+ return updated;
24312
+ },
24313
+ removeImage: (subLayer, order) => {
24314
+ const removed = removeSpriteImageInternal(
24315
+ currentSprite,
24316
+ subLayer,
24317
+ order,
24318
+ operationResult
24319
+ );
24320
+ if (removed) {
24321
+ didMutateImages = true;
24322
+ }
24323
+ return removed;
24324
+ }
24325
+ };
24326
+ for (const sourceItem of sourceItems) {
24327
+ const spriteId = sourceItem.spriteId;
24328
+ const sprite = sprites.get(spriteId);
24329
+ if (!sprite) {
24330
+ const init = mutator.add(sourceItem);
24331
+ if (!init) {
24332
+ continue;
24333
+ }
24334
+ if (addSpriteInternal(spriteId, init)) {
24335
+ changedCount++;
24336
+ isRequiredRender = true;
24337
+ }
24338
+ continue;
24339
+ }
24340
+ currentSprite = sprite;
24341
+ operationResult.isUpdated = false;
24342
+ didMutateImages = false;
24343
+ const decision = mutator.modify(
24344
+ sourceItem,
24345
+ sprite,
24346
+ updateObject
24347
+ );
24348
+ if (decision === "remove") {
24349
+ if (removeSpriteInternal(spriteId)) {
24350
+ changedCount++;
24351
+ isRequiredRender = true;
24352
+ }
24353
+ } else {
24354
+ const updateResult = updateSpriteInternal(spriteId, updateObject);
24355
+ let spriteChanged = false;
24356
+ switch (updateResult) {
24357
+ case "updated":
24358
+ spriteChanged = true;
24359
+ break;
24360
+ case "isRequiredRender":
24361
+ spriteChanged = true;
24362
+ isRequiredRender = true;
24363
+ break;
24364
+ }
24365
+ if (didMutateImages) {
24366
+ spriteChanged = true;
24367
+ isRequiredRender = true;
24368
+ }
24369
+ if (spriteChanged) {
24370
+ changedCount++;
24371
+ }
24372
+ }
24373
+ updateObject.isEnabled = void 0;
24374
+ updateObject.location = void 0;
24375
+ updateObject.interpolation = void 0;
24376
+ updateObject.tag = void 0;
24377
+ operationResult.isUpdated = false;
24378
+ didMutateImages = false;
24379
+ }
24380
+ if (isRequiredRender) {
24381
+ ensureRenderTargetEntries();
24382
+ scheduleRender();
24383
+ }
24384
+ return changedCount;
24385
+ };
24064
24386
  const updateForEach = (updater) => {
24065
24387
  let updatedCount = 0;
24066
24388
  let isRequiredRender = false;
@@ -24135,14 +24457,21 @@ const createSpriteLayer = (options) => {
24135
24457
  registerImage,
24136
24458
  registerTextGlyph,
24137
24459
  unregisterImage,
24460
+ unregisterAllImages,
24461
+ getAllImageIds,
24138
24462
  addSprite,
24463
+ addSprites,
24139
24464
  removeSprite,
24465
+ removeSprites,
24466
+ removeAllSprites,
24467
+ removeAllSpriteImages,
24140
24468
  getSpriteState,
24141
24469
  addSpriteImage,
24142
24470
  updateSpriteImage,
24143
24471
  removeSpriteImage,
24144
24472
  updateSprite,
24145
24473
  updateBulk,
24474
+ mutateSprites,
24146
24475
  updateForEach,
24147
24476
  on: addEventListener2,
24148
24477
  off: removeEventListener2