JavaScript Canvas - Typed Arrays
Written by Ian Elliot   
Monday, 01 January 2024
Article Index
JavaScript Canvas - Typed Arrays
Operators

Operators

If you create an array of a given type then the data stored is of that type. However, the arithmetic and other operators that you might apply to an array element perform the usual JavaScript operations after type conversion. This is very reasonable, but it can also be confusing. For example, what would you expect the result of:

bytes[0]=0xFF; 
console.log(~bytes[0]);

to be?

The "~" is a bitwise NOT, see the previous chapter, and so the byte 11111111 should be converted into 00000000 and the result should be 0. However, it is actually -256. How can this be? The answer is that all JavaScript bitwise operations work with a 32-bit integer and so the value that the NOT operates on is:

00000000000000000000000011111111 i.e. 0x000000FF

and when you apply the bitwise NOT you get:

11111111111111111111111100000000 i.e. 0xFFFFFF00

which treated as a 32-bit signed integer is -256. The result isn’t truncated back to 8-bits before being printed, thus the output is -256. Notice that the final value is actually treated as a full floating-point value.

In this case the high order bits are lost if you assign the operation back to an array element of type Uint8Array. That is:

bytes[0]=0xFF;
bytes[0]=~bytes[0]; 
console.log(bytes[0]);

does give the expected result of 0, but only because the 32-bit result is truncated to 8-bits by the type conversion. 

The same is true of arithmetic when the value stored in the array will be converted into a 64-bit float. Notice that none of the integer or floating-point array types will lose precision in this conversion. What non-JavaScript frameworks do when you pass a typed array is another matter and you just have to investigate on a case-by-case basis.

smallcover180

The ArrayBuffer

In many cases you can simply use typed arrays as described above, but sometimes you need to do more sophisticated things. Every typed array makes use of an ArrayBuffer object to store its data. This is simply the block of memory that the typed array allocates to store its data and it doesn't have methods that allow you to access the data. To access the data you need a typed array which provides a view into the ArrayBuffer object. 

The reason for this two-level approach is so that you can acquire data and only later determine how you want to treat it. It is even possible to use multiple views with a single ArrayBuffer, so providing for alternative interpretations of the data. Some API calls return an ArrayBuffer, leaving it up to you how to set up a view to process the data.  

Think of the ArrayBuffer as just being the data storage and the view as being how to interpret the data. 

When you create a typed array an ArrayBuffer object is automatically created big enough to store the array. You can retrieve a reference to the ArrayBuffer via the buffer property. For example:

var buffer=bytes.buffer; 

To associate a new view with an existing ArrayBuffer all you have to do is specify it within the constructor. For example:

var buffer= bytes.buffer; 
var uint16=new Uint16Array(buffer);
console.log(uint16.length);

In this case a 10-byte buffer is now viewed via uint16 as five 16-bit unsigned integers. You can see the advantages of this approach in that you can get to the individual high and low bytes of the 16-bit integer via the bytes array and the entire 16-bit integer via the uint16 array. This saves a lot of additional work that would be necessary if you needed to combine the high and low bytes as a special operation. 

If you want to do a lot of this sort of view swapping then you can create an instance of the ArrayBuffer directly. For example:

var buffer= new ArrayBuffer(10);

creates a buffer with ten bytes initialized to 0 and: 

var bytes=new Uint8Array(buffer);

creates an array of unsigned 8-bit integers using it.  You can also set another view into the same buffer using something like:

var uint16=new Uint16Array(buffer); 

Notice that arrays that share the same ArrayBuffer really do share the same data. In our example if you store something in uint16[0] then you have modified what is stored in bytes[0] and bytes[1] which share the same location in the buffer.

You can also specify an offset and a length for a view which determines exactly which part of the ArrayBuffer it uses. 

For example:

var uint16=new Uint16Array(buffer,5,2);

specifies that the view starts in the ArrayBuffer at the sixth byte and creates just two 16-byte integers. In this case uint16[0] is the same storage location as bytes[5] and bytes[6] and uint16[1] is bytes[7] and bytes[8]. 

