Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ gpu-io
- [GPULayerFilter](README.md#gpulayerfilter)
- [GPULayerWrap](README.md#gpulayerwrap)
- [GPULayerState](README.md#gpulayerstate)
- [GPULayerRange](README.md#gpulayerrange)
- [GPULayerRegion](README.md#gpulayerregion)
- [ImageFormat](README.md#imageformat)
- [ImageType](README.md#imagetype)
- [GLSLVersion](README.md#glslversion)
Expand Down Expand Up @@ -366,6 +368,34 @@ This data structure also includes a reference back to the GPULayer that it origi

___

### GPULayerRange

Ƭ **GPULayerRange**: `Object`

#### Type declaration

| Name | Type |
| :------ | :------ |
| `start` | `number` |
| `end` | `number` |

___

### GPULayerRegion

Ƭ **GPULayerRegion**: `Object`

#### Type declaration

| Name | Type |
| :------ | :------ |
| `x` | `number` |
| `y` | `number` |
| `width` | `number` |
| `height` | `number` |

___

### ImageFormat

Ƭ **ImageFormat**: typeof [`RGB`](README.md#rgb) \| typeof [`RGBA`](README.md#rgba)
Expand Down
44 changes: 43 additions & 1 deletion docs/classes/GPULayer.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- [decrementBufferIndex](GPULayer.md#decrementbufferindex)
- [getStateAtIndex](GPULayer.md#getstateatindex)
- [setFromArray](GPULayer.md#setfromarray)
- [setAtIndex2D](GPULayer.md#setatindex2d)
- [setAtIndex1D](GPULayer.md#setatindex1d)
- [resize](GPULayer.md#resize)
- [clear](GPULayer.md#clear)
- [getValues](GPULayer.md#getvalues)
Expand Down Expand Up @@ -202,13 +204,53 @@ ___

### setFromArray

▸ **setFromArray**(`array`): `void`
▸ **setFromArray**(`array`, `range?`): `void`

#### Parameters

| Name | Type |
| :------ | :------ |
| `array` | `number`[] \| [`GPULayerArray`](../README.md#gpulayerarray) |
| `range?` | [`GPULayerRange`](../README.md#gpulayerrange) \| [`GPULayerRegion`](../README.md#gpulayerregion) |

#### Returns

`void`

___

### setAtIndex2D

▸ **setAtIndex2D**(`x`, `y`, `components`): `void`

Set a single value at a given 2D location in the layer.

#### Parameters

| Name | Type |
| :------ | :------ |
| `x` | `number` |
| `y` | `number` |
| `components` | `number`[] \| [`GPULayerArray`](../README.md#gpulayerarray) |

#### Returns

`void`

___

### setAtIndex1D

▸ **setAtIndex1D**(`index`, `components`): `void`

Set a single value at a given 1D location in the layer.

#### Parameters

| Name | Type |
| :------ | :------ |
| `index` | `number` |
| `components` | `number`[] \| [`GPULayerArray`](../README.md#gpulayerarray) |

#### Returns

Expand Down
139 changes: 123 additions & 16 deletions src/GPULayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import {
ImageType,
validImageFormats,
validImageTypes,
GPULayerRange,
GPULayerRegion,
} from './constants';
import {
readPixelsAsync,
Expand Down Expand Up @@ -655,24 +657,128 @@ export class GPULayer {
}
}

setFromArray(array: GPULayerArray | number[]) {
const {
_composer,
_glInternalFormat,
_glFormat,
_glType,
width,
height,
_currentTexture,
} = this;
const { gl } = _composer;
const validatedArray = GPULayer.validateGPULayerArray(array, this);
gl.bindTexture(gl.TEXTURE_2D, _currentTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, _glInternalFormat, width, height, 0, _glFormat, _glType, validatedArray);
// Unbind texture.
gl.bindTexture(gl.TEXTURE_2D, null);
/**
* Get texImage2D regions for layer range or region
* @private
*/
_getTexImage2DRegions(range?: GPULayerRange | GPULayerRegion): {
x: number;
y: number;
width: number;
height: number;
sliceStart: number;
sliceEnd: number;
}[] {
const { width, height } = this;
if (!range) {
// no range
return [{x: 0, y: 0, width, height, sliceStart: 0, sliceEnd: width*height}];
} else if ((range as GPULayerRegion).width !== undefined) {
// this is a 2D region
const { x, y, width, height } = range as GPULayerRegion;
return [{x, y, width, height, sliceStart: 0, sliceEnd: width*height}];
}

if ((range as GPULayerRange).start !== undefined) {
// this is a 1D range
const { width } = this;
const { start, end } = range as GPULayerRange;
const length = end - start;

let regions = []
const numRows = Math.ceil(length / width);
if (numRows > 0) {
// first region, top cap
regions.push({
x: start % width,
y: Math.floor(start / width),
width: Math.min(width - start % width, length),
height: 1,
sliceStart: 0,
sliceEnd: Math.min(width - start % width, length),
});
}
if (numRows > 1) {
// end region, bottom cap
regions.push({
x: 0,
y: Math.floor(end / width),
width: end % width,
height: 1,
sliceStart: length - end % width,
sliceEnd: length,
});
}
if (numRows > 2) {
// middle region
regions.push({
x: 0,
y: Math.floor(start / width) + 1,
width: width,
height: numRows - 2,
sliceStart: width - start % width + 1,
sliceEnd: width - start % width + 1 + (numRows - 2) * width,
});
}

return regions;
}
return [];
}

setFromArray(array: GPULayerArray | number[], range?: GPULayerRange | GPULayerRegion) {
const {
_composer,
_glFormat,
_glType,
_currentTexture,
numComponents
} = this;
const { gl } = _composer;
const regions = this._getTexImage2DRegions(range);
gl.bindTexture(gl.TEXTURE_2D, _currentTexture);
for (const { x, y, width, height, sliceStart, sliceEnd } of regions) {
const validatedArray = GPULayer.validateGPULayerArray(
array.slice(sliceStart*numComponents, sliceEnd*numComponents),
this,
// if no range was passed, we're setting the whole layer and we can validate the whole array
!range ? undefined : Math.min(sliceEnd - sliceStart, array.length / numComponents)
);
gl.texSubImage2D(
gl.TEXTURE_2D,
0,
x,
y,
width,
height,
_glFormat,
_glType,
validatedArray
);
}
// Unbind texture.
gl.bindTexture(gl.TEXTURE_2D, null);
}

/**
* Set a single value at a given 2D location in the layer.
* @param x
* @param y
* @param components
*/
setAtIndex2D(x: number, y: number, components: GPULayerArray | number[]) {
this.setFromArray(components, {x, y, width: 1, height: 1});
}

/**
* Set a single value at a given 1D location in the layer.
* @param index
* @param components
*/
setAtIndex1D(index: number, components: GPULayerArray | number[]) {
this.setFromArray(components, {start: index, end: index+1});
}

// setFromImage(image: HTMLImageElement) {
// const { name, _composer, width, height, _currentTexture, _glInternalFormat, _glFormat, _glType, numComponents, type } = this;
// const { gl } = _composer;
Expand Down Expand Up @@ -1190,5 +1296,6 @@ export class GPULayer {
static validateGPULayerArray(
array: GPULayerArray | number[],
layer: GPULayer,
validateSubarrayLength?: number
): GPULayerArray;
}
19 changes: 16 additions & 3 deletions src/GPULayerHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,14 +889,21 @@ export function minMaxValuesForType(type: GPULayerType) {
* Recasts typed array to match GPULayer.internalType.
* @private
*/
GPULayer.validateGPULayerArray = (array: GPULayerArray | number[], layer: GPULayer) => {
GPULayer.validateGPULayerArray = (array: GPULayerArray | number[], layer: GPULayer, validateSubarrayLength : number | null = null) => {
const { numComponents, width, height, name } = layer;
const glNumChannels = layer._glNumChannels;
const internalType = layer._internalType;
const length = layer.is1D() ? layer.length : null;

// Check that data is correct length (user error).
if (array.length !== width * height * numComponents) { // Either the correct length for WebGLTexture size

// Are we validating a subarray?
if (validateSubarrayLength) {
if (array.length !== validateSubarrayLength * numComponents) {
throw new Error(`Invalid data length: ${array.length} for GPULayer "${name}" of numComponents: ${numComponents}.`);
}
// Otherwise our data
} else if (array.length !== width * height * numComponents) { // Either the correct length for WebGLTexture size
if (!length || (length && array.length !== length * numComponents)) { // Of the correct length for 1D array.
throw new Error(`Invalid data length: ${array.length} for GPULayer "${name}" of ${length ? `length ${length} and ` : ''}dimensions: [${width}, ${height}] and numComponents: ${numComponents}.`);
}
Expand Down Expand Up @@ -948,7 +955,13 @@ GPULayer.validateGPULayerArray = (array: GPULayerArray | number[], layer: GPULay

// Then check if array needs to be lengthened.
// This could be because glNumChannels !== numComponents or because length !== width * height.
const arrayLength = width * height * glNumChannels;

const arrayLength =
// If we are validating a subarray, we need to use the subarray length.
validateSubarrayLength ? validateSubarrayLength * glNumChannels :
// otherwise we can use the layer length.
width * height * glNumChannels;

const shouldResize = array.length !== arrayLength;

let validatedArray = array as GPULayerArray;
Expand Down
11 changes: 11 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ export type GPULayerState = {
texture: WebGLTexture,
layer: GPULayer,
}
export type GPULayerRange = {
start: number,
end: number,
}

export type GPULayerRegion = {
x: number,
y: number,
width: number,
height: number,
}

// For image urls that are passed in and inited as textures.
/**
Expand Down