@tangram.gl/tangram-renderer 0.0.0-stage → 0.0.1
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/README.md +60 -2
- package/dist/core.js +36923 -0
- package/dist/core.js.map +6 -0
- package/dist/index.js +32 -0
- package/dist/loaders-gl-worker.js +82 -0
- package/dist/loaders-gl-worker.js.map +6 -0
- package/dist/map-logic.js +2 -0
- package/dist/map-logic.js.map +6 -0
- package/dist/projected-basemaps-worker.js +3 -0
- package/dist/projected-basemaps-worker.js.map +6 -0
- package/dist/shader-modules.js +141 -0
- package/dist/shader-modules.js.map +6 -0
- package/dist/style-schema.js +126 -0
- package/dist/style-schema.js.map +6 -0
- package/dist/tangram-style.schema.json +882 -0
- package/dist/tangram.debug.mjs +37139 -0
- package/dist/tangram.debug.mjs.map +6 -0
- package/dist/tangram.min.mjs +4950 -0
- package/dist/types/builders/earcut.d.ts +15 -0
- package/dist/types/core.d.ts +29 -0
- package/dist/types/gl/constants.d.ts +2 -0
- package/dist/types/gl/globe_mesh.d.ts +48 -0
- package/dist/types/gl/glsl.d.ts +17 -0
- package/dist/types/gl/vertex-constants.d.ts +4 -0
- package/dist/types/gl/vertex-types.d.ts +34 -0
- package/dist/types/gl/vertex_data.d.ts +32 -0
- package/dist/types/gl/vertex_elements.d.ts +8 -0
- package/dist/types/gl/vertex_layout.d.ts +32 -0
- package/dist/types/glsl.d.ts +14 -0
- package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
- package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
- package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/lights/light-definitions.d.ts +121 -0
- package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
- package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
- package/dist/types/map-logic/globe-terrain.d.ts +54 -0
- package/dist/types/map-logic/index.d.ts +33 -0
- package/dist/types/map-logic/label-identity.d.ts +22 -0
- package/dist/types/map-logic/label-placement.d.ts +32 -0
- package/dist/types/map-logic/screen-bounds.d.ts +15 -0
- package/dist/types/map-logic/terrain-data.d.ts +55 -0
- package/dist/types/map-logic/terrain-error.d.ts +51 -0
- package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
- package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
- package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
- package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
- package/dist/types/map-logic/terrain-query.d.ts +33 -0
- package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
- package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
- package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
- package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
- package/dist/types/map-logic/tile-residency.d.ts +17 -0
- package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
- package/dist/types/procedures/js-yaml.d.ts +15 -0
- package/dist/types/procedures/mesh-projector.d.ts +107 -0
- package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
- package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
- package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
- package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
- package/dist/types/procedures/terrain-draping.d.ts +165 -0
- package/dist/types/procedures/terrain-globe.d.ts +6 -0
- package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
- package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
- package/dist/types/procedures/web-mercator-math.d.ts +4 -0
- package/dist/types/scene/globals.d.ts +11 -0
- package/dist/types/scene/ground_footprint.d.ts +16 -0
- package/dist/types/scene/host_frame.d.ts +54 -0
- package/dist/types/scene/projection_math.d.ts +52 -0
- package/dist/types/scene/scene-resource-types.d.ts +177 -0
- package/dist/types/scene/scene_bundle.d.ts +69 -0
- package/dist/types/scene/visibility_adapter.d.ts +106 -0
- package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
- package/dist/types/selection/terrain_surface.d.ts +149 -0
- package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
- package/dist/types/shader-modules/height-decode.d.ts +13 -0
- package/dist/types/shader-modules/hillshade.d.ts +13 -0
- package/dist/types/shader-modules/index.d.ts +6 -0
- package/dist/types/shader-modules/planar.d.ts +12 -0
- package/dist/types/shader-modules/sphere-map.d.ts +13 -0
- package/dist/types/shader-modules/triplanar.d.ts +15 -0
- package/dist/types/sources/decoded_tile_store.d.ts +95 -0
- package/dist/types/sources/tile_source_metadata.d.ts +32 -0
- package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
- package/dist/types/tile/tile_id.d.ts +37 -0
- package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
- package/dist/types/types.d.ts +293 -0
- package/dist/types/utils/geo.d.ts +57 -0
- package/dist/types/utils/hash.d.ts +1 -0
- package/dist/types/utils/log.d.ts +27 -0
- package/dist/types/utils/props.d.ts +6 -0
- package/dist/types/utils/subscribe.d.ts +10 -0
- package/dist/types/utils/thread.d.ts +6 -0
- package/dist/types/utils/urls.d.ts +26 -0
- package/dist/types/utils/utils.d.ts +26 -0
- package/dist/types/utils/version.d.ts +2 -0
- package/dist/types/utils/worker-types.d.ts +68 -0
- package/dist/types/utils/worker_broker.d.ts +3 -0
- package/package.json +103 -3
- package/src/builders/common.ts +41 -0
- package/src/builders/earcut.d.ts +15 -0
- package/src/builders/geometry-types.ts +60 -0
- package/src/builders/points.ts +115 -0
- package/src/builders/polygons.ts +225 -0
- package/src/builders/polylines.ts +682 -0
- package/src/builders/wireframe.ts +31 -0
- package/src/core.d.ts +29 -0
- package/src/core.ts +33 -0
- package/src/experimental/loaders-gl-worker.ts +19 -0
- package/src/experimental/projected-basemaps-worker.ts +13 -0
- package/src/experimental/projected-mesh-cache.ts +137 -0
- package/src/experimental/projected-mesh-domain.ts +110 -0
- package/src/experimental/projected-mesh-refinement.ts +193 -0
- package/src/experimental/projected-mesh.ts +399 -0
- package/src/experimental/projected-normal.ts +26 -0
- package/src/experimental/projected-symbol-mesh.ts +74 -0
- package/src/gl/constants.ts +18 -0
- package/src/gl/context.ts +103 -0
- package/src/gl/extensions.ts +30 -0
- package/src/gl/globe_mesh.ts +285 -0
- package/src/gl/glsl.ts +257 -0
- package/src/gl/mesh-types.ts +56 -0
- package/src/gl/render_state.ts +111 -0
- package/src/gl/shader_program.ts +1310 -0
- package/src/gl/texture.ts +814 -0
- package/src/gl/uniform_buffer.ts +340 -0
- package/src/gl/vao.ts +154 -0
- package/src/gl/vbo_mesh.ts +349 -0
- package/src/gl/vertex-constants.ts +19 -0
- package/src/gl/vertex-types.ts +35 -0
- package/src/gl/vertex_data.ts +126 -0
- package/src/gl/vertex_elements.ts +54 -0
- package/src/gl/vertex_layout.ts +241 -0
- package/src/glsl.d.ts +14 -0
- package/src/gpu/luma_device_renderer.ts +579 -0
- package/src/gpu/pixel_readback.ts +51 -0
- package/src/gpu/resource_lifecycle.ts +32 -0
- package/src/gpu/tangram_gpu_backend.ts +195 -0
- package/src/index.d.ts +155 -0
- package/src/index.ts +105 -0
- package/src/labels/collision-types.ts +94 -0
- package/src/labels/collision.ts +273 -0
- package/src/labels/collision_grid.ts +61 -0
- package/src/labels/intersect.ts +30 -0
- package/src/labels/label-types.ts +91 -0
- package/src/labels/label.ts +207 -0
- package/src/labels/label_line.ts +801 -0
- package/src/labels/label_point.ts +131 -0
- package/src/labels/main-pass-types.ts +73 -0
- package/src/labels/main_pass.ts +221 -0
- package/src/labels/point_anchor.ts +100 -0
- package/src/labels/point_placement.ts +150 -0
- package/src/labels/projected-pass.ts +259 -0
- package/src/labels/repeat_group.ts +72 -0
- package/src/lights/ambient_light.glsl +18 -0
- package/src/lights/directional_light.glsl +39 -0
- package/src/lights/geographic-lights.ts +21 -0
- package/src/lights/light-definitions.ts +285 -0
- package/src/lights/light.ts +590 -0
- package/src/lights/lighting-uniforms.ts +80 -0
- package/src/lights/lighting-wgsl.ts +116 -0
- package/src/lights/material.glsl +237 -0
- package/src/lights/material.ts +145 -0
- package/src/lights/native-falloff.ts +37 -0
- package/src/lights/point_light.glsl +96 -0
- package/src/lights/spot_light.glsl +111 -0
- package/src/map-logic/draped-terrain-bounds.ts +71 -0
- package/src/map-logic/globe-terrain-preparation.ts +217 -0
- package/src/map-logic/globe-terrain.ts +296 -0
- package/src/map-logic/index.ts +37 -0
- package/src/map-logic/label-identity.ts +36 -0
- package/src/map-logic/label-placement.ts +117 -0
- package/src/map-logic/screen-bounds.ts +34 -0
- package/src/map-logic/terrain-data.ts +107 -0
- package/src/map-logic/terrain-error.ts +134 -0
- package/src/map-logic/terrain-ground-topology.ts +164 -0
- package/src/map-logic/terrain-height-field.ts +254 -0
- package/src/map-logic/terrain-navigation.ts +50 -0
- package/src/map-logic/terrain-neighborhood.ts +43 -0
- package/src/map-logic/terrain-query.ts +51 -0
- package/src/map-logic/terrain-resource-budget.ts +124 -0
- package/src/map-logic/terrain-transition-topology.ts +164 -0
- package/src/map-logic/tile-build-queue.ts +110 -0
- package/src/map-logic/tile-cache-policy.ts +89 -0
- package/src/map-logic/tile-residency.ts +50 -0
- package/src/map-logic/triangle-query-index.ts +265 -0
- package/src/procedures/js-yaml.d.ts +15 -0
- package/src/procedures/mesh-projector.ts +191 -0
- package/src/procedures/mlt-loaders.ts +55 -0
- package/src/procedures/mvt-dependencies.d.ts +20 -0
- package/src/procedures/mvt-legacy.ts +150 -0
- package/src/procedures/mvt-loaders.ts +53 -0
- package/src/procedures/mvt-parser.ts +48 -0
- package/src/procedures/mvt-properties.ts +36 -0
- package/src/procedures/mvt-tile-provider.ts +72 -0
- package/src/procedures/pmtiles-loader.ts +75 -0
- package/src/procedures/projected-coordinate-transform.ts +79 -0
- package/src/procedures/projection-batch-executor.ts +140 -0
- package/src/procedures/scene-yaml-legacy.ts +16 -0
- package/src/procedures/scene-yaml-loaders.ts +10 -0
- package/src/procedures/terrain-draping.ts +713 -0
- package/src/procedures/terrain-globe.ts +32 -0
- package/src/procedures/terrain-styled-topology.ts +184 -0
- package/src/procedures/web-mercator-legacy.ts +24 -0
- package/src/procedures/web-mercator-math.ts +50 -0
- package/src/scene/camera.ts +298 -0
- package/src/scene/camera_base.ts +116 -0
- package/src/scene/classic_scene.ts +20 -0
- package/src/scene/external_camera.ts +101 -0
- package/src/scene/globals.ts +120 -0
- package/src/scene/ground_footprint.ts +139 -0
- package/src/scene/host_frame.ts +326 -0
- package/src/scene/projected_tile_lod.ts +125 -0
- package/src/scene/projection_math.ts +142 -0
- package/src/scene/projection_shaders.ts +183 -0
- package/src/scene/renderer.ts +516 -0
- package/src/scene/scene-resource-types.ts +174 -0
- package/src/scene/scene.ts +2378 -0
- package/src/scene/scene_bundle.ts +304 -0
- package/src/scene/scene_debug.ts +129 -0
- package/src/scene/scene_loader.ts +402 -0
- package/src/scene/scene_worker.ts +528 -0
- package/src/scene/view.ts +683 -0
- package/src/scene/visibility_adapter.ts +319 -0
- package/src/scene/worker_url.ts +15 -0
- package/src/selection/draped-terrain-surface.ts +377 -0
- package/src/selection/selection-types.ts +79 -0
- package/src/selection/selection.ts +542 -0
- package/src/selection/selection_fragment.glsl +18 -0
- package/src/selection/selection_globals.glsl +9 -0
- package/src/selection/selection_pixels.ts +59 -0
- package/src/selection/selection_vertex.glsl +15 -0
- package/src/selection/terrain_surface.ts +298 -0
- package/src/shader-modules/globe-horizon.ts +52 -0
- package/src/shader-modules/height-decode.ts +41 -0
- package/src/shader-modules/hillshade.ts +70 -0
- package/src/shader-modules/index.ts +10 -0
- package/src/shader-modules/planar.ts +24 -0
- package/src/shader-modules/sphere-map.ts +38 -0
- package/src/shader-modules/triplanar.ts +45 -0
- package/src/sources/aligned_tile_source.ts +122 -0
- package/src/sources/data_source.ts +655 -0
- package/src/sources/decoded_tile_store.ts +245 -0
- package/src/sources/geojson.ts +300 -0
- package/src/sources/mvt.ts +247 -0
- package/src/sources/raster.ts +341 -0
- package/src/sources/shared_tile_source_adapter.ts +117 -0
- package/src/sources/sources.ts +10 -0
- package/src/sources/tile_source_adapter.ts +84 -0
- package/src/sources/tile_source_metadata.ts +65 -0
- package/src/sources/tile_source_state.ts +112 -0
- package/src/sources/topojson.ts +90 -0
- package/src/styles/feature_annotations.ts +16 -0
- package/src/styles/filter.ts +190 -0
- package/src/styles/geometry-style-types.ts +209 -0
- package/src/styles/globe_visibility_wgsl.ts +22 -0
- package/src/styles/layer-types.ts +45 -0
- package/src/styles/layer.ts +586 -0
- package/src/styles/lines/dasharray.ts +49 -0
- package/src/styles/lines/lines.ts +707 -0
- package/src/styles/lines/lines_wgsl.ts +224 -0
- package/src/styles/points/point-runtime-types.ts +125 -0
- package/src/styles/points/points.ts +1079 -0
- package/src/styles/points/points_fragment.glsl +126 -0
- package/src/styles/points/points_vertex.glsl +230 -0
- package/src/styles/points/points_wgsl.ts +189 -0
- package/src/styles/polygons/polygons.ts +268 -0
- package/src/styles/polygons/polygons_fragment.glsl +173 -0
- package/src/styles/polygons/polygons_vertex.glsl +213 -0
- package/src/styles/polygons/polygons_wgsl.ts +127 -0
- package/src/styles/property-types.ts +125 -0
- package/src/styles/raster/raster.ts +33 -0
- package/src/styles/raster/raster_globals.glsl +46 -0
- package/src/styles/style-mixing-types.ts +101 -0
- package/src/styles/style-runtime-types.ts +220 -0
- package/src/styles/style-schema.ts +133 -0
- package/src/styles/style.ts +1033 -0
- package/src/styles/style_globals.glsl +69 -0
- package/src/styles/style_manager.ts +464 -0
- package/src/styles/style_parser.ts +620 -0
- package/src/styles/text/font_manager.ts +180 -0
- package/src/styles/text/fontfaceobserver.d.ts +10 -0
- package/src/styles/text/text-types.ts +220 -0
- package/src/styles/text/text.ts +378 -0
- package/src/styles/text/text_canvas.ts +773 -0
- package/src/styles/text/text_labels.ts +436 -0
- package/src/styles/text/text_segments.ts +142 -0
- package/src/styles/text/text_settings.ts +228 -0
- package/src/styles/text/text_wgsl.ts +159 -0
- package/src/styles/text/text_wrap.ts +194 -0
- package/src/tile/globe_tile_preload.ts +40 -0
- package/src/tile/tangram_tileset_2d.ts +108 -0
- package/src/tile/tile-build-types.ts +155 -0
- package/src/tile/tile.ts +807 -0
- package/src/tile/tile_id.ts +172 -0
- package/src/tile/tile_manager.ts +889 -0
- package/src/tile/tile_pyramid.ts +119 -0
- package/src/tile/tile_resource_cache.ts +91 -0
- package/src/tile/tile_traversal_adapter.ts +132 -0
- package/src/types.ts +332 -0
- package/src/utils/debounce.ts +17 -0
- package/src/utils/debug_settings.ts +43 -0
- package/src/utils/errors.ts +12 -0
- package/src/utils/functions.ts +95 -0
- package/src/utils/geo.ts +364 -0
- package/src/utils/hash.ts +14 -0
- package/src/utils/log.ts +108 -0
- package/src/utils/media_capture.ts +193 -0
- package/src/utils/merge.ts +44 -0
- package/src/utils/obb.ts +134 -0
- package/src/utils/props.ts +43 -0
- package/src/utils/slice.ts +14 -0
- package/src/utils/subscribe.ts +58 -0
- package/src/utils/task.ts +154 -0
- package/src/utils/thread.ts +26 -0
- package/src/utils/urls.ts +223 -0
- package/src/utils/utils.ts +310 -0
- package/src/utils/vector.ts +197 -0
- package/src/utils/version.ts +8 -0
- package/src/utils/worker-types.ts +79 -0
- package/src/utils/worker_broker.ts +523 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
// Tangram
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) 2013-2016 Brett Camper and Mapzen
|
|
4
|
+
// Copyright (c) 2026 vis.gl contributors
|
|
5
|
+
|
|
6
|
+
import type {TaskRecord} from '../../utils/task';
|
|
7
|
+
import type {TextSettingsResult} from './text_settings';
|
|
8
|
+
import type {LabelPointCoordinate} from '../../labels/label-types';
|
|
9
|
+
import type {TextTable, TextSize, TextLine, TextMeasurement, TextAtlas, TextAtlasCursor, TextSizesTask, TextRasterTask} from './text-types';
|
|
10
|
+
import log from '../../utils/log';
|
|
11
|
+
import Utils from '../../utils/utils';
|
|
12
|
+
import Texture from '../../gl/texture';
|
|
13
|
+
import FontManager from './font_manager';
|
|
14
|
+
import Task from '../../utils/task';
|
|
15
|
+
import StyleParser from '../style_parser';
|
|
16
|
+
/** Temporary parser boundary until the full style parser is checked. */
|
|
17
|
+
const typedStyleParser = StyleParser as unknown as {parsePositiveNumber(value: unknown): number};
|
|
18
|
+
/** Texture registry boundary; GPU ownership remains with the existing implementation. */
|
|
19
|
+
const typedTexture = Texture as unknown as {
|
|
20
|
+
getTexcoordsForSprite(origin: LabelPointCoordinate, size: LabelPointCoordinate, atlas: LabelPointCoordinate): number[];
|
|
21
|
+
create(context: unknown, name: string, options: {element: HTMLCanvasElement; filtering: string; UNPACK_PREMULTIPLY_ALPHA_WEBGL: boolean}): unknown;
|
|
22
|
+
retain(name: string): void;
|
|
23
|
+
release(name: string): void;
|
|
24
|
+
};
|
|
25
|
+
import MultiLine from './text_wrap';
|
|
26
|
+
import { splitLabelText, isTextRTL, isTextNeutral, isTextCurveBlacklisted } from './text_segments';
|
|
27
|
+
import debugSettings from '../../utils/debug_settings';
|
|
28
|
+
|
|
29
|
+
/** Measures, packs, and rasterizes per-tile label atlases on the main thread. */
|
|
30
|
+
export default class TextCanvas {
|
|
31
|
+
/** Canvas and context installed by createCanvas. */
|
|
32
|
+
declare canvas: HTMLCanvasElement;
|
|
33
|
+
declare context: CanvasRenderingContext2D;
|
|
34
|
+
/** Pixel paddings around rendered text. */
|
|
35
|
+
declare vertical_text_buffer: number;
|
|
36
|
+
declare horizontal_text_buffer: number;
|
|
37
|
+
declare background_size: number;
|
|
38
|
+
/** Device-pixel font size installed by setFont before measurement. */
|
|
39
|
+
declare px_size: number;
|
|
40
|
+
/** CSS size parser and shared bounded measurement/segmentation cache. */
|
|
41
|
+
declare static font_size_re: RegExp;
|
|
42
|
+
declare static cache: {
|
|
43
|
+
text: Record<string, Record<string, TextMeasurement>>;
|
|
44
|
+
text_count: number;
|
|
45
|
+
text_count_max: number;
|
|
46
|
+
segment: Record<string, string[]>;
|
|
47
|
+
segment_count_max: number;
|
|
48
|
+
stats: {text_hits: number; text_misses: number; segment_hits: number; segment_misses: number};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
constructor () {
|
|
53
|
+
this.createCanvas(); // create initial canvas and context
|
|
54
|
+
this.vertical_text_buffer = 8; // vertical pixel padding around text
|
|
55
|
+
this.horizontal_text_buffer = 4; // text styling such as italic emphasis is not measured by the Canvas API, so padding is necessary
|
|
56
|
+
this.background_size = 4; // padding around label for optional background box (TODO: make configurable?)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
createCanvas () {
|
|
60
|
+
this.canvas = document.createElement('canvas');
|
|
61
|
+
this.canvas.style.backgroundColor = 'transparent'; // render text on transparent background
|
|
62
|
+
this.context = this.canvas.getContext('2d')!;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
resize (width: number, height: number): void {
|
|
66
|
+
this.canvas.width = width;
|
|
67
|
+
this.canvas.height = height;
|
|
68
|
+
this.context.clearRect(0, 0, width, height);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Set font style params for canvas drawing
|
|
72
|
+
setFont ({ font_css, fill, stroke, stroke_width, px_size, supersample }: TextSettingsResult): void {
|
|
73
|
+
this.px_size = px_size!;
|
|
74
|
+
let ctx = this.context;
|
|
75
|
+
let dpr = Utils.device_pixel_ratio! * supersample;
|
|
76
|
+
|
|
77
|
+
if (stroke && stroke_width! > 0) {
|
|
78
|
+
ctx.strokeStyle = stroke;
|
|
79
|
+
ctx.lineWidth = stroke_width! * dpr;
|
|
80
|
+
}
|
|
81
|
+
ctx.fillStyle = fill!;
|
|
82
|
+
|
|
83
|
+
ctx.font = font_css!;
|
|
84
|
+
ctx.miterLimit = 2;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async textSizes (tile_id: string, texts: TextTable): Promise<TextTable | undefined> {
|
|
88
|
+
await FontManager.loadFonts();
|
|
89
|
+
return Task.add({
|
|
90
|
+
type: 'textSizes',
|
|
91
|
+
run: this.processTextSizesTask.bind(this),
|
|
92
|
+
texts,
|
|
93
|
+
tile_id,
|
|
94
|
+
cursor: {
|
|
95
|
+
styles: Object.keys(texts),
|
|
96
|
+
texts: null,
|
|
97
|
+
style_idx: null,
|
|
98
|
+
text_idx: null
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
processTextSizesTask (task: TaskRecord<TextTable>): boolean {
|
|
104
|
+
let { cursor, texts } = task as TextSizesTask;
|
|
105
|
+
cursor.style_idx = cursor.style_idx || 0;
|
|
106
|
+
|
|
107
|
+
while (cursor.style_idx! < cursor.styles.length) {
|
|
108
|
+
let style = cursor.styles[cursor.style_idx!];
|
|
109
|
+
if (cursor.text_idx == null) {
|
|
110
|
+
cursor.text_idx = 0;
|
|
111
|
+
cursor.texts = Object.keys(texts[style]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let text_infos = texts[style];
|
|
115
|
+
let first = true;
|
|
116
|
+
|
|
117
|
+
while (cursor.text_idx < cursor.texts!.length) {
|
|
118
|
+
let text = cursor.texts![cursor.text_idx];
|
|
119
|
+
let text_info = text_infos[text];
|
|
120
|
+
let text_settings = text_info.text_settings;
|
|
121
|
+
|
|
122
|
+
if (first) {
|
|
123
|
+
this.setFont(text_settings);
|
|
124
|
+
first = false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// add size of full text string
|
|
128
|
+
text_info.size = this.textSize(style, text, text_settings).size;
|
|
129
|
+
|
|
130
|
+
// if text may curve, calculate per-segment as well
|
|
131
|
+
if (text_settings.can_articulate) {
|
|
132
|
+
let rtl = false;
|
|
133
|
+
let bidi = false;
|
|
134
|
+
if (isTextRTL(text)) {
|
|
135
|
+
if (!isTextNeutral(text)) {
|
|
136
|
+
bidi = true;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
rtl = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
text_info.isRTL = rtl;
|
|
144
|
+
text_info.no_curving = bidi || isTextCurveBlacklisted(text); // used in LabelLine to prevent curved labels
|
|
145
|
+
text_info.vertical_buffer = this.vertical_text_buffer;
|
|
146
|
+
text_info.segment_sizes = [];
|
|
147
|
+
|
|
148
|
+
if (!text_info.no_curving) {
|
|
149
|
+
let segments = splitLabelText(text, rtl, TextCanvas.cache);
|
|
150
|
+
text_info.segments = segments;
|
|
151
|
+
for (let i = 0; i < segments.length; i++){
|
|
152
|
+
text_info.segment_sizes!.push(this.textSize(style, segments[i], text_settings).size);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
cursor.text_idx++;
|
|
158
|
+
|
|
159
|
+
if (!Task.shouldContinue(task)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
cursor.text_idx = null;
|
|
164
|
+
cursor.style_idx!++;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Task.finish(task, texts);
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Computes width and height of text based on current font style
|
|
172
|
+
// Includes word wrapping, returns size info for whole text block and individual lines
|
|
173
|
+
textSize(style: string, text: string, { transform, text_wrap, max_lines, stroke_width = 0, background_color, background_stroke_width = 0, background_width, underline_width = 0, supersample }: TextSettingsResult): TextMeasurement {
|
|
174
|
+
// Check cache first
|
|
175
|
+
TextCanvas.cache.text[style] = TextCanvas.cache.text[style] || {};
|
|
176
|
+
if (TextCanvas.cache.text[style][text]) {
|
|
177
|
+
TextCanvas.cache.stats.text_hits++;
|
|
178
|
+
return TextCanvas.cache.text[style][text];
|
|
179
|
+
}
|
|
180
|
+
TextCanvas.cache.stats.text_misses++;
|
|
181
|
+
TextCanvas.cache.text_count++;
|
|
182
|
+
|
|
183
|
+
// Calc and store in cache
|
|
184
|
+
const dpr = Utils.device_pixel_ratio! * supersample;
|
|
185
|
+
const str = this.applyTextTransform(text, transform);
|
|
186
|
+
const ctx = this.context;
|
|
187
|
+
const vertical_buffer = this.vertical_text_buffer * dpr;
|
|
188
|
+
const horizontal_buffer = (stroke_width + this.horizontal_text_buffer) * dpr;
|
|
189
|
+
|
|
190
|
+
background_width = background_width != null ? background_width : this.background_size; // apply default background width
|
|
191
|
+
const background_size = (background_color || background_stroke_width) ? (background_width + background_stroke_width) * dpr : 0;
|
|
192
|
+
|
|
193
|
+
const leading = (2 + underline_width + (underline_width ? (stroke_width + 1) : 0)) * dpr; // adjust for underline and text stroke
|
|
194
|
+
const line_height = this.px_size + leading; // px_size already in device pixels
|
|
195
|
+
|
|
196
|
+
// Parse string into series of lines if it exceeds the text wrapping value or contains line breaks
|
|
197
|
+
// const multiline = MultiLine.parse(str, text_wrap!, max_lines!, line_height, ctx);
|
|
198
|
+
let { width, height, lines } = MultiLine.parse(str, text_wrap!, max_lines!, line_height, ctx);
|
|
199
|
+
width += background_size * 2;
|
|
200
|
+
height += background_size * 2;
|
|
201
|
+
|
|
202
|
+
let collision_size: LabelPointCoordinate = [
|
|
203
|
+
width / dpr,
|
|
204
|
+
height / dpr
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
let texture_size: LabelPointCoordinate = [
|
|
208
|
+
width + 2 * horizontal_buffer,
|
|
209
|
+
height + 2 * vertical_buffer
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
let logical_size: LabelPointCoordinate = [
|
|
213
|
+
texture_size[0] / dpr,
|
|
214
|
+
texture_size[1] / dpr,
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
// Returns lines (w/per-line info for drawing) and text's overall bounding box + canvas size
|
|
218
|
+
TextCanvas.cache.text[style][text] = {
|
|
219
|
+
lines,
|
|
220
|
+
size: {
|
|
221
|
+
collision_size, texture_size, logical_size,
|
|
222
|
+
horizontal_buffer, vertical_buffer, dpr, line_height, background_size
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
return TextCanvas.cache.text[style][text];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Draw multiple lines of text
|
|
229
|
+
drawTextMultiLine (lines: TextLine[], [x, y]: LabelPointCoordinate, size: TextSize, text_settings: TextSettingsResult & {align?: string}, label_type?: string): void {
|
|
230
|
+
const { dpr, collision_size, texture_size, line_height, horizontal_buffer, vertical_buffer } = size;
|
|
231
|
+
|
|
232
|
+
// draw optional background box
|
|
233
|
+
if (text_settings.background_color || text_settings.background_stroke_color) {
|
|
234
|
+
const background_stroke_color = text_settings.background_stroke_color;
|
|
235
|
+
const background_stroke_width = (text_settings.background_stroke_width || 0) * dpr;
|
|
236
|
+
|
|
237
|
+
this.context.save();
|
|
238
|
+
|
|
239
|
+
if (text_settings.background_color) {
|
|
240
|
+
this.context.fillStyle = text_settings.background_color;
|
|
241
|
+
this.context.fillRect(
|
|
242
|
+
// shift to "foreground" stroke texture for curved labels (separate stroke and fill textures)
|
|
243
|
+
x + horizontal_buffer + (label_type === 'curved' ? texture_size[0] : 0) + background_stroke_width,
|
|
244
|
+
y + vertical_buffer + background_stroke_width,
|
|
245
|
+
dpr * collision_size[0] - background_stroke_width * 2,
|
|
246
|
+
dpr * collision_size[1] - background_stroke_width * 2
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// optional stroke around background box
|
|
251
|
+
if (background_stroke_color && background_stroke_width) {
|
|
252
|
+
this.context.strokeStyle = background_stroke_color;
|
|
253
|
+
this.context.lineWidth = background_stroke_width;
|
|
254
|
+
this.context.strokeRect(
|
|
255
|
+
// shift to "foreground" stroke texture for curved labels (separate stroke and fill textures)
|
|
256
|
+
x + horizontal_buffer + (label_type === 'curved' ? texture_size[0] : 0) + background_stroke_width! * 0.5,
|
|
257
|
+
y + vertical_buffer + background_stroke_width * 0.5,
|
|
258
|
+
dpr * collision_size[0] - background_stroke_width,
|
|
259
|
+
dpr * collision_size[1] - background_stroke_width
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.context.restore();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// draw text
|
|
267
|
+
const underline_width = text_settings.underline_width || 0;
|
|
268
|
+
const stroke_width = text_settings.stroke_width || 0;
|
|
269
|
+
const voffset = underline_width ? // offset text position to account for underline and text stroke
|
|
270
|
+
((underline_width + stroke_width + 1) * 0.5 * dpr) : 0;
|
|
271
|
+
let ty = y - voffset;
|
|
272
|
+
for (let line_num=0; line_num < lines.length; line_num++) {
|
|
273
|
+
let line = lines[line_num];
|
|
274
|
+
this.drawTextLine(line, [x, ty], size, text_settings, label_type);
|
|
275
|
+
ty += line_height;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
this.drawTextDebug([x, y], size, label_type);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Draw single line of text at specified location, adjusting for buffer and baseline
|
|
282
|
+
drawTextLine(line: TextLine, [x, y]: LabelPointCoordinate, size: TextSize, text_settings: TextSettingsResult & {align?: string}, type?: string): void {
|
|
283
|
+
const { stroke, stroke_width, transform, align = 'center' } = text_settings;
|
|
284
|
+
const { horizontal_buffer, vertical_buffer, texture_size, background_size, line_height, dpr } = size;
|
|
285
|
+
const underline_width = (text_settings.underline_width || 0) * dpr;
|
|
286
|
+
const text = this.applyTextTransform(line.text, transform);
|
|
287
|
+
|
|
288
|
+
// Text alignment
|
|
289
|
+
let tx!: number;
|
|
290
|
+
if (align === 'left') {
|
|
291
|
+
tx = x + horizontal_buffer + background_size;
|
|
292
|
+
}
|
|
293
|
+
else if (align === 'center') {
|
|
294
|
+
tx = x + texture_size[0] / 2 - line.width / 2;
|
|
295
|
+
}
|
|
296
|
+
else if (align === 'right') {
|
|
297
|
+
tx = x + texture_size[0] - line.width - horizontal_buffer - background_size;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// In the absence of better Canvas TextMetrics (not supported by browsers yet),
|
|
301
|
+
// 0.75 buffer produces a better approximate vertical centering of text
|
|
302
|
+
const ty = y + vertical_buffer * 0.75 + line_height + background_size - underline_width * 0.5;
|
|
303
|
+
|
|
304
|
+
// Draw stroke and fill separately for curved text. Offset stroke in texture atlas by shift.
|
|
305
|
+
const shift = (stroke && stroke_width! > 0 && type === 'curved') ? texture_size[0] : 0;
|
|
306
|
+
|
|
307
|
+
// optional text underline
|
|
308
|
+
if (underline_width) {
|
|
309
|
+
this.context.save();
|
|
310
|
+
this.context.strokeStyle = this.context.fillStyle;
|
|
311
|
+
this.context.lineWidth = underline_width;
|
|
312
|
+
|
|
313
|
+
// adjust the underline to account for the text stroke
|
|
314
|
+
const uy = ty + ((stroke_width! * 0.5 + 2) * dpr) + this.context.lineWidth * 0.5;
|
|
315
|
+
|
|
316
|
+
this.context.beginPath();
|
|
317
|
+
this.context.moveTo(tx + shift, uy);
|
|
318
|
+
this.context.lineTo(tx + shift + line.width, uy);
|
|
319
|
+
this.context.stroke();
|
|
320
|
+
this.context.restore();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (stroke && stroke_width! > 0) {
|
|
324
|
+
this.context.strokeText(text, tx + shift, ty);
|
|
325
|
+
}
|
|
326
|
+
this.context.fillText(text, tx, ty);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Draw optional text debug boxes
|
|
330
|
+
drawTextDebug ([x, y]: LabelPointCoordinate, size: TextSize, label_type?: string): void {
|
|
331
|
+
const { dpr, horizontal_buffer, vertical_buffer, texture_size, collision_size } = size;
|
|
332
|
+
const line_width = 2;
|
|
333
|
+
|
|
334
|
+
if (debugSettings.draw_label_collision_boxes) {
|
|
335
|
+
this.context.save();
|
|
336
|
+
this.context.strokeStyle = 'blue';
|
|
337
|
+
this.context.lineWidth = line_width;
|
|
338
|
+
this.context.strokeRect(x + horizontal_buffer, y + vertical_buffer, dpr * collision_size[0], dpr * collision_size[1]);
|
|
339
|
+
if (label_type === 'curved'){
|
|
340
|
+
this.context.strokeRect(x + texture_size[0] + horizontal_buffer, y + vertical_buffer, dpr * collision_size[0], dpr * collision_size[1]);
|
|
341
|
+
}
|
|
342
|
+
this.context.restore();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (debugSettings.draw_label_texture_boxes) {
|
|
346
|
+
this.context.save();
|
|
347
|
+
this.context.strokeStyle = 'green';
|
|
348
|
+
this.context.lineWidth = line_width;
|
|
349
|
+
// stroke is applied internally, so the outer border is the edge of the texture
|
|
350
|
+
this.context.strokeRect(x + line_width, y + line_width, texture_size[0] - 2 * line_width, texture_size[1] - 2 * line_width);
|
|
351
|
+
if (label_type === 'curved') {
|
|
352
|
+
this.context.strokeRect(x + line_width + texture_size[0], y + line_width, texture_size[0] - 2 * line_width, texture_size[1] - 2 * line_width);
|
|
353
|
+
}
|
|
354
|
+
this.context.restore();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
rasterize (texts: TextTable, textures: TextAtlas[], tile_id: string, texture_prefix: string, resource_context: unknown): Promise<string[] | undefined> {
|
|
359
|
+
return Task.add({
|
|
360
|
+
type: 'rasterizeLabels',
|
|
361
|
+
run: this.processRasterizeTask.bind(this),
|
|
362
|
+
cancel: this.cancelRasterizeTask.bind(this),
|
|
363
|
+
pause_factor: 2, // pause 2 frames when task run past allowed time
|
|
364
|
+
user_moving_view: false, // don't run task when user is moving view
|
|
365
|
+
texts,
|
|
366
|
+
textures,
|
|
367
|
+
texture_prefix,
|
|
368
|
+
resource_context,
|
|
369
|
+
tile_id,
|
|
370
|
+
cursor: {
|
|
371
|
+
styles: Object.keys(texts),
|
|
372
|
+
texts: null,
|
|
373
|
+
style_idx: 0,
|
|
374
|
+
text_idx: null,
|
|
375
|
+
texture_idx: 0,
|
|
376
|
+
texture_resize: true,
|
|
377
|
+
texture_names: []
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
processRasterizeTask (task: TaskRecord<string[]>): boolean {
|
|
383
|
+
let { cursor, texts, textures } = task as TextRasterTask;
|
|
384
|
+
let texture;
|
|
385
|
+
|
|
386
|
+
// Rasterize one texture at a time, so we only have to keep one canvas in memory (they can be large)
|
|
387
|
+
while (cursor.texture_idx < (task as TextRasterTask).textures.length) {
|
|
388
|
+
texture = textures[cursor.texture_idx];
|
|
389
|
+
|
|
390
|
+
if (cursor.texture_resize) {
|
|
391
|
+
cursor.texture_resize = false;
|
|
392
|
+
this.resize(...texture.texture_size);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
while (cursor.style_idx! < cursor.styles.length) {
|
|
396
|
+
let style = cursor.styles[cursor.style_idx!];
|
|
397
|
+
if (cursor.text_idx == null) {
|
|
398
|
+
cursor.text_idx = 0;
|
|
399
|
+
cursor.texts = Object.keys(texts[style]);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let text_infos = texts[style];
|
|
403
|
+
let first = true;
|
|
404
|
+
|
|
405
|
+
while (cursor.text_idx < cursor.texts!.length) {
|
|
406
|
+
let text = cursor.texts![cursor.text_idx];
|
|
407
|
+
let text_info = text_infos[text];
|
|
408
|
+
let text_settings = text_info.text_settings;
|
|
409
|
+
|
|
410
|
+
// set font on first occurence of new font style
|
|
411
|
+
if (first) {
|
|
412
|
+
this.setFont(text_settings);
|
|
413
|
+
first = false;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (text_settings.can_articulate) {
|
|
417
|
+
text_info.texcoords = text_info.texcoords || {};
|
|
418
|
+
for (let t = 0; t < text_info.type!.length; t++) {
|
|
419
|
+
let type = text_info.type![t];
|
|
420
|
+
if (type === 'straight') {
|
|
421
|
+
// Only render for current texture
|
|
422
|
+
if (text_info.textures![t] !== cursor.texture_idx) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
let word = (text_info.isRTL) ? text.split(undefined!).reverse().join() : text;
|
|
427
|
+
let cache = texture.texcoord_cache[style][word];
|
|
428
|
+
|
|
429
|
+
let texcoord;
|
|
430
|
+
if (cache.texcoord) {
|
|
431
|
+
texcoord = cache.texcoord;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
let texture_position = cache.texture_position;
|
|
435
|
+
let { size, lines } = this.textSize(style, word, text_settings);
|
|
436
|
+
|
|
437
|
+
this.drawTextMultiLine(lines, texture_position, size, text_settings, type);
|
|
438
|
+
|
|
439
|
+
texcoord = typedTexture.getTexcoordsForSprite(
|
|
440
|
+
texture_position,
|
|
441
|
+
size.texture_size,
|
|
442
|
+
texture.texture_size
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
cache.texcoord = texcoord;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
text_info.texcoords[type] = {
|
|
449
|
+
texcoord,
|
|
450
|
+
texture_id: cache.texture_id
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
else if (type === 'curved') {
|
|
454
|
+
let words = text_info.segments!;
|
|
455
|
+
text_info.texcoords.curved = text_info.texcoords.curved || [];
|
|
456
|
+
text_info.texcoords_stroke = text_info.texcoords_stroke || [];
|
|
457
|
+
|
|
458
|
+
for (let w = 0; w < words.length; w++){
|
|
459
|
+
// Only render for current texture
|
|
460
|
+
if ((text_info.textures![t] as number[])[w] !== cursor.texture_idx) {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
let word = words[w];
|
|
465
|
+
let cache = texture.texcoord_cache[style][word];
|
|
466
|
+
|
|
467
|
+
let texcoord;
|
|
468
|
+
let texcoord_stroke;
|
|
469
|
+
if (cache.texcoord){
|
|
470
|
+
texcoord = cache.texcoord;
|
|
471
|
+
texcoord_stroke = cache.texcoord_stroke!;
|
|
472
|
+
text_info.texcoords_stroke.push(texcoord_stroke!);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
let texture_position = cache.texture_position;
|
|
476
|
+
let { size, lines } = this.textSize(style, word, text_settings);
|
|
477
|
+
|
|
478
|
+
this.drawTextMultiLine(lines, texture_position, size, text_settings, type);
|
|
479
|
+
|
|
480
|
+
texcoord = typedTexture.getTexcoordsForSprite(
|
|
481
|
+
texture_position,
|
|
482
|
+
size.texture_size,
|
|
483
|
+
texture.texture_size
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
let texture_position_stroke: LabelPointCoordinate = [
|
|
487
|
+
texture_position[0] + size.texture_size[0],
|
|
488
|
+
texture_position[1]
|
|
489
|
+
];
|
|
490
|
+
|
|
491
|
+
texcoord_stroke = typedTexture.getTexcoordsForSprite(
|
|
492
|
+
texture_position_stroke,
|
|
493
|
+
size.texture_size,
|
|
494
|
+
texture.texture_size
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
cache.texcoord = texcoord;
|
|
498
|
+
cache.texcoord_stroke = texcoord_stroke;
|
|
499
|
+
|
|
500
|
+
// NB: texture_id is the same between stroke and fill, so it's not duplicated here
|
|
501
|
+
text_info.texcoords_stroke.push(texcoord_stroke!);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
text_info.texcoords.curved.push({
|
|
505
|
+
texcoord,
|
|
506
|
+
texture_id: cache.texture_id
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
let lines = this.textSize(style, text, text_settings).lines;
|
|
514
|
+
const aligned_text_settings: TextSettingsResult & {align?: string} = { ...text_settings };
|
|
515
|
+
|
|
516
|
+
for (let align in text_info.align) {
|
|
517
|
+
// Only render for current texture
|
|
518
|
+
if (text_info.align![align].texture_id !== cursor.texture_idx) {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
aligned_text_settings.align = align;
|
|
523
|
+
this.drawTextMultiLine(lines, text_info.align![align].texture_position!, text_info.size!, aligned_text_settings);
|
|
524
|
+
|
|
525
|
+
text_info.align![align].texcoords = typedTexture.getTexcoordsForSprite(
|
|
526
|
+
text_info.align![align].texture_position!,
|
|
527
|
+
text_info.size!.texture_size,
|
|
528
|
+
texture.texture_size
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
cursor.text_idx++;
|
|
534
|
+
|
|
535
|
+
if (!Task.shouldContinue(task)) {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
cursor.text_idx = null;
|
|
540
|
+
cursor.style_idx!++;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Create GL texture (canvas element will be reused for next texture)
|
|
544
|
+
let tname = (task as TextRasterTask).texture_prefix + cursor.texture_idx;
|
|
545
|
+
typedTexture.create(task.resource_context, tname, {
|
|
546
|
+
element: this.canvas,
|
|
547
|
+
filtering: 'linear',
|
|
548
|
+
UNPACK_PREMULTIPLY_ALPHA_WEBGL: true
|
|
549
|
+
});
|
|
550
|
+
typedTexture.retain(tname);
|
|
551
|
+
cursor.texture_names.push(tname);
|
|
552
|
+
|
|
553
|
+
cursor.texture_idx++;
|
|
554
|
+
cursor.texture_resize = true;
|
|
555
|
+
cursor.style_idx = 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
Task.finish(task, cursor.texture_names);
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Free any textures that have been allocated part-way through label rasterization for a tile
|
|
563
|
+
cancelRasterizeTask (task: TaskRecord<string[]>): undefined {
|
|
564
|
+
log('trace', `RasterizeTask: release textures [${(task as TextRasterTask).cursor.texture_names.join(', ')}]`);
|
|
565
|
+
(task as TextRasterTask).cursor.texture_names.forEach(t => typedTexture.release(t));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Place text labels within an atlas of the given max size
|
|
569
|
+
setTextureTextPositions (texts: TextTable, max_texture_size: number): TextAtlas[] {
|
|
570
|
+
let texture: TextAtlasCursor = {
|
|
571
|
+
cx: 0,
|
|
572
|
+
cy: 0,
|
|
573
|
+
width: 0,
|
|
574
|
+
height: 0,
|
|
575
|
+
column_width: 0,
|
|
576
|
+
texture_id: 0,
|
|
577
|
+
texcoord_cache: {}
|
|
578
|
+
},
|
|
579
|
+
textures: TextAtlas[] = [];
|
|
580
|
+
|
|
581
|
+
for (let style in texts) {
|
|
582
|
+
let text_infos = texts[style];
|
|
583
|
+
|
|
584
|
+
for (let text in text_infos) {
|
|
585
|
+
let text_info = text_infos[text];
|
|
586
|
+
let texture_position: LabelPointCoordinate;
|
|
587
|
+
|
|
588
|
+
if (text_info.text_settings.can_articulate) {
|
|
589
|
+
text_info.textures = [];
|
|
590
|
+
texture.texcoord_cache[style] = texture.texcoord_cache[style] || {};
|
|
591
|
+
|
|
592
|
+
for (let t = 0; t < text_info.type!.length; t++) {
|
|
593
|
+
let type = text_info.type![t];
|
|
594
|
+
|
|
595
|
+
if (type === 'straight') {
|
|
596
|
+
let word = (text_info.isRTL) ? text.split(undefined!).reverse().join() : text;
|
|
597
|
+
|
|
598
|
+
if (!texture.texcoord_cache[style][word]) {
|
|
599
|
+
let size = text_info.size!.texture_size;
|
|
600
|
+
texture_position = this.placeText(size[0], size[1], style, texture, textures, max_texture_size);
|
|
601
|
+
texture.texcoord_cache[style][word] = {
|
|
602
|
+
texture_id: texture.texture_id,
|
|
603
|
+
texture_position
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
text_info.textures![t] = texture.texture_id;
|
|
608
|
+
}
|
|
609
|
+
else if (type === 'curved') {
|
|
610
|
+
text_info.textures![t] = [];
|
|
611
|
+
|
|
612
|
+
for (let w = 0; w < text_info.segment_sizes!.length; w++) {
|
|
613
|
+
let word = text_info.segments![w];
|
|
614
|
+
|
|
615
|
+
if (!texture.texcoord_cache[style][word]) {
|
|
616
|
+
let size = text_info.segment_sizes![w].texture_size;
|
|
617
|
+
let width = 2 * size[0]; // doubled to account for side-by-side rendering of fill and stroke
|
|
618
|
+
texture_position = this.placeText(width, size[1], style, texture, textures, max_texture_size);
|
|
619
|
+
texture.texcoord_cache[style][word] = {
|
|
620
|
+
texture_id: texture.texture_id,
|
|
621
|
+
texture_position
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
(text_info.textures![t] as number[]).push(texture.texture_id);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
// rendered size is same for all alignments
|
|
633
|
+
let size = text_info.size!.texture_size;
|
|
634
|
+
|
|
635
|
+
// but each alignment needs to be rendered separately
|
|
636
|
+
for (let align in text_info.align) {
|
|
637
|
+
texture_position = this.placeText (size[0], size[1], style, texture, textures, max_texture_size);
|
|
638
|
+
text_info.align![align].texture_id = texture.texture_id;
|
|
639
|
+
text_info.align![align].texture_position = texture_position;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// save final texture
|
|
646
|
+
if (texture.column_width > 0 && texture.height > 0) {
|
|
647
|
+
textures[texture.texture_id] = {
|
|
648
|
+
texture_size: [texture.width, texture.height],
|
|
649
|
+
texcoord_cache: texture.texcoord_cache
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// return computed texture sizes and UV cache
|
|
654
|
+
return textures;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Place text sprite in texture atlas, enlarging current texture, or starting new one if max texture size reached
|
|
658
|
+
placeText (text_width: number, text_height: number, style: string, texture: TextAtlasCursor, textures: TextAtlas[], max_texture_size: number): LabelPointCoordinate {
|
|
659
|
+
let texture_position: LabelPointCoordinate;
|
|
660
|
+
|
|
661
|
+
// TODO: what if first label is wider than entire max texture?
|
|
662
|
+
|
|
663
|
+
if (texture.cy + text_height > max_texture_size) {
|
|
664
|
+
// start new column
|
|
665
|
+
texture.cx += texture.column_width;
|
|
666
|
+
texture.cy = 0;
|
|
667
|
+
texture.column_width = text_width;
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
// expand current column
|
|
671
|
+
texture.column_width = Math.max(texture.column_width, text_width);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (texture.cx + texture.column_width <= max_texture_size) {
|
|
675
|
+
// add label to current texture
|
|
676
|
+
texture_position = [texture.cx, texture.cy];
|
|
677
|
+
|
|
678
|
+
texture.cy += text_height;
|
|
679
|
+
|
|
680
|
+
// expand texture if needed
|
|
681
|
+
texture.height = Math.max(texture.height, texture.cy);
|
|
682
|
+
texture.width = Math.max(texture.width, texture.cx + texture.column_width);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
// start new texture
|
|
686
|
+
// save size and cache of last texture
|
|
687
|
+
textures[texture.texture_id] = {
|
|
688
|
+
texture_size: [texture.width, texture.height],
|
|
689
|
+
texcoord_cache: texture.texcoord_cache
|
|
690
|
+
};
|
|
691
|
+
texture.texcoord_cache = {}; // reset cache
|
|
692
|
+
texture.texcoord_cache[style] = {};
|
|
693
|
+
|
|
694
|
+
texture.texture_id++;
|
|
695
|
+
texture.cx = 0;
|
|
696
|
+
texture.cy = text_height;
|
|
697
|
+
texture.column_width = text_width;
|
|
698
|
+
texture.width = text_width;
|
|
699
|
+
texture.height = text_height;
|
|
700
|
+
texture_position = [0, 0]; // TODO: allocate zero array once
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return texture_position;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Called before rasterization
|
|
707
|
+
applyTextTransform (text: string, transform?: string): string {
|
|
708
|
+
if (transform === 'capitalize') {
|
|
709
|
+
return text.replace(/\w\S*/g, function (txt) {
|
|
710
|
+
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
else if (transform === 'uppercase') {
|
|
714
|
+
return text.toUpperCase();
|
|
715
|
+
}
|
|
716
|
+
else if (transform === 'lowercase') {
|
|
717
|
+
return text.toLowerCase();
|
|
718
|
+
}
|
|
719
|
+
return text;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Convert font CSS-style size ('12px', '14pt', '1.5em', etc.) to pixel size (adjusted for device pixel ratio)
|
|
723
|
+
// Defaults units to pixels if not specified
|
|
724
|
+
static fontPixelSize (size: unknown): number | undefined {
|
|
725
|
+
if (size == null) {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
size = (typeof size === 'string') ? size : String(size); // need a string for regex
|
|
729
|
+
|
|
730
|
+
// The regexp captures a numeric string; preserve it until the legacy unit conversion/parser runs.
|
|
731
|
+
let [, px_size, units] = ((size as string).match(TextCanvas.font_size_re) || []) as [string?, (string | number)?, string?];
|
|
732
|
+
units = units || 'px';
|
|
733
|
+
|
|
734
|
+
if (units === 'em') {
|
|
735
|
+
px_size = Number(px_size) * 16;
|
|
736
|
+
} else if (units === 'pt') {
|
|
737
|
+
px_size = Number(px_size) / 0.75;
|
|
738
|
+
} else if (units === '%') {
|
|
739
|
+
px_size = Number(px_size) / 6.25;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
px_size = typedStyleParser.parsePositiveNumber(px_size);
|
|
743
|
+
px_size *= Utils.device_pixel_ratio!;
|
|
744
|
+
return px_size;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
static pruneTextCache () {
|
|
748
|
+
if (TextCanvas.cache.text_count > TextCanvas.cache.text_count_max) {
|
|
749
|
+
TextCanvas.cache.text = {};
|
|
750
|
+
TextCanvas.cache.text_count = 0;
|
|
751
|
+
log('debug', 'TextCanvas: pruning text cache');
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (Object.keys(TextCanvas.cache.segment).length > TextCanvas.cache.segment_count_max) {
|
|
755
|
+
TextCanvas.cache.segment = {};
|
|
756
|
+
log('debug', 'TextCanvas: pruning segment cache');
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Extract font size and units
|
|
763
|
+
TextCanvas.font_size_re = /((?:[0-9]*\.)?[0-9]+)\s*(px|pt|em|%)?/;
|
|
764
|
+
|
|
765
|
+
// Cache sizes of rendered text
|
|
766
|
+
TextCanvas.cache = {
|
|
767
|
+
text: {}, // size and line parsing, by text style, then text string
|
|
768
|
+
text_count: 0, // current size of cache (measured as # of entries)
|
|
769
|
+
text_count_max: 2000, // prune cache when it exceeds this size
|
|
770
|
+
segment: {}, // segmentation of text (by run of characters or grapheme clusters), by text string
|
|
771
|
+
segment_count_max: 2000, // prune cache when it exceeds this size
|
|
772
|
+
stats: { text_hits: 0, text_misses: 0, segment_hits: 0, segment_misses: 0 }
|
|
773
|
+
};
|