@@ -399,10 +399,10 @@ Q5.modules.canvas = ($, q) => {
399
399
400
400
if ( Q5 . _server ) {
401
401
if ( Q5 . _createServerCanvas ) {
402
- q . canvas = Q5 . _createServerCanvas ( 100 , 100 ) ;
402
+ q . canvas = Q5 . _createServerCanvas ( 200 , 200 ) ;
403
403
}
404
404
} else if ( $ . _scope == 'image' || $ . _scope == 'graphics' ) {
405
- q . canvas = new $ . _Canvas ( 100 , 100 ) ;
405
+ q . canvas = new $ . _Canvas ( 200 , 200 ) ;
406
406
}
407
407
408
408
if ( ! $ . canvas ) {
@@ -413,12 +413,13 @@ Q5.modules.canvas = ($, q) => {
413
413
} else $ . noCanvas ( ) ;
414
414
}
415
415
416
- let c = $ . canvas ;
416
+ $ . displayDensity = ( ) => window . devicePixelRatio || 1 ;
417
+
417
418
$ . width = 200 ;
418
419
$ . height = 200 ;
419
420
$ . _pixelDensity = 1 ;
420
421
421
- $ . displayDensity = ( ) => window . devicePixelRatio || 1 ;
422
+ let c = $ . canvas ;
422
423
423
424
if ( c ) {
424
425
c . width = 200 ;
@@ -1327,13 +1328,23 @@ Q5.renderers.c2d.shapes = ($) => {
1327
1328
} ;
1328
1329
} ;
1329
1330
Q5 . renderers . c2d . image = ( $ , q ) => {
1331
+ const c = $ . canvas ;
1332
+
1333
+ if ( c ) {
1334
+ // polyfill for HTMLCanvasElement
1335
+ c . convertToBlob ??= ( opt ) =>
1336
+ new Promise ( ( resolve ) => {
1337
+ c . toBlob ( ( blob ) => resolve ( blob ) , opt . type , opt . quality ) ;
1338
+ } ) ;
1339
+ }
1340
+
1330
1341
$ . _tint = null ;
1331
1342
let imgData = null ;
1332
1343
1333
1344
$ . createImage = ( w , h , opt ) => {
1334
1345
opt ??= { } ;
1335
1346
opt . alpha ??= true ;
1336
- opt . colorSpace ??= $ . canvas . colorSpace || Q5 . canvasOptions . colorSpace ;
1347
+ opt . colorSpace ??= c . colorSpace || Q5 . canvasOptions . colorSpace ;
1337
1348
return new Q5 . Image ( $ , w , h , opt ) ;
1338
1349
} ;
1339
1350
@@ -1491,14 +1502,13 @@ Q5.renderers.c2d.image = ($, q) => {
1491
1502
if ( ! f ) $ . _softFilter ( type , value ) ;
1492
1503
1493
1504
$ . ctx . globalCompositeOperation = 'source-over' ;
1494
- $ . ctx . drawImage ( $ . canvas , 0 , 0 , $ . canvas . w , $ . canvas . h ) ;
1505
+ $ . ctx . drawImage ( c , 0 , 0 , c . w , c . h ) ;
1495
1506
$ . ctx . restore ( ) ;
1496
1507
$ . modified = $ . _retint = true ;
1497
1508
} ;
1498
1509
1499
1510
if ( $ . _scope == 'image' ) {
1500
1511
$ . resize = ( w , h ) => {
1501
- let c = $ . canvas ;
1502
1512
let o = new $ . _Canvas ( c . width , c . height ) ;
1503
1513
let tmpCtx = o . getContext ( '2d' , {
1504
1514
colorSpace : c . colorSpace
@@ -1516,13 +1526,13 @@ Q5.renderers.c2d.image = ($, q) => {
1516
1526
}
1517
1527
1518
1528
$ . _getImageData = ( x , y , w , h ) => {
1519
- return $ . ctx . getImageData ( x , y , w , h , { colorSpace : $ . canvas . colorSpace } ) ;
1529
+ return $ . ctx . getImageData ( x , y , w , h , { colorSpace : c . colorSpace } ) ;
1520
1530
} ;
1521
1531
1522
1532
$ . trim = ( ) => {
1523
1533
let pd = $ . _pixelDensity || 1 ;
1524
- let w = $ . canvas . width ;
1525
- let h = $ . canvas . height ;
1534
+ let w = c . width ;
1535
+ let h = c . height ;
1526
1536
let data = $ . _getImageData ( 0 , 0 , w , h ) . data ;
1527
1537
let left = w ,
1528
1538
right = 0 ,
@@ -1563,7 +1573,7 @@ Q5.renderers.c2d.image = ($, q) => {
1563
1573
1564
1574
$ . inset = ( x , y , w , h , dx , dy , dw , dh ) => {
1565
1575
let pd = $ . _pixelDensity || 1 ;
1566
- $ . ctx . drawImage ( $ . canvas , x * pd , y * pd , w * pd , h * pd , dx , dy , dw , dh ) ;
1576
+ $ . ctx . drawImage ( c , x * pd , y * pd , w * pd , h * pd , dx , dy , dw , dh ) ;
1567
1577
1568
1578
$ . modified = $ . _retint = true ;
1569
1579
} ;
@@ -1589,7 +1599,7 @@ Q5.renderers.c2d.image = ($, q) => {
1589
1599
w ??= $ . width ;
1590
1600
h ??= $ . height ;
1591
1601
let img = $ . createImage ( w , h , { pixelDensity : pd } ) ;
1592
- img . ctx . drawImage ( $ . canvas , x , y , w * pd , h * pd , 0 , 0 , w , h ) ;
1602
+ img . ctx . drawImage ( c , x , y , w * pd , h * pd , 0 , 0 , w , h ) ;
1593
1603
img . width = w ;
1594
1604
img . height = h ;
1595
1605
return img ;
@@ -1610,7 +1620,7 @@ Q5.renderers.c2d.image = ($, q) => {
1610
1620
let mod = $ . _pixelDensity || 1 ;
1611
1621
for ( let i = 0 ; i < mod ; i ++ ) {
1612
1622
for ( let j = 0 ; j < mod ; j ++ ) {
1613
- let idx = 4 * ( ( y * mod + i ) * $ . canvas . width + x * mod + j ) ;
1623
+ let idx = 4 * ( ( y * mod + i ) * c . width + x * mod + j ) ;
1614
1624
$ . pixels [ idx ] = c . r ;
1615
1625
$ . pixels [ idx + 1 ] = c . g ;
1616
1626
$ . pixels [ idx + 2 ] = c . b ;
@@ -1620,7 +1630,7 @@ Q5.renderers.c2d.image = ($, q) => {
1620
1630
} ;
1621
1631
1622
1632
$ . loadPixels = ( ) => {
1623
- imgData = $ . _getImageData ( 0 , 0 , $ . canvas . width , $ . canvas . height ) ;
1633
+ imgData = $ . _getImageData ( 0 , 0 , c . width , c . height ) ;
1624
1634
q . pixels = imgData . data ;
1625
1635
} ;
1626
1636
$ . updatePixels = ( ) => {
@@ -1635,20 +1645,6 @@ Q5.renderers.c2d.image = ($, q) => {
1635
1645
1636
1646
if ( $ . _scope == 'image' ) return ;
1637
1647
1638
- $ . _saveCanvas = async ( data , ext ) => {
1639
- data = data . canvas || data ;
1640
- if ( data instanceof OffscreenCanvas ) {
1641
- const blob = await data . convertToBlob ( { type : 'image/' + ext } ) ;
1642
-
1643
- return await new Promise ( ( resolve ) => {
1644
- const reader = new FileReader ( ) ;
1645
- reader . onloadend = ( ) => resolve ( reader . result ) ;
1646
- reader . readAsDataURL ( blob ) ;
1647
- } ) ;
1648
- }
1649
- return data . toDataURL ( 'image/' + ext ) ;
1650
- } ;
1651
-
1652
1648
$ . tint = function ( c ) {
1653
1649
$ . _tint = ( c . _q5Color ? c : $ . color ( ...arguments ) ) . toString ( ) ;
1654
1650
} ;
@@ -3370,9 +3366,9 @@ Q5.modules.input = ($, q) => {
3370
3366
l ( 'touchend' , ( e ) => $ . _ontouchend ( e ) ) ;
3371
3367
l ( 'touchcancel' , ( e ) => $ . _ontouchend ( e ) ) ;
3372
3368
3373
- c . addEventListener ( 'wheel' , ( e ) => $ . _onwheel ( e ) ) ;
3369
+ if ( c ) c . addEventListener ( 'wheel' , ( e ) => $ . _onwheel ( e ) ) ;
3374
3370
3375
- if ( ! $ . _isGlobal ) l = c . addEventListener . bind ( c ) ;
3371
+ if ( ! $ . _isGlobal && c ) l = c . addEventListener . bind ( c ) ;
3376
3372
3377
3373
l ( pointer + 'down' , ( e ) => $ . _onmousedown ( e ) ) ;
3378
3374
l ( 'touchstart' , ( e ) => $ . _ontouchstart ( e ) ) ;
@@ -4473,23 +4469,22 @@ Q5.modules.util = ($, q) => {
4473
4469
async function saveFile ( data , name , ext ) {
4474
4470
name = name || 'untitled' ;
4475
4471
ext = ext || 'png' ;
4472
+
4473
+ let blob ;
4476
4474
if ( imgRegex . test ( ext ) ) {
4477
- if ( $ . canvas ?. renderer == 'webgpu' && data . canvas ?. renderer == 'c2d' ) {
4478
- data = await $ . _g . _saveCanvas ( data , ext ) ;
4479
- } else {
4480
- data = await $ . _saveCanvas ( data , ext ) ;
4481
- }
4475
+ let cnv = data . canvas || data ;
4476
+ blob = await cnv . convertToBlob ( { type : 'image/' + ext } ) ;
4482
4477
} else {
4483
4478
let type = 'text/plain' ;
4484
4479
if ( ext == 'json' ) {
4485
4480
if ( typeof data != 'string' ) data = JSON . stringify ( data ) ;
4486
4481
type = 'text/json' ;
4487
4482
}
4488
- data = new Blob ( [ data ] , { type } ) ;
4489
- data = URL . createObjectURL ( data ) ;
4483
+ blob = new Blob ( [ data ] , { type } ) ;
4490
4484
}
4485
+
4491
4486
let a = document . createElement ( 'a' ) ;
4492
- a . href = data ;
4487
+ a . href = URL . createObjectURL ( blob ) ;
4493
4488
a . download = name + '.' + ext ;
4494
4489
a . click ( ) ;
4495
4490
setTimeout ( ( ) => URL . revokeObjectURL ( a . href ) , 1000 ) ;
@@ -4501,7 +4496,6 @@ Q5.modules.util = ($, q) => {
4501
4496
b = a ;
4502
4497
a = $ ;
4503
4498
}
4504
- if ( a == $ . canvas ) a = $ ;
4505
4499
if ( c ) saveFile ( a , b , c ) ;
4506
4500
else if ( b ) {
4507
4501
let lastDot = b . lastIndexOf ( '.' ) ;
@@ -4870,7 +4864,7 @@ Q5.Vector.fromAngles = (th, ph, l) => new Q5.Vector().fromAngles(th, ph, l);
4870
4864
Q5 . renderers . webgpu = { } ;
4871
4865
4872
4866
Q5 . renderers . webgpu . canvas = ( $ , q ) => {
4873
- let c = $ . canvas ;
4867
+ const c = $ . canvas ;
4874
4868
4875
4869
if ( $ . colorMode ) $ . colorMode ( 'rgb' , 1 ) ;
4876
4870
@@ -5095,9 +5089,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5095
5089
}
5096
5090
5097
5091
const addColor = ( r , g , b , a ) => {
5098
- let isColor = r . _q5Color ;
5099
-
5100
- if ( usingRGB === false || ( g === undefined && ! isColor && typeof r !== 'number' ) ) {
5092
+ if ( usingRGB === false || ( g === undefined && ! r . _q5Color && typeof r !== 'number' ) ) {
5101
5093
if ( usingRGB === false || typeof r == 'string' || ! Array . isArray ( r ) ) {
5102
5094
r = $ . color ( r , g , b , a ) ;
5103
5095
} else {
@@ -5110,7 +5102,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5110
5102
}
5111
5103
a ??= _colorFormat ;
5112
5104
5113
- if ( isColor === true ) {
5105
+ if ( r . _q5Color ) {
5114
5106
let c = r ;
5115
5107
if ( usingRGB ) ( { r, g, b, a } = c ) ;
5116
5108
else {
@@ -6671,70 +6663,69 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6671
6663
6672
6664
$ . _textureBindGroups = [ ] ;
6673
6665
6674
- $ . _saveCanvas = async ( g , ext ) => {
6675
- let makeFrame = g . _drawStack ?. length ;
6676
- if ( makeFrame ) {
6677
- g . _render ( ) ;
6678
- g . _finishRender ( ) ;
6679
- }
6666
+ if ( c ) {
6667
+ // polyfill for canvas.convertToBlob
6668
+ c . convertToBlob = async ( opt ) => {
6669
+ let makeFrame = $ . _drawStack ?. length ;
6670
+ if ( makeFrame ) {
6671
+ $ . _render ( ) ;
6672
+ $ . _finishRender ( ) ;
6673
+ }
6680
6674
6681
- let texture = g . _texture ;
6675
+ let texture = $ . _texture ;
6682
6676
6683
- if ( makeFrame ) g . _beginRender ( ) ;
6677
+ // this changes the value of $._texture
6678
+ if ( makeFrame ) $ . _beginRender ( ) ;
6684
6679
6685
- let w = texture . width ,
6686
- h = texture . height ,
6687
- bytesPerRow = Math . ceil ( ( w * 4 ) / 256 ) * 256 ;
6680
+ let w = texture . width ,
6681
+ h = texture . height ,
6682
+ bytesPerRow = Math . ceil ( ( w * 4 ) / 256 ) * 256 ;
6688
6683
6689
- let buffer = Q5 . device . createBuffer ( {
6690
- size : bytesPerRow * h ,
6691
- usage : GPUBufferUsage . COPY_DST | GPUBufferUsage . MAP_READ
6692
- } ) ;
6684
+ let buffer = Q5 . device . createBuffer ( {
6685
+ size : bytesPerRow * h ,
6686
+ usage : GPUBufferUsage . COPY_DST | GPUBufferUsage . MAP_READ
6687
+ } ) ;
6693
6688
6694
- let en = Q5 . device . createCommandEncoder ( ) ;
6689
+ let en = Q5 . device . createCommandEncoder ( ) ;
6695
6690
6696
- en . copyTextureToBuffer ( { texture } , { buffer, bytesPerRow, rowsPerImage : h } , { width : w , height : h } ) ;
6691
+ en . copyTextureToBuffer ( { texture } , { buffer, bytesPerRow, rowsPerImage : h } , { width : w , height : h } ) ;
6697
6692
6698
- Q5 . device . queue . submit ( [ en . finish ( ) ] ) ;
6693
+ Q5 . device . queue . submit ( [ en . finish ( ) ] ) ;
6699
6694
6700
- await buffer . mapAsync ( GPUMapMode . READ ) ;
6695
+ await buffer . mapAsync ( GPUMapMode . READ ) ;
6701
6696
6702
- let pad = new Uint8Array ( buffer . getMappedRange ( ) ) ;
6703
- let data = new Uint8Array ( w * h * 4 ) ; // unpadded data
6697
+ let pad = new Uint8Array ( buffer . getMappedRange ( ) ) ;
6698
+ let data = new Uint8Array ( w * h * 4 ) ; // unpadded data
6704
6699
6705
- // Remove padding from each row and swap BGR to RGB
6706
- for ( let y = 0 ; y < h ; y ++ ) {
6707
- const p = y * bytesPerRow ; // padded row offset
6708
- const u = y * w * 4 ; // unpadded row offset
6709
- for ( let x = 0 ; x < w ; x ++ ) {
6710
- const pp = p + x * 4 ; // padded pixel offset
6711
- const up = u + x * 4 ; // unpadded pixel offset
6712
- data [ up + 0 ] = pad [ pp + 2 ] ; // R <- B
6713
- data [ up + 1 ] = pad [ pp + 1 ] ; // G <- G
6714
- data [ up + 2 ] = pad [ pp + 0 ] ; // B <- R
6715
- data [ up + 3 ] = pad [ pp + 3 ] ; // A <- A
6700
+ // Remove padding from each row and swap BGR to RGB
6701
+ for ( let y = 0 ; y < h ; y ++ ) {
6702
+ const p = y * bytesPerRow ; // padded row offset
6703
+ const u = y * w * 4 ; // unpadded row offset
6704
+ for ( let x = 0 ; x < w ; x ++ ) {
6705
+ const pp = p + x * 4 ; // padded pixel offset
6706
+ const up = u + x * 4 ; // unpadded pixel offset
6707
+ data [ up + 0 ] = pad [ pp + 2 ] ; // R <- B
6708
+ data [ up + 1 ] = pad [ pp + 1 ] ; // G <- G
6709
+ data [ up + 2 ] = pad [ pp + 0 ] ; // B <- R
6710
+ data [ up + 3 ] = pad [ pp + 3 ] ; // A <- A
6711
+ }
6716
6712
}
6717
- }
6718
6713
6719
- buffer . unmap ( ) ;
6714
+ buffer . unmap ( ) ;
6720
6715
6721
- let colorSpace = $ . canvas . colorSpace ;
6722
- data = new Uint8ClampedArray ( data . buffer ) ;
6723
- data = new ImageData ( data , w , h , { colorSpace } ) ;
6724
- let cnv = new $ . _Canvas ( w , h ) ;
6725
- let ctx = cnv . getContext ( '2d' , { colorSpace } ) ;
6726
- ctx . putImageData ( data , 0 , 0 ) ;
6716
+ let colorSpace = $ . canvas . colorSpace ;
6717
+ data = new Uint8ClampedArray ( data . buffer ) ;
6718
+ data = new ImageData ( data , w , h , { colorSpace } ) ;
6727
6719
6728
- $ . _buffers . push ( buffer ) ;
6720
+ let cnv = new OffscreenCanvas ( w , h ) ;
6721
+ let ctx = cnv . getContext ( '2d' , { colorSpace } ) ;
6722
+ ctx . putImageData ( data , 0 , 0 ) ;
6729
6723
6730
- // Convert to blob then data URL
6731
- let blob = await cnv . convertToBlob ( { type : 'image/' + ext } ) ;
6732
- return await new Promise ( ( resolve ) => {
6733
- let r = new FileReader ( ) ;
6734
- r . onloadend = ( ) => resolve ( r . result ) ;
6735
- r . readAsDataURL ( blob ) ;
6736
- } ) ;
6737
- } ;
6724
+ $ . _buffers . push ( buffer ) ;
6725
+
6726
+ return await cnv . convertToBlob ( opt ) ;
6727
+ } ;
6728
+ }
6738
6729
6739
6730
let makeSampler = ( filter ) => {
6740
6731
$ . _imageSampler = Q5 . device . createSampler ( {
0 commit comments