Skip to content

Commit

Permalink
[GLJS-1144] Fixed user-rendered icons (internal-2091)
Browse files Browse the repository at this point in the history
  • Loading branch information
underoot committed Jan 2, 2025
1 parent bb99506 commit 4060201
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 6 deletions.
19 changes: 14 additions & 5 deletions src/render/image_atlas.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import assert from 'assert';
import {RGBAImage} from '../util/image';
import {register} from '../util/web_worker_transfer';
import ResolvedImage from '../style-spec/expression/types/resolved_image';
import potpack from 'potpack';
import {ImageIdWithOptions} from '../style-spec/expression/types/image_id_with_options';

import type {StyleImage} from '../style/style_image';
import type ImageManager from './image_manager';
Expand Down Expand Up @@ -153,18 +153,27 @@ export default class ImageAtlas {
positions[id] = new ImagePosition(bin, src, padding);

if (src.hasRenderCallback) {
this.haveRenderCallbacks.push(id);
this.haveRenderCallbacks.push(ImageIdWithOptions.deserializeId(id));
}
}
}

patchUpdatedImages(imageManager: ImageManager, texture: Texture, scope: string) {
this.haveRenderCallbacks = this.haveRenderCallbacks.filter(id => imageManager.hasImage(id, scope));
imageManager.dispatchRenderCallbacks(this.haveRenderCallbacks, scope);

for (const name in imageManager.getUpdatedImages(scope)) {
const imageKey = ResolvedImage.build(name).getSerializedPrimary();
this.patchUpdatedImage(this.iconPositions[imageKey], imageManager.getImage(name, scope), texture);
this.patchUpdatedImage(this.patternPositions[imageKey], imageManager.getImage(name, scope), texture);
for (const id of Object.keys(this.iconPositions)) {
if (ImageIdWithOptions.deserializeId(id) === name) {
this.patchUpdatedImage(this.iconPositions[id], imageManager.getImage(name, scope), texture);
}
}

for (const id of Object.keys(this.patternPositions)) {
if (ImageIdWithOptions.deserializeId(id) === name) {
this.patchUpdatedImage(this.patternPositions[id], imageManager.getImage(name, scope), texture);
}
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/style-spec/expression/types/image_id_with_options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class ImageIdWithOptions {
}
}

static deserializeId(serialized: string): string {
return JSON.parse(serialized).id;
}

static deserializeFromString(serialized: string): ImageIdWithOptions {
const deserializedObject = JSON.parse(serialized);
const options: RasterizationOptions = {params: deserializedObject.options.params};
Expand Down
2 changes: 1 addition & 1 deletion src/symbol/symbol_layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export function performSymbolLayout(bucket: SymbolBucket,
let isUSVGIcon = false;
if (feature.icon && feature.icon.namePrimary) {
const iconSizeFactor = getRasterizedIconSize(bucket.iconSizeData, unevaluatedLayoutValues['icon-size'], canonical, bucket.zoom, feature);
const scaleFactor = iconSizeFactor * sizes.iconScaleFactor * pixelRatio;
const scaleFactor = iconSizeFactor * sizes.iconScaleFactor * pixelRatio;
const primaryImageSerialized = feature.icon.getPrimary().scaleSelf(scaleFactor).serialize();
const image = imageMap[primaryImageSerialized];
if (image) {
Expand Down
59 changes: 59 additions & 0 deletions test/integration/image/dot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const size = 200;

export const image = {
width: size,
height: size,
data: new Uint8Array(size * size * 4),

onAdd: function () {
const canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
this.context = canvas.getContext('2d');
},

render: function () {
const radius = (size / 2) * 0.3;
const outerRadius = (size / 2) * 0.7 + radius;
const context = this.context;

// Draw the outer circle.
context.clearRect(0, 0, this.width, this.height);
context.beginPath();
context.arc(
this.width / 2,
this.height / 2,
outerRadius,
0,
Math.PI * 2
);
context.fillStyle = `rgba(255, 200, 200, 1)`;
context.fill();

// Draw the inner circle.
context.beginPath();
context.arc(
this.width / 2,
this.height / 2,
radius,
0,
Math.PI * 2
);
context.fillStyle = 'rgba(255, 100, 100, 1)';
context.strokeStyle = 'white';
context.lineWidth = 2 + 4;
context.fill();
context.stroke();

// Update this image's data with data from the canvas.
this.data = context.getImageData(
0,
0,
this.width,
this.height
).data;

// Return `true` to let the map know that the image was updated.
return true;
}
}
8 changes: 8 additions & 0 deletions test/integration/lib/operation-handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ export const operationHandlers = {
setTimeout(doneCb, params[0]);
},
addImage(map, params, doneCb) {
if (params[1].endsWith('.js')) {
import(params[1].replace('./', '../')).then(({image}) => {
map.addImage(params[0], image, params[2] || {});
doneCb();
});
return;
}

const image = new Image();
image.onload = () => {
map.addImage(params[0], image, params[2] || {});
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions test/integration/render-tests/image/render-callback/style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"version": 8,
"metadata": {
"test": {
"width": 200,
"height": 200,
"operations": [
[
"addImage",
"dot",
"./image/dot.js"
],
[
"wait"
]
]
}
},
"sources": {
"dot-point": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
0,
0
]
}
}
]
}
}
},
"layers": [
{
"id": "layer-with-dot",
"type": "symbol",
"source": "dot-point",
"layout": {
"icon-image": "dot"
}
}
]
}

0 comments on commit 4060201

Please # to comment.