org.apache.harmony.awt.gl.image.GifDecoder Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.awt.gl.image;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import com.google.code.appengine.awt.image.ColorModel;
import com.google.code.appengine.awt.image.ImageConsumer;
import com.google.code.appengine.awt.image.IndexColorModel;
public class GifDecoder extends ImageDecoder {
// ImageConsumer hints: common
private static final int baseHints =
ImageConsumer.SINGLEPASS | ImageConsumer.COMPLETESCANLINES |
ImageConsumer.SINGLEFRAME;
// ImageConsumer hints: interlaced
private static final int interlacedHints =
baseHints | ImageConsumer.RANDOMPIXELORDER;
// Impossible color value - no translucent pixels allowed
static final int IMPOSSIBLE_VALUE = 0x0FFFFFFF;
// I/O buffer
private static final int MIN_BUFFER_SIZE = 1024;
private static final int MAX_BUFFER_SIZE = 2097152;
private int buffer_size;
private byte buffer[];
GifDataStream gifDataStream = new GifDataStream();
GifGraphicBlock currBlock;
// Pointer to native structure which store decoding state
// between subsequent decoding/IO-suspension cycles
private long hNativeDecoder; // NULL initially
// Number of bytes eaten by the native decoder
private int bytesConsumed;
private boolean consumersPrepared;
private Hashtable properties = new Hashtable();
// Could be set up by java code or native method when
// transparent pixel index changes or local color table encountered
private boolean forceRGB;
private byte screenBuffer[];
private int screenRGBBuffer[];
ColorModel gcm;
public GifDecoder(DecodingImageSource src, InputStream is) {
super(src, is);
try {
int available_bytes = is.available();
if (available_bytes < MIN_BUFFER_SIZE) {
buffer_size = MIN_BUFFER_SIZE;
} else if (available_bytes > MAX_BUFFER_SIZE) {
buffer_size = MAX_BUFFER_SIZE;
} else {
buffer_size = available_bytes;
}
} catch (IOException e) {
buffer_size = MIN_BUFFER_SIZE;
}
buffer = new byte[buffer_size];
}
// private static native int[] toRGB(byte imageData[], byte colormap[], int transparentColor);
//
// private static native void releaseNativeDecoder(long hDecoder);
//
// private native int decode(
// byte input[],
// int bytesInBuffer,
// long hDecoder,
// GifDataStream dataStream,
// GifGraphicBlock currBlock
// );
private int[] getScreenRGBBuffer() {
if (screenRGBBuffer == null) {
if (screenBuffer != null) {
int transparentColor =
gifDataStream.logicalScreen.globalColorTable.cm.getTransparentPixel();
transparentColor = transparentColor > 0 ? transparentColor : IMPOSSIBLE_VALUE;
// screenRGBBuffer =
// toRGB(
// screenBuffer,
// gifDataStream.logicalScreen.globalColorTable.colors,
// transparentColor
// );
} else {
int size = gifDataStream.logicalScreen.logicalScreenHeight *
gifDataStream.logicalScreen.logicalScreenWidth;
screenRGBBuffer = new int[size];
}
}
return screenRGBBuffer;
}
private void prepareConsumers() {
GifLogicalScreen gls = gifDataStream.logicalScreen;
setDimensions(gls.logicalScreenWidth,
gls.logicalScreenHeight);
setProperties(properties);
currBlock = gifDataStream.graphicBlocks.get(0);
if (forceRGB) {
setColorModel(ColorModel.getRGBdefault());
} else {
gcm = gls.globalColorTable.getColorModel(currBlock.transparentColor);
setColorModel(gcm);
}
// Fill screen buffer with the background or transparent color
if (forceRGB) {
int fillColor = 0xFF000000;
if (gls.backgroundColor != IMPOSSIBLE_VALUE) {
fillColor = gls.backgroundColor;
}
Arrays.fill(getScreenRGBBuffer(), fillColor);
} else {
int fillColor = 0;
if (gls.backgroundColor != IMPOSSIBLE_VALUE) {
fillColor = gls.backgroundColor;
} else {
fillColor = gls.globalColorTable.cm.getTransparentPixel();
}
screenBuffer = new byte[gls.logicalScreenHeight*gls.logicalScreenWidth];
Arrays.fill(screenBuffer, (byte) fillColor);
}
setHints(interlacedHints); // XXX - always random pixel order
}
@Override
public void decodeImage() throws IOException {
try {
int bytesRead = 0;
int needBytes, offset, bytesInBuffer = 0;
boolean eosReached = false;
GifGraphicBlock blockToDispose = null;
// Create new graphic block
if (currBlock == null) {
currBlock = new GifGraphicBlock();
gifDataStream.graphicBlocks.add(currBlock);
}
// Read from the input stream
for (;;) {
needBytes = buffer_size - bytesInBuffer;
offset = bytesInBuffer;
bytesRead = inputStream.read(buffer, offset, needBytes);
if (bytesRead < 0) {
eosReached = true;
bytesRead = 0;
} // Don't break, maybe something left in buffer
// Keep track on how much bytes left in buffer
bytesInBuffer += bytesRead;
// Here we pass number of new bytes read from the input stream (bytesRead)
// since native decoder uses java buffer and doesn't have its own
// buffer. So it adds this number to the number of bytes left
// in buffer from the previous call.
int numLines = 0;
// int numLines = decode(
// buffer,
// bytesRead,
// hNativeDecoder,
// gifDataStream,
// currBlock);
// Keep track on how much bytes left in buffer
bytesInBuffer -= bytesConsumed;
if (
!consumersPrepared &&
gifDataStream.logicalScreen.completed &&
gifDataStream.logicalScreen.globalColorTable.completed &&
(currBlock.imageData != null || // Have transparent pixel filled
currBlock.rgbImageData != null)
) {
prepareConsumers();
consumersPrepared = true;
}
if (bytesConsumed < 0) {
break; // Error exit
}
if (currBlock != null) {
if (numLines != 0) {
// Dispose previous image only before showing next
if (blockToDispose != null) {
blockToDispose.dispose();
blockToDispose = null;
}
currBlock.sendNewData(this, numLines);
}
if (currBlock.completed && hNativeDecoder != 0) {
blockToDispose = currBlock; // Dispose only before showing new pixels
currBlock = new GifGraphicBlock();
gifDataStream.graphicBlocks.add(currBlock);
}
}
if (hNativeDecoder == 0) {
break;
}
if (eosReached && numLines == 0) { // Maybe image is truncated...
// releaseNativeDecoder(hNativeDecoder);
break;
}
}
} finally {
closeStream();
}
// Here all animation goes
// Repeat image loopCount-1 times or infinitely if loopCount = 0
if (gifDataStream.loopCount != 1) {
if (currBlock.completed == false) {
gifDataStream.graphicBlocks.remove(currBlock);
}
int numFrames = gifDataStream.graphicBlocks.size();
// At first last block will be disposed
GifGraphicBlock gb =
gifDataStream.graphicBlocks.get(numFrames-1);
ImageLoader.beginAnimation();
while (gifDataStream.loopCount != 1) {
if (gifDataStream.loopCount != 0) {
gifDataStream.loopCount--;
}
// Show all frames
for (int i=0; i graphicBlocks = new ArrayList(10); // Of GifGraphicBlocks
// Comments from the image
String comments[];
}
class GifLogicalScreen {
// Indicates that reading of this block accomplished
boolean completed = false;
int logicalScreenWidth;
int logicalScreenHeight;
int backgroundColor = IMPOSSIBLE_VALUE;
GifColorTable globalColorTable = new GifColorTable();
}
class GifGraphicBlock {
// Indicates that reading of this block accomplished
boolean completed = false;
final static int DISPOSAL_NONE = 0;
final static int DISPOSAL_NODISPOSAL = 1;
final static int DISPOSAL_BACKGROUND = 2;
final static int DISPOSAL_RESTORE = 3;
int disposalMethod;
int delayTime; // Multiplied by 10 already
int transparentColor = IMPOSSIBLE_VALUE;
int imageLeft;
int imageTop;
int imageWidth;
int imageHeight;
// Auxilliary variables to minimize computations
int imageRight;
int imageBottom;
boolean interlace;
// Don't need local color table - if it is specified
// image data are converted to RGB in the native code
byte imageData[] = null;
int rgbImageData[] = null;
private int currY = 0; // Current output scanline
int[] getRgbImageData() {
if (rgbImageData == null) {
// rgbImageData =
// toRGB(
// imageData,
// gifDataStream.logicalScreen.globalColorTable.colors,
// transparentColor
// );
if (transparentColor != IMPOSSIBLE_VALUE) {
transparentColor =
gifDataStream.logicalScreen.globalColorTable.cm.getRGB(transparentColor);
transparentColor &= 0x00FFFFFF;
}
}
return rgbImageData;
}
private void replaceTransparentPixels(int numLines) {
List graphicBlocks = gifDataStream.graphicBlocks;
int prevBlockIndex = graphicBlocks.indexOf(this) - 1;
if (prevBlockIndex >= 0) {
int maxY = currY + numLines + imageTop;
int offset = currY * imageWidth;
// Update right and bottom coordinates
imageRight = imageLeft + imageWidth;
imageBottom = imageTop + imageHeight;
int globalWidth = gifDataStream.logicalScreen.logicalScreenWidth;
int pixelValue, imageOffset;
int rgbData[] = forceRGB ? getRgbImageData() : null;
for (int y = currY + imageTop; y < maxY; y++) {
imageOffset = globalWidth * y + imageLeft;
for (int x = imageLeft; x < imageRight; x++) {
pixelValue = forceRGB ?
rgbData[offset] :
imageData[offset] & 0xFF;
if (pixelValue == transparentColor) {
if (forceRGB) {
pixelValue = getScreenRGBBuffer() [imageOffset];
rgbData[offset] = pixelValue;
} else {
pixelValue = screenBuffer [imageOffset];
imageData[offset] = (byte) pixelValue;
}
}
offset++;
imageOffset++;
} // for
} // for
} // if (prevBlockIndex >= 0)
}
public void sendNewData(GifDecoder decoder, int numLines) {
// Get values for transparent pixels
// from the perevious frames
if (transparentColor != IMPOSSIBLE_VALUE) {
replaceTransparentPixels(numLines);
}
if (forceRGB) {
decoder.setPixels(
imageLeft,
imageTop + currY,
imageWidth,
numLines,
ColorModel.getRGBdefault(),
getRgbImageData(),
currY*imageWidth,
imageWidth
);
} else {
decoder.setPixels(
imageLeft,
imageTop + currY,
imageWidth,
numLines,
gcm,
imageData,
currY*imageWidth,
imageWidth
);
}
currY += numLines;
}
public void dispose() {
imageComplete(ImageConsumer.SINGLEFRAMEDONE);
// Show current frame until delayInterval will not elapse
if (delayTime > 0) {
try {
Thread.sleep(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Thread.yield(); // Allow consumers to consume data
}
// Don't dispose if image is outside of the visible area
if (imageLeft > gifDataStream.logicalScreen.logicalScreenWidth ||
imageTop > gifDataStream.logicalScreen.logicalScreenHeight) {
disposalMethod = DISPOSAL_NONE;
}
switch(disposalMethod) {
case DISPOSAL_BACKGROUND: {
if (forceRGB) {
getRgbImageData(); // Ensure that transparentColor is RGB, not index
int data[] = new int[imageWidth*imageHeight];
// Compatibility: Fill with transparent color if we have one
if (transparentColor != IMPOSSIBLE_VALUE) {
Arrays.fill(
data,
transparentColor
);
} else {
Arrays.fill(
data,
gifDataStream.logicalScreen.backgroundColor
);
}
setPixels(
imageLeft,
imageTop,
imageWidth,
imageHeight,
ColorModel.getRGBdefault(),
data,
0,
imageWidth
);
sendToScreenBuffer(data);
} else {
byte data[] = new byte[imageWidth*imageHeight];
// Compatibility: Fill with transparent color if we have one
if (transparentColor != IMPOSSIBLE_VALUE) {
Arrays.fill(
data,
(byte) transparentColor
);
} else {
Arrays.fill(
data,
(byte) gifDataStream.logicalScreen.backgroundColor
);
}
setPixels(
imageLeft,
imageTop,
imageWidth,
imageHeight,
gcm,
data,
0,
imageWidth
);
sendToScreenBuffer(data);
}
break;
}
case DISPOSAL_RESTORE: {
screenBufferToScreen();
break;
}
case DISPOSAL_NONE:
case DISPOSAL_NODISPOSAL:
default: {
// Copy transmitted data to the screen buffer
Object data = forceRGB ? (Object) getRgbImageData() : imageData;
sendToScreenBuffer(data);
break;
}
}
}
private void sendToScreenBuffer(Object data) {
int dataInt[];
byte dataByte[];
int width = gifDataStream.logicalScreen.logicalScreenWidth;
if (forceRGB) {
dataInt = (int[]) data;
if (imageWidth == width) {
System.arraycopy(dataInt,
0,
getScreenRGBBuffer(),
imageLeft + imageTop*width,
dataInt.length
);
} else { // Each scanline
copyScanlines(dataInt, getScreenRGBBuffer(), width);
}
} else {
dataByte = (byte[]) data;
if (imageWidth == width) {
System.arraycopy(dataByte,
0,
screenBuffer,
imageLeft + imageTop*width,
dataByte.length
);
} else { // Each scanline
copyScanlines(dataByte, screenBuffer, width);
}
}
} // sendToScreenBuffer
private void copyScanlines(Object src, Object dst, int width) {
for (int i=0; i 0) {
if (transparentColor == IMPOSSIBLE_VALUE) {
return cm =
new IndexColorModel(8, size, colors, 0, false);
}
if (transparentColor > size) {
size = transparentColor + 1;
}
return cm =
new IndexColorModel(8, size, colors, 0, false, transparentColor);
}
return cm = null; // Force default ARGB color model
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy