@swf2es/context3d 0.1.18 → 0.1.19
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/backend.d.ts +30 -4
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +17 -0
- package/dist/backend.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/webgl/context.d.ts +27 -3
- package/dist/webgl/context.d.ts.map +1 -1
- package/dist/webgl/context.js +196 -29
- package/dist/webgl/context.js.map +1 -1
- package/dist/webgl/textures.d.ts +2 -0
- package/dist/webgl/textures.d.ts.map +1 -1
- package/dist/webgl/textures.js.map +1 -1
- package/package.json +1 -1
- package/src/backend.ts +51 -4
- package/src/index.ts +2 -0
- package/src/webgl/context.ts +256 -33
- package/src/webgl/textures.ts +2 -0
package/dist/webgl/context.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SAMPLER_FILTERS, SAMPLER_MIPS, SAMPLER_WRAPS, ScissorBoundsError, TextureBindingError, VERTEX_FORMATS, VertexOffsetError, } from "../backend.js";
|
|
1
|
+
import { MOST_BACK_BUFFER, SAMPLER_FILTERS, SAMPLER_MIPS, SAMPLER_WRAPS, ScissorBoundsError, screenPixels, TextureBindingError, VERTEX_FORMATS, VertexOffsetError, } from "../backend.js";
|
|
2
2
|
import { GlState } from "../device/state.js";
|
|
3
3
|
import { blockBytes } from "../dxt.js";
|
|
4
4
|
import { Context3DError } from "../errors.js";
|
|
@@ -31,6 +31,13 @@ export class WebGLContext3D {
|
|
|
31
31
|
drawState;
|
|
32
32
|
width = 0;
|
|
33
33
|
height = 0;
|
|
34
|
+
/**
|
|
35
|
+
* The back buffer's storage: its size, or less where the GPU allows less. The SWF still
|
|
36
|
+
* sees the size it asked for, drawn at this one and stretched to it when presented.
|
|
37
|
+
*/
|
|
38
|
+
drawnWidth = 0;
|
|
39
|
+
drawnHeight = 0;
|
|
40
|
+
gpuMost = null;
|
|
34
41
|
disposed = false;
|
|
35
42
|
samplerFormats = true;
|
|
36
43
|
buffers = new Map();
|
|
@@ -71,6 +78,8 @@ export class WebGLContext3D {
|
|
|
71
78
|
resolveDirty = false;
|
|
72
79
|
depthBuffer = null;
|
|
73
80
|
antiAlias = 0;
|
|
81
|
+
/** The back buffer's pixels to a CSS pixel and to a scissor rectangle's unit. */
|
|
82
|
+
scale = 1;
|
|
74
83
|
depthWrite = true;
|
|
75
84
|
depthCompare = "less";
|
|
76
85
|
blendEnabled = false;
|
|
@@ -150,17 +159,17 @@ export class WebGLContext3D {
|
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
this.drawState.bindFramebuffer(state ? targetFramebuffer(state) : this.framebuffer);
|
|
153
|
-
this.drawState.viewport(0, 0, texture?.width ?? this.
|
|
162
|
+
this.drawState.viewport(0, 0, texture?.width ?? this.drawnWidth, texture?.height ?? this.drawnHeight);
|
|
154
163
|
}
|
|
155
164
|
/** Temporary attachments and separate READ/DRAW bindings bypass the hot target cache. */
|
|
156
165
|
bindFramebuffer(target, framebuffer) {
|
|
157
166
|
this.drawState.forget();
|
|
158
167
|
this.gl.bindFramebuffer(target, framebuffer);
|
|
159
168
|
}
|
|
160
|
-
configure(width, height, depth = false, antiAlias = 0) {
|
|
169
|
+
configure(width, height, depth = false, antiAlias = 0, scale = 1) {
|
|
161
170
|
this.alive();
|
|
162
|
-
range(32, width - 32,
|
|
163
|
-
range(32, height - 32,
|
|
171
|
+
range(32, width - 32, MOST_BACK_BUFFER);
|
|
172
|
+
range(32, height - 32, MOST_BACK_BUFFER);
|
|
164
173
|
if (this.renderTexture) {
|
|
165
174
|
throw new Context3DError("operation", "Context3D experiment: resize while rendering to texture");
|
|
166
175
|
}
|
|
@@ -171,10 +180,18 @@ export class WebGLContext3D {
|
|
|
171
180
|
height === this.height &&
|
|
172
181
|
depth === (this.depthBuffer !== null) &&
|
|
173
182
|
antiAlias === this.antiAlias) {
|
|
183
|
+
if (scale !== this.scale) {
|
|
184
|
+
this.scale = scale;
|
|
185
|
+
this.showAt();
|
|
186
|
+
this.applyScissor();
|
|
187
|
+
}
|
|
174
188
|
return;
|
|
175
189
|
}
|
|
176
190
|
const samples = selectSamples(this.gl, antiAlias, depth);
|
|
177
191
|
const gl = this.gl;
|
|
192
|
+
const most = this.storageLimit();
|
|
193
|
+
const drawnWidth = Math.min(width, most[0]);
|
|
194
|
+
const drawnHeight = Math.min(height, most[1]);
|
|
178
195
|
const texture = gl.createTexture();
|
|
179
196
|
const framebuffer = gl.createFramebuffer();
|
|
180
197
|
const depthBuffer = depth ? gl.createRenderbuffer() : null;
|
|
@@ -192,7 +209,7 @@ export class WebGLContext3D {
|
|
|
192
209
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
193
210
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
194
211
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
195
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8,
|
|
212
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, drawnWidth, drawnHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
196
213
|
this.bindFramebuffer(gl.FRAMEBUFFER, resolveFramebuffer ?? framebuffer);
|
|
197
214
|
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
198
215
|
if (resolveFramebuffer) {
|
|
@@ -201,16 +218,16 @@ export class WebGLContext3D {
|
|
|
201
218
|
}
|
|
202
219
|
this.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
|
|
203
220
|
gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
|
|
204
|
-
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8,
|
|
221
|
+
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8, drawnWidth, drawnHeight);
|
|
205
222
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
|
|
206
223
|
}
|
|
207
224
|
if (depthBuffer) {
|
|
208
225
|
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
|
|
209
226
|
if (samples) {
|
|
210
|
-
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8,
|
|
227
|
+
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, drawnWidth, drawnHeight);
|
|
211
228
|
}
|
|
212
229
|
else {
|
|
213
|
-
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8,
|
|
230
|
+
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, drawnWidth, drawnHeight);
|
|
214
231
|
}
|
|
215
232
|
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
|
|
216
233
|
}
|
|
@@ -242,13 +259,68 @@ export class WebGLContext3D {
|
|
|
242
259
|
this.antiAlias = antiAlias;
|
|
243
260
|
this.width = width;
|
|
244
261
|
this.height = height;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.
|
|
248
|
-
|
|
262
|
+
this.drawnWidth = drawnWidth;
|
|
263
|
+
this.drawnHeight = drawnHeight;
|
|
264
|
+
this.scale = scale;
|
|
265
|
+
gl.canvas.width = drawnWidth;
|
|
266
|
+
gl.canvas.height = drawnHeight;
|
|
267
|
+
this.showAt();
|
|
268
|
+
// Readback's storage waits for a read: a back buffer as large as a 4K screen is 33 MB each.
|
|
269
|
+
this.pixels = new Uint8Array(0);
|
|
270
|
+
this.topDown = new Uint8Array(0);
|
|
249
271
|
this.applyScissor();
|
|
250
272
|
this.clear(0, 0, 0, 1);
|
|
251
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Shown at the size asked for, in CSS pixels, as an unstyled canvas of that size would be,
|
|
276
|
+
* divided by its scale: a best-resolution back buffer shows its pixels at the screen's.
|
|
277
|
+
*/
|
|
278
|
+
showAt() {
|
|
279
|
+
const style = this.gl.canvas.style;
|
|
280
|
+
if (!style) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const plain = this.scale === 1;
|
|
284
|
+
style.width = plain && this.drawnWidth === this.width ? "" : `${this.width / this.scale}px`;
|
|
285
|
+
style.height = plain && this.drawnHeight === this.height ? "" : `${this.height / this.scale}px`;
|
|
286
|
+
}
|
|
287
|
+
/** The largest back buffer storage the GPU gives: a texture, a renderbuffer and a viewport. */
|
|
288
|
+
storageLimit() {
|
|
289
|
+
if (!this.gpuMost) {
|
|
290
|
+
const gl = this.gl;
|
|
291
|
+
const viewport = gl.getParameter(gl.MAX_VIEWPORT_DIMS);
|
|
292
|
+
const side = Math.min(gl.getParameter(gl.MAX_TEXTURE_SIZE), gl.getParameter(gl.MAX_RENDERBUFFER_SIZE));
|
|
293
|
+
this.gpuMost = Int32Array.of(Math.min(side, viewport[0]), Math.min(side, viewport[1]));
|
|
294
|
+
}
|
|
295
|
+
return this.gpuMost;
|
|
296
|
+
}
|
|
297
|
+
unconfigure() {
|
|
298
|
+
this.alive();
|
|
299
|
+
if (this.renderTexture) {
|
|
300
|
+
throw new Context3DError("operation", "Context3D experiment: resize while rendering to texture");
|
|
301
|
+
}
|
|
302
|
+
const gl = this.gl;
|
|
303
|
+
gl.deleteTexture(this.texture);
|
|
304
|
+
gl.deleteFramebuffer(this.framebuffer);
|
|
305
|
+
gl.deleteFramebuffer(this.resolveFramebuffer);
|
|
306
|
+
gl.deleteRenderbuffer(this.depthBuffer);
|
|
307
|
+
gl.deleteRenderbuffer(this.colorBuffer);
|
|
308
|
+
this.drawState.forget();
|
|
309
|
+
this.texture = null;
|
|
310
|
+
this.framebuffer = null;
|
|
311
|
+
this.resolveFramebuffer = null;
|
|
312
|
+
this.colorBuffer = null;
|
|
313
|
+
this.depthBuffer = null;
|
|
314
|
+
this.resolveDirty = false;
|
|
315
|
+
this.antiAlias = 0;
|
|
316
|
+
this.scale = 1;
|
|
317
|
+
this.width = 0;
|
|
318
|
+
this.height = 0;
|
|
319
|
+
this.drawnWidth = 0;
|
|
320
|
+
this.drawnHeight = 0;
|
|
321
|
+
this.pixels = new Uint8Array(0);
|
|
322
|
+
this.topDown = new Uint8Array(0);
|
|
323
|
+
}
|
|
252
324
|
clear(red, green, blue, alpha, depth = 1, mask = 0xffffffff, stencil = 0) {
|
|
253
325
|
this.target(false, (mask & 1) !== 0);
|
|
254
326
|
const gl = this.gl;
|
|
@@ -401,13 +473,33 @@ export class WebGLContext3D {
|
|
|
401
473
|
}
|
|
402
474
|
const width = this.renderTexture?.width ?? this.width;
|
|
403
475
|
const height = this.renderTexture?.height ?? this.height;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
476
|
+
let left = Math.max(0, Math.min(width, this.scissorLeft));
|
|
477
|
+
let top = Math.max(0, Math.min(height, this.scissorTop));
|
|
478
|
+
let right = Math.max(0, Math.min(width, this.scissorRight));
|
|
479
|
+
let bottom = Math.max(0, Math.min(height, this.scissorBottom));
|
|
480
|
+
// AIR tells a rectangle outside from its own unit, then scales it to a back buffer
|
|
481
|
+
// configured for best resolution, whose pixels are the screen's.
|
|
408
482
|
this.scissorOutside = right <= left || bottom <= top;
|
|
483
|
+
if (!this.renderTexture && this.scale !== 1) {
|
|
484
|
+
const scale = this.scale;
|
|
485
|
+
left = Math.max(0, Math.min(width, screenPixels(this.scissorLeft, scale)));
|
|
486
|
+
top = Math.max(0, Math.min(height, screenPixels(this.scissorTop, scale)));
|
|
487
|
+
right = Math.max(0, Math.min(width, screenPixels(this.scissorRight, scale)));
|
|
488
|
+
bottom = Math.max(0, Math.min(height, screenPixels(this.scissorBottom, scale)));
|
|
489
|
+
}
|
|
490
|
+
let drawnHeight = height;
|
|
491
|
+
if (!this.renderTexture && (this.drawnWidth !== width || this.drawnHeight !== height)) {
|
|
492
|
+
// The back buffer's storage is smaller than its size: the rectangle shrinks with it.
|
|
493
|
+
const x = this.drawnWidth / width;
|
|
494
|
+
const y = this.drawnHeight / height;
|
|
495
|
+
left = Math.round(left * x);
|
|
496
|
+
right = Math.round(right * x);
|
|
497
|
+
top = Math.round(top * y);
|
|
498
|
+
bottom = Math.round(bottom * y);
|
|
499
|
+
drawnHeight = this.drawnHeight;
|
|
500
|
+
}
|
|
409
501
|
gl.enable(gl.SCISSOR_TEST);
|
|
410
|
-
gl.scissor(left, this.renderTexture ? top :
|
|
502
|
+
gl.scissor(left, this.renderTexture ? top : drawnHeight - bottom, Math.max(0, right - left), Math.max(0, bottom - top));
|
|
411
503
|
}
|
|
412
504
|
setBlend(source, destination) {
|
|
413
505
|
this.alive();
|
|
@@ -456,7 +548,7 @@ export class WebGLContext3D {
|
|
|
456
548
|
break;
|
|
457
549
|
}
|
|
458
550
|
}
|
|
459
|
-
createBuffer(count, stride) {
|
|
551
|
+
createBuffer(count, stride, dynamic = false) {
|
|
460
552
|
this.alive();
|
|
461
553
|
range(1, count - 1, stride === 0 ? 524287 : 65535);
|
|
462
554
|
range(0, stride, 64);
|
|
@@ -482,7 +574,7 @@ export class WebGLContext3D {
|
|
|
482
574
|
}
|
|
483
575
|
gl.bindVertexArray(this.vao);
|
|
484
576
|
gl.bindBuffer(target, handle);
|
|
485
|
-
gl.bufferData(target, bytes, gl.STATIC_DRAW);
|
|
577
|
+
gl.bufferData(target, bytes, dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
|
|
486
578
|
const state = {
|
|
487
579
|
handle,
|
|
488
580
|
indices: stride === 0 ? new Uint16Array(count) : null,
|
|
@@ -636,8 +728,8 @@ export class WebGLContext3D {
|
|
|
636
728
|
}
|
|
637
729
|
return state;
|
|
638
730
|
}
|
|
639
|
-
createTexture(width, height, cube = false, format = "bgra") {
|
|
640
|
-
return this.allocateTexture(width, height, cube, false, format);
|
|
731
|
+
createTexture(width, height, cube = false, format = "bgra", streaming = 0) {
|
|
732
|
+
return this.allocateTexture(width, height, cube, false, format, streaming);
|
|
641
733
|
}
|
|
642
734
|
createRectangleTexture(width, height) {
|
|
643
735
|
return this.allocateTexture(width, height, false, true, "bgra");
|
|
@@ -669,7 +761,7 @@ export class WebGLContext3D {
|
|
|
669
761
|
}
|
|
670
762
|
return size * faces;
|
|
671
763
|
}
|
|
672
|
-
allocateTexture(width, height, cube, rectangle, format) {
|
|
764
|
+
allocateTexture(width, height, cube, rectangle, format, streaming = 0) {
|
|
673
765
|
this.alive();
|
|
674
766
|
range(1, width - 1, 2048);
|
|
675
767
|
range(1, height - 1, 2048);
|
|
@@ -681,6 +773,7 @@ export class WebGLContext3D {
|
|
|
681
773
|
}
|
|
682
774
|
const faces = cube ? 6 : 1;
|
|
683
775
|
const levels = rectangle ? 1 : 1 + Math.floor(Math.log2(Math.max(width, height)));
|
|
776
|
+
range(streaming, 1, levels);
|
|
684
777
|
if (!rectangle && ((width & (width - 1)) !== 0 || (height & (height - 1)) !== 0)) {
|
|
685
778
|
throw new Context3DError("texturePowerOfTwo", "Context3D texture size must be a power of two");
|
|
686
779
|
}
|
|
@@ -737,6 +830,7 @@ export class WebGLContext3D {
|
|
|
737
830
|
multisample: null,
|
|
738
831
|
samples: 0,
|
|
739
832
|
antiAlias: 0,
|
|
833
|
+
base: 0,
|
|
740
834
|
};
|
|
741
835
|
const texture = {
|
|
742
836
|
width,
|
|
@@ -745,6 +839,7 @@ export class WebGLContext3D {
|
|
|
745
839
|
cube,
|
|
746
840
|
rectangle,
|
|
747
841
|
format,
|
|
842
|
+
streaming,
|
|
748
843
|
upload: (pixels, level = 0, face = 0, sourceWidth, sourceHeight) => {
|
|
749
844
|
this.alive();
|
|
750
845
|
this.textureState(texture);
|
|
@@ -1383,7 +1478,10 @@ export class WebGLContext3D {
|
|
|
1383
1478
|
throw new TextureBindingError("unuploaded");
|
|
1384
1479
|
}
|
|
1385
1480
|
}
|
|
1386
|
-
|
|
1481
|
+
const base = texture.streaming
|
|
1482
|
+
? this.streamingBase(texture, resource, this.samplerStates[slot] >>> 5 !== 0)
|
|
1483
|
+
: 0;
|
|
1484
|
+
for (let face = 0; !texture.streaming && face < resource.uploaded.length; face++) {
|
|
1387
1485
|
if (!texture.cube && !(resource.uploaded[face] & 1)) {
|
|
1388
1486
|
throw new TextureBindingError("unuploaded");
|
|
1389
1487
|
}
|
|
@@ -1403,6 +1501,13 @@ export class WebGLContext3D {
|
|
|
1403
1501
|
gl.bindSampler(slot, this.sampler(this.samplerStates[slot]));
|
|
1404
1502
|
this.texturesApplied[slot] = 1;
|
|
1405
1503
|
}
|
|
1504
|
+
if (resource.base !== base) {
|
|
1505
|
+
const target = texture.cube ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
|
|
1506
|
+
gl.activeTexture(gl.TEXTURE0 + slot);
|
|
1507
|
+
gl.bindTexture(target, resource.handle);
|
|
1508
|
+
gl.texParameteri(target, gl.TEXTURE_BASE_LEVEL, base);
|
|
1509
|
+
resource.base = base;
|
|
1510
|
+
}
|
|
1406
1511
|
if (!state.samplersSet) {
|
|
1407
1512
|
gl.uniform1i(binding.location, slot);
|
|
1408
1513
|
}
|
|
@@ -1425,6 +1530,45 @@ export class WebGLContext3D {
|
|
|
1425
1530
|
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, first * 2);
|
|
1426
1531
|
this.changedTarget();
|
|
1427
1532
|
}
|
|
1533
|
+
/**
|
|
1534
|
+
* The level a streaming texture is drawn from, as AIR 51 draws one: with mip filtering, the
|
|
1535
|
+
* finest whose levels down to the smallest are uploaded on every face, which must reach its
|
|
1536
|
+
* streaming level (#3700 before); without, its level 0 alone (#3713), as a texture that
|
|
1537
|
+
* does not stream.
|
|
1538
|
+
*/
|
|
1539
|
+
streamingBase(texture, resource, mip) {
|
|
1540
|
+
const faces = resource.uploaded.length;
|
|
1541
|
+
let uploaded = 0;
|
|
1542
|
+
for (let face = 0; face < faces; face++) {
|
|
1543
|
+
uploaded |= resource.uploaded[face];
|
|
1544
|
+
}
|
|
1545
|
+
if (!uploaded) {
|
|
1546
|
+
throw new TextureBindingError("unuploaded");
|
|
1547
|
+
}
|
|
1548
|
+
if (!mip) {
|
|
1549
|
+
for (let face = 0; face < faces; face++) {
|
|
1550
|
+
if (!(resource.uploaded[face] & 1)) {
|
|
1551
|
+
throw new TextureBindingError("streaming");
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
return 0;
|
|
1555
|
+
}
|
|
1556
|
+
let base = texture.levels;
|
|
1557
|
+
while (base > 0) {
|
|
1558
|
+
let whole = true;
|
|
1559
|
+
for (let face = 0; face < faces; face++) {
|
|
1560
|
+
whole &&= (resource.uploaded[face] & (1 << (base - 1))) !== 0;
|
|
1561
|
+
}
|
|
1562
|
+
if (!whole) {
|
|
1563
|
+
break;
|
|
1564
|
+
}
|
|
1565
|
+
base--;
|
|
1566
|
+
}
|
|
1567
|
+
if (base > texture.streaming) {
|
|
1568
|
+
throw new TextureBindingError("mipmaps");
|
|
1569
|
+
}
|
|
1570
|
+
return base;
|
|
1571
|
+
}
|
|
1428
1572
|
uniforms(locations, bank, previous, values) {
|
|
1429
1573
|
for (let i = 0; i < locations.length; i++) {
|
|
1430
1574
|
if (locations[i] && (bank.writing || previous[i] !== bank.versions[i])) {
|
|
@@ -1460,7 +1604,7 @@ export class WebGLContext3D {
|
|
|
1460
1604
|
if (this.scissor) {
|
|
1461
1605
|
gl.disable(gl.SCISSOR_TEST);
|
|
1462
1606
|
}
|
|
1463
|
-
gl.blitFramebuffer(0, 0, this.
|
|
1607
|
+
gl.blitFramebuffer(0, 0, this.drawnWidth, this.drawnHeight, 0, 0, this.drawnWidth, this.drawnHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
1464
1608
|
if (this.scissor) {
|
|
1465
1609
|
gl.enable(gl.SCISSOR_TEST);
|
|
1466
1610
|
}
|
|
@@ -1474,12 +1618,34 @@ export class WebGLContext3D {
|
|
|
1474
1618
|
this.target(true);
|
|
1475
1619
|
this.resolve();
|
|
1476
1620
|
const gl = this.gl;
|
|
1621
|
+
const scaleX = this.drawnWidth / this.width;
|
|
1622
|
+
const scaleY = this.drawnHeight / this.height;
|
|
1623
|
+
const drawnWidth = Math.min(this.drawnWidth, Math.ceil(width * scaleX));
|
|
1624
|
+
const drawnHeight = Math.min(this.drawnHeight, Math.ceil(height * scaleY));
|
|
1625
|
+
if (this.pixels.length < drawnWidth * drawnHeight * 4) {
|
|
1626
|
+
this.pixels = new Uint8Array(drawnWidth * drawnHeight * 4);
|
|
1627
|
+
}
|
|
1628
|
+
if (this.topDown.length < width * height * 4) {
|
|
1629
|
+
this.topDown = new Uint8Array(width * height * 4);
|
|
1630
|
+
}
|
|
1477
1631
|
// GL's rows run bottom-up, so the top-left region starts height rows below the top.
|
|
1478
|
-
gl.readPixels(0, this.
|
|
1632
|
+
gl.readPixels(0, this.drawnHeight - drawnHeight, drawnWidth, drawnHeight, gl.RGBA, gl.UNSIGNED_BYTE, this.pixels);
|
|
1479
1633
|
const row = width * 4;
|
|
1634
|
+
if (drawnWidth === width && drawnHeight === height) {
|
|
1635
|
+
for (let y = 0; y < height; y++) {
|
|
1636
|
+
const from = (height - y - 1) * row;
|
|
1637
|
+
this.topDown.set(this.pixels.subarray(from, from + row), y * row);
|
|
1638
|
+
}
|
|
1639
|
+
return this.topDown;
|
|
1640
|
+
}
|
|
1641
|
+
// Storage smaller than the back buffer: each pixel is the stored one it falls on.
|
|
1642
|
+
const source = new Uint32Array(this.pixels.buffer, 0, drawnWidth * drawnHeight);
|
|
1643
|
+
const target = new Uint32Array(this.topDown.buffer, 0, width * height);
|
|
1480
1644
|
for (let y = 0; y < height; y++) {
|
|
1481
|
-
const from = (
|
|
1482
|
-
|
|
1645
|
+
const from = (drawnHeight - 1 - Math.min(drawnHeight - 1, Math.floor(y * scaleY))) * drawnWidth;
|
|
1646
|
+
for (let x = 0; x < width; x++) {
|
|
1647
|
+
target[y * width + x] = source[from + Math.min(drawnWidth - 1, Math.floor(x * scaleX))];
|
|
1648
|
+
}
|
|
1483
1649
|
}
|
|
1484
1650
|
return this.topDown;
|
|
1485
1651
|
}
|
|
@@ -1492,7 +1658,8 @@ export class WebGLContext3D {
|
|
|
1492
1658
|
if (this.scissor) {
|
|
1493
1659
|
gl.disable(gl.SCISSOR_TEST);
|
|
1494
1660
|
}
|
|
1495
|
-
|
|
1661
|
+
// To the drawing buffer the browser gave, which it may have made smaller than the canvas.
|
|
1662
|
+
gl.blitFramebuffer(0, 0, this.drawnWidth, this.drawnHeight, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
1496
1663
|
if (this.scissor) {
|
|
1497
1664
|
gl.enable(gl.SCISSOR_TEST);
|
|
1498
1665
|
}
|