Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

701 lines
19 KiB
C#

using System;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.Drawing.Imaging;
using System.Xml;
using Mainsoft.Drawing.Configuration;
using imageio = javax.imageio;
using stream = javax.imageio.stream;
using awt = java.awt;
using image = java.awt.image;
using spi = javax.imageio.spi;
using dom = org.w3c.dom;
namespace Mainsoft.Drawing.Imaging {
/// <summary>
/// Summary description for ImageCodec.
/// </summary>
public class ImageCodec : IDisposable {
#region Members
imageio.ImageReader _nativeReader = null;
imageio.ImageWriter _nativeWriter = null;
stream.ImageInputStream _nativeStream = null;
ImageFormat _imageFormat = null;
int _currentFrame = 0;
#endregion
#region Constructros
protected ImageCodec() {
}
static ImageCodec() {
}
#endregion
#region Internal properties
internal imageio.ImageReader NativeReader {
get { return _nativeReader; }
set {
_nativeReader = value;
if (value == null)
return;
_imageFormat = MimeTypesToImageFormat( value.getOriginatingProvider().getMIMETypes() );
}
}
internal imageio.ImageWriter NativeWriter {
get { return _nativeWriter; }
set {
_nativeWriter = value;
if (value == null)
return;
_imageFormat = MimeTypesToImageFormat( value.getOriginatingProvider().getMIMETypes() );
}
}
internal stream.ImageInputStream NativeStream {
get { return _nativeStream; }
set {
_nativeStream = value;
if (value == null)
return;
if (NativeReader != null)
NativeReader.setInput( value );
if (NativeWriter != null)
NativeWriter.setOutput( value );
}
}
#endregion
#region ImageCodec factory methods
public static ImageCodec CreateReader(stream.ImageInputStream inputStream) {
java.util.Iterator iter = imageio.ImageIO.getImageReaders( inputStream );
return CreateReader(iter);
}
public static ImageCodec CreateReader(ImageFormat imageFormat) {
return CreateReader( ImageFormatToClsid( imageFormat ) );
}
public static ImageCodec CreateReader(Guid clsid) {
ImageCodec codec = null;
try {
ImageCodecInfo codecInfo = FindDecoder(clsid);
java.util.Iterator iter = imageio.ImageIO.getImageReadersByMIMEType( codecInfo.MimeType );
codec = CreateReader(iter);
}
catch {}
if (codec == null) {
ImageFormat format = ClsidToImageFormat(clsid);
string name = (format != null) ? format.ToString() : clsid.ToString();
throw new NotSupportedException(String.Format("The '{0}' format decoder is not installed.", name));
}
return codec;
}
private static ImageCodec CreateReader(java.util.Iterator iter) {
if ( !iter.hasNext() )
return null;
ImageCodec imageCodec = new ImageCodec();
imageCodec.NativeReader = (imageio.ImageReader) iter.next();
return imageCodec;
}
public static ImageCodec CreateWriter(ImageFormat imageFormat) {
return CreateWriter( ImageFormatToClsid( imageFormat ) );
}
public static ImageCodec CreateWriter(Guid clsid) {
ImageCodec codec = null;
try {
ImageCodecInfo codecInfo = FindEncoder(clsid);
java.util.Iterator iter = imageio.ImageIO.getImageWritersByMIMEType( codecInfo.MimeType );
codec = CreateWriter(iter);
}
catch {}
if (codec == null) {
ImageFormat format = ClsidToImageFormat(clsid);
string name = (format != null) ? format.ToString() : clsid.ToString();
throw new NotSupportedException(String.Format("The '{0}' format encoder is not installed.", name));
}
return codec;
}
private static ImageCodec CreateWriter(java.util.Iterator iter) {
if ( !iter.hasNext() )
return null;
ImageCodec imageCodec = new ImageCodec();
imageCodec.NativeWriter = (imageio.ImageWriter) iter.next();
return imageCodec;
}
#endregion
#region Codec enumerations
internal static Hashtable Decoders {
get {
const string MYNAME = "System.Drawing.Imaging.ImageCodecInfo.decoders";
Hashtable o = (Hashtable) AppDomain.CurrentDomain.GetData (MYNAME);
if (o != null)
return o;
o = new ReaderSpiIterator().Iterate();
AppDomain.CurrentDomain.SetData(MYNAME, o);
return o;
}
}
internal static Hashtable Encoders {
get {
const string MYNAME = "System.Drawing.Imaging.ImageCodecInfo.encoders";
Hashtable o = (Hashtable) AppDomain.CurrentDomain.GetData (MYNAME);
if (o != null)
return o;
o = new WriterSpiIterator().Iterate();
AppDomain.CurrentDomain.SetData(MYNAME, o);
return o;
}
}
internal static ImageCodecInfo FindEncoder (Guid clsid) {
ImageCodecInfo codec = (ImageCodecInfo) Encoders[clsid];
if (codec == null) {
// .net saves in png if cannot find requested encoder. atc id 316563
codec = (ImageCodecInfo) Encoders[ ImageCodec.PngClsid ];
}
return codec;
}
internal static ImageCodecInfo FindDecoder (Guid clsid) {
ImageCodecInfo codec = (ImageCodecInfo) Decoders[clsid];
if (codec == null) {
ImageFormat format = ClsidToImageFormat(clsid);
string name = (format != null) ? format.ToString() : clsid.ToString();
throw new NotSupportedException(String.Format("The '{0}' format decoder is not installed.", name));
}
return codec;
}
#endregion
#region SpiIterators
abstract class BaseSpiIterator {
protected abstract java.util.Iterator GetIterator (string mimeType);
protected abstract spi.ImageReaderWriterSpi GetNext (java.util.Iterator iter);
#region ProcessOneCodec
private ImageCodecInfo ProcessOneCodec (Guid clsid, Guid formatID, string mimeType) {
ImageCodecInfo ici = new ImageCodecInfo ();
ici.Clsid = clsid;
ici.FormatID = formatID;
ici.MimeType = mimeType;
java.util.Iterator iter = null;
try {
iter = GetIterator (mimeType);
}
catch(Exception) {
return null;
}
while (iter.hasNext ()) {
spi.ImageReaderWriterSpi rw = GetNext (iter);
ici.CodecName = rw.getDescription (java.util.Locale.getDefault ());
//ici.DllName = null;
foreach (string suffix in rw.getFileSuffixes ()) {
if (ici.FilenameExtension != null)
ici.FilenameExtension += ";";
ici.FilenameExtension += "*."+suffix;
}
ici.Flags = ImageCodecFlags.Builtin|ImageCodecFlags.SupportBitmap;
if (rw is spi.ImageReaderSpi)
ici.Flags |= ImageCodecFlags.Decoder;
if (rw is spi.ImageWriterSpi)
ici.Flags |= ImageCodecFlags.Encoder;
ici.FormatDescription = string.Join(";",
rw.getFormatNames());
try {
ici.Version = (int)Convert.ToDouble(rw.getVersion ());
}
catch (Exception) {
ici.Version = 1;
}
break;
}
return ici;
}
#endregion
internal Hashtable Iterate () {
// TBD: Insert Exception handling here
NameValueCollection nvc = (NameValueCollection) System.Configuration.ConfigurationSettings
.GetConfig ("mainsoft.drawing/codecs");
Hashtable codecs = new Hashtable (10);
for (int i=0; i<nvc.Count; i++) {
Guid clsid = new Guid (nvc.GetKey (i));
ImageFormat format = ClsidToImageFormat (clsid);
ImageCodecInfo codec = ProcessOneCodec (clsid, format.Guid, nvc[i]);
if ((codec != null) && (codec.FilenameExtension != null))
codecs [clsid] = codec;
}
return codecs;
}
}
class ReaderSpiIterator: BaseSpiIterator {
protected override java.util.Iterator GetIterator(string mimeType) {
return imageio.ImageIO.getImageReadersByMIMEType (mimeType);
}
protected override javax.imageio.spi.ImageReaderWriterSpi GetNext(java.util.Iterator iter) {
imageio.ImageReader r = (imageio.ImageReader) iter.next ();
return r.getOriginatingProvider ();
}
}
class WriterSpiIterator: BaseSpiIterator {
protected override java.util.Iterator GetIterator(string mimeType) {
return imageio.ImageIO.getImageWritersByMIMEType (mimeType);
}
protected override javax.imageio.spi.ImageReaderWriterSpi GetNext(java.util.Iterator iter) {
imageio.ImageWriter w = (imageio.ImageWriter) iter.next ();
return w.getOriginatingProvider ();
}
}
#endregion
#region Clsid and FormatID
static Guid BmpClsid = new Guid ("557cf400-1a04-11d3-9a73-0000f81ef32e");
static Guid JpegClsid = new Guid ("557cf401-1a04-11d3-9a73-0000f81ef32e");
static Guid GifClsid = new Guid ("557cf402-1a04-11d3-9a73-0000f81ef32e");
static Guid EmfClsid = new Guid ("557cf403-1a04-11d3-9a73-0000f81ef32e");
static Guid WmfClsid = new Guid ("557cf404-1a04-11d3-9a73-0000f81ef32e");
static Guid TiffClsid = new Guid ("557cf405-1a04-11d3-9a73-0000f81ef32e");
static Guid PngClsid = new Guid ("557cf406-1a04-11d3-9a73-0000f81ef32e");
static Guid IconClsid = new Guid ("557cf407-1a04-11d3-9a73-0000f81ef32e");
private static ImageFormat MimeTypesToImageFormat (string [] mimeTypes) {
foreach (ImageCodecInfo codec in Decoders.Values)
for (int i=0; i<mimeTypes.Length; i++)
if (codec.MimeType == mimeTypes [i])
return ClsidToImageFormat (codec.Clsid);
return null;
}
internal static ImageFormat ClsidToImageFormat (Guid clsid) {
if (clsid.Equals (BmpClsid))
return ImageFormat.Bmp;
else if (clsid.Equals (JpegClsid))
return ImageFormat.Jpeg;
else if (clsid.Equals (GifClsid))
return ImageFormat.Gif;
else if (clsid.Equals (EmfClsid))
return ImageFormat.Emf;
else if (clsid.Equals (WmfClsid))
return ImageFormat.Wmf;
else if (clsid.Equals (TiffClsid))
return ImageFormat.Tiff;
else if (clsid.Equals (PngClsid))
return ImageFormat.Png;
else if (clsid.Equals (IconClsid))
return ImageFormat.Icon;
else
return null;
}
internal static Guid ImageFormatToClsid (ImageFormat format) {
if (format == null)
return Guid.Empty;
if (format.Guid.Equals (ImageFormat.Bmp.Guid))
return BmpClsid;
else if (format.Guid.Equals (ImageFormat.Jpeg.Guid))
return JpegClsid;
else if (format.Guid.Equals (ImageFormat.Gif))
return GifClsid;
else if (format.Guid.Equals (ImageFormat.Emf.Guid))
return EmfClsid;
else if (format.Guid.Equals (ImageFormat.Wmf.Guid))
return WmfClsid;
else if (format.Guid.Equals (ImageFormat.Tiff.Guid))
return TiffClsid;
else if (format.Guid.Equals (ImageFormat.Png.Guid))
return PngClsid;
else if (format.Guid.Equals (ImageFormat.Icon.Guid))
return IconClsid;
else
return Guid.Empty;
}
private FrameDimension FormatFrameDimesion {
get {
if (ImageFormat == null)
return FrameDimension.Page;
if (ImageFormat.Guid.Equals (ImageFormat.Bmp.Guid))
return FrameDimension.Page;
else if (ImageFormat.Guid.Equals (ImageFormat.Jpeg.Guid))
return FrameDimension.Page;
else if (ImageFormat.Guid.Equals (ImageFormat.Gif))
return FrameDimension.Time;
else if (ImageFormat.Guid.Equals (ImageFormat.Emf.Guid))
return FrameDimension.Page;
else if (ImageFormat.Guid.Equals (ImageFormat.Wmf.Guid))
return FrameDimension.Page;
else if (ImageFormat.Guid.Equals (ImageFormat.Tiff.Guid))
return FrameDimension.Page;
else if (ImageFormat.Guid.Equals (ImageFormat.Png.Guid))
return FrameDimension.Page;
else if (ImageFormat.Guid.Equals (ImageFormat.Icon.Guid))
return FrameDimension.Resolution;
else
return FrameDimension.Page;
}
}
#endregion
#region Image read/write methods
internal PlainImage ReadPlainImage() {
awt.Image img = ReadImage( _currentFrame );
if (img == null)
return null;
// its possible to fail to load thumbnails and metadata, but image is ok.
awt.Image [] th = null;
#if THUMBNAIL_SUPPORTED
try {
th = ReadThumbnails( _currentFrame );
}
catch (Exception) {}
#endif
XmlDocument md = null;
imageio.metadata.IIOMetadata nativeMd = null;
try {
nativeMd = ReadImageMetadata( _currentFrame );
md = ConvertImageMetadata( nativeMd );
}
catch (Exception) {}
float [] resolution = GetResolution( md );
PlainImage pi = new PlainImage( img, th, ImageFormat, resolution[0], resolution[1], FormatFrameDimesion );
pi.NativeMetadata = nativeMd;
return pi;
}
internal PlainImage ReadNextPlainImage() {
_currentFrame++;
return ReadPlainImage();
}
private awt.Image ReadImage(int frame) {
if (NativeStream == null)
throw new Exception("Input stream not specified");
try {
return NativeReader.read (frame);
}
catch (java.lang.IndexOutOfBoundsException) {
return null;
}
catch (java.io.IOException ex) {
throw new System.IO.IOException(ex.Message, ex);
}
}
#if THUMBNAIL_SUPPORTED
private awt.Image [] ReadThumbnails(int frameIndex) {
awt.Image [] thArray = null;
try {
if (NativeReader.readerSupportsThumbnails()) {
int tmbNumber = NativeReader.getNumThumbnails(frameIndex);
if (tmbNumber > 0) {
thArray = new awt.Image[ tmbNumber ];
for (int i = 0; i < tmbNumber; i++) {
thArray[i] = NativeReader.readThumbnail(frameIndex, i);
}
}
}
return thArray;
}
catch (java.io.IOException ex) {
throw new System.IO.IOException(ex.Message, ex);
}
}
#endif
internal void WritePlainImage(PlainImageCollection pic) {
if ((pic == null) || (pic.Count == 0))
return;
if (pic.Count == 1) {
WritePlainImage( pic[0] );
return;
}
try {
if (NativeWriter.canWriteSequence ()) {
NativeWriter.prepareWriteSequence (null);
for (int i=0; i < pic.Count; i++) {
imageio.IIOImage iio = GetIIOImageContainer( pic[i] );
NativeWriter.writeToSequence (iio, null);
}
NativeWriter.endWriteSequence ();
}
else
WritePlainImage( pic[0] );
}
catch (java.io.IOException ex) {
throw new System.IO.IOException(ex.Message, ex);
}
}
internal void WritePlainImage(PlainImage pi) {
try {
imageio.IIOImage iio = GetIIOImageContainer( pi );
WriteImage( iio );
}
catch (java.io.IOException ex) {
throw new System.IO.IOException(ex.Message, ex);
}
}
private void WriteImage(imageio.IIOImage iio) {
if (NativeStream == null)
throw new Exception("Output stream not specified");
NativeWriter.write( iio );
}
private imageio.IIOImage GetIIOImageContainer(PlainImage pi) {
java.util.ArrayList al = null;
// prepare thumbnails list
if (pi.Thumbnails != null) {
al = new java.util.ArrayList( pi.Thumbnails.Length );
for (int i=0; i < pi.Thumbnails.Length; i++)
al.add(pi.Thumbnails[i]);
}
// prepare IIOImage container
if (pi.NativeImage is image.BufferedImage) {
imageio.IIOImage iio = new javax.imageio.IIOImage(
(image.BufferedImage)pi.NativeImage, al, null /*pi.NativeMetadata*/);
return iio;
}
else
// TBD: This codec is for raster formats only
throw new NotSupportedException("Only raster formats are supported");
}
private imageio.metadata.IIOMetadata ReadImageMetadata(int frameIndex) {
if (NativeStream == null)
throw new Exception("Input stream not specified");
try {
imageio.metadata.IIOMetadata md = NativeReader.getImageMetadata( frameIndex );
return md;
}
catch (java.io.IOException ex) {
throw new System.IO.IOException(ex.Message, ex);
}
}
#endregion
#region Extra properties
public ImageFormat ImageFormat {
get { return _imageFormat; }
}
#endregion
#region Metadata parse
private float [] GetResolution(XmlDocument metaData) {
if (metaData == null)
return new float[]{0, 0};
ResolutionConfigurationCollection rcc =
(ResolutionConfigurationCollection)
ConfigurationSettings.GetConfig ("mainsoft.drawing/codecsmetadata");
if (rcc == null)
throw new ConfigurationException("Configuration section codecsmetadata not found");
ResolutionConfiguration rc = rcc[ ImageFormat.ToString() ];
if (rc == null)
return new float[]{0, 0};
// Horizontal resolution
string xResPath = rc.XResPath;
string xRes;
if (xResPath == string.Empty)
xRes = rc.XResDefault;
else
xRes = GetValueFromMetadata(metaData, xResPath);
if ((xRes == null) || (xRes == string.Empty))
xRes = rc.XResDefault;
// Vertical resolution
string yResPath = rc.YResPath;
string yRes;
if (yResPath == string.Empty)
yRes = rc.YResDefault;
else
yRes = GetValueFromMetadata(metaData, yResPath);
if ((yRes == null) || (yRes == string.Empty))
yRes = rc.YResDefault;
// Resolution units
string resUnitsPath = rc.UnitsTypePath;
string resUnitsType;
if (resUnitsPath == string.Empty)
resUnitsType = rc.UnitsTypeDefault;
else
resUnitsType = GetValueFromMetadata(metaData, resUnitsPath);
if (resUnitsType == null)
resUnitsType = rc.UnitsTypeDefault;
// Unit scale
string unitScale = rc.UnitsScale[resUnitsType].ToString();
// Adjust resolution to its units
float [] res = new float[2];
res[0] = ParseFloatValue(xRes) * ParseFloatValue(unitScale);
res[1] = ParseFloatValue(yRes) * ParseFloatValue(unitScale);
return res;
}
private string GetValueFromMetadata(XmlDocument metaData, string path) {
XmlNode n = metaData.SelectSingleNode(path);
if (n == null)
return null;
return n.InnerText;
}
private XmlDocument ConvertImageMetadata(imageio.metadata.IIOMetadata metaData) {
string [] formatNames = metaData.getMetadataFormatNames();
dom.Element rootNode = (dom.Element) metaData.getAsTree(formatNames[0]);
XmlDocument _metadataDocument = new XmlDocument();
XmlConvert(rootNode, _metadataDocument);
return _metadataDocument;
}
private void XmlConvert(dom.Node jNode, XmlNode nNode) {
XmlDocument document = nNode.OwnerDocument;
if (document == null)
document = (XmlDocument)nNode;
XmlNode n = null;
switch (jNode.getNodeType()) {
case 1 :
n = document.CreateNode(XmlNodeType.Element, jNode.getNodeName(), jNode.getNamespaceURI());
break;
case 4 :
n = document.CreateNode(XmlNodeType.CDATA, jNode.getNodeName(), jNode.getNamespaceURI());
break;
default:
return;
}
//set value
n.InnerText = jNode.getNodeValue();
nNode.AppendChild( n );
//copy attributes
org.w3c.dom.NamedNodeMap nm = jNode.getAttributes();
for (int i=0; i<nm.getLength(); i++) {
XmlAttribute a = document.CreateAttribute( nm.item(i).getNodeName() );
a.Value = nm.item(i).getNodeValue();
n.Attributes.Append( a );
}
//copy childs
org.w3c.dom.NodeList nl = jNode.getChildNodes();
for (int i=0; i<nl.getLength(); i++) {
XmlConvert(nl.item(i), n);
}
}
protected virtual float ParseFloatValue(string strValue) {
try {
if ((strValue != null) && (strValue != "")) {
int dividerPos = strValue.IndexOf("/");
if (dividerPos < 0) {
return float.Parse(strValue);
}
else {
return float.Parse(strValue.Substring( 0, dividerPos )) /
float.Parse(strValue.Substring( dividerPos + 1 ));
}
}
return float.NaN;
}
catch (Exception) {
return float.NaN;
}
}
#endregion
#region IDisposable members
public void Dispose() {
if (NativeReader != null) {
NativeReader.dispose();
NativeReader = null;
}
if (NativeWriter != null) {
NativeWriter.dispose();
NativeWriter = null;
}
}
#endregion
}
}