geojs 1.6.5 → 1.7.2
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/CHANGELOG.md +18 -0
- package/geo.js +963 -288
- package/geo.lean.js +963 -288
- package/geo.lean.min.js +3 -3
- package/geo.min.js +5 -5
- package/package.json +17 -15
- package/src/canvas/pixelmapFeature.js +208 -0
- package/src/index.js +1 -0
- package/src/pixelmapFeature.js +97 -249
- package/src/pixelmapLayer.js +145 -0
- package/src/quadFeature.js +1 -1
- package/src/tileLayer.js +8 -2
- package/src/webgl/index.js +2 -0
- package/src/webgl/lookupTable2D.js +122 -0
- package/src/webgl/pixelmapFeature.frag +47 -0
- package/src/webgl/pixelmapFeature.js +203 -0
- package/src/webgl/quadFeature.js +41 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
var inherit = require('../inherit');
|
|
2
|
+
var timestamp = require('../timestamp');
|
|
3
|
+
var vgl = require('vgl');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Switch to a specific texture unit.
|
|
7
|
+
*
|
|
8
|
+
* @param {vgl.renderState} renderState An object that contains the context
|
|
9
|
+
* used for drawing.
|
|
10
|
+
* @param {number} textureUnit The number of the texture unit [0-15].
|
|
11
|
+
*/
|
|
12
|
+
function activateTextureUnit(renderState, textureUnit) {
|
|
13
|
+
if (textureUnit >= 0 && textureUnit <= 31) {
|
|
14
|
+
renderState.m_context.activeTexture(vgl.GL.TEXTURE0 + textureUnit);
|
|
15
|
+
} else {
|
|
16
|
+
throw Error('[error] Texture unit ' + textureUnit + ' is not supported');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a new instance of class webgl_lookupTable2D.
|
|
22
|
+
*
|
|
23
|
+
* @class
|
|
24
|
+
* @alias geo.webgl.lookupTable2D
|
|
25
|
+
* @param {object} arg Options object.
|
|
26
|
+
* @param {number} [arg.maxWidth] Maximum width to use for the texture. If the
|
|
27
|
+
* number of colors set is less than this, the texture is 1D. If greater, it
|
|
28
|
+
* will be a rectangle of maxWidth x whatever height is necessary.
|
|
29
|
+
* @param {number[]} [arg.colorTable] Initial color table for the texture.
|
|
30
|
+
* This is of the form RGBARGBA... where each value is an integer on the
|
|
31
|
+
* scale [0,255].
|
|
32
|
+
* @extends vgl.texture
|
|
33
|
+
* @returns {geo.webgl.lookupTable2D}
|
|
34
|
+
*/
|
|
35
|
+
var webgl_lookupTable2D = function (arg) {
|
|
36
|
+
'use strict';
|
|
37
|
+
|
|
38
|
+
if (!(this instanceof webgl_lookupTable2D)) {
|
|
39
|
+
return new webgl_lookupTable2D(arg);
|
|
40
|
+
}
|
|
41
|
+
arg = arg || {};
|
|
42
|
+
vgl.texture.call(this);
|
|
43
|
+
|
|
44
|
+
var m_setupTimestamp = timestamp(),
|
|
45
|
+
m_maxWidth = arg.maxWidth || 4096,
|
|
46
|
+
m_colorTable = new Uint8Array([0, 0, 0, 0]),
|
|
47
|
+
m_colorTableOrig,
|
|
48
|
+
m_this = this;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create lookup table, initialize parameters, and bind data to it.
|
|
52
|
+
*
|
|
53
|
+
* @param {vgl.renderState} renderState An object that contains the context
|
|
54
|
+
* used for drawing.
|
|
55
|
+
*/
|
|
56
|
+
this.setup = function (renderState) {
|
|
57
|
+
activateTextureUnit(renderState, m_this.textureUnit());
|
|
58
|
+
|
|
59
|
+
renderState.m_context.deleteTexture(m_this.m_textureHandle);
|
|
60
|
+
m_this.m_textureHandle = renderState.m_context.createTexture();
|
|
61
|
+
renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, m_this.m_textureHandle);
|
|
62
|
+
renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D, vgl.GL.TEXTURE_MIN_FILTER, vgl.GL.NEAREST);
|
|
63
|
+
renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D, vgl.GL.TEXTURE_MAG_FILTER, vgl.GL.NEAREST);
|
|
64
|
+
renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D, vgl.GL.TEXTURE_WRAP_S, vgl.GL.CLAMP_TO_EDGE);
|
|
65
|
+
renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D, vgl.GL.TEXTURE_WRAP_T, vgl.GL.CLAMP_TO_EDGE);
|
|
66
|
+
renderState.m_context.pixelStorei(vgl.GL.UNPACK_ALIGNMENT, 1);
|
|
67
|
+
renderState.m_context.pixelStorei(vgl.GL.UNPACK_FLIP_Y_WEBGL, true);
|
|
68
|
+
|
|
69
|
+
renderState.m_context.texImage2D(
|
|
70
|
+
vgl.GL.TEXTURE_2D, 0, vgl.GL.RGBA, m_this.width, m_this.height, 0,
|
|
71
|
+
vgl.GL.RGBA, vgl.GL.UNSIGNED_BYTE, m_colorTable);
|
|
72
|
+
|
|
73
|
+
renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, null);
|
|
74
|
+
m_setupTimestamp.modified();
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get/set color table.
|
|
79
|
+
*
|
|
80
|
+
* @param {number[]} [val] An array of RGBARGBA... integers on a scale
|
|
81
|
+
* of [0, 255]. `undefined` to get the current value.
|
|
82
|
+
* @returns {number[]|this}
|
|
83
|
+
*/
|
|
84
|
+
this.colorTable = function (val) {
|
|
85
|
+
if (val === undefined) {
|
|
86
|
+
return m_colorTableOrig;
|
|
87
|
+
}
|
|
88
|
+
m_colorTableOrig = val;
|
|
89
|
+
if (val.length < 4) {
|
|
90
|
+
val = [0, 0, 0, 0];
|
|
91
|
+
}
|
|
92
|
+
m_this.width = Math.min(m_maxWidth, val.length / 4);
|
|
93
|
+
m_this.height = Math.ceil(val.length / 4 / m_maxWidth);
|
|
94
|
+
if (!(val instanceof Uint8Array) || val.length !== m_this.width * m_this.height * 4) {
|
|
95
|
+
if (val.length < m_this.width * m_this.height * 4) {
|
|
96
|
+
val = val.concat(new Array(m_this.width * m_this.height * 4 - val.length).fill(0));
|
|
97
|
+
}
|
|
98
|
+
m_colorTable = new Uint8Array(val);
|
|
99
|
+
} else {
|
|
100
|
+
m_colorTable = val;
|
|
101
|
+
}
|
|
102
|
+
m_this.modified();
|
|
103
|
+
return m_this;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get maxWidth value.
|
|
108
|
+
*
|
|
109
|
+
* @returns {number} The maxWidth of the texture used.
|
|
110
|
+
*/
|
|
111
|
+
this.maxWidth = function () {
|
|
112
|
+
return m_maxWidth;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
this.colorTable(arg.colorTable || []);
|
|
116
|
+
|
|
117
|
+
return this;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
inherit(webgl_lookupTable2D, vgl.texture);
|
|
121
|
+
|
|
122
|
+
module.exports = webgl_lookupTable2D;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* pixelmapFeature fragment shader */
|
|
2
|
+
|
|
3
|
+
varying highp vec2 iTextureCoord;
|
|
4
|
+
uniform sampler2D sampler2d;
|
|
5
|
+
uniform sampler2D lutSampler;
|
|
6
|
+
uniform int lutWidth;
|
|
7
|
+
uniform int lutHeight;
|
|
8
|
+
uniform mediump float opacity;
|
|
9
|
+
uniform highp vec2 crop;
|
|
10
|
+
|
|
11
|
+
void main(void) {
|
|
12
|
+
if ((crop.s < 1.0 && iTextureCoord.s > crop.s) || (crop.t < 1.0 && 1.0 - iTextureCoord.t > crop.t)) {
|
|
13
|
+
discard;
|
|
14
|
+
}
|
|
15
|
+
// to add anti-aliasing, we would need to know the current pixel size
|
|
16
|
+
// (probably computed in the vertex shader) and then sample the base image at
|
|
17
|
+
// multiple points, then average the output color.
|
|
18
|
+
highp vec4 lutValue = texture2D(sampler2d, iTextureCoord);
|
|
19
|
+
highp vec2 lutCoord;
|
|
20
|
+
lutCoord.s = (
|
|
21
|
+
mod(
|
|
22
|
+
// add 0.5 to handle float imprecision
|
|
23
|
+
floor(lutValue.r * 255.0 + 0.5) +
|
|
24
|
+
floor(lutValue.g * 255.0 + 0.5) * 256.0,
|
|
25
|
+
float(lutWidth)
|
|
26
|
+
// center in pixel
|
|
27
|
+
) + 0.5) / float(lutWidth);
|
|
28
|
+
// Our image is top-down, so invert the coordinate
|
|
29
|
+
lutCoord.t = 1.0 - (
|
|
30
|
+
floor(
|
|
31
|
+
(
|
|
32
|
+
// add 0.5 to handle float imprecision
|
|
33
|
+
floor(lutValue.r * 255.0 + 0.5) +
|
|
34
|
+
floor(lutValue.g * 255.0 + 0.5) * 256.0 +
|
|
35
|
+
floor(lutValue.b * 255.0 + 0.5) * 256.0 * 256.0
|
|
36
|
+
// We may want an option to use the alpha channel to allow more indices
|
|
37
|
+
) / float(lutWidth)
|
|
38
|
+
// center in pixel
|
|
39
|
+
) + 0.5) / float(lutHeight);
|
|
40
|
+
if (lutCoord.t < 0.0) {
|
|
41
|
+
discard;
|
|
42
|
+
}
|
|
43
|
+
mediump vec4 color = texture2D(lutSampler, lutCoord);
|
|
44
|
+
|
|
45
|
+
color.a *= opacity;
|
|
46
|
+
gl_FragColor = color;
|
|
47
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
var inherit = require('../inherit');
|
|
2
|
+
var registerFeature = require('../registry').registerFeature;
|
|
3
|
+
var pixelmapFeature = require('../pixelmapFeature');
|
|
4
|
+
var lookupTable2D = require('./lookupTable2D');
|
|
5
|
+
var util = require('../util');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a new instance of class webgl.pixelmapFeature.
|
|
9
|
+
*
|
|
10
|
+
* @class
|
|
11
|
+
* @alias geo.webgl.pixelmapFeature
|
|
12
|
+
* @extends geo.pixelmapFeature
|
|
13
|
+
* @param {geo.pixelmapFeature.spec} arg
|
|
14
|
+
* @returns {geo.webgl.pixelmapFeature}
|
|
15
|
+
*/
|
|
16
|
+
var webgl_pixelmapFeature = function (arg) {
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
if (!(this instanceof webgl_pixelmapFeature)) {
|
|
20
|
+
return new webgl_pixelmapFeature(arg);
|
|
21
|
+
}
|
|
22
|
+
pixelmapFeature.call(this, arg);
|
|
23
|
+
|
|
24
|
+
var object = require('./object');
|
|
25
|
+
object.call(this);
|
|
26
|
+
|
|
27
|
+
const vgl = require('vgl');
|
|
28
|
+
const fragmentShader = require('./pixelmapFeature.frag');
|
|
29
|
+
|
|
30
|
+
var m_quadFeature,
|
|
31
|
+
m_quadFeatureInit,
|
|
32
|
+
s_exit = this._exit,
|
|
33
|
+
m_lookupTable,
|
|
34
|
+
m_this = this;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* If the specified coordinates are in the rendered quad, use the basis
|
|
38
|
+
* information from the quad to determine the pixelmap index value so that it
|
|
39
|
+
* can be included in the `found` results.
|
|
40
|
+
*
|
|
41
|
+
* @param {geo.geoPosition} geo Coordinate.
|
|
42
|
+
* @param {string|geo.transform|null} [gcs] Input gcs. `undefined` to use
|
|
43
|
+
* the interface gcs, `null` to use the map gcs, or any other transform.
|
|
44
|
+
* @returns {geo.feature.searchResult} An object with a list of features and
|
|
45
|
+
* feature indices that are located at the specified point.
|
|
46
|
+
*/
|
|
47
|
+
this.pointSearch = function (geo, gcs) {
|
|
48
|
+
if (m_quadFeature && m_this.m_info) {
|
|
49
|
+
let result = m_quadFeature.pointSearch(geo, gcs);
|
|
50
|
+
// use the last index by preference, since for tile layers, this is the
|
|
51
|
+
// topmosttile
|
|
52
|
+
let idxIdx = result.index.length - 1;
|
|
53
|
+
for (; idxIdx >= 0; idxIdx -= 1) {
|
|
54
|
+
if (result.extra[result.index[idxIdx]]._quad &&
|
|
55
|
+
result.extra[result.index[idxIdx]]._quad.image) {
|
|
56
|
+
const img = result.extra[result.index[idxIdx]]._quad.image;
|
|
57
|
+
const basis = result.extra[result.index[idxIdx]].basis;
|
|
58
|
+
const x = Math.floor(basis.x * img.width);
|
|
59
|
+
const y = Math.floor(basis.y * img.height);
|
|
60
|
+
const canvas = document.createElement('canvas');
|
|
61
|
+
canvas.width = canvas.height = 1;
|
|
62
|
+
const context = canvas.getContext('2d');
|
|
63
|
+
context.drawImage(img, x, y, 1, 1, 0, 0, 1, 1);
|
|
64
|
+
const pixel = context.getImageData(0, 0, 1, 1).data;
|
|
65
|
+
const idx = pixel[0] + pixel[1] * 256 + pixel[2] * 256 * 256;
|
|
66
|
+
result = {
|
|
67
|
+
index: [idx],
|
|
68
|
+
found: [m_this.data()[idx]]
|
|
69
|
+
};
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {index: [], found: []};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Given the loaded pixelmap image, create a texture for the colors and a
|
|
79
|
+
* quad that will use it.
|
|
80
|
+
*/
|
|
81
|
+
this._computePixelmap = function () {
|
|
82
|
+
var data = m_this.data() || [],
|
|
83
|
+
colorFunc = m_this.style.get('color');
|
|
84
|
+
|
|
85
|
+
let indexRange = m_this.indexModified(undefined, 'clear');
|
|
86
|
+
let fullUpdate = m_this.dataTime().timestamp() >= m_this.buildTime().timestamp() || indexRange === undefined;
|
|
87
|
+
if (!m_lookupTable) {
|
|
88
|
+
m_lookupTable = lookupTable2D();
|
|
89
|
+
m_lookupTable.setTextureUnit(1);
|
|
90
|
+
fullUpdate = true;
|
|
91
|
+
}
|
|
92
|
+
let clrLen = Math.max(1, data.length);
|
|
93
|
+
const maxWidth = m_lookupTable.maxWidth();
|
|
94
|
+
if (clrLen > maxWidth && clrLen % maxWidth) {
|
|
95
|
+
clrLen += maxWidth - (clrLen % maxWidth);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let colors;
|
|
99
|
+
if (!fullUpdate) {
|
|
100
|
+
colors = m_lookupTable.colorTable();
|
|
101
|
+
fullUpdate = colors.length !== clrLen * 4;
|
|
102
|
+
indexRange[0] = Math.max(0, indexRange[0]);
|
|
103
|
+
indexRange[1] = Math.min(data.length, indexRange[1] + 1);
|
|
104
|
+
}
|
|
105
|
+
if (fullUpdate) {
|
|
106
|
+
colors = new Uint8Array(clrLen * 4);
|
|
107
|
+
indexRange = [0, data.length];
|
|
108
|
+
}
|
|
109
|
+
for (let i = indexRange[0]; i < indexRange[1]; i += 1) {
|
|
110
|
+
const d = data[i];
|
|
111
|
+
const color = util.convertColor(colorFunc.call(m_this, d, i));
|
|
112
|
+
colors[i * 4] = color.r * 255;
|
|
113
|
+
colors[i * 4 + 1] = color.g * 255;
|
|
114
|
+
colors[i * 4 + 2] = color.b * 255;
|
|
115
|
+
colors[i * 4 + 3] = color.a === undefined ? 255 : (color.a * 255);
|
|
116
|
+
}
|
|
117
|
+
m_this.m_info = {colors: colors};
|
|
118
|
+
// check if colors haven't changed
|
|
119
|
+
var oldcolors = m_lookupTable.colorTable();
|
|
120
|
+
if (oldcolors && oldcolors.length === colors.length) {
|
|
121
|
+
let idx = indexRange[0] * 4;
|
|
122
|
+
for (; idx < indexRange[1] * 4; idx += 1) {
|
|
123
|
+
if (colors[idx] !== oldcolors[idx]) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (idx === indexRange[1] * 4) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
m_lookupTable.colorTable(colors);
|
|
132
|
+
/* If we haven't made a quad feature, make one now */
|
|
133
|
+
if (!m_quadFeature) {
|
|
134
|
+
m_quadFeature = m_this.layer().createFeature('quad', {
|
|
135
|
+
selectionAPI: false,
|
|
136
|
+
gcs: m_this.gcs(),
|
|
137
|
+
visible: m_this.visible(undefined, true)
|
|
138
|
+
});
|
|
139
|
+
m_quadFeatureInit = false;
|
|
140
|
+
}
|
|
141
|
+
if (!m_quadFeatureInit) {
|
|
142
|
+
m_this.dependentFeatures([m_quadFeature]);
|
|
143
|
+
m_quadFeature.setShader('image_fragment', fragmentShader);
|
|
144
|
+
m_quadFeature._hookBuild = (prog) => {
|
|
145
|
+
const lutSampler = new vgl.uniform(vgl.GL.INT, 'lutSampler');
|
|
146
|
+
lutSampler.set(m_lookupTable.textureUnit());
|
|
147
|
+
prog.addUniform(lutSampler);
|
|
148
|
+
const lutWidth = new vgl.uniform(vgl.GL.INT, 'lutWidth');
|
|
149
|
+
lutWidth.set(m_lookupTable.width);
|
|
150
|
+
prog.addUniform(lutWidth);
|
|
151
|
+
const lutHeight = new vgl.uniform(vgl.GL.INT, 'lutHeight');
|
|
152
|
+
lutHeight.set(m_lookupTable.height);
|
|
153
|
+
prog.addUniform(lutHeight);
|
|
154
|
+
};
|
|
155
|
+
m_quadFeature._hookRenderImageQuads = (renderState, quads) => {
|
|
156
|
+
quads.forEach((quad) => {
|
|
157
|
+
if (quad.image && quad.texture && !quad.texture.nearestPixel()) {
|
|
158
|
+
quad.texture.setNearestPixel(true);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
m_lookupTable.bind(renderState, quads);
|
|
162
|
+
};
|
|
163
|
+
if (m_quadFeatureInit === false) {
|
|
164
|
+
m_quadFeature.style({
|
|
165
|
+
image: m_this.m_srcImage,
|
|
166
|
+
position: m_this.style.get('position')})
|
|
167
|
+
.data([{}])
|
|
168
|
+
.draw();
|
|
169
|
+
}
|
|
170
|
+
m_quadFeatureInit = true;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Destroy. Deletes the associated quadFeature.
|
|
176
|
+
*
|
|
177
|
+
* @returns {this}
|
|
178
|
+
*/
|
|
179
|
+
this._exit = function () {
|
|
180
|
+
if (m_quadFeature && m_this.layer()) {
|
|
181
|
+
m_this.layer().deleteFeature(m_quadFeature);
|
|
182
|
+
m_quadFeature = null;
|
|
183
|
+
m_this.dependentFeatures([]);
|
|
184
|
+
}
|
|
185
|
+
s_exit();
|
|
186
|
+
return m_this;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
if (arg.quadFeature) {
|
|
190
|
+
m_quadFeature = arg.quadFeature;
|
|
191
|
+
}
|
|
192
|
+
this._init(arg);
|
|
193
|
+
return this;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
inherit(webgl_pixelmapFeature, pixelmapFeature);
|
|
197
|
+
|
|
198
|
+
// Now register it
|
|
199
|
+
var capabilities = {};
|
|
200
|
+
capabilities[pixelmapFeature.capabilities.lookup] = true;
|
|
201
|
+
|
|
202
|
+
registerFeature('webgl', 'pixelmap', webgl_pixelmapFeature, capabilities);
|
|
203
|
+
module.exports = webgl_pixelmapFeature;
|
package/src/webgl/quadFeature.js
CHANGED
|
@@ -3,6 +3,8 @@ var registerFeature = require('../registry').registerFeature;
|
|
|
3
3
|
var quadFeature = require('../quadFeature');
|
|
4
4
|
var timestamp = require('../timestamp');
|
|
5
5
|
|
|
6
|
+
let _memoryCheckLargestTested = 4096 * 4096;
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Create a new instance of class quadFeature.
|
|
8
10
|
*
|
|
@@ -158,7 +160,7 @@ var webgl_quadFeature = function (arg) {
|
|
|
158
160
|
* Build this feature.
|
|
159
161
|
*/
|
|
160
162
|
this._build = function () {
|
|
161
|
-
var mapper, mat, prog, srctex, unicrop, unicropsource, geom, context;
|
|
163
|
+
var mapper, mat, prog, srctex, unicrop, unicropsource, geom, context, sampler2d;
|
|
162
164
|
|
|
163
165
|
if (!m_this.position()) {
|
|
164
166
|
return;
|
|
@@ -183,6 +185,10 @@ var webgl_quadFeature = function (arg) {
|
|
|
183
185
|
prog.addUniform(new vgl.projectionUniform('projectionMatrix'));
|
|
184
186
|
prog.addUniform(new vgl.floatUniform('opacity', 1.0));
|
|
185
187
|
prog.addUniform(new vgl.floatUniform('zOffset', 0.0));
|
|
188
|
+
/* Use texture unit 0 */
|
|
189
|
+
sampler2d = new vgl.uniform(vgl.GL.INT, 'sampler2d');
|
|
190
|
+
sampler2d.set(0);
|
|
191
|
+
prog.addUniform(sampler2d);
|
|
186
192
|
context = m_this.renderer()._glContext();
|
|
187
193
|
unicrop = new vgl.uniform(context.FLOAT_VEC2, 'crop');
|
|
188
194
|
unicrop.set([1.0, 1.0]);
|
|
@@ -194,6 +200,9 @@ var webgl_quadFeature = function (arg) {
|
|
|
194
200
|
context.VERTEX_SHADER, context, vertexShaderImage));
|
|
195
201
|
prog.addShader(vgl.getCachedShader(
|
|
196
202
|
context.FRAGMENT_SHADER, context, fragmentShaderImage));
|
|
203
|
+
if (m_this._hookBuild) {
|
|
204
|
+
m_this._hookBuild(prog);
|
|
205
|
+
}
|
|
197
206
|
mat.addAttribute(prog);
|
|
198
207
|
mat.addAttribute(new vgl.blend());
|
|
199
208
|
/* This is similar to vgl.planeSource */
|
|
@@ -355,14 +364,22 @@ var webgl_quadFeature = function (arg) {
|
|
|
355
364
|
cropsrc = {x0: 0, y0: 0, x1: 1, y1: 1}, quadcropsrc,
|
|
356
365
|
w, h, quadw, quadh;
|
|
357
366
|
|
|
367
|
+
if (m_this._hookRenderImageQuads) {
|
|
368
|
+
m_this._hookRenderImageQuads(renderState, m_quads.imgQuads);
|
|
369
|
+
}
|
|
358
370
|
context.bindBuffer(context.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition);
|
|
359
371
|
$.each(m_quads.imgQuads, function (idx, quad) {
|
|
360
372
|
if (!quad.image) {
|
|
361
373
|
return;
|
|
362
374
|
}
|
|
363
375
|
quad.texture.bind(renderState);
|
|
364
|
-
if
|
|
365
|
-
|
|
376
|
+
// only check if the context is out of memory when using modestly large
|
|
377
|
+
// textures. The check is slow.
|
|
378
|
+
if (quad.image.width * quad.image.height > _memoryCheckLargestTested) {
|
|
379
|
+
_memoryCheckLargestTested = quad.image.width * quad.image.height;
|
|
380
|
+
if (context.getError() === context.OUT_OF_MEMORY) {
|
|
381
|
+
console.log('Insufficient GPU memory for texture');
|
|
382
|
+
}
|
|
366
383
|
}
|
|
367
384
|
if (quad.opacity !== opacity) {
|
|
368
385
|
opacity = quad.opacity;
|
|
@@ -450,6 +467,27 @@ var webgl_quadFeature = function (arg) {
|
|
|
450
467
|
m_this.modified();
|
|
451
468
|
};
|
|
452
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Set the image or color vertex or fragment shader.
|
|
472
|
+
*
|
|
473
|
+
* @param {string} shaderType One of `image_vertex`, `image_fragment`,
|
|
474
|
+
* `color_vertex`, or `color_fragment`.
|
|
475
|
+
* @param {string} shaderCode The shader program.
|
|
476
|
+
* @returns {this} The class instance on success, undefined in an unknown
|
|
477
|
+
* shaderType was specified.
|
|
478
|
+
*/
|
|
479
|
+
this.setShader = function (shaderType, shaderCode) {
|
|
480
|
+
switch (shaderType) {
|
|
481
|
+
case 'image_vertex': vertexShaderImage = shaderCode; break;
|
|
482
|
+
case 'image_fragment': fragmentShaderImage = shaderCode; break;
|
|
483
|
+
case 'color_vertex': vertexShaderColor = shaderCode; break;
|
|
484
|
+
case 'color_fragment': fragmentShaderColor = shaderCode; break;
|
|
485
|
+
default:
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
return m_this;
|
|
489
|
+
};
|
|
490
|
+
|
|
453
491
|
/**
|
|
454
492
|
* Destroy.
|
|
455
493
|
*/
|