Skip to content

Instantly share code, notes, and snippets.

@DDR0
Created April 22, 2013 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DDR0/5437738 to your computer and use it in GitHub Desktop.
Save DDR0/5437738 to your computer and use it in GitHub Desktop.
Fixed version of layerData.
lData.moveLayerData = function(oldLayer, newLayer, options) { //Copies the layer image data from the oldLayer to the newLayer in a reasonably efficient manner.
/* oldLayer: The old layer of data to copy from. (type "layerCanvas")
newLayer: The new layer of data to copy to. (May be the old layer.)
options: (map)
oldOrigin.x/y and newOrigin.x/y are optional. They specify the
origin point for the rectange being copied from the old layer
to the new layer, on their respective layers. Will default to
area.x/y if not supplied, or the layer x/y if area not supplied.
channels (optional) specifies the mapping of the channels from old
to new. This works like convertBuffer's outputChannels option.
Basically, key is old channel, value is new channel. Works with
lists or maps. An undefined value means to ignore the channel.
area: A bounding box, telling what to move. Optional if
oldOrigin.x/y, newOrigin.x/y, width, and height and height are
defined.
width (optional) is the width of the square area to copy.
This is an inclusive measurement, like a boundingBox.
height (optional) is height of the area to copy. Inclusive.
optimization: Either 'block', 'line', or 'none'. Ensures that the
layer is being copied at at least the specified optimization
level. Optional, doesn't affect actual optimization applied.
*/
var oldBaseX = options.oldOrigin ? options.oldOrigin.x : oldLayer.x, newBaseX = options.newOrigin ? options.newOrigin.x : newLayer.x;
var oldBaseY = options.oldOrigin ? options.oldOrigin.y : oldLayer.y, newBaseY = options.newOrigin ? options.newOrigin.y : newLayer.y;
var oldChans = oldLayer.channels, newChans = newLayer.channels;
var defaultChannels = _.range(Math.min(oldChans, newChans));
var channels = options.channels || defaultChannels;
var width = options.width || options.area.width;
var height = options.height || options.area.height;
var oldOffsetX = -oldLayer.x, newOffsetX = -newLayer.x + (oldBaseX - newBaseX);
var oldOffsetY = -oldLayer.y, newOffsetY = -newLayer.y + (oldBaseY - newBaseY);
var oldArray = new Uint8ClampedArray(oldLayer.buffer), newArray = new Uint8ClampedArray(newLayer.buffer);
//TODO: Clip the copy rectangle so that it fits inside the read and write layers.
var oldBlockStart, newBlockStart, blockLength, line, column, channel;
if(oldBaseX === newBaseX && width===oldLayer.width && width===newLayer.width && oldLayer.channels===newLayer.channels && _.isEqual(channels, defaultChannels) ) { //We want to copy full lines into full lines. This means we don't have to skip spaces (columns), but can copy the entire contiguous section in one go.
oldBlockStart = (oldOffsetX+(oldBaseY+oldOffsetY)*width)*oldLayer.channels; //Commented out oldBaseY because it was cancelling out some math and causing the equasion to be 0.
newBlockStart = (newOffsetX+(newBaseY+newOffsetY)*width)*newLayer.channels;
blockLength = width*height*oldLayer.channels;
newArray.set(oldArray.subarray(oldBlockStart, oldBlockStart+blockLength), newBlockStart);
c.log('Block copy!');
} else if(oldLayer.channels===newLayer.channels && _.isEqual(channels, defaultChannels) ) { //Even though we have to stop copying to avoid overwriting some columns, each line of data we want to copy is still contiguous, and we can copy that.
if(options.optimization === "block") throw new Error("Block-level layer copy operation specified, but only line-level copy possible.");
blockLength = width*channels.length;
for (line = 0; line < height; line++) {
oldBlockStart = (
(oldOffsetY + oldBaseY + line) * oldLayer.width + //Y offset, including line.
oldOffsetX + oldBaseX) * //X offset
oldLayer.channels; //Size of a pixel, in Uint8s.
newBlockStart = (
(newOffsetY + newBaseY + line) * newLayer.width +
newOffsetX + newBaseX) *
newLayer.channels;
//c.log(oldBlockStart, blockLength, newBlockStart, oldArray.length, newArray.length);
if(oldBlockStart+blockLength < oldArray.length && newBlockStart < + blockLength < newArray.length) {
newArray.set(oldArray.subarray(oldBlockStart, oldBlockStart+blockLength), newBlockStart);
}
}
c.log("Line copy.");
} else { //There are no optimizations we can apply. Since an individual pixel's data either isn't contiguous or isn't consistent between source and destination, it can't be directly copied. Since we can't copy pixels, we can't copy blocks but must copy each and every channel over manually.
if(options.optimization && options.optimization !== "none") throw new Error("Block-level or line-level layer copy operation specified, but only channel-level copy possible.");
var copyChans = function(to, from) {newArray[newBlockStart+to] = oldArray[oldBlockStart+from];};
for(line = 0; line < height; line++) {
oldBlockStart = (
(oldOffsetY + oldBaseY + line) * oldLayer.width +
oldOffsetX + oldBaseX) *
oldLayer.channels;
newBlockStart = (
(newOffsetY + newBaseY + line) * newLayer.width +
newOffsetX + newBaseX) *
newLayer.channels;
for(column = 0; column < width; column++, oldBlockStart+=oldChans, newBlockStart+=newChans) {
channels.forEach(copyChans);
}
}
c.log("Channel copy.");
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment