I'm making a really in-efficient redrawing tool that takes each pixel of a canvas and takes it's color values and plugs that data into a linear gradient CSS property on a separate div. I want the linear gradient to create a non-fading effect where every pixel is going to have a separate color. I also don't know how the linear gradient will align with my pixel-color values since it reads every line one by one.
My other values like percent and data are arbitrary because I already have that figured out
var data=(img_ctx.getImageData(x,y,1,1).data);
draw.style.background+=" ,linear-gradient(0deg, "+Math.floor(percent)+"%, rgba("+data+")";
I just got a div that has no style properties for background, and I added the comma to allow multiple colors, I'm just not sure how to use linear gradient well enough to figure this out.
Linear gradients have only a single axis and fill their container in the perpendicular direction. In other words, you can only control the width of a color along that axis, making it impossible to create a 1px x 1px color within a div larger than 1px.
So the only way to achieve the redraw is to create a stack of divs with 1px of height (or columns with 1px width).
You can then contain the colors to 1px along the main axis by adding two stops for each color indicating the start and end along the main axis.
const scale = 0.1;
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = onload;
fetch('https://i.kym-cdn.com/entries/icons/original/000/006/428/637738.jpg')
.then((res) => res.blob())
.then((blob) => (img.src = URL.createObjectURL(blob)));
function onload() {
const w = img.width * scale;
const h = img.height * scale;
canvas.width = w;
canvas.height = h;
canvas.style.width = w.toString() + 'px';
canvas.style.height = h.toString() + 'px';
ctx.drawImage(img, 0, 0, w, h);
const pixelData = Array.from(ctx.getImageData(0, 0, w, h).data);
for (let row = 0; row < h; row++) {
const rowDiv = document.createElement('div');
rowDiv.style.width = w.toString() + 'px';
rowDiv.style.height = '1px';
const rowStart = row * w * 4;
let background = 'linear-gradient(to right,';
for (let col = 0; col < w; col++) {
const pixelStart = rowStart + col * 4;
background +=
'rgba(' +
pixelData[pixelStart] +
',' +
pixelData[pixelStart + 1] +
',' +
pixelData[pixelStart + 2] +
',' +
pixelData[pixelStart + 3] +
')' + col + 'px ' + (col + 1) + 'px,';
}
rowDiv.style.background = background.slice(0, -1) + ')';
document.body.append(rowDiv);
}
}
<h1>Canvas</h1>
<canvas></canvas>
<h1>Gradients</h1>
But at this point you might as well just make a div for every pixel.
Related
I have this function to draw a sprite object (which is just an object with an array of pixel values):
this.draw = (ctx, x, y, scale) => {
let i = 0;
let startIndex, red, green, blue, alpha, currentX, currentY;
while(i < this.size.width * this.size.height){
startIndex = i * 4;
red = this.pixArray[startIndex];
green = this.pixArray[startIndex + 1];
blue = this.pixArray[startIndex + 2];
alpha = this.pixArray[startIndex + 3];
// Draw, but not if the pixel is invisible
if(alpha != 0){
ctx.fillStyle = `rgba(${red}, " + ${green} + ", " + ${blue} + ", " + ${alpha / 255.0} + ")`;
currentX = i % this.size.width;
currentY = (i - currentX) / this.size.width;
ctx.fillRect(Math.round(x + (currentX * scale)), Math.round(y + (currentY * scale)),
Math.round(scale), Math.round(scale));
}
i++;
}
}
The only thing missing from this is pixArray, which is an Uint8Array of pixel values.
However, performance is fairly abysmal. I have found that some of the performance is lost to the canvas changing state (ctx.fillStyle), but it is necessary that I modify this each iteration. Even if the fillStyle remains unchanged, the performance is still unacceptable. I realize I have the option or pre-rendering, but I wish to avoid this.
Use an ImageData to blit your array directly to a temporary canvas and then draw that to the destination canvas with the appropriate scale in a single operation:
const { width, height } = this.size;
const tCan = document.createElement('canvas');
// block scope to clean up temporary variables
{
const tCtx = tCan.getContext('2d');
const imgData = tCtx.createImageData(width, height);
tCan.width = width;
tCan.height = height;
imgData.data.set(this.pixArray);
tCtx.putImageData(imgData, 0, 0);
}
this.draw = (ctx, x, y, scale) => {
ctx.drawImage(tCan, x, y, Math.round(width * scale), Math.round(height * scale));
};
I guess I'll just have to show it:
function drawSector(ctx, cxy, rInner, rOuter, radStart, radWidth, color) {
ctx.beginPath();
ctx.arc(cxy, cxy, rInner, radStart, radStart + radWidth);
ctx.lineTo(cxy + rOuter * Math.cos(radStart + radWidth), cxy + rOuter * Math.sin(radStart + radWidth));
ctx.arc(cxy, cxy, rOuter, radStart + radWidth, radStart, true);
ctx.lineTo(cxy + rInner * Math.cos(radStart), cxy + rInner * Math.sin(radStart));
ctx.fillStyle = color;
ctx.fill();
ctx.stroke();
}
function makecircle(diam) {
var canv = document.createElement("canvas");
canv.width = diam; canv.height = diam;
document.getElementById("c").appendChild(canv);
var ctx = canv.getContext("2d");
ctx.strokeStyle = "rgba(0,0,0,1)";
ctx.lineWidth = 5;
var centerXY = diam / 2;
var centerRadius = diam / 6;
var sectorHeight = (centerXY - centerRadius) / 5 - 2;
var sectorAngle = Math.PI * 2 / 15;
for (var r = 0; r < 15; r++) {
for (var h = 0; h < 5; h++) {
drawSector(ctx, centerXY, centerRadius + sectorHeight * h, centerRadius + sectorHeight * (h + 1), r * sectorAngle, sectorAngle, "rgba(255,0,0,0.5)");
}
}
}
makecircle(500);
body {
background-color:darkgreen;
}
<div id="c"></div>
So there is this circle divided into sectors. I want there to be a little space between each sector (currently illustrated by black stroke). Cutting the height/angle width of each sector works but since its a constant angle it widens outwards and I don't like the look.
I figured there must be a way to draw stroke ontop of the circle and have that area become transparent again, is there? (also I can't ditch filling each sector separately since I'm going to make them different colors).
You can "erase" existing pixels by setting globalCompositeOperation='destination-out'.
Then draw your radiating lines. Instead of being visible, the radiating lines will erase any existing pixels that the lines overlap.
When you're done erasing, be sure to set globalCompositeOperation back to its default of 'source-over'.
for this project http://biduleohm.free.fr/ledohm/ (sorry, the user interface is in french but the code is in english) I need an angular gradient but it doesn't exists in native so I've implemented it using a linear gradient on a line and I draw the lines more and more longer to form a triangle. The result is graphically OK but the speed isn't really good (1850 ms for 125 triangles). It's in the tab [RĂ©partition], it redraws if there is a keyup event on one of the inputs, don't be afraid of the apparent slowness, I've limited to maximum one redraw every 2000 ms.
Before I used a simple linear gradient on the whole triangle (but this doesn't match the reality) and the speed was OK, it draws thousands of triangles in less than a second. This function was used :
drawFrontLightForColor : function(x, y, w, h, color) {
var x2 = x - w;
var x3 = x + w;
var gradient = Distri.frontCanvas.createLinearGradient(x2, y, x3, y);
gradient.addColorStop(0, 'rgba(' + color + ', ' + Distri.lightEdgeAlpha + ')');
gradient.addColorStop(0.5, 'rgba(' + color + ', ' + (color == Distri.lightColors.cw ? Distri.lightCenterAlphaCw : Distri.lightCenterAlphaOther) + ')');
gradient.addColorStop(1, 'rgba(' + color + ', ' + Distri.lightEdgeAlpha + ')');
Distri.frontCanvas.fillStyle = gradient;
Distri.frontCanvas.beginPath();
Distri.frontCanvas.moveTo(x, y);
Distri.frontCanvas.lineTo(x2, (y + h));
Distri.frontCanvas.lineTo(x3, (y + h));
Distri.frontCanvas.lineTo(x, y);
Distri.frontCanvas.fill();
Distri.frontCanvas.closePath();
},
Then I switched to this function :
drawFrontLightForColor : function(x, y, w, h, centerColor, edgeColor) {
var ratio = w / h;
var tmpY;
var tmpW;
var x2;
var x3;
var gradient;
Distri.frontCanvas.lineWidth = 1;
for (var tmpH = 0; tmpH < h; tmpH++) {
tmpY = y + tmpH;
tmpW = Math.round(tmpH * ratio);
x2 = x - tmpW;
x3 = x + tmpW;
gradient = Distri.frontCanvas.createLinearGradient(x2, tmpY, x3, tmpY);
gradient.addColorStop(0, edgeColor);
gradient.addColorStop(0.5, centerColor);
gradient.addColorStop(1, edgeColor);
Distri.frontCanvas.beginPath();
Distri.frontCanvas.moveTo(x2, tmpY);
Distri.frontCanvas.lineTo(x, tmpY);
Distri.frontCanvas.lineTo(x3, tmpY);
Distri.frontCanvas.strokeStyle = gradient;
Distri.frontCanvas.stroke();
Distri.frontCanvas.closePath();
}
},
You can find the whole source here
I can't put the beginPath, stroke, closePath out of the loop because of the gradient which is changing every iteration (I've tried but it used the last gradient for every line (which, ironically, is identical to the first function...) which is understandable but not what I want).
I accept any advice (including redo the whole function and modify his caller to outsource some code) to improve the speed let's say 5x (ideally more).
I think you took the wrong way from the start : when doing so much changes of color, you have better operate at the pixel level.
So yes that could be with a webgl pixel shader, but you'll have to fight just to get the boilerplate running ok on all platform (or get a lib to do that for you).
And anyway there's a solution perfect for your need, and fast enough (a few ms) : use raw pixel data, update them one by one with the relevant function, then draw the result.
The steps to do that are :
- create a buffer same size as the canvas.
- iterate through it's pixel, keeping track of the x,y of the point.
- normalize the coordinates so they match your 'space'.
- compute the value for the normalized (x,y) out of all the data that you have.
- write a color (in my example i choose greyscale) out of that value.
- draw the whole buffer to canvas.
I did a jsfiddle, and here's the result with 4 data points :
fiddle is here :
http://jsfiddle.net/gamealchemist/KsM9c/3/
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var width = canvas.width,
height = canvas.height;
// builds an image for the target canvas
function buildImage(targetCanvas, valueForXY, someData) {
var width = targetCanvas.width;
var height = targetCanvas.height;
var tempImg = ctx.createImageData(width, height);
var buffer = tempImg.data;
var offset = 0;
var xy = [0, 0];
function normalizeXY(xy) {
xy[0] = xy[0] / width ;
xy[1] = xy[1] / height;
}
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++, offset += 4) {
xy[0] = x; xy[1]=y;
normalizeXY(xy);
var val = Math.floor(valueForXY(xy, someData) * 255);
buffer[offset] = val;
buffer[offset + 1] = val;
buffer[offset + 2] = val;
buffer[offset + 3] = 255;
}
ctx.putImageData(tempImg, 0, 0);
}
// return normalized (0->1) value for x,y and
// provided data.
// xy is a 2 elements array
function someValueForXY(xy, someData) {
var res = 0;
for (var i = 0; i < someData.length; i++) {
var thisData = someData[i];
var dist = Math.pow(sq(thisData[0] - xy[0]) + sq(thisData[1] - xy[1]), -0.55);
localRes = 0.04 * dist;
res += localRes;
}
if (res > 1) res = 1;
return res;
}
var someData = [
[0.6, 0.2],
[0.35, 0.8],
[0.2, 0.5],
[0.6, 0.75]
];
buildImage(canvas, someValueForXY, someData);
// ------------------------
function sq(x) {
return x * x
}
In fact the GameAlchemist's solution isn't fast or I do something really wrong. I've implemented this algo only for the top view because the front view is much more complex.
For 120 lights the top view take 100-105 ms with the old code and it take 1650-1700 ms with this code (and moreover it still lacks a few things in the new code like the color for example):
drawTopLightForColor_ : function(canvasW, canvasD, rampX, rampY, rampZ, ledsArrays, color) {
function sq(x) {
return x * x;
}
var tmpImg = Distri.topCanvasCtx.createImageData(canvasW, canvasD);
var rawData = tmpImg.data;
var ledsArray = ledsArrays[color];
var len = ledsArray.length;
var i = 0;
for (var y = 0; y < canvasD; y++) {
for (var x = 0; x < canvasW; x++, i += 4) {
var intensity = 0;
for (var j = 0; j < len; j++) {
intensity += 2 * Math.pow(
sq((rampX + ledsArray[j].x) - x) +
sq((rampZ + ledsArray[j].y) - y),
-0.5
);
}
if (intensity > 1) {
intensity = 1;
}
intensity = Math.round(intensity * 255);
rawData[i] = intensity;
rawData[i + 1] = intensity;
rawData[i + 2] = intensity;
rawData[i + 3] = 255;
}
}
Distri.topCanvasCtx.putImageData(tmpImg, 0, 0);
},
Am I doing something wrong?
I have a function named generateNoise() which creates a canvas element and paints random RGBA values to it; which, gives the appearance of noise.
My Question
What would be the best way to infinitely animate the noise to give the appearance of movement. So that it may have more life?
JSFiddle
function generateNoise(opacity) {
if(!!!document.createElement('canvas').getContext) {
return false;
}
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
x,y,
r,g,b,
opacity = opacity || .2;
canvas.width = 55;
canvas.height = 55;
for (x = 0; x < canvas.width; x++){
for (y = 0; y < canvas.height; y++){
r = Math.floor(Math.random() * 255);
g = Math.floor(Math.random() * 255);
b = Math.floor(Math.random() * 255);
ctx.fillStyle = 'rgba(' + r + ',' + b + ',' + g + ',' + opacity + ')';
ctx.fillRect(x,y,1,1);
}
}
document.body.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")";
}
generateNoise(.8);
Update 1/2017: I rewrote the entire answer as it started to become rather messy, and to address some of the issues pointed out in the comments. The original answer can be found here. The new answer has in essence the same code but improved, and with a couple of new techniques, one utilizes a new feature available since this answer was first posted.
For a "true" random look we would need to use pixel-level rendering. We can optimize this using 32-bit unsigned buffers instead of 8-bit, and we can also turn off the alpha-channel in more recent browsers which speeds up the entire process (for older browsers we can simply set a black opaque background for the canvas element).
We create a reusable ImageData object once outside the main loop so the main cost is only putImageData() and not both inside the loop.
var ctx = c.getContext("2d", {alpha: false}); // context without alpha channel.
var idata = ctx.createImageData(c.width, c.height); // create image data
var buffer32 = new Uint32Array(idata.data.buffer); // get 32-bit view
(function loop() {
noise(ctx);
requestAnimationFrame(loop)
})()
function noise(ctx) {
var len = buffer32.length - 1;
while(len--) buffer32[len] = Math.random() < 0.5 ? 0 : -1>>0;
ctx.putImageData(idata, 0, 0);
}
/* for browsers wo/2d alpha disable support */
#c {background:#000}
<canvas id=c width=640 height=320></canvas>
A very efficient way, at the cost of some memory but reduced cost on the CPU, is to pre-render a larger off-screen canvas with the noise once, then place that canvas into the main one using random integer offsets.
It require a few extra preparation steps but the loop can run entirely on the GPU.
var w = c.width;
var h = c.height;
var ocanvas = document.createElement("canvas"); // create off-screen canvas
ocanvas.width = w<<1; // set offscreen canvas x2 size
ocanvas.height = h<<1;
var octx = ocanvas.getContext("2d", {alpha: false});
var idata = octx.createImageData(ocanvas.width, ocanvas.height);
var buffer32 = new Uint32Array(idata.data.buffer); // get 32-bit view
// render noise once, to the offscreen-canvas
noise(octx);
// main loop draw the offscreen canvas to random offsets
var ctx = c.getContext("2d", {alpha: false});
(function loop() {
var x = (w * Math.random())|0; // force integer values for position
var y = (h * Math.random())|0;
ctx.drawImage(ocanvas, -x, -y); // draw static noise (pun intended)
requestAnimationFrame(loop)
})()
function noise(ctx) {
var len = buffer32.length - 1;
while(len--) buffer32[len] = Math.random() < 0.5 ? 0 : -1>>0;
ctx.putImageData(idata, 0, 0);
}
/* for browsers wo/2d alpha disable support */
#c {background:#000}
<canvas id=c width=640 height=320></canvas>
Do note though that with the latter technique you may risk getting "freezes" where the new random offset is similar to the previous one. To work around this problem, set criteria for the random position to disallow too close positions in a row.
I tried to make a similar function a while ago. I set each pixel random value, and in addition to that, I overlayed a sinusodial wave that traveled upwards with time just to make it look more realistic. You can play with the constants in the wave to get different effects.
var canvas = null;
var context = null;
var time = 0;
var intervalId = 0;
var makeNoise = function() {
var imgd = context.createImageData(canvas.width, canvas.height);
var pix = imgd.data;
for (var i = 0, n = pix.length; i < n; i += 4) {
var c = 7 + Math.sin(i/50000 + time/7); // A sine wave of the form sin(ax + bt)
pix[i] = pix[i+1] = pix[i+2] = 40 * Math.random() * c; // Set a random gray
pix[i+3] = 255; // 100% opaque
}
context.putImageData(imgd, 0, 0);
time = (time + 1) % canvas.height;
}
var setup = function() {
canvas = document.getElementById("tv");
context = canvas.getContext("2d");
}
setup();
intervalId = setInterval(makeNoise, 50);
<canvas id="tv" width="400" height="300"></canvas>
I used it as a preloader on a site. I also added a volume rocker as a loading bar, here's a screenshot:
I re-wrote your code so each step is separate so you can re-use things without having to create and re-create each time, reduced in-loop calls and hopefully made it clear enough to be able to follow by reading it.
function generateNoise(opacity, h, w) {
function makeCanvas(h, w) {
var canvas = document.createElement('canvas');
canvas.height = h;
canvas.width = w;
return canvas;
}
function randomise(data, opacity) { // see prev. revision for 8-bit
var i, x;
for (i = 0; i < data.length; ++i) {
x = Math.floor(Math.random() * 0xffffff); // random RGB
data[i] = x | opacity; // set all of RGBA for pixel in one go
}
}
function initialise(opacity, h, w) {
var canvas = makeCanvas(h, w),
context = canvas.getContext('2d'),
image = context.createImageData(h, w),
data = new Uint32Array(image.data.buffer);
opacity = Math.floor(opacity * 0x255) << 24; // make bitwise OR-able
return function () {
randomise(data, opacity); // could be in-place for less overhead
context.putImageData(image, 0, 0);
// you may want to consider other ways of setting the canvas
// as the background so you can take this out of the loop, too
document.body.style.backgroundImage = "url(" + canvas.toDataURL("image/png") + ")";
};
}
return initialise(opacity || 0.2, h || 55, w || 55);
}
Now you can create some interval or timeout loop which keeps re-invoking the generated function.
window.setInterval(
generateNoise(.8, 200, 200),
100
);
Or with requestAnimationFrame as in Ken's answer
var noise = generateNoise(.8, 200, 200);
(function loop() {
noise();
requestAnimationFrame(loop);
})();
DEMO
Ken's answer looked pretty good, but after looking at some videos of real TV static, I had some ideas and here's what I came up with (two versions):
http://jsfiddle.net/2bzqs/
http://jsfiddle.net/EnQKm/
Summary of changes:
Instead of every pixel being independently assigned a color, a run of multiple pixels will get a single color, so you get these short, variable-sized horizontal lines.
I apply a gamma curve (with the Math.pow) to bias the color toward black a little.
I don't apply the gamma in a "band" area to simulate the banding.
Here's the main part of the code:
var w = ctx.canvas.width,
h = ctx.canvas.height,
idata = ctx.createImageData(w, h),
buffer32 = new Uint32Array(idata.data.buffer),
len = buffer32.length,
run = 0,
color = 0,
m = Math.random() * 6 + 4,
band = Math.random() * 256 * 256,
p = 0,
i = 0;
for (; i < len;) {
if (run < 0) {
run = m * Math.random();
p = Math.pow(Math.random(), 0.4);
if (i > band && i < band + 48 * 256) {
p = Math.random();
}
color = (255 * p) << 24;
}
run -= 1;
buffer32[i++] = color;
}
I happen to have just written a script that does just this, by getting the pixels from a black canvas and just altering random alpha values and using putImageData
Result can be found at http://mouseroot.github.io/Video/index.html
var currentAnimationFunction = staticScreen
var screenObject = document.getElementById("screen").getContext("2d");
var pixels = screenObject.getImageData(0,0,500,500);
function staticScreen()
{
requestAnimationFrame(currentAnimationFunction);
//Generate static
for(var i=0;i < pixels.data.length;i+=4)
{
pixels.data[i] = 255;
pixels.data[i + 1] = 255;
pixels.data[i + 2] = 255;
pixels.data[i + 3] = Math.floor((254-155)*Math.random()) + 156;
}
screenObject.putImageData(pixels,0,0,0,0,500,500);
//Draw 'No video input'
screenObject.fillStyle = "black";
screenObject.font = "30pt consolas";
screenObject.fillText("No video input",100,250,500);
}
Mine doesn't look identical to real TV static, but it's similar nonetheless. I'm just looping through all the pixels on canvas, and changing the RGB colour components of each pixel at a random coordinate to a random colour. The demo can be found over at CodePen.
The code is as follows:
// Setting up the canvas - size, setting a background, and getting the image data(all of the pixels) of the canvas.
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 400;
canvasData = ctx.createImageData(canvas.width, canvas.height);
//Event listeners that set the canvas size to that of the window when the page loads, and each time the user resizes the window
window.addEventListener("load", windowResize);
window.addEventListener("resize", windowResize);
function windowResize(){
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
}
//A function that manipulates the array of pixel colour data created above using createImageData()
function setPixel(x, y, r, g, b, a){
var index = (x + y * canvasData.width) * 4;
canvasData.data[index] = r;
canvasData.data[index + 1] = g;
canvasData.data[index + 2] = b;
canvasData.data[index + 3] = a;
}
window.requestAnimationFrame(mainLoop);
function mainLoop(){
// Looping through all the colour data and changing each pixel to a random colour at a random coordinate, using the setPixel function defined earlier
for(i = 0; i < canvasData.data.length / 4; i++){
var red = Math.floor(Math.random()*256);
var green = Math.floor(Math.random()*256);
var blue = Math.floor(Math.random()*256);
var randX = Math.floor(Math.random()*canvas.width);
var randY = Math.floor(Math.random()*canvas.height);
setPixel(randX, randY, red, green, blue, 255);
}
//Place the image data we created and manipulated onto the canvas
ctx.putImageData(canvasData, 0, 0);
//And then do it all again...
window.requestAnimationFrame(mainLoop);
}
You can do it like this:
window.setInterval('generateNoise(.8)',50);
The 2nd arg 50 is a delay in milliseconds. Increasing 50 will slow it down and decreasing visa versa.
though.. this is going to severely affect web page performance. If it were me, I'd do the rendering server-side and render a handful of frame iterations and output as an animated gif. Not quite the same as infinite randomness, but would be a huge performance boost and IMO most people won't even notice.
I have some image data in canvas, and now I need to take the left half of the image, flip it and apply it to the right, like a mirror effect.
Example, from this:
To this:
I got this far (I have the image data ready):
ctx.drawImage(this, 0, 0, 960, 540);
var imgData = ctx.getImageData(0,0,960,540);
// loop through the data and apply mirror ??
Width & height is known. Any ideas?
Loop through the image data
If the current pixel is in the left half of the image, copy it to a position on the right:
for(var y = 0; y < height; y++) {
for(var x = 0; x < width / 2; x++) { // divide by 2 to only loop through the left half of the image.
var offset = ((width* y) + x) * 4; // Pixel origin
// Get pixel
var r = data[offset];
var g = data[offset + 1];
var b = data[offset + 2];
var a = data[offset + 3];
// Calculate how far to the right the mirrored pixel is
var mirrorOffset = (width - (x * 2)) * 4;
// Get set mirrored pixel's colours
data[offset + mirrorOffset] = r;
data[offset + 1 + mirrorOffset] = g;
data[offset + 2 + mirrorOffset] = b;
data[offset + 3 + mirrorOffset] = a;
}
}
I haven't tested this, but it should (More-or less) work, or at least give you an idea of how to do it.