• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / deck.gl / 23980348825

04 Apr 2026 01:58PM UTC coverage: 80.375% (+0.006%) from 80.369%
23980348825

Pull #10152

github

web-flow
Merge a194fb188 into 8dec4eee4
Pull Request #10152: feat(core): AttributeManager allocates Buffers for constant WebGPU at…

3117 of 3766 branches covered (82.77%)

Branch coverage included in aggregate %.

5 of 30 new or added lines in 1 file covered. (16.67%)

78 existing lines in 14 files now uncovered.

14289 of 17890 relevant lines covered (79.87%)

26621.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.43
/modules/layers/src/text-layer/text-background-layer/text-background-layer.ts
1
// deck.gl
2
// SPDX-License-Identifier: MIT
3
// Copyright (c) vis.gl contributors
4

5
import {Layer, project32, picking, UNIT} from '@deck.gl/core';
6
import {Geometry} from '@luma.gl/engine';
7
import {Model} from '@luma.gl/engine';
8

9
import {TextBackgroundProps, textBackgroundUniforms} from './text-background-layer-uniforms';
10
import {TextModuleProps, textUniforms} from '../text-uniforms';
11
import vs from './text-background-layer-vertex.glsl';
12
import fs from './text-background-layer-fragment.glsl';
13

14
import type {
15
  LayerProps,
16
  LayerDataSource,
17
  Accessor,
18
  Unit,
19
  Position,
20
  Color,
21
  UpdateParameters,
22
  DefaultProps
23
} from '@deck.gl/core';
24

25
type _TextBackgroundLayerProps<DataT> = {
26
  data: LayerDataSource<DataT>;
27
  billboard?: boolean;
28
  sizeScale?: number;
29
  sizeUnits?: Unit;
30
  sizeMinPixels?: number;
31
  sizeMaxPixels?: number;
32
  fontSize?: number;
33

34
  borderRadius?: number | Readonly<[number, number, number, number]>;
35
  padding?: Readonly<[number, number]> | Readonly<[number, number, number, number]>;
36

37
  getPosition?: Accessor<DataT, Position>;
38
  getSize?: Accessor<DataT, number>;
39
  getAngle?: Accessor<DataT, number>;
40
  getPixelOffset?: Accessor<DataT, Readonly<[number, number]>>;
41
  getBoundingRect?: Accessor<DataT, Readonly<[number, number, number, number]>>;
42
  getClipRect?: Accessor<DataT, [x: number, y: number, width: number, height: number]>;
43
  getFillColor?: Accessor<DataT, Color>;
44
  getLineColor?: Accessor<DataT, Color>;
45
  getLineWidth?: Accessor<DataT, number>;
46
};
47

48
export type TextBackgroundLayerProps<DataT = unknown> = _TextBackgroundLayerProps<DataT> &
49
  LayerProps;
50

51
const defaultProps: DefaultProps<TextBackgroundLayerProps> = {
4✔
52
  billboard: true,
53
  sizeScale: 1,
54
  sizeUnits: 'pixels',
55
  sizeMinPixels: 0,
56
  sizeMaxPixels: Number.MAX_SAFE_INTEGER,
57
  fontSize: 1,
58

59
  borderRadius: {type: 'object', value: 0},
60
  padding: {type: 'array', value: [0, 0, 0, 0]},
61

UNCOV
62
  getPosition: {type: 'accessor', value: (x: any) => x.position},
×
63
  getSize: {type: 'accessor', value: 1},
64
  getAngle: {type: 'accessor', value: 0},
65
  getPixelOffset: {type: 'accessor', value: [0, 0]},
66
  getBoundingRect: {type: 'accessor', value: [0, 0, 0, 0]},
67
  getClipRect: {type: 'accessor', value: [0, 0, -1, -1]},
68
  getFillColor: {type: 'accessor', value: [0, 0, 0, 255]},
69
  getLineColor: {type: 'accessor', value: [0, 0, 0, 255]},
70
  getLineWidth: {type: 'accessor', value: 1}
71
};
72

73
export default class TextBackgroundLayer<DataT = any, ExtraPropsT extends {} = {}> extends Layer<
74
  ExtraPropsT & Required<_TextBackgroundLayerProps<DataT>>
