使用Uint32Array的画布:正在渲染错误的颜色

时间:2016-08-29 19:22:58

标签: javascript html canvas 32-bit

我正在创建一个JS画布,我想在其中显示一个不同颜色的框。

我正在使用uint32来提高速度,我的颜色永远不会正确显示! 我在这里主要看了一些例子:https://stackoverflow.com/a/19502117有人在评论中说:

  

(小I或JS会抛出错误)。 OP的提示:Uint32的颜色也可以简单地使用十六进制 - 不需要进行移位:0xff00000 =黑色+ alpha设置为255;对于little-endian / LSB CPU,与big-endian / MSB CPU相反。“

我确定我的笔记本电脑是小端的。

我在这里有一个关于我的问题的演示:http://jsfiddle.net/GhwUC/357/

Sponsorship.first

这里讨论的颜色是:

color

但小提琴显示黄色的颜色: color fail

其他颜色也一样,非常感谢!

编辑:感谢@Oriol快速回答!我使用以下函数来反转我的颜色(如果有人感兴趣的话):

var canvas = document.getElementById('canvas');
var canvasWidth  = canvas.width;
var canvasHeight = canvas.height;
var ctx = canvas.getContext('2d');
var imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);

var buf = new ArrayBuffer(imageData.data.length);
var buf8 = new Uint8ClampedArray(buf);
var data = new Uint32Array(buf);

for (var y = 0; y < canvasHeight; ++y) {
    for (var x = 0; x < canvasWidth; ++x) {
        data[y * canvasWidth + x] = 0xff80d7ff // Should be light blue (#80d7ff)
    }
}

imageData.data.set(buf8);

ctx.putImageData(imageData, 0, 0);

使用它:function reverseUint32 (uint32) { var s32 = new Uint32Array(4); var s8 = new Uint8Array(s32.buffer); var t32 = new Uint32Array(4); var t8 = new Uint8Array(t32.buffer); reverseUint32 = function (x) { s32[0] = x; t8[0] = s8[3]; t8[1] = s8[2]; t8[2] = s8[1]; t8[3] = s8[0]; return t32[0]; } return reverseUint32(uint32); };

2 个答案:

答案 0 :(得分:5)

当您将Uint8Array缓冲区视为小端的Uint32时会发生这种情况:

var buf = new Uint8Array([0x12, 0x34, 0x56, 0x78]).buffer;
console.log(new Uint32Array(buf)[0].toString(16));
// "78563412" in little endian, "12345678" in big endian

所以在小端,订单变成AABBGGRR而不是AARRGGBB。

您可以将0x80d7ffff转换为0xffffd780,但这样就无法在大端机器上运行。

要避免这些问题,您可以使用DataView,它允许指定字节序,默认为大端:

view.setUint32(offset, 0xffffd780, true);  // #80d7ff, in little endian
view.setUint32(offset, 0x80d7ffff, false); // #80d7ff, in big endian

var canvas = document.getElementById('canvas'),
    canvasWidth  = canvas.width,
    canvasHeight = canvas.height,
    ctx = canvas.getContext('2d'),
    imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
    view = new DataView(imageData.data.buffer);
for (var y = 0; y < canvasHeight; ++y) {
  for (var x = 0; x < canvasWidth; ++x) {
    var offset = 4 * (y * canvasWidth + x);
    view.setUint32(offset, 0x80d7ffff); // light blue (#80d7ff)
  }
}
ctx.putImageData(imageData, 0, 0);
<canvas id="canvas" height="256" width="256"></canvas>

但似乎浏览器没有优化DataView,所以它很慢。然后,最好在Uint8ClampedArray

中单独设置颜色组件

var canvas = document.getElementById('canvas'),
    canvasWidth  = canvas.width,
    canvasHeight = canvas.height,
    ctx = canvas.getContext('2d'),
    imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight),
    data = imageData.data;
for (var y = 0; y < canvasHeight; ++y) {
  for (var x = 0; x < canvasWidth; ++x) {
    var offset = 4 * (y * canvasWidth + x);
    // light blue (#80d7ff)
    data[offset+0] = 0x80; // red
    data[offset+1] = 0xd7; // green
    data[offset+2] = 0xff; // blue
    data[offset+3] = 0xff; // alpha
  }
}
ctx.putImageData(imageData, 0, 0);
<canvas id="canvas" height="256" width="256"></canvas>

答案 1 :(得分:3)

所有关于表现。

最佳解决方案是进行endian测试。

var isLittleEndian = true;
(()=>{
    var buf = new ArrayBuffer(4);
    var buf8 = new Uint8ClampedArray(buf);
    var data = new Uint32Array(buf);
    data[0] = 0x0F000000;
    if(buf8[0] === 0x0f){
        isLittleEndian = false;
    }
})();

根据测试写入像素。

var imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
var data = new Uint32Array(imageData.data.buffer);
var val = 0xffffd780;
if(isLittleEndian){
    val = 0x80d7ffff;
}
var i = 0;
while(i < data.length) {
    data[i++] = val; 
}

了解效果

在使用(查看)view.Uint32(i,val);编写一个值所花费的时间内,您可以使用(直接)data[i] = val;

编写33个值

Firefox的统计数据。

用于恒定循环时间测试。

  • 直接写入*每个周期10,000个:23222 97.07%100%。
  • 查看每周期写入10,000次:702:2.93%3.02%

