123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926 |
- using PaintDotNet.SystemLayer;
- using System;
- using System.Collections.Specialized;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Imaging;
- using System.IO;
- using System.IO.Compression;
- using System.Reflection;
- using System.Runtime.InteropServices;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.Text;
- using System.Windows.Forms;
- using System.Xml;
- namespace PaintDotNet
- {
- [Serializable]
- public sealed class Document : IDeserializationCallback, IDisposable, ICloneable
- {
- public Surface surface;
- private int width;
- private int height;
- private NameValueCollection userMetaData;
- [NonSerialized]
- private Threading.ThreadPool threadPool = new Threading.ThreadPool();
- /*[NonSerialized]
- private InvalidateEventHandler layerInvalidatedDelegate;*/
- [NonSerialized]
- private Vector<Rectangle> updateRegion;
- [NonSerialized]
- private bool dirty;
- private Version savedWith;
- [NonSerialized]
- private Metadata metadata = null;
- [NonSerialized]
- private XmlDocument headerXml;
- private const string headerXmlSkeleton = "<pdnImage><custom></custom></pdnImage>";
- private XmlDocument HeaderXml
- {
- get
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("Document");
- }
- if (this.headerXml == null)
- {
- this.headerXml = new XmlDocument();
- this.headerXml.LoadXml(headerXmlSkeleton);
- }
- return this.headerXml;
- }
- }
- /// <summary>
- /// Gets or sets the units that are used for measuring the document's physical (printed) size.
- /// </summary>
- /// <remarks>
- /// If this property is set to MeasurementUnit.Pixel, then Dpu will be reset to 1.
- /// If this property has not been set in the image's metadata, its default value
- /// will be MeasurementUnit.Inch.
- /// If the EXIF data for the image is invalid (such as "ResolutionUnit = 0" or something),
- /// then the default DpuUnit will be returned.
- /// </remarks>
- public MeasurementUnit DpuUnit
- {
- get
- {
- PropertyItem[] pis = this.Metadata.GetExifValues(ExifTagID.ResolutionUnit);
- if (pis.Length == 0)
- {
- this.DpuUnit = DefaultDpuUnit;
- return DefaultDpuUnit;
- }
- else
- {
- try
- {
- ushort unit = Exif.DecodeShortValue(pis[0]);
- // Guard against bad data in the EXIF store
- switch ((MeasurementUnit)unit)
- {
- case MeasurementUnit.Pixel:
- case MeasurementUnit.Inch:
- case MeasurementUnit.Mil:
- case MeasurementUnit.Centimeter:
- case MeasurementUnit.Millimeter:
- case MeasurementUnit.Micron:
- case MeasurementUnit.Nano:
- return (MeasurementUnit)unit;
- default:
- this.Metadata.RemoveExifValues(ExifTagID.ResolutionUnit);
- return this.DpuUnit; // recursive call
- }
- }
- catch (Exception)
- {
- this.Metadata.RemoveExifValues(ExifTagID.ResolutionUnit);
- return this.DpuUnit; // recursive call
- }
- }
- }
- set
- {
- PropertyItem pi = Exif.CreateShort(ExifTagID.ResolutionUnit, (ushort)value);
- this.Metadata.ReplaceExifValues(ExifTagID.ResolutionUnit, new PropertyItem[1] { pi });
- if (value == MeasurementUnit.Pixel)
- {
- this.DpuX = 1.0;
- this.DpuY = 1.0;
- }
- Dirty = true;
- }
- }
- public static MeasurementUnit DefaultDpuUnit
- {
- get
- {
- return MeasurementUnit.Inch;
- }
- }
- public static double defaultDpi = 96;
- public static double DefaultDpi
- {
- get
- {
- return defaultDpi;
- }
- }
- /// <summary>
- /// 1英寸 = 2.54厘米
- /// </summary>
- public static double CmPerInch = 2.54;
- /// <summary>
- /// 默认值 = dpi / 单位
- /// </summary>
- public double defaultDpcm;
- public double DefaultDpcm
- {
- get
- {
- return defaultDpi / CmPerInch;
- }
- }
- public const double MinimumDpu = 0.01;
- public const double MaximumDpu = 32767.0;
- public static double InchesToCentimeters(double inches)
- {
- return inches * CmPerInch;
- }
- public static double MilsToCentimeters(double mils)
- {
- return mils * CmPerInch * 0.001;
- }
- public static double CentimetersToInches(double centimeters)
- {
- return centimeters / CmPerInch;
- }
- public static double CentimetersToMils(double centimeters)
- {
- return 1000 * centimeters / CmPerInch;
- }
- public static double DotsPerInchToDotsPerCm(double dpi)
- {
- return dpi / CmPerInch;
- }
- public static double DotsPerMilToDotsPerCm(double dpi)
- {
- return 0.001 * dpi / CmPerInch;
- }
- public static double DotsPerCmToDotsPerInch(double dpcm)
- {
- return dpcm * CmPerInch;
- }
- public static double DotsPerCmToDotsPerMil(double dpcm)
- {
- return dpcm * CmPerInch * 0.001;
- }
- public static double GetDefaultDpu(MeasurementUnit units)
- {
- double dpu;
- switch (units)
- {
- case MeasurementUnit.Inch:
- dpu = defaultDpi;
- break;
- case MeasurementUnit.Mil:
- dpu = defaultDpi / 1000;
- break;
- case MeasurementUnit.Centimeter:
- dpu = defaultDpi / CmPerInch;
- break;
- case MeasurementUnit.Millimeter:
- dpu = defaultDpi / CmPerInch / 10;
- break;
- case MeasurementUnit.Micron:
- dpu = defaultDpi / CmPerInch / 10000;
- break;
- case MeasurementUnit.Nano:
- dpu = defaultDpi / CmPerInch / 10000000;
- break;
- case MeasurementUnit.Pixel:
- dpu = 1.0;
- break;
- default:
- throw new InvalidEnumArgumentException("DpuUnit", (int)units, typeof(MeasurementUnit));
- }
- return dpu;
- }
- /// <summary>
- /// Ensures that the document's DpuX, DpuY, and DpuUnits properties are set.
- /// If they are not already set, they are initialized to their default values (96, 96 , inches).
- /// </summary>
- private void InitializeDpu()
- {
- this.DpuUnit = this.DpuUnit;
- this.DpuX = this.DpuX;
- this.DpuY = this.DpuY;
- }
- private byte[] GetDoubleAsRationalExifData(double value)
- {
- uint numerator;
- uint denominator;
- if (Math.IEEERemainder(value, 1.0) == 0)
- {
- numerator = (uint)value;
- denominator = 1;
- }
- else
- {
- double s = value * 1000.0;
- numerator = (uint)Math.Floor(s);
- denominator = 1000;
- }
- return Exif.EncodeRationalValue(numerator, denominator);
- }
- /// <summary>
- /// Gets or sets the Document's dots-per-unit scale in the X direction.
- /// </summary>
- /// <remarks>
- /// If DpuUnit is equal to MeasurementUnit.Pixel, then this property may not be set
- /// to any value other than 1.0. Setting DpuUnit to MeasurementUnit.Pixel will reset
- /// this property to 1.0. This property may only be set to a value greater than 0.
- /// One dot is always equal to one pixel. This property will not return a value less
- /// than MinimumDpu, nor a value larger than MaximumDpu.
- /// </remarks>
- public double DpuX
- {
- get
- {
- PropertyItem[] pis = this.Metadata.GetExifValues(ExifTagID.XResolution);
- if (pis.Length == 0)
- {
- double defaultDpu = GetDefaultDpu(this.DpuUnit);
- this.DpuX = defaultDpu;
- return defaultDpu;
- }
- else
- {
- try
- {
- uint numerator;
- uint denominator;
- Exif.DecodeRationalValue(pis[0], out numerator, out denominator);
- if (denominator == 0)
- {
- throw new DivideByZeroException(); // will be caught by the below catch{}
- }
- else
- {
- return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator));
- }
- }
- catch
- {
- this.Metadata.RemoveExifValues(ExifTagID.XResolution);
- return this.DpuX; // recursive call;
- }
- }
- }
- set
- {
- if (value <= 0.0)
- {
- throw new ArgumentOutOfRangeException("value", value, "must be > 0.0");
- }
- if (this.DpuUnit == MeasurementUnit.Pixel && value != 1.0)
- {
- throw new ArgumentOutOfRangeException("value", value, "if DpuUnit == Pixel, then value must equal 1.0");
- }
- byte[] data = GetDoubleAsRationalExifData(value);
- PropertyItem pi = Exif.CreatePropertyItem(ExifTagID.XResolution, ExifTagType.Rational, data);
- this.Metadata.ReplaceExifValues(ExifTagID.XResolution, new PropertyItem[1] { pi });
- Dirty = true;
- }
- }
- /// <summary>
- /// Gets or sets the Document's dots-per-unit scale in the Y direction.
- /// </summary>
- /// <remarks>
- /// If DpuUnit is equal to MeasurementUnit.Pixel, then this property may not be set
- /// to any value other than 1.0. Setting DpuUnit to MeasurementUnit.Pixel will reset
- /// this property to 1.0. This property may only be set to a value greater than 0.
- /// One dot is always equal to one pixel. This property will not return a value less
- /// than MinimumDpu, nor a value larger than MaximumDpu.
- /// </remarks>
- public double DpuY
- {
- get
- {
- PropertyItem[] pis = this.Metadata.GetExifValues(ExifTagID.YResolution);
- if (pis.Length == 0)
- {
- // If there's no DpuY setting, default to the DpuX setting
- double dpu = this.DpuX;
- this.DpuY = dpu;
- return dpu;
- }
- else
- {
- try
- {
- uint numerator;
- uint denominator;
- Exif.DecodeRationalValue(pis[0], out numerator, out denominator);
- if (denominator == 0)
- {
- throw new DivideByZeroException(); // will be caught by the below catch{}
- }
- else
- {
- return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator));
- }
- }
- catch
- {
- this.Metadata.RemoveExifValues(ExifTagID.YResolution);
- return this.DpuY; // recursive call;
- }
- }
- }
- set
- {
- if (value <= 0.0)
- {
- throw new ArgumentOutOfRangeException("value", value, "must be > 0.0");
- }
- if (this.DpuUnit == MeasurementUnit.Pixel && value != 1.0)
- {
- throw new ArgumentOutOfRangeException("value", value, "if DpuUnit == Pixel, then value must equal 1.0");
- }
- byte[] data = GetDoubleAsRationalExifData(value);
- PropertyItem pi = Exif.CreatePropertyItem(ExifTagID.YResolution, ExifTagType.Rational, data);
- this.Metadata.ReplaceExifValues(ExifTagID.YResolution, new PropertyItem[1] { pi });
- Dirty = true;
- }
- }
- /// <summary>
- /// Gets the Document's measured physical width based on the DpuUnit and DpuX properties.
- /// </summary>
- public double PhysicalWidth
- {
- get
- {
- return (double)this.Width / (double)this.DpuX;
- }
- }
- /// <summary>
- /// Gets the Document's measured physical height based on the DpuUnit and DpuY properties.
- /// </summary>
- public double PhysicalHeight
- {
- get
- {
- return (double)this.Height / (double)this.DpuY;
- }
- }
- //
- // Conversion Matrix:
- //
- // GetPhysical[X|Y](x, unit), where dpu = this.dpuX or dpuY
- //
- // dpu | px | in | cm |
- // unit | | | |
- // -------------+------+------+------------+
- // px | x | x | x |
- // -------------+------+------+------------+
- // in | x / | x / | x / |
- // | 96 | dpuX | (dpuX*2.54)|
- // -------------+------+------+------------+
- // cm | x / |x*2.54| x / dpuX |
- // | 37.8| /dpuX| |
- // -------------+------+------+------------+
- public static double PixelToPhysical(double pixel, MeasurementUnit resultUnit, MeasurementUnit dpuUnit, double dpu)
- {
- double result;
- if (resultUnit == MeasurementUnit.Pixel)
- {
- result = pixel;
- }
- else
- {
- if (resultUnit == dpuUnit)
- {
- result = pixel / dpu;
- }
- else if (dpuUnit == MeasurementUnit.Pixel)
- {
- double defaultDpu = GetDefaultDpu(dpuUnit);
- result = pixel / defaultDpu;
- }
- else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
- {
- result = pixel / (CmPerInch * dpu);
- }
- else // if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
- {
- result = (pixel * CmPerInch) / dpu;
- }
- }
- return result;
- }
- /// <summary>
- /// 像素换算到物理长度
- /// </summary>
- /// <param name="pixel"></param>
- /// <param name="resultUnit"></param>
- /// <returns></returns>
- public double PixelToPhysicalX(double pixel, MeasurementUnit resultUnit)
- {
- double result;
- if (resultUnit == MeasurementUnit.Pixel)
- {
- result = pixel;
- }
- else
- {
- MeasurementUnit dpuUnit = this.DpuUnit;
- if (resultUnit == dpuUnit)
- {
- result = pixel / this.DpuX;
- }
- else if (dpuUnit == MeasurementUnit.Pixel)
- {
- double defaultDpuX = GetDefaultDpu(dpuUnit);
- result = pixel / defaultDpuX;
- }
- else if (dpuUnit == MeasurementUnit.Centimeter && resultUnit == MeasurementUnit.Inch)
- {
- result = pixel / this.DpuX; // (CmPerInch * this.DpuX);
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Millimeter)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Millimeter);
- result = pixel / defaultDpuY;
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Micron)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Micron);
- result = pixel / defaultDpuY;
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Nano)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Nano);
- result = pixel / defaultDpuY;
- }
- else
- {
- result = pixel / this.DpuX; //(pixel * CmPerInch) / this.DpuX;
- }
- }
- return result;
- }
- /// <summary>
- /// 像素换算到物理长度
- /// </summary>
- /// <param name="pixel"></param>
- /// <param name="resultUnit"></param>
- /// <returns></returns>
- public double PixelToPhysicalY(double pixel, MeasurementUnit resultUnit)
- {
- double result;
- if (resultUnit == MeasurementUnit.Pixel)
- {
- result = pixel;
- }
- else
- {
- MeasurementUnit dpuUnit = this.DpuUnit;
- if (resultUnit == dpuUnit)
- {
- double defaultDpuY = GetDefaultDpu(dpuUnit);
- result = pixel / defaultDpuY;
- //result = pixel / this.DpuY;
- }
- else if (dpuUnit == MeasurementUnit.Pixel)
- {
- double defaultDpuY = GetDefaultDpu(dpuUnit);
- result = pixel / defaultDpuY;
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Mil)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Mil);
- result = pixel / defaultDpuY; // pixel / (CmPerInch * this.DpuY);
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Centimeter)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Centimeter);
- result = pixel / defaultDpuY; // pixel / (CmPerInch * this.DpuY);
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Millimeter)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Millimeter);
- result = pixel / defaultDpuY;
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Micron)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Micron);
- result = pixel / defaultDpuY;
- }
- else if (dpuUnit == MeasurementUnit.Inch && resultUnit == MeasurementUnit.Nano)
- {
- double defaultDpuY = GetDefaultDpu(MeasurementUnit.Nano);
- result = pixel / defaultDpuY;
- }
- else
- {
- result = pixel / this.DpuX;//(pixel * CmPerInch) / this.DpuY;
- }
- }
- return result;
- }
- private static bool IsValidMeasurementUnit(MeasurementUnit unit)
- {
- switch (unit)
- {
- case MeasurementUnit.Pixel:
- case MeasurementUnit.Inch:
- case MeasurementUnit.Centimeter:
- case MeasurementUnit.Millimeter:
- case MeasurementUnit.Micron:
- case MeasurementUnit.Nano:
- return true;
- default:
- return false;
- }
- }
- public static double ConvertMeasurement(
- double sourceLength,
- MeasurementUnit sourceUnits,
- MeasurementUnit basisDpuUnits,
- double basisDpu,
- MeasurementUnit resultDpuUnits)
- {
- // Validation
- if (!IsValidMeasurementUnit(sourceUnits))
- {
- throw new InvalidEnumArgumentException("sourceUnits", (int)sourceUnits, typeof(MeasurementUnit));
- }
- if (!IsValidMeasurementUnit(basisDpuUnits))
- {
- throw new InvalidEnumArgumentException("basisDpuUnits", (int)basisDpuUnits, typeof(MeasurementUnit));
- }
- if (!IsValidMeasurementUnit(resultDpuUnits))
- {
- throw new InvalidEnumArgumentException("resultDpuUnits", (int)resultDpuUnits, typeof(MeasurementUnit));
- }
- if (basisDpuUnits == MeasurementUnit.Pixel && basisDpu != 1.0)
- {
- throw new ArgumentOutOfRangeException("basisDpuUnits, basisDpu", "if basisDpuUnits is Pixel, then basisDpu must equal 1.0");
- }
- // Case 1. No conversion is necessary if they want the same units out.
- if (sourceUnits == resultDpuUnits)
- {
- return sourceLength;
- }
- // Case 2. Simple inches -> centimeters
- if (sourceUnits == MeasurementUnit.Inch && resultDpuUnits == MeasurementUnit.Centimeter)
- {
- return InchesToCentimeters(sourceLength);
- }
- // Case 2. Simple mils -> centimeters
- if (sourceUnits == MeasurementUnit.Mil && resultDpuUnits == MeasurementUnit.Centimeter)
- {
- return MilsToCentimeters(sourceLength);
- }
- // Case 3. Simple centimeters -> inches.
- if (sourceUnits == MeasurementUnit.Centimeter && resultDpuUnits == MeasurementUnit.Inch)
- {
- return CentimetersToInches(sourceLength);
- }
- // Case 3. Simple centimeters -> inches.
- if (sourceUnits == MeasurementUnit.Centimeter && resultDpuUnits == MeasurementUnit.Mil)
- {
- return CentimetersToMils(sourceLength);
- }
- // At this point we know we are converting from non-pixels to pixels, or from pixels
- // to non-pixels.
- // Cases 4 through 8 cover conversion from non-pixels to pixels.
- // Cases 9 through 11 cover conversion from pixels to non-pixels.
- // Case 4. Conversion from pixels to inches/centimeters when basis is in pixels too.
- // This means we must use the default DPU for the desired result measurement.
- // No need to compare lengthUnits != resultDpuUnits, since we already know this to
- // be true from case 1.
- if (sourceUnits == MeasurementUnit.Pixel && basisDpuUnits == MeasurementUnit.Pixel)
- {
- double dpu = GetDefaultDpu(resultDpuUnits);
- double lengthInOrCm = sourceLength / dpu;
- return lengthInOrCm;
- }
- // Case 5. Conversion from inches/centimeters to pixels when basis is in pixels too.
- // This means we must use the default DPU for the given input measurement.
- if (sourceUnits != MeasurementUnit.Pixel && basisDpuUnits == MeasurementUnit.Pixel)
- {
- double dpu = GetDefaultDpu(sourceUnits);
- double resultPx = sourceLength * dpu;
- return resultPx;
- }
- // Case 6. Conversion from inches/centimeters to pixels, when basis is in same units as input.
- if (sourceUnits == basisDpuUnits && resultDpuUnits == MeasurementUnit.Pixel)
- {
- double resultPx = sourceLength * basisDpu;
- return resultPx;
- }
- // Case 7. Conversion from inches to pixels, when basis is in centimeters.
- if (sourceUnits == MeasurementUnit.Inch && basisDpuUnits == MeasurementUnit.Centimeter)
- {
- double dpi = DotsPerCmToDotsPerInch(basisDpu);
- double resultPx = sourceLength * dpi;
- return resultPx;
- }
- // Case 7. Conversion from inches to pixels, when basis is in centimeters.
- if (sourceUnits == MeasurementUnit.Mil && basisDpuUnits == MeasurementUnit.Centimeter)
- {
- double dpi = DotsPerCmToDotsPerMil(basisDpu);
- double resultPx = sourceLength * dpi;
- return resultPx;
- }
- // Case 8. Conversion from centimeters to pixels, when basis is in inches.
- if (sourceUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Inch)
- {
- double dpcm = DotsPerInchToDotsPerCm(basisDpu);
- double resultPx = sourceLength * dpcm;
- return resultPx;
- }
- // Case 8. Conversion from centimeters to pixels, when basis is in inches.
- if (sourceUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Mil)
- {
- double dpcm = DotsPerMilToDotsPerCm(basisDpu);
- double resultPx = sourceLength * dpcm;
- return resultPx;
- }
- // Case 9. Converting from pixels to inches/centimeters, when the basis and result
- // units are the same.
- if (basisDpuUnits == resultDpuUnits)
- {
- double resultInOrCm = sourceLength / basisDpu;
- return resultInOrCm;
- }
- // Case 10. Converting from pixels to centimeters, when the basis is in inches.
- if (resultDpuUnits == MeasurementUnit.Centimeter && basisDpuUnits == MeasurementUnit.Inch)
- {
- double dpcm = DotsPerInchToDotsPerCm(basisDpu);
- double resultCm = sourceLength / dpcm;
- return resultCm;
- }
- // Case 11. Converting from pixels to inches, when the basis is in centimeters.
- if (resultDpuUnits == MeasurementUnit.Inch && basisDpuUnits == MeasurementUnit.Centimeter)
- {
- double dpi = DotsPerCmToDotsPerInch(basisDpu);
- double resultIn = sourceLength / dpi;
- return resultIn;
- }
- // Should not be possible to get here, but must appease the compiler.
- throw new InvalidOperationException();
- }
- public double PixelAreaToPhysicalArea(double area, MeasurementUnit resultUnit)
- {
- double xScale = PixelToPhysicalX(1.0, resultUnit);
- double yScale = PixelToPhysicalY(1.0, resultUnit);
- return area * xScale * yScale;
- }
- private static string GetUnitsAbbreviation(MeasurementUnit units)
- {
- string result;
- switch (units)
- {
- case MeasurementUnit.Pixel:
- result = string.Empty;
- break;
- case MeasurementUnit.Inch:
- result = PdnResources.GetString("MeasurementUnit.Inch.Abbreviation");
- break;
- case MeasurementUnit.Mil:
- result = PdnResources.GetString("MeasurementUnit.Mil.Abbreviation");
- break;
- case MeasurementUnit.Centimeter:
- result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
- break;
- case MeasurementUnit.Millimeter:
- result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
- break;
- case MeasurementUnit.Micron:
- result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
- break;
- case MeasurementUnit.Nano:
- result = PdnResources.GetString("MeasurementUnit.Centimeter.Abbreviation");
- break;
- default:
- throw new InvalidEnumArgumentException("MeasurementUnit was invalid");
- }
- return result;
- }
- public void CoordinatesToStrings(MeasurementUnit units, int x, int y, out string xString, out string yString, out string unitsString)
- {
- string unitsAbbreviation = GetUnitsAbbreviation(units);
- unitsString = GetUnitsAbbreviation(units);
- if (units == MeasurementUnit.Pixel)
- {
- xString = x.ToString();
- yString = y.ToString();
- }
- else
- {
- double physicalX = PixelToPhysicalX(x, units);
- xString = physicalX.ToString("F2");
- double physicalY = PixelToPhysicalY(y, units);
- yString = physicalY.ToString("F2");
- }
- }
- public Version SavedWithVersion
- {
- get
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Document");
- }
- if (savedWith == null)
- {
- savedWith = PdnInfo.GetVersion();
- }
- return savedWith;
- }
- }
- [field: NonSerialized]
- public event EventHandler DirtyChanged;
- private void OnDirtyChanged()
- {
- if (DirtyChanged != null)
- {
- DirtyChanged(this, EventArgs.Empty);
- }
- }
- /// <summary>
- /// Keeps track of whether the document has changed at all since it was last opened
- /// or saved. This is something that is not reset to true by any method in the Document
- /// class, but is set to false anytime anything is changed.
- /// This way we can prompt the user to save a changed document when they go to quit.
- /// </summary>
- public bool Dirty
- {
- get
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("Document");
- }
- return this.dirty;
- }
- set
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("Document");
- }
- if (this.dirty != value)
- {
- this.dirty = value;
- OnDirtyChanged();
- }
- }
- }
- /// <summary>
- /// Exposes a collection for access to the layers, and for manipulation of
- /// the way the document contains the layers (add/remove/move).
- /// </summary>
- /*public LayerList Layers
- {
- get
- {
- return null;
- if (disposed)
- {
- throw new ObjectDisposedException("Document");
- }
- return layers;
- }
- }*/
- /// <summary>
- /// Width of the document, in pixels. All contained layers must be this wide as well.
- /// </summary>
- public int Width
- {
- get
- {
- return width;
- }
- }
- /// <summary>
- /// Height of the document, in pixels. All contained layers must be this tall as well.
- /// </summary>
- public int Height
- {
- get
- {
- return height;
- }
- }
- /// <summary>
- /// The size of the document, in pixels. This is a convenience property that wraps up
- /// the Width and Height properties in one Size structure.
- /// </summary>
- public Size Size
- {
- get
- {
- return new Size(Width, Height);
- }
- }
- public Rectangle Bounds
- {
- get
- {
- return new Rectangle(0, 0, Width, Height);
- }
- }
- public Metadata Metadata
- {
- get
- {
- if (metadata == null)
- {
- metadata = new Metadata(userMetaData);
- }
- return metadata;
- }
- }
- public void ReplaceMetaDataFrom(Document other)
- {
- this.Metadata.ReplaceWithDataFrom(other.Metadata);
- }
- public void ClearMetaData()
- {
- this.Metadata.Clear();
- }
- /// <summary>
- /// Clears a portion of a surface to transparent.
- /// </summary>
- /// <param name="surface">The surface to partially clear</param>
- /// <param name="roi">The rectangle to clear</param>
- private unsafe void ClearBackground(Surface surface, Rectangle roi)
- {
- roi.Intersect(surface.Bounds);
- for (int y = roi.Top; y < roi.Bottom; y++)
- {
- ColorBgra* ptr = surface.GetPointAddressUnchecked(roi.Left, y);
- Memory.SetToZero(ptr, (ulong)roi.Width * ColorBgra.SizeOf);
- }
- }
- /// <summary>
- /// Clears a portion of a surface to transparent.
- /// </summary>
- /// <param name="surface">The surface to partially clear</param>
- /// <param name="rois">The array of Rectangles designating the areas to clear</param>
- /// <param name="startIndex">The start index within the rois array to clear</param>
- /// <param name="length">The number of Rectangles in the rois array (staring with startIndex) to clear</param>
- private void ClearBackground(Surface surface, Rectangle[] rois, int startIndex, int length)
- {
- for (int i = startIndex; i < startIndex + length; i++)
- {
- ClearBackground(surface, rois[i]);
- }
- }
- public void Render(RenderArgs args, bool clearBackground)
- {
- Render(args, args.Surface.Bounds, clearBackground);
- }
- /// <summary>
- /// Renders a requested region of the document. Will clear the background of the input
- /// before rendering if requested.
- /// </summary>
- /// <param name="args">Contains information used to control where rendering occurs.</param>
- /// <param name="roi">The rectangular region to render.</param>
- /// <param name="clearBackground">If true, 'args' will be cleared to zero before rendering.</param>
- public void Render(RenderArgs args, Rectangle roi, bool clearBackground)
- {
- /*int startIndex;
- if (clearBackground)
- {
- BitmapLayer layer0;
- layer0 = this.layers[0] as BitmapLayer;
- // Special case: if the first layer is a visible BitmapLayer with full opacity using
- // the default blend op, we can just copy the pixels straight over
- if (layer0 != null &&
- layer0.Visible &&
- layer0.Opacity == 255 &&
- layer0.BlendOp.GetType() == UserBlendOps.GetDefaultBlendOp())
- {
- args.Surface.CopySurface(layer0.Surface);
- startIndex = 1;
- }
- else
- {
- ClearBackground(args.Surface, roi);
- startIndex = 0;
- }
- }
- else
- {
- startIndex = 0;
- }
- for (int i = startIndex; i < this.layers.Count; ++i)
- {
- Layer layer = (Layer)this.layers[i];
- if (layer.Visible)
- {
- layer.Render(args, roi);
- }
- }*/
- }
- public void Render(RenderArgs args, Rectangle[] roi, int startIndex, int length, bool clearBackground)
- {
- /*int startLayerIndex;
- if (clearBackground)
- {
- BitmapLayer layer0;
- layer0 = this.layers[0] as BitmapLayer;
- // Special case: if the first layer is a visible BitmapLayer with full opacity using
- // the default blend op, we can just copy the pixels straight over
- if (layer0 != null &&
- layer0.Visible &&
- layer0.Opacity == 255 &&
- layer0.BlendOp.GetType() == UserBlendOps.GetDefaultBlendOp())
- {
- args.Surface.CopySurface(layer0.Surface, roi, startIndex, length);
- startLayerIndex = 1;
- }
- else
- {
- ClearBackground(args.Surface, roi, startIndex, length);
- startLayerIndex = 0;
- }
- }
- else
- {
- startLayerIndex = 0;
- }
- for (int i = startLayerIndex; i < this.layers.Count; ++i)
- {
- Layer layer = (Layer)this.layers[i];
- if (layer.Visible)
- {
- layer.RenderUnchecked(args, roi, startIndex, length);
- }
- }*/
- }
- private sealed class UpdateScansContext
- {
- private Document document;
- private RenderArgs dst;
- private Rectangle[] scans;
- private int startIndex;
- private int length;
- public void UpdateScans(object context)
- {
- document.Render(dst, scans, startIndex, length, true);
- }
- public UpdateScansContext(Document document, RenderArgs dst, Rectangle[] scans, int startIndex, int length)
- {
- this.document = document;
- this.dst = dst;
- this.scans = scans;
- this.startIndex = startIndex;
- this.length = length;
- }
- }
- /// <summary>
- /// Constructs a blank document (zero layers) of the given width and height.
- /// </summary>
- /// <param name="width"></param>
- /// <param name="height"></param>
- public Document(int width, int height)
- {
- this.width = width;
- this.height = height;
- this.Dirty = true;
- this.updateRegion = new Vector<Rectangle>();
- //layers = new LayerList(this);
- //SetupEvents();
- userMetaData = new NameValueCollection();
- Invalidate();
- }
- /// <summary>
- /// Sets up event handling for contained objects.
- /// </summary>
- /*private void SetupEvents()
- {
- layers.Changed += new EventHandler(LayerListChangedHandler);
- layers.Changing += new EventHandler(LayerListChangingHandler);
- layerInvalidatedDelegate = new InvalidateEventHandler(LayerInvalidatedHandler);
- foreach (Layer layer in layers)
- {
- layer.Invalidated += layerInvalidatedDelegate;
- }
- }*/
- /// <summary>
- /// Called after deserialization occurs so that certain things that are non-serializable
- /// can be set up.
- /// </summary>
- /// <param name="sender"></param>
- public void OnDeserialization(object sender)
- {
- this.updateRegion = new Vector<Rectangle>();
- this.updateRegion.Add(this.Bounds);
- this.threadPool = new PaintDotNet.Threading.ThreadPool();
- //SetupEvents();
- Dirty = true;
- }
- [field: NonSerialized]
- public event InvalidateEventHandler Invalidated;
- /// <summary>
- /// Raises the Invalidated event.
- /// </summary>
- /// <param name="e"></param>
- private void OnInvalidated(InvalidateEventArgs e)
- {
- if (Invalidated != null)
- {
- Invalidated(this, e);
- }
- }
- /*/// <summary>
- /// Handles the Changing event that is raised from the contained LayerList.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void LayerListChangingHandler(object sender, EventArgs e)
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Document");
- }
- foreach (Layer layer in Layers)
- {
- layer.Invalidated -= layerInvalidatedDelegate;
- }
- }*/
- /*/// <summary>
- /// Handles the Changed event that is raised from the contained LayerList.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void LayerListChangedHandler(object sender, EventArgs e)
- {
- foreach (Layer layer in Layers)
- {
- layer.Invalidated += layerInvalidatedDelegate;
- }
- Invalidate();
- }*/
- /*/// <summary>
- /// Handles the Invalidated event that is raised from any contained Layer.
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void LayerInvalidatedHandler(object sender, InvalidateEventArgs e)
- {
- Invalidate(e.InvalidRect);
- }*/
- /// <summary>
- /// Causes the whole document to be invalidated, forcing a full rerender on
- /// the next call to Update.
- /// </summary>
- public void Invalidate()
- {
- Dirty = true;
- Rectangle rect = new Rectangle(0, 0, Width, Height);
- updateRegion.Clear();
- updateRegion.Add(rect);
- OnInvalidated(new InvalidateEventArgs(rect));
- }
- /*/// <summary>
- /// Invalidates a portion of the document. The given region is then tagged
- /// for rerendering during the next call to Update.
- /// </summary>
- /// <param name="roi">The region of interest to be invalidated.</param>
- public void Invalidate(Rectangle roi)
- {
- Dirty = true;
- Rectangle rect = Rectangle.Intersect(roi, this.Bounds);
- updateRegion.Add(rect);
- OnInvalidated(new InvalidateEventArgs(rect));
- }*/
- /*/// <summary>
- /// Clears the document's update region. This is called at the end of the
- /// Update method.
- /// </summary>
- private void Validate()
- {
- updateRegion.Clear();
- }*/
- /// <summary>
- /// Creates a document that consists of one BitmapLayer.
- /// </summary>
- /// <param name="image">The Image to make a copy of that will be the first layer ("Background") in the document.</param>
- public static Document FromImage(Image image)
- {
- if (image == null)
- {
- throw new ArgumentNullException("image");
- }
- Document document = new Document(image.Width, image.Height);
- Surface surface = new Surface(new Size(image.Width, image.Height));
- surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
- surface.PixelFormat = image.PixelFormat;
- //BitmapLayer layer = Layer.CreateBackgroundLayer(image.Width, image.Height);
- //layer.Surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
- //layer.Surface.PixelFormat = image.PixelFormat;
- Bitmap asBitmap = image as Bitmap;
- // Copy pixels
- if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format32bppArgb)
- {
- unsafe
- {
- BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
- try
- {
- for (int y = 0; y < bData.Height; ++y)
- {
- uint* srcPtr = (uint*)((byte*)bData.Scan0.ToPointer() + (y * bData.Stride));
- ColorBgra* dstPtr = surface.GetRowAddress(y);
- for (int x = 0; x < bData.Width; ++x)
- {
- dstPtr->Bgra = *srcPtr;
- ++srcPtr;
- ++dstPtr;
- }
- }
- }
- finally
- {
- asBitmap.UnlockBits(bData);
- bData = null;
- }
- }
- }
- else if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format24bppRgb)
- {
- unsafe
- {
- BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
- try
- {
- for (int y = 0; y < bData.Height; ++y)
- {
- byte* srcPtr = (byte*)bData.Scan0.ToPointer() + (y * bData.Stride);
- ColorBgra* dstPtr = surface.GetRowAddress(y);
- for (int x = 0; x < bData.Width; ++x)
- {
- byte b = *srcPtr;
- byte g = *(srcPtr + 1);
- byte r = *(srcPtr + 2);
- byte a = 255;
- *dstPtr = ColorBgra.FromBgra(b, g, r, a);
- srcPtr += 3;
- ++dstPtr;
- }
- }
- }
- finally
- {
- asBitmap.UnlockBits(bData);
- bData = null;
- }
- }
- }
- else
- {
- using (RenderArgs args = new RenderArgs(surface))
- {
- args.Graphics.CompositingMode = CompositingMode.SourceCopy;
- args.Graphics.SmoothingMode = SmoothingMode.None;
- args.Graphics.DrawImage(image, args.Bounds, args.Bounds, GraphicsUnit.Pixel);
- }
- }
- // Console.Write(string.Format("Copy pixels end:{0};", DateTime.Now.Millisecond % 10000));
- // Transfer metadata
- // Sometimes GDI+ does not honor the resolution tags that we
- // put in manually via the EXIF properties.
- document.DpuUnit = MeasurementUnit.Inch;
- document.DpuX = image.HorizontalResolution;
- document.DpuY = image.VerticalResolution;
- PropertyItem[] pis;
- try
- {
- pis = image.PropertyItems;
- }
- catch (Exception ex)
- {
- Tracing.Ping("Exception while retreiving image's PropertyItems: " + ex.ToString());
- pis = null;
- }
- if (pis != null)
- {
- for (int i = 0; i < pis.Length; ++i)
- {
- document.Metadata.AddExifValues(new PropertyItem[] { pis[i] });
- }
- }
- //document.Layers.Add(layer);
- document.surface = surface;
- document.Invalidate();
- return document;
- }
- public static Document FromMat1(OpenCvSharp.Mat mat)
- {
- if (mat == null)
- {
- throw new ArgumentNullException("image");
- }
- Image image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat);
- Bitmap asBitmap = image as Bitmap;
- Document document = new Document(mat.Width, mat.Height);
- Surface surface = new Surface(new Size(mat.Width, mat.Height));
- surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
- surface.PixelFormat = asBitmap.PixelFormat;
- //BitmapLayer layer = Layer.CreateBackgroundLayer(mat.Width, mat.Height);
- //layer.Surface.Clear(ColorBgra.FromBgra(0, 0, 0, 0));
- // Copy pixels
- if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format32bppArgb)
- {
- unsafe
- {
- BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
- try
- {
- for (int y = 0; y < bData.Height; ++y)
- {
- uint* srcPtr = (uint*)((byte*)bData.Scan0.ToPointer() + (y * bData.Stride));
- ColorBgra* dstPtr = surface.GetRowAddress(y);
- for (int x = 0; x < bData.Width; ++x)
- {
- dstPtr->Bgra = *srcPtr;
- ++srcPtr;
- ++dstPtr;
- }
- }
- }
- finally
- {
- asBitmap.UnlockBits(bData);
- bData = null;
- }
- }
- }
- else if (asBitmap != null && asBitmap.PixelFormat == PixelFormat.Format24bppRgb)
- {
- unsafe
- {
- BitmapData bData = asBitmap.LockBits(new Rectangle(0, 0, asBitmap.Width, asBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
- try
- {
- for (int y = 0; y < bData.Height; ++y)
- {
- byte* srcPtr = (byte*)bData.Scan0.ToPointer() + (y * bData.Stride);
- ColorBgra* dstPtr = surface.GetRowAddress(y);
- for (int x = 0; x < bData.Width; ++x)
- {
- byte b = *srcPtr;
- byte g = *(srcPtr + 1);
- byte r = *(srcPtr + 2);
- byte a = 255;
- *dstPtr = ColorBgra.FromBgra(b, g, r, a);
- srcPtr += 3;
- ++dstPtr;
- }
- }
- }
- finally
- {
- asBitmap.UnlockBits(bData);
- bData = null;
- }
- }
- }
- else
- {
- using (RenderArgs args = new RenderArgs(surface))
- {
- args.Graphics.CompositingMode = CompositingMode.SourceCopy;
- args.Graphics.SmoothingMode = SmoothingMode.None;
- args.Graphics.DrawImage(image, args.Bounds, args.Bounds, GraphicsUnit.Pixel);
- }
- }
- // Transfer metadata
- // Sometimes GDI+ does not honor the resolution tags that we
- // put in manually via the EXIF properties.
- document.DpuUnit = MeasurementUnit.Inch;
- document.DpuX = image.HorizontalResolution;
- document.DpuY = image.VerticalResolution;
- PropertyItem[] pis;
- try
- {
- pis = image.PropertyItems;
- }
- catch (Exception ex)
- {
- Tracing.Ping("Exception while retreiving image's PropertyItems: " + ex.ToString());
- pis = null;
- }
- if (pis != null)
- {
- for (int i = 0; i < pis.Length; ++i)
- {
- document.Metadata.AddExifValues(new PropertyItem[] { pis[i] });
- }
- }
- document.surface = surface;
- //document.Layers.Add(layer);
- document.Invalidate();
- return document;
- }
- public static Document FromByteArr(byte[] arr, int w, int h)
- {
- Document document = new Document(w, h);
- Surface surface = new Surface(new Size(w, h), PixelFormat.Format24bppRgb);
- Marshal.Copy(arr, 0, surface.Scan0.Pointer, arr.Length);
- document.surface = surface;
- document.Invalidate();
- return document;
- }
- public unsafe static Document FromImageMat(OpenCvSharp.Mat mat)
- {//用于调试FromImage,完成后可以去掉该封装
- return Document.FromMat(mat);
- }
- public unsafe static Document FromMat(OpenCvSharp.Mat mat)
- {
- if (mat == null)
- {
- throw new ArgumentNullException("image");
- }
- if (mat.Channels() == 3)
- {
- OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGR2BGRA);
- }
- else if (mat.Channels() == 1)
- {
- OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.GRAY2BGRA);
- }
- Document document = new Document(mat.Width, mat.Height);
- Surface surface = new Surface(new Size(mat.Width, mat.Height));
- surface.mat = mat;
- surface.scan0.VoidStar = (void*)mat.Data;
- surface.Stride = (int)mat.Step();
- document.surface = surface;
- //document.Layers.Add(layer);
- document.Invalidate();
- return document;
- /*if (mat.Channels() == 3)
- {
- OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGR2BGRA);
- }
- else if (mat.Channels() == 1)
- {
- OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.GRAY2BGRA);
- }
- Document document = new Document(mat.Width, mat.Height);
- Surface surface = new Surface(new Size(mat.Width, mat.Height));
- byte[] arr = new byte[mat.Total() * mat.ElemSize()];
- Marshal.Copy(mat.Data, arr, 0, arr.Length);
- Marshal.Copy(arr, 0, surface.Scan0.Pointer, arr.Length);
- document.surface = surface;
- document.Invalidate();*/
- }
- public unsafe static Document FromImageMatForHistogram(OpenCvSharp.Mat mat, Surface oldSurface)
- {
- return Document.FromMatForHistogram(mat, oldSurface);
- }
- public unsafe static Document FromMatForHistogram(OpenCvSharp.Mat mat, Surface oldSurface)
- {
- if (mat == null)
- {
- throw new ArgumentNullException("image");
- }
- if (mat.Channels() == 3)
- {
- OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.BGR2BGRA);
- }
- else if (mat.Channels() == 1)
- {
- OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.GRAY2BGRA);
- }
- Document document = new Document(mat.Width, mat.Height);
- Surface surface = new Surface(new Size(mat.Width, mat.Height));
- surface.mat = mat;
- surface.BackUpMat = oldSurface.BackUpMat;
- surface.scan0.VoidStar = (void*)mat.Data;
- surface.Stride = (int)mat.Step();
- document.surface = surface;
- document.Invalidate();
- return document;
- }
- public static byte[] MagicBytes
- {
- get
- {
- return Encoding.UTF8.GetBytes("PDN3");
- }
- }
- /// <summary>
- /// Deserializes a Document from a stream.
- /// </summary>
- /// <param name="stream">The stream to deserialize from. This stream must be seekable.</param>
- /// <returns>The Document that was stored in stream.</returns>
- /// <remarks>
- /// This is the only supported way to deserialize a Document instance from disk.
- /// </remarks>
- public static Document FromStream(Stream stream)
- {
- long oldPosition = stream.Position;
- bool pdn21Format = true;
- // Version 2.1+ file format:
- // Starts with bytes as defined by MagicBytes
- // Next three bytes are 24-bit unsigned int 'N' (first byte is low-word, second byte is middle-word, third byte is high word)
- // The next N bytes are a string, this is the document header (it is XML, UTF-8 encoded)
- // Important: 'N' indicates a byte count, not a character count. 'N' bytes may result in less than 'N' characters,
- // depending on how the characters decode as per UTF8
- // If the next 2 bytes are 0x00, 0x01: This signifies that non-compressed .NET serialized data follows.
- // If the next 2 bytes are 0x1f, 0x8b: This signifies the start of the gzip compressed .NET serialized data
- //
- // Version 2.0 and previous file format:
- // Starts with 0x1f, 0x8b: this signifies the start of the gzip compressed .NET serialized data.
- // Read in the 'magic' bytes
- for (int i = 0; i < MagicBytes.Length; ++i)
- {
- int theByte = stream.ReadByte();
- if (theByte == -1)
- {
- throw new EndOfStreamException();
- }
- if (theByte != MagicBytes[i])
- {
- pdn21Format = false;
- break;
- }
- }
- // Read in the header if we found the 'magic' bytes identifying a PDN 2.1 file
- XmlDocument headerXml = null;
- if (pdn21Format)
- {
- int low = stream.ReadByte();
- if (low == -1)
- {
- throw new EndOfStreamException();
- }
- int mid = stream.ReadByte();
- if (mid == -1)
- {
- throw new EndOfStreamException();
- }
- int high = stream.ReadByte();
- if (high == -1)
- {
- throw new EndOfStreamException();
- }
- int byteCount = low + (mid << 8) + (high << 16);
- byte[] bytes = new byte[byteCount];
- int bytesRead = Utility.ReadFromStream(stream, bytes, 0, byteCount);
- if (bytesRead != byteCount)
- {
- throw new EndOfStreamException("expected " + byteCount + " bytes, but only got " + bytesRead);
- }
- string xml = Encoding.UTF8.GetString(bytes);
- headerXml = new XmlDocument();
- headerXml.LoadXml(xml);
- }
- else
- {
- stream.Position = oldPosition; // rewind and try as v2.0-or-earlier file
- }
- // Start reading the data section of the file. Determine if it's gzip or regular
- long oldPosition2 = stream.Position;
- int first = stream.ReadByte();
- if (first == -1)
- {
- throw new EndOfStreamException();
- }
- int second = stream.ReadByte();
- if (second == -1)
- {
- throw new EndOfStreamException();
- }
- Document document;
- object docObject;
- BinaryFormatter formatter = new BinaryFormatter();
- SerializationFallbackBinder sfb = new SerializationFallbackBinder();
- sfb.AddAssembly(Assembly.GetExecutingAssembly()); // first try PaintDotNet.Data.dll
- sfb.AddAssembly(typeof(Utility).Assembly); // second, try PaintDotNet.Core.dll
- sfb.AddAssembly(typeof(SystemLayer.Memory).Assembly); // third, try PaintDotNet.SystemLayer.dll
- formatter.Binder = sfb;
- if (first == 0 && second == 1)
- {
- DeferredFormatter deferred = new DeferredFormatter();
- formatter.Context = new StreamingContext(formatter.Context.State, deferred);
- docObject = formatter.UnsafeDeserialize(stream, null);
- deferred.FinishDeserialization(stream);
- }
- else if (first == 0x1f && second == 0x8b)
- {
- stream.Position = oldPosition2; // rewind to the start of 0x1f, 0x8b
- GZipStream gZipStream = new GZipStream(stream, CompressionMode.Decompress, true);
- docObject = formatter.UnsafeDeserialize(gZipStream, null);
- }
- else
- {
- throw new FormatException("file is not a valid document");
- }
- document = (Document)docObject;
- document.Dirty = true;
- document.headerXml = headerXml;
- document.Invalidate();
- return document;
- }
- /// <summary>
- /// Saves the Document to the given Stream with only the default headers and no
- /// IO completion callback.
- /// </summary>
- /// <param name="stream">The Stream to serialize the Document to.</param>
- public void SaveToStream(Stream stream)
- {
- SaveToStream(stream, null);
- }
- /// <summary>
- /// Saves the Document to the given Stream with the default and given headers, and
- /// using the given IO completion callback.
- /// </summary>
- /// <param name="stream">The Stream to serialize the Document to.</param>
- /// <param name="callback">
- /// This can be used to keep track of the number of uncompressed bytes that are written. The
- /// values reported through the IOEventArgs.Count+Offset will vary from 1 to approximately
- /// Layers.Count*Width*Height*sizeof(ColorBgra). The final number will actually be higher
- /// because of hierarchical overhead, so make sure to cap any progress reports to 100%. This
- /// callback will be wired to the IOFinished event of a SiphonStream. Events may be raised
- /// from any thread. May be null.
- /// </param>
- public void SaveToStream(Stream stream, IOEventHandler callback)
- {
- InitializeDpu();
- PrepareHeader();
- string headerText = this.HeaderXml.OuterXml;
- // Write the header
- byte[] magicBytes = Document.MagicBytes;
- stream.Write(magicBytes, 0, magicBytes.Length);
- byte[] headerBytes = Encoding.UTF8.GetBytes(headerText);
- stream.WriteByte((byte)(headerBytes.Length & 0xff));
- stream.WriteByte((byte)((headerBytes.Length & 0xff00) >> 8));
- stream.WriteByte((byte)((headerBytes.Length & 0xff0000) >> 16));
- stream.Write(headerBytes, 0, headerBytes.Length);
- stream.Flush();
- // Copy version info
- this.savedWith = PdnInfo.GetVersion();
- // Write 0x00, 0x01 to indicate normal .NET serialized data
- stream.WriteByte(0x00);
- stream.WriteByte(0x01);
- // Write the remainder of the file (gzip compressed)
- SiphonStream siphonStream = new SiphonStream(stream);
- BinaryFormatter formatter = new BinaryFormatter();
- DeferredFormatter deferred = new DeferredFormatter(true, null);
- SaveProgressRelay relay = new SaveProgressRelay(deferred, callback);
- formatter.Context = new StreamingContext(formatter.Context.State, deferred);
- formatter.Serialize(siphonStream, this);
- deferred.FinishSerialization(siphonStream);
- stream.Flush();
- }
- private class SaveProgressRelay
- {
- private DeferredFormatter formatter;
- private IOEventHandler ioCallback;
- private long lastReportedBytes;
- public SaveProgressRelay(DeferredFormatter formatter, IOEventHandler ioCallback)
- {
- this.formatter = formatter;
- this.ioCallback = ioCallback;
- this.formatter.ReportedBytesChanged += new EventHandler(Formatter_ReportedBytesChanged);
- }
- private void Formatter_ReportedBytesChanged(object sender, EventArgs e)
- {
- long reportedBytes = formatter.ReportedBytes;
- bool raiseEvent;
- long length = 0;
- lock (this)
- {
- raiseEvent = (reportedBytes > lastReportedBytes);
- if (raiseEvent)
- {
- length = reportedBytes - this.lastReportedBytes;
- this.lastReportedBytes = reportedBytes;
- }
- }
- if (raiseEvent && ioCallback != null)
- {
- ioCallback(this, new IOEventArgs(IOOperationType.Write, reportedBytes - length, (int)length));
- }
- }
- }
- private void PrepareHeader()
- {
- XmlDocument xd = this.HeaderXml;
- XmlElement pdnImage = (XmlElement)xd.SelectSingleNode("/pdnImage");
- pdnImage.SetAttribute("width", this.Width.ToString());
- pdnImage.SetAttribute("height", this.Height.ToString());
- pdnImage.SetAttribute("layers", "1");//this.Layers.Count.ToString()
- pdnImage.SetAttribute("savedWithVersion", this.SavedWithVersion.ToString(4));
- }
- ~Document()
- {
- Dispose(false);
- }
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- private bool disposed = false;
- private void Dispose(bool disposing)
- {
- if (!disposed)
- {
- if (disposing)
- {
- /*foreach (Layer layer in layers)
- {
- layer.Dispose();
- }*/
- }
- disposed = true;
- }
- }
- public Document Clone()
- {
- // I cheat.
- MemoryStream stream = new MemoryStream();
- SaveToStream(stream);
- stream.Seek(0, SeekOrigin.Begin);
- return (Document)Document.FromStream(stream);
- }
- object ICloneable.Clone()
- {
- return Clone();
- }
- }
- }
|