You can see that things could become very complicated with views sharing overlapping and non-overlapping portions of the ArrayBuffer, but in general things are usually kept simple.

Block Copy

One of the very standard operations that you have to perform with binary data is moving it from one place to another. The direct, and not very efficient, way of doing this is to simply write a suitable for loop that transfers the data one element at a time.

A better method is to use the typed array's set method which will transfer the contents of one array or part of an array to another: 

array1.set(array2)

This copies all of the contents of array2 into array1 and:

array1.set(array2,offset)

copies all of array2 into array1 starting at array1[offset]

If for any reason the copy operation results in an attempt to access beyond the end of array1 then an exception is thrown. 

You can even use set to move data within a single ArrayBuffer. For example, if we set the first 50 bytes of an array to 255 and then define a view of these first 50 bytes, we can use this to move all 50 to the top of the array:

var bytes=new Uint8Array(100);
for(var i=0;i<50;i++){ bytes[i]=0xFF; };
var buffer= bytes.buffer; var start=new Uint8Array(buffer,0,50); bytes.set(start,50);

In fact, we can make this example even simpler by using the subarray method which constructs a new view on the same buffer. That is:

var array2=array1.subarray(start,length);

returns a view, array2, into the same ArrayBuffer as array1 uses. The new view starts with array1[start] and has length elements.

So we could write the previous example as:

var start=bytes.subarray(0,50); bytes.set(start,50);

or, if you prefer one-liners, as:

bytes.set(bytes.subarray(0,50),50);

In Chapter but not in this extract

  • Structures and Arrays
  • Binary Representations
  • DataView
  • Byte Order - the Endian Problem
  • Unpacking the Data

Summary

  • A type array is similar to a standard JavaScript Array object, but all of its elements are of a single simple type and it lacks some of the methods of Array.

  • The behavior of all of the array data types is to roll over to zero, but Uint8ClampedArray saturates when you try to make it go beyond its maximum.

  • The ArrayBuffer is used by all typed arrays to store their data. It is simply a block of memory a given number of bytes in size.

  • You can use the set method to copy the contents of one typed array to another.

  • You can use a DataView object to assign custom views to an ArrayBuffer. views can overlap and divide up a buffer in complicated ways.

  • Multi-byte values raise the issue of whether you store the most significant byte at high address value, big-endian, or at the low address value, little-endian. 

Now available as a paperback or ebook from Amazon.

JavaScript Bitmap Graphics
With Canvas

largecover360

 

Contents

  1. JavaScript Graphics
  2. Getting Started With Canvas
  3. Drawing Paths
      Extract: Basic Paths
      Extract: SVG Paths
      Extract: Bezier Curves
  4. Stroke and Fill
      Extract: Stroke Properties 
      Extract: Fill and Holes
      Extract: Gradient & Pattern Fills
  5. Transformations
      Extract: Transformations
      Extract: Custom Coordinates 
      Extract  Graphics State
  6. Text
      Extract: Text, Typography & SVG 
      Extract: Unicode
  7. Clipping, Compositing and Effects
      Extract: Clipping & Basic Compositing
  8. Generating Bitmaps
      Extract:  Introduction To Bitmaps
      Extract :  Animation 
  9. WebWorkers & OffscreenCanvas
      Extract: Web Workers
      Extract: OffscreenCanvas
  10. Bit Manipulation In JavaScript
      Extract: Bit Manipulation
  11. Typed Arrays
      Extract: Typed Arrays 
  12. Files, blobs, URLs & Fetch
      Extract: Blobs & Files
      Extract: Read/Writing Local Files
      Extract: Fetch API **NEW!
  13. Image Processing
      Extract: ImageData
      Extract:The Filter API
  14. 3D WebGL
      Extract: WebGL 3D
  15. 2D WebGL
    Extract: WebGL Convolutions

<ASIN:B07XJQDS4Z>

<ASIN:1871962579>

<ASIN:1871962560>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 01 January 2024 )