首先%是总数的百分比。第二个百分比是最快(直接)

的百分比

每10,000次写入的演出时间(1 / 1,000,000秒的时间)

  • 直接1701次循环* 10000次写入。平均值:43.063(1e-6秒)。差异:0.210
  • 查看1701个周期* 10000次写入。平均值:1424.832(1e-6秒)。差异:379.441

请注意View的高差异值是由于Javascript优化。通过View进行短暂的写入突发可能会慢得多。

对于每10,000次写入的性能时间(1 / 1,000,000秒的时间),允许方差稳定。

  • 直接31636周期* 10000次写入。平均值:29.981(1e-6秒)。差异:0.005
  • 查看31802个周期* 10000次写入。平均值:982.195(1e-6秒)。差异:0.154

更多测试

根据评论中的要求进行更多测试。测试单元是对其中一个测试功能的调用。因此,在以下测试中,每个测试单元10000个32位写入。

比较8Bit写入32Bit写入

功能测试

  testFunctions = [{
          func:function(){
              for(i = 0; i < 40000; i ++){
                  data[i++] = 0xFF;
                  data[i++] = 0xFF;
                  data[i++] = 0xd7;
                  data[i] = 0x80;
              }},
          name:"8Bit",
      },{
          func:function(){
              for(i = 0; i < 10000; i ++){
                  view2[i] = 0x80d7ffff;
             }},
          name:"32Bit",
      }
  ];

共同背景

var i
var i,arr;
var data = new Uint8ClampedArray(100000);
var view2 = new Uint32Array(data.buffer);

测试结果raw dump。

Test complete Results.
Function name, Calls per sec, % cycle time, % of best time
32Bit : 33743 : 76.84% 100%.
8Bit : 10170 : 23.16% 30.14%

Total cycles : 1000
Stable cycles : 899 Total.
Tests per cycle : 660
Testing cycles stable for : 800 of 800 cycles 100.00%
Max test variance 0.000%
Test results are good.

List of all test function results.
Mean times in micro secs 1/1,000,000 times mark with ms in milliseconds 1/1,000
# calls, total time, mean time
--------------------
Test function : 8Bit
62264 tests 6122.825ms Mean : 98
61942 tests 6088.945ms Mean : 98
62283 tests 6124.810ms Mean : 98
62233 tests 6121.010ms Mean : 98
Variance : 0.000micro sec. normalised : 0.000%
Ran : 248722 over 24457.590ms Mean : 98.333micro sec
--------------------
Test function : 32Bit
62084 tests 1839.180ms Mean : 30
61738 tests 1829.285ms Mean : 30
62282 tests 1846.225ms Mean : 30
62390 tests 1849.650ms Mean : 30
Variance : 0.000micro sec. normalised : 0.000%
Ran : 248494 over 7364.340ms Mean : 29.636micro sec
Total number of tests run : 497216

查看和直接撰写

本答案开头描述的测试运行的详细视图。

功能和共享上下文

 sharedFunction = function(){ 
    var i;
    var data = new Uint8ClampedArray(100000);
    var view1 = new DataView(data.buffer);
    var view2 = new Uint32Array(data.buffer);
 }
  testFunctions = [{
          func:function(){
              for(i = 0; i < 10000; i ++){
                  view1.setUint32(i, 0x80d7ffff); 
              }            
          },
          name:"View",
      },{
          func:function(){
              for(i = 0; i < 10000; i ++){
                  view2[i] = 0x80d7ffff;
             }},
          name:"Direct",
      }
  ];

结果

Test complete Results.
Calls per sec, % cycle time, % of best time
Direct : 35766 : 97.07% 100%.
View : 1080 : 2.93% 3.02%

Total cycles : 1000
Stable cycles : 899 Total.
Tests per cycle : 73
Testing cycles stable for : 800 of 800 cycles 100.00%
Max test variance 5.231%
Test results are good.

Mean times in micro secs 1/1,000,000 times mark with ms in milliseconds 1/1,000
# calls, total time, mean time
--------------------
Test function : View
8583 tests 7850.680ms Mean : 915
8454 tests 7830.950ms Mean : 926
8201 tests 7639.375ms Mean : 932
8459 tests 7883.150ms Mean : 932
Variance : 48.445micro sec. normalised : 5.231%
Ran : 33697 over 31204.155ms Mean : 926.022micro sec
--------------------
Test function : Direct
8434 tests 235.295ms Mean : 28
8347 tests 234.190ms Mean : 28
8451 tests 237.045ms Mean : 28
8260 tests 229.900ms Mean : 28
Variance : 0.009micro sec. normalised : 0.033%
Ran : 33492 over 936.430ms Mean : 27.960micro sec
Total number of tests run : 67189
  

注意每个测试功能作为4个独立的功能运行。当所有4个当前循环测试时间与先前的测试循环时间匹配时,稳定测试。 Javascript优化会导致时间变化,因为无法确定何时发生优化,测试代码会等待所有测试函数返回至少100个循环的稳定时间。进一步的循环时间不稳定性将在方差值中显示。

     

每个周期的测试是一个平均值(未在结果中注明)

     

所有测试函数都是通过testFunction[ Math.floor(Math.random() * testFunctionCount * testsPerFunction) ]();

以随机顺序运行的      

时间是performance.now();,仅测量测试函数的内部内容。