using Resources; using SmartCoalApplication.Base; using SmartCoalApplication.Base.Enum; using System; using System.Collections.Specialized; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Windows.Forms; namespace SmartCoalApplication.Core { [Serializable] public sealed class Document : IDisposable { public Surface surface; private NameValueCollection userMetaData; private int width; private int height; private Threading.ThreadPool threadPool = new Threading.ThreadPool(); private Vector updateRegion; private bool dirty; [NonSerialized] private Metadata metadata = null; public Metadata Metadata { get { if (metadata == null) { metadata = new Metadata(userMetaData); } return metadata; } } public MeasurementUnit DpuUnit { get { return DefaultDpuUnit; /*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.Centimeter: case MeasurementUnit.Millimeter: case MeasurementUnit.Micron: case MeasurementUnit.Nano: return (MeasurementUnit)unit; default: this.Metadata.RemoveExifValues(ExifTagID.ResolutionUnit); return this.DpuUnit; } } catch (Exception) { this.Metadata.RemoveExifValues(ExifTagID.ResolutionUnit); return this.DpuUnit; } }*/ } 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; } } /// /// 1英寸 = 2.54厘米 /// public static double CmPerInch = 2.54; /// /// 默认值 = dpi / 单位 /// public double defaultDpcm; public const double MinimumDpu = 0.01; public const double MaximumDpu = 32767.0; public static double GetDefaultDpu(MeasurementUnit units) { double dpu; switch (units) { case MeasurementUnit.Inch: dpu = defaultDpi; 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; } public double DpuX { get { return this.DpuX; /*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"); } Dirty = true; } } public double DpuY { get { return this.DpuY; /*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(); } else { return Math.Min(MaximumDpu, Math.Max(MinimumDpu, (double)numerator / (double)denominator)); } } catch { return this.DpuY; } }*/ } 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"); } Dirty = true; } } /// /// 像素换算到物理长度 /// /// /// /// 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; } /// /// 像素换算到物理长度 /// /// /// /// 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.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 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.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) { 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"); } } [field: NonSerialized] public event EventHandler DirtyChanged; private void OnDirtyChanged() { if (DirtyChanged != null) { DirtyChanged(this, EventArgs.Empty); } } 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(); } } } public int Width { get { return width; } } public int Height { get { return height; } } public Size Size { get { return new Size(Width, Height); } } public Document(int width, int height) { this.width = width; this.height = height; this.Dirty = true; this.updateRegion = new Vector(); Invalidate(); } [field: NonSerialized] public event InvalidateEventHandler Invalidated; private void OnInvalidated(InvalidateEventArgs e) { if (Invalidated != null) { Invalidated(this, e); } } public void Invalidate() { Dirty = true; Rectangle rect = new Rectangle(0, 0, Width, Height); updateRegion.Clear(); updateRegion.Add(rect); OnInvalidated(new InvalidateEventArgs(rect)); } /// /// Creates a document that consists of one BitmapLayer. /// /// The Image to make a copy of that will be the first layer ("Background") in the document. 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); } } // 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 FromMat(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)); // 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); } } document.DpuUnit = MeasurementUnit.Inch; document.DpuX = image.HorizontalResolution; document.DpuY = image.VerticalResolution; document.surface = surface; document.Invalidate(); return document; } ~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 void Render(RenderArgs args, bool clearBackground) { Render(args, args.Surface.Bounds, clearBackground); } /// /// Renders a requested region of the document. Will clear the background of the input /// before rendering if requested. /// /// Contains information used to control where rendering occurs. /// The rectangular region to render. /// If true, 'args' will be cleared to zero before rendering. 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); } }*/ } public static double DotsPerCmToDotsPerInch(double dpcm) { return dpcm * CmPerInch; } } }