/*
* ImageBlock.java
* Transform
*
* Copyright (c) 2001-2010 Flagstone Software Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Flagstone Software Ltd. nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.flagstone.transform.util.image;
import java.util.Arrays;
import java.util.List;
import java.util.zip.Deflater;
import com.flagstone.transform.video.ImageBlock;
/**
* ImageBlocker is used to sub-divide an image into a set of blocks so they can
* be streamed using Screen Video. Image blocks are compared so only pixel
* information for the portions of the image that change are sent.
*
* @see ImageBlock
*/
public final class ImageBlocker {
/** Number of colour channels in an RGB pixel. */
private static final int RGB_CHANNELS = 3;
/**
* Return an image stored in a a file as a list of ImageBlock objects that
* can be used when creating ScreenVideo streams.
*
* The image is divided by tiling blocks of the specified width and height
* across the image. For blocks at the right and bottom edges the size of
* the block may be reduced so that it fits the image exactly. In other
* words the blocks are not padded with extra pixel information.
*
* @param blocks
* a list of ImageBlock objects
* @param blockWidth
* the width of a block in pixels
* @param blockHeight
* the height of a block in pixels
* @param imageWidth
* the width of the image in pixels
* @param imageHeight
* the height of the image in pixels
* @param image
* the image data
*/
public void getImageAsBlocks(final List<ImageBlock> blocks,
final int blockWidth, final int blockHeight,
final int imageWidth, final int imageHeight,
final byte[] image) {
final ImageFilter filter = new ImageFilter();
byte[] img = filter.removeAlpha(image);
img = filter.invertRGB(img, imageWidth, imageHeight);
filter.reverseRGB(img);
final int columns = (imageWidth + blockWidth - 1) / blockWidth;
final int rows = (imageHeight + blockHeight - 1) / blockHeight;
final byte[] blockData = new byte[blockHeight * blockWidth
* RGB_CHANNELS];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
final int xOffset = j * blockWidth;
final int yOffset = i * blockHeight;
final int xSpan = (imageWidth - xOffset > blockWidth)
? blockWidth : imageWidth - xOffset;
final int ySpan = (imageHeight - yOffset > blockHeight)
? blockHeight : imageHeight - yOffset;
int offset = 0;
int idx;
for (int k = 0; k < ySpan; k++) {
for (int l = 0; l < xSpan; l++, offset += RGB_CHANNELS) {
idx = (yOffset + k) * (imageWidth * RGB_CHANNELS)
+ (xOffset + l) * RGB_CHANNELS;
blockData[offset] = img[idx];
blockData[offset + 1] = img[idx + 1];
blockData[offset + 2] = img[idx + 2];
}
}
blocks.add(new ImageBlock(xSpan, ySpan,
zip(blockData, offset)));
}
}
}
/**
* Compress the image using the ZIP format.
* @param image the image data.
* @param length the number of bytes from the image to compress.
* @return the compressed image.
*/
private byte[] zip(final byte[] image, final int length) {
final Deflater deflater = new Deflater();
deflater.setInput(image, 0, length);
deflater.finish();
final byte[] compressedData = new byte[image.length];
final int bytesCompressed = deflater.deflate(compressedData);
final byte[] newData = Arrays.copyOf(compressedData, bytesCompressed);
return newData;
}
}