///////////////////////////////////////////////////////////////////////////////// // Paint.NET // // Copyright (C) Rick Brewster, Tom Jackson, and past contributors. // // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // // See src/Resources/Files/License.txt for full licensing and attribution // // details. // // . // ///////////////////////////////////////////////////////////////////////////////// using System; using System.Collections; using System.Collections.Specialized; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Runtime.Serialization; using System.Threading; namespace PaintDotNet { [Serializable] public class BitmapLayer : Layer, IDeserializationCallback { public override Surface RenderThumbnail(int maxEdgeLength) { Size thumbSize = Utility.ComputeThumbnailSize(this.Size, maxEdgeLength); Surface thumb = new Surface(thumbSize); thumb.SuperSamplingFitSurface(this.surface); Surface thumb2 = new Surface(thumbSize); thumb2.ClearWithCheckboardPattern(); UserBlendOps.NormalBlendOp nbop = new UserBlendOps.NormalBlendOp(); nbop.Apply(thumb2, thumb); thumb.Dispose(); thumb = null; return thumb2; } private bool disposed = false; protected override void Dispose(bool disposing) { if (!disposed) { disposed = true; try { if (disposing) { if (surface != null) { surface.Dispose(); surface = null; } } } finally { base.Dispose(disposing); } } } [NonSerialized] private BinaryPixelOp compiledBlendOp = null; private void CompileBlendOp() { bool isDefaultOp = (properties.blendOp.GetType() == UserBlendOps.GetDefaultBlendOp()); if (this.Opacity == 255) { this.compiledBlendOp = properties.blendOp; } else { this.compiledBlendOp = properties.blendOp.CreateWithOpacity(this.Opacity); } } protected override void OnPropertyChanged(string propertyName) { compiledBlendOp = null; base.OnPropertyChanged (propertyName); } [Serializable] internal sealed class BitmapLayerProperties : ICloneable, ISerializable { public UserBlendOp blendOp; internal int opacity; // this is ONLY used when loading older version PDN files! should normally equal -1 private const string blendOpTag = "blendOp"; private const string opacityTag = "opacity"; public static string BlendOpName { get { return PdnResources.GetString("BitmapLayer.Properties.BlendOp.Name"); } } public BitmapLayerProperties(UserBlendOp blendOp) { this.blendOp = blendOp; this.opacity = -1; } public BitmapLayerProperties(BitmapLayerProperties cloneMe) { this.blendOp = cloneMe.blendOp; this.opacity = -1; } public object Clone() { return new BitmapLayerProperties(this); } public BitmapLayerProperties(SerializationInfo info, StreamingContext context) { this.blendOp = (UserBlendOp)info.GetValue(blendOpTag, typeof(UserBlendOp)); // search for 'opacity' and load it if it exists this.opacity = -1; foreach (SerializationEntry entry in info) { if (entry.Name == opacityTag) { this.opacity = (int)((byte)entry.Value); break; } } } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(blendOpTag, this.blendOp); } } private BitmapLayerProperties properties; private Surface surface; public override object SaveProperties() { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } object baseProperties = base.SaveProperties(); return new List(properties.Clone(), new List(baseProperties, null)); } public override void LoadProperties(object oldState, bool suppressEvents) { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } List list = (List)oldState; // Get the base class' state, and our state LayerProperties baseState = (LayerProperties)list.Tail.Head; BitmapLayerProperties blp = (BitmapLayerProperties)(((List)oldState).Head); // Opacity is only couriered for compatibility with PDN v2.0 and v1.1 // files. It should not be present in v2.1+ files (well, it'll be // part of the base class' serialization) if (blp.opacity != -1) { baseState.opacity = (byte)blp.opacity; blp.opacity = -1; } // Have the base class load its properties base.LoadProperties(baseState, suppressEvents); // Now load our properties, and announce them to the world bool raiseBlendOp = false; if (blp.blendOp.GetType() != properties.blendOp.GetType()) { if (!suppressEvents) { raiseBlendOp = true; OnPropertyChanging(BitmapLayerProperties.BlendOpName); } } this.properties = (BitmapLayerProperties)blp.Clone(); this.compiledBlendOp = null; Invalidate(); if (raiseBlendOp) { OnPropertyChanged(BitmapLayerProperties.BlendOpName); } } public void SetBlendOp(UserBlendOp blendOp) { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } if (blendOp.GetType() != properties.blendOp.GetType()) { OnPropertyChanging(BitmapLayerProperties.BlendOpName); properties.blendOp = blendOp; compiledBlendOp = null; Invalidate(); OnPropertyChanged(BitmapLayerProperties.BlendOpName); } } public override object Clone() { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } return (object)new BitmapLayer(this); } public Surface Surface { get { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } return surface; } } public UserBlendOp BlendOp { get { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } return properties.blendOp; } } public BitmapLayer(int width, int height) : this(width, height, ColorBgra.FromBgra(255, 255, 255, 0)) { } public BitmapLayer(int width, int height, ColorBgra fillColor) : base(width, height) { this.surface = new Surface(width, height); // clear to see-through white, 0x00ffffff this.Surface.Clear(fillColor); this.properties = new BitmapLayerProperties(UserBlendOps.CreateDefaultBlendOp()); } /// /// Creates a new BitmapLayer of the same size as the given Surface, and copies the /// pixels from the given Surface. /// /// The Surface to copy pixels from. public BitmapLayer(Surface surface) : this(surface, false) { } /// /// Creates a new BitmapLayer of the same size as the given Surface, and either /// copies the pixels of the given Surface or takes ownership of it. /// /// The Surface. /// /// true to take ownership of the surface (make sure to Dispose() it yourself), or /// false to copy its pixels /// public BitmapLayer(Surface surface, bool takeOwnership) : base(surface.Width, surface.Height) { if (takeOwnership) { this.surface = surface; } else { this.surface = surface.Clone(); } this.properties = new BitmapLayerProperties(UserBlendOps.CreateDefaultBlendOp()); } protected BitmapLayer(BitmapLayer copyMe) : base(copyMe) { this.surface = copyMe.Surface.Clone(); this.properties = (BitmapLayerProperties)copyMe.properties.Clone(); } protected unsafe override void RenderImpl(RenderArgs args, Rectangle roi) { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } if (Opacity == 0) { return; } if (compiledBlendOp == null) { CompileBlendOp(); } for (int y = roi.Top; y < roi.Bottom; ++y) { ColorBgra *dstPtr = args.Surface.GetPointAddressUnchecked(roi.Left, y); ColorBgra *srcPtr = this.surface.GetPointAddressUnchecked(roi.Left, y); this.compiledBlendOp.Apply(dstPtr, srcPtr, roi.Width); } } protected unsafe override void RenderImpl(RenderArgs args, Rectangle[] rois, int startIndex, int length) { if (disposed) { throw new ObjectDisposedException("BitmapLayer"); } if (Opacity == 0) { return; } if (compiledBlendOp == null) { CompileBlendOp(); } for (int i = startIndex; i < startIndex + length; ++i) { Rectangle roi = rois[i]; for (int y = roi.Top; y < roi.Bottom; ++y) { ColorBgra *dstPtr = args.Surface.GetPointAddressUnchecked(roi.Left, y); ColorBgra *srcPtr = this.surface.GetPointAddressUnchecked(roi.Left, y); this.compiledBlendOp.Apply(dstPtr, srcPtr, roi.Width); } } } public override PdnBaseForm CreateConfigDialog() { BitmapLayerPropertiesDialog blpd = new BitmapLayerPropertiesDialog(); blpd.Layer = this; return blpd; } public void OnDeserialization(object sender) { if (this.properties.opacity != -1) { this.PushSuppressPropertyChanged(); base.Opacity = (byte)this.properties.opacity; this.properties.opacity = -1; this.PopSuppressPropertyChanged(); } this.compiledBlendOp = null; } } }