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