blob: d5227b87c9e40757d62f093f2a8b1fb61c498b8b [file] [log] [blame]
/*
* Licensed 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.
* under the License.
*/
package org.apache.commons.imaging.formats.wbmp;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.imaging.ImageFormat;
import org.apache.commons.imaging.ImageInfo;
import org.apache.commons.imaging.ImageParser;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.common.bytesource.ByteSource;
public class WbmpImageParser extends ImageParser
{
public WbmpImageParser()
{
}
@Override
public String getName()
{
return "Wbmp-Custom";
}
@Override
public String getDefaultExtension()
{
return DEFAULT_EXTENSION;
}
private static final String DEFAULT_EXTENSION = ".wbmp";
private static final String ACCEPTED_EXTENSIONS[] =
{
".wbmp",
};
@Override
protected String[] getAcceptedExtensions()
{
return ACCEPTED_EXTENSIONS;
}
@Override
protected ImageFormat[] getAcceptedTypes()
{
return new ImageFormat[]
{
ImageFormat.IMAGE_FORMAT_WBMP, //
};
}
@Override
public boolean embedICCProfile(File src, File dst, byte profile[])
{
return false;
}
@Override
public IImageMetadata getMetadata(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
return null;
}
@Override
public ImageInfo getImageInfo(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
WbmpHeader wbmpHeader = readWbmpHeader(byteSource);
return new ImageInfo("WBMP", 1, new ArrayList<String>(),
ImageFormat.IMAGE_FORMAT_WBMP,
"Wireless Application Protocol Bitmap",
wbmpHeader.height, "image/vnd.wap.wbmp", 1,
0, 0, 0, 0,
wbmpHeader.width, false, false, false,
ImageInfo.COLOR_TYPE_BW,
ImageInfo.COMPRESSION_ALGORITHM_NONE);
}
@Override
public Dimension getImageSize(ByteSource byteSource,
Map params)
throws ImageReadException, IOException
{
WbmpHeader wbmpHeader = readWbmpHeader(byteSource);
return new Dimension(wbmpHeader.width, wbmpHeader.height);
}
@Override
public byte[] getICCProfileBytes(ByteSource byteSource,
Map params)
throws ImageReadException, IOException
{
return null;
}
static class WbmpHeader
{
int typeField;
byte fixHeaderField;
int width;
int height;
public WbmpHeader(int typeField, byte fixHeaderField, int width, int height)
{
this.typeField = typeField;
this.fixHeaderField = fixHeaderField;
this.width = width;
this.height = height;
}
public void dump(PrintWriter pw)
{
pw.println("WbmpHeader");
pw.println("TypeField: " + typeField);
pw.println("FixHeaderField: 0x" + Integer.toHexString(0xff & fixHeaderField));
pw.println("Width: " + width);
pw.println("Height: " + height);
}
}
private int readMultiByteInteger(InputStream is)
throws ImageReadException, IOException
{
int value = 0;
int nextByte;
int totalBits = 0;
do
{
nextByte = readByte("Header", is, "Error reading WBMP header");
value <<= 7;
value |= nextByte & 0x7f;
totalBits += 7;
if (totalBits > 31)
throw new ImageReadException("Overflow reading WBMP multi-byte field");
} while ((nextByte & 0x80) != 0);
return value;
}
private void writeMultiByteInteger(OutputStream os, int value)
throws IOException
{
boolean wroteYet = false;
for (int position = 4*7; position > 0; position -= 7)
{
int next7Bits = 0x7f & (value >>> position);
if (next7Bits != 0 || wroteYet)
{
os.write(0x80 | next7Bits);
wroteYet = true;
}
}
os.write(0x7f & value);
}
private WbmpHeader readWbmpHeader(ByteSource byteSource)
throws ImageReadException, IOException
{
InputStream is = null;
try
{
is = byteSource.getInputStream();
return readWbmpHeader(is);
}
finally
{
try
{
if (is != null)
is.close();
}
catch (IOException ignored)
{
}
}
}
private WbmpHeader readWbmpHeader(InputStream is)
throws ImageReadException, IOException
{
int typeField = readMultiByteInteger(is);
if (typeField != 0)
throw new ImageReadException("Invalid/unsupported WBMP type " + typeField);
byte fixHeaderField = readByte("FixHeaderField", is, "Invalid WBMP File");
if ((fixHeaderField & 0x9f) != 0)
throw new ImageReadException("Invalid/unsupported WBMP FixHeaderField 0x" +
Integer.toHexString(0xff & fixHeaderField));
int width = readMultiByteInteger(is);
int height = readMultiByteInteger(is);
return new WbmpHeader(typeField, fixHeaderField, width, height);
}
@Override
public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
throws ImageReadException, IOException
{
readWbmpHeader(byteSource).dump(pw);
return true;
}
private BufferedImage readImage(WbmpHeader wbmpHeader, InputStream is)
throws IOException
{
int rowLength = (wbmpHeader.width + 7) / 8;
byte[] image = readByteArray("Pixels", rowLength * wbmpHeader.height, is,
"Error reading image pixels");
DataBufferByte dataBuffer = new DataBufferByte(image, image.length);
WritableRaster raster = WritableRaster.createPackedRaster(dataBuffer,
wbmpHeader.width, wbmpHeader.height, 1, null);
int[] palette = {0x000000, 0xffffff};
IndexColorModel colorModel = new IndexColorModel(1, 2, palette, 0,
false, -1, DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster,
colorModel.isAlphaPremultiplied(), new Properties());
}
@Override
public final BufferedImage getBufferedImage(ByteSource byteSource,
Map params) throws ImageReadException, IOException
{
InputStream is = null;
try
{
is = byteSource.getInputStream();
WbmpHeader wbmpHeader = readWbmpHeader(is);
return readImage(wbmpHeader, is);
}
finally
{
try
{
if (is != null)
is.close();
}
catch (IOException ignored)
{
}
}
}
@Override
public void writeImage(BufferedImage src, OutputStream os, Map params)
throws ImageWriteException, IOException
{
// make copy of params; we'll clear keys as we consume them.
params = (params == null) ? new HashMap() : new HashMap(params);
// clear format key.
if (params.containsKey(PARAM_KEY_FORMAT))
params.remove(PARAM_KEY_FORMAT);
if (params.size() > 0)
{
Object firstKey = params.keySet().iterator().next();
throw new ImageWriteException("Unknown parameter: " + firstKey);
}
writeMultiByteInteger(os, 0); // typeField
os.write(0); // fixHeaderField
writeMultiByteInteger(os, src.getWidth());
writeMultiByteInteger(os, src.getHeight());
for (int y = 0; y < src.getHeight(); y++)
{
int pixel = 0;
int nextBit = 0x80;
for (int x = 0; x < src.getWidth(); x++)
{
int argb = src.getRGB(x, y);
int red = 0xff & (argb >> 16);
int green = 0xff & (argb >> 8);
int blue = 0xff & (argb >> 0);
int sample = (red + green + blue) / 3;
if (sample > 127)
pixel |= nextBit;
nextBit >>>= 1;
if (nextBit == 0)
{
os.write(pixel);
pixel = 0;
nextBit = 0x80;
}
}
if (nextBit != 0x80)
os.write(pixel);
}
}
/**
* Extracts embedded XML metadata as XML string.
* <p>
*
* @param byteSource
* File containing image data.
* @param params
* Map of optional parameters, defined in SanselanConstants.
* @return Xmp Xml as String, if present. Otherwise, returns null.
*/
@Override
public String getXmpXml(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
return null;
}
}