75
> {
76
  static defaultProps = defaultProps;
4✔
77
  static layerName = 'TextBackgroundLayer';
4✔
78

79
  state!: {
80
    model?: Model;
81
  };
82

83
  getShaders() {
84
    return super.getShaders({
57✔
85
      vs,
86
      fs,
87
      modules: [project32, picking, textBackgroundUniforms, textUniforms]
88
    });
89
  }
90

91
  initializeState() {
92
    this.getAttributeManager()!.addInstanced({
56✔
93
      instancePositions: {
94
        size: 3,
95
        type: 'float64',
96
        fp64: this.use64bitPositions(),
97
        transition: true,
98
        accessor: 'getPosition'
99
      },
100
      instanceSizes: {
101
        size: 1,
102
        transition: true,
103
        accessor: 'getSize',
104
        defaultValue: 1
105
      },
106
      instanceAngles: {
107
        size: 1,
108
        transition: true,
109
        accessor: 'getAngle'
110
      },
111
      instanceRects: {
112
        size: 4,
113
        accessor: 'getBoundingRect'
114
      },
115
      instanceClipRect: {
116
        size: 4,
117
        accessor: 'getClipRect',
118
        defaultValue: [0, 0, -1, -1]
119
      },
120
      instancePixelOffsets: {
121
        size: 2,
122
        transition: true,
123
        accessor: 'getPixelOffset'
124
      },
125
      instanceFillColors: {
126
        size: 4,
127
        transition: true,
128
        type: 'unorm8',
129
        accessor: 'getFillColor',
130
        defaultValue: [0, 0, 0, 255]
131
      },
132
      instanceLineColors: {
133
        size: 4,
134
        transition: true,
135
        type: 'unorm8',
136
        accessor: 'getLineColor',
137
        defaultValue: [0, 0, 0, 255]
138
      },
139
      instanceLineWidths: {
140
        size: 1,
141
        transition: true,
142
        accessor: 'getLineWidth',
143
        defaultValue: 1
144
      }
145
    });
146
  }
147

148
  updateState(params: UpdateParameters<this>) {
149
    super.updateState(params);
140✔
150
    const {changeFlags} = params;
140✔
151
    if (changeFlags.extensionsChanged) {
140✔
152
      this.state.model?.destroy();
56✔
153
      this.state.model = this._getModel();
56✔
154
      this.getAttributeManager()!.invalidateAll();
56✔
155
    }
156
  }
157

158
  draw({uniforms}) {
159
    const {billboard, sizeScale, sizeUnits, sizeMinPixels, sizeMaxPixels, getLineWidth, fontSize} =
160
      this.props;
235✔
161
    let {padding, borderRadius} = this.props;
235✔
162

163
    if (padding.length < 4) {
235✔
164
      padding = [padding[0], padding[1], padding[0], padding[1]];
4✔
165
    }
166

167
    if (!Array.isArray(borderRadius)) {
235✔
168
      borderRadius = [
235✔
169
        borderRadius as number,
170
        borderRadius as number,
171
        borderRadius as number,
172
        borderRadius as number
173
      ];
174
    }
175

176
    const model = this.state.model!;
235✔
177
    const textBackgroundProps: TextBackgroundProps = {
235✔
178
      billboard,
179
      stroked: Boolean(getLineWidth),
180
      borderRadius: borderRadius as [number, number, number, number],
181
      padding: padding as [number, number, number, number],
182
      sizeUnits: UNIT[sizeUnits],
183
      sizeScale,
184
      sizeMinPixels,
185
      sizeMaxPixels
186
    };
187
    const textProps: TextModuleProps = {
235✔
188
      fontSize,
189
      viewport: this.context.viewport
190
    };
191
    model.shaderInputs.setProps({textBackground: textBackgroundProps, text: textProps});
235✔
192
    model.draw(this.context.renderPass);
235✔
193
  }
194

195
  protected _getModel(): Model {
196
    // a square that minimally cover the unit circle
197
    const positions = [0, 0, 1, 0, 0, 1, 1, 1];
56✔
198

199
    return new Model(this.context.device, {
56✔
200
      ...this.getShaders(),
201
      id: this.props.id,
202
      bufferLayout: this.getAttributeManager()!.getBufferLayouts(),
203
      geometry: new Geometry({
204
        topology: 'triangle-strip',
205
        vertexCount: 4,
206
        attributes: {
207
          positions: {size: 2, value: new Float32Array(positions)}
208
        }
209
      }),
210
      isInstanced: true
211
    });
212
  }
213
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc