SurfaceBox.cs :  » GUI » Paint.net » PaintDotNet » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » GUI » Paint.net 
Paint.net » PaintDotNet » SurfaceBox.cs
/////////////////////////////////////////////////////////////////////////////////
// Paint.NET                                                                   //
// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors.     //
// Portions Copyright (C) Microsoft Corporation. All Rights Reserved.          //
// See src/Resources/Files/License.txt for full licensing and attribution      //
// details.                                                                    //
// .                                                                           //
/////////////////////////////////////////////////////////////////////////////////

using PaintDotNet.SystemLayer;
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Forms;

namespace PaintDotNet{
    /// <summary>
    /// Renders a Surface to the screen.
    /// </summary>
    public sealed class SurfaceBox 
        : Control
    {
        public const int MaxSideLength = 32767;

        private int justPaintWhite = 0; // when this is non-zero, we just paint white (startup optimization)
        private ScaleFactor scaleFactor;
        private PaintDotNet.Threading.ThreadPool threadPool = new PaintDotNet.Threading.ThreadPool();
        private SurfaceBoxRendererList rendererList;
        private SurfaceBoxBaseRenderer baseRenderer;
        private Surface surface;

        // Each concrete instance of SurfaceBox holds a strong reference to a single
        // double buffer Surface. Statically, we store a weak reference to it. This way
        // when there are no more SurfaceBox instances, the double buffer Surface can
        // be cleaned up by the GC.
        private static WeakReference<Surface> doubleBufferSurfaceWeakRef = null;
        private Surface doubleBufferSurface = null;

        public SurfaceBoxRendererList RendererList
        {
            get
            {
                return this.rendererList;
            }
        }

        public Surface Surface
        {
            get
            {
                return this.surface;
            }

            set
            {
                this.surface = value;
                this.baseRenderer.Source = value;

                if (this.surface != null)
                {
                    // Maintain the scalefactor
                    this.Size = this.scaleFactor.ScaleSize(surface.Size);
                    this.rendererList.SourceSize = this.surface.Size;
                    this.rendererList.DestinationSize = this.Size;
                }

                Invalidate();
            }
        }

        [Obsolete("This functionality was moved to the DocumentView class", true)]
        public bool DrawGrid 
        {
            get 
            {
                return false;
            }

            set 
            {
            }
        }

        public void FitToSize(Size fit)
        {
            ScaleFactor newSF = ScaleFactor.Min(fit.Width, surface.Width,
                                                fit.Height, surface.Height,
                                                ScaleFactor.MinValue);

            this.scaleFactor = newSF;
            this.Size = this.scaleFactor.ScaleSize(surface.Size);
        }

        public void RenderTo(Surface dst)
        {
            dst.Clear(ColorBgra.Transparent);

            if (this.surface != null)
            {
                SurfaceBoxRendererList sbrl = new SurfaceBoxRendererList(this.surface.Size, dst.Size);
                SurfaceBoxBaseRenderer sbbr = new SurfaceBoxBaseRenderer(sbrl, this.surface);
                sbrl.Add(sbbr, true);
                sbrl.Render(dst, new Point(0, 0));
                sbrl.Remove(sbbr);
            }
        }

        /// <summary>
        /// Increments the "just paint white" counter. When this counter is non-zero,
        /// the OnPaint() method will only paint white. This is used as an optimization
        /// during Paint.NET's startup so that it doesn't have to touch all the pages
        /// of the blank document's layer.
        /// </summary>
        public void IncrementJustPaintWhite()
        {
            ++this.justPaintWhite;
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            // This code fixes the size of the surfaceBox as necessary to 
            // maintain the aspect ratio of the surface. Keeping the mouse
            // within 32767 is delegated to the new overflow-checking code
            // in Tool.cs.

            Size mySize = this.Size;
            if (this.Width == MaxSideLength && surface != null)
            { 
                // Windows forms probably clamped this control's width, so we have to fix the height.
                mySize.Height = (int)(((long)(MaxSideLength + 1) * (long)surface.Height) / (long)surface.Width);
            }
            else if (mySize.Width == 0)
            {
                mySize.Width = 1;
            }

            if (this.Width == MaxSideLength && surface != null)
            { 
                // Windows forms probably clamped this control's height, so we have to fix the width.
                mySize.Width = (int)(((long)(MaxSideLength + 1) * (long)surface.Width) / (long)surface.Height);
            }
            else if (mySize.Height == 0) 
            {
                mySize.Height = 1;
            }

            if (mySize != this.Size) 
            {
                this.Size = mySize;
            }
           
            if (surface == null)
            {
                this.scaleFactor = ScaleFactor.OneToOne;
            }
            else
            {
                ScaleFactor newSF = ScaleFactor.Max(this.Width, surface.Width,
                                                    this.Height, surface.Height,
                                                    ScaleFactor.OneToOne);

                this.scaleFactor = newSF;
            }

            this.rendererList.DestinationSize = this.Size;
        }

        public ScaleFactor ScaleFactor
        {
            get
            {
                return this.scaleFactor;
            }
        }

        public SurfaceBox()
        {
            InitializeComponent();
            this.scaleFactor = ScaleFactor.OneToOne;

            this.rendererList = new SurfaceBoxRendererList(this.Size, this.Size);
            this.rendererList.Invalidated += new InvalidateEventHandler(Renderers_Invalidated);
            this.baseRenderer = new SurfaceBoxBaseRenderer(this.rendererList, null);
            this.rendererList.Add(this.baseRenderer, false);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.baseRenderer != null)
                {
                    this.rendererList.Remove(this.baseRenderer);
                    this.baseRenderer.Dispose();
                    this.baseRenderer = null;
                }

                if (this.doubleBufferSurface != null)
                {
                    this.doubleBufferSurface.Dispose();
                    this.doubleBufferSurface = null;
                }
            }

            base.Dispose(disposing);
        }

        /// <summary>
        /// This event is raised after painting has been performed. This is required because
        /// the normal Paint event is raised *before* painting has been performed.
        /// </summary>
        public event PaintEventHandler2 Painted;
        private void OnPainted(PaintEventArgs2 e)
        {
            if (Painted != null)
            {
                Painted(this, e);
            }
        }

        public event PaintEventHandler2 PrePaint;
        private void OnPrePaint(PaintEventArgs2 e)
        {
            if (PrePaint != null)
            {
                PrePaint(this, e);
            }
        }

        private Surface GetDoubleBuffer(Size size)
        {
            Surface localDBSurface = null;
            Size oldSize = new Size(0, 0);

            // If we already have a double buffer surface reference, but if that surface
            // is already disposed then don't worry about it.
            if (this.doubleBufferSurface != null && this.doubleBufferSurface.IsDisposed)
            {
                oldSize = this.doubleBufferSurface.Size;
                this.doubleBufferSurface = null;
            }

            // If we already have a double buffer surface reference, but if that surface
            // is too small, then nuke it.
            if (this.doubleBufferSurface != null && 
                (this.doubleBufferSurface.Width < size.Width || this.doubleBufferSurface.Height < size.Height))
            {
                oldSize = this.doubleBufferSurface.Size;
                this.doubleBufferSurface.Dispose();
                this.doubleBufferSurface = null;
                doubleBufferSurfaceWeakRef = null;
            }

            // If we don't have a double buffer, then we'd better get one.
            if (this.doubleBufferSurface != null)
            {
                // Got one!
                localDBSurface = this.doubleBufferSurface;
            }
            else if (doubleBufferSurfaceWeakRef != null)
            {
                // First, try to get the one that's already shared amongst all SurfaceBox instances.
                localDBSurface = doubleBufferSurfaceWeakRef.Target;

                // If it's disposed, then forget about it.
                if (localDBSurface != null && localDBSurface.IsDisposed)
                {
                    oldSize = localDBSurface.Size;
                    localDBSurface = null;
                    doubleBufferSurfaceWeakRef = null;
                }
            }

            // Make sure the surface is big enough.
            if (localDBSurface != null && (localDBSurface.Width < size.Width || localDBSurface.Height < size.Height))
            {
                oldSize = localDBSurface.Size;
                localDBSurface.Dispose();
                localDBSurface = null;
                doubleBufferSurfaceWeakRef = null;
            }

            // So, do we have a surface? If not then we'd better make one.
            if (localDBSurface == null)
            {
                Size newSize = new Size(Math.Max(size.Width, oldSize.Width), Math.Max(size.Height, oldSize.Height));
                localDBSurface = new Surface(newSize.Width, newSize.Height);
                doubleBufferSurfaceWeakRef = new WeakReference<Surface>(localDBSurface);
            }

            this.doubleBufferSurface = localDBSurface;
            Surface window = localDBSurface.CreateWindow(0, 0, size.Width, size.Height);
            return window;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.surface != null)
            {
                PdnRegion clipRegion = null;
                Rectangle[] rects = this.realUpdateRects;
                
                if (rects == null)
                {
                    clipRegion = new PdnRegion(e.Graphics.Clip, true);
                    clipRegion.Intersect(e.ClipRectangle);
                    rects = clipRegion.GetRegionScansReadOnlyInt();
                }

                if (this.justPaintWhite > 0)
                {
                    PdnGraphics.FillRectangles(e.Graphics, Color.White, rects);
                }
                else
                {
                    foreach (Rectangle rect in rects)
                    {
                        if (e.Graphics.IsVisible(rect))
                        {
                            PaintEventArgs2 e2 = new PaintEventArgs2(e.Graphics, rect);
                            OnPaintImpl(e2);
                        }
                    }
                }

                if (clipRegion != null)
                {
                    clipRegion.Dispose();
                    clipRegion = null;
                }
            }

            if (this.justPaintWhite > 0)
            {
                --this.justPaintWhite;
            }

            base.OnPaint(e);
        }

        private void OnPaintImpl(PaintEventArgs2 e)
        {
            using (Surface doubleBuffer = GetDoubleBuffer(e.ClipRectangle.Size))
            {
                using (RenderArgs renderArgs = new RenderArgs(doubleBuffer))
                {
                    OnPrePaint(e);
                    DrawArea(renderArgs, e.ClipRectangle.Location);
                    OnPainted(e);

                    IntPtr tracking;
                    Point childOffset;
                    Size parentSize;
                    doubleBuffer.GetDrawBitmapInfo(out tracking, out childOffset, out parentSize);

                    PdnGraphics.DrawBitmap(e.Graphics, e.ClipRectangle, e.Graphics.Transform,
                        tracking, childOffset.X, childOffset.Y);
                }
            }
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            // do nothing so as to avoid flicker
            // tip: for debugging, uncomment the next line!
            //base.OnPaintBackground(pevent);
        }

        private class RenderContext
        {
            public Surface[] windows;
            public Point[] offsets;
            public Rectangle[] rects;
            public SurfaceBox owner;
            public WaitCallback waitCallback;

            public void RenderThreadMethod(object indexObject)
            {
                int index = (int)indexObject;
                this.owner.rendererList.Render(windows[index], offsets[index]);
                this.windows[index].Dispose();
                this.windows[index] = null;
            }
        }

        private RenderContext renderContext;

        /// <summary>
        /// Draws an area of the SurfaceBox.
        /// </summary>
        /// <param name="ra">The rendering surface object to draw to.</param>
        /// <param name="offset">The virtual offset of ra, in client (destination) coordinates.</param>
        /// <remarks>
        /// If drawing to ra.Surface or ra.Bitmap, copy the roi of the source surface to (0,0) of ra.Surface or ra.Bitmap
        /// If drawing to ra.Graphics, copy the roi of the surface to (roi.X, roi.Y) of ra.Graphics
        /// </remarks>
        private unsafe void DrawArea(RenderArgs ra, Point offset)
        {
            if (surface == null)
            {
                return;
            }

            if (renderContext == null || (renderContext.windows != null && renderContext.windows.Length != Processor.LogicalCpuCount))
            {
                renderContext = new RenderContext();
                renderContext.owner = this;
                renderContext.waitCallback = new WaitCallback(renderContext.RenderThreadMethod);
                renderContext.windows = new Surface[Processor.LogicalCpuCount];
                renderContext.offsets = new Point[Processor.LogicalCpuCount];
                renderContext.rects = new Rectangle[Processor.LogicalCpuCount];
            }

            Utility.SplitRectangle(ra.Bounds, renderContext.rects);

            for (int i = 0; i < renderContext.rects.Length; ++i)
            {
                if (renderContext.rects[i].Width > 0 && renderContext.rects[i].Height > 0)
                {
                    renderContext.offsets[i] = new Point(renderContext.rects[i].X + offset.X, renderContext.rects[i].Y + offset.Y);
                    renderContext.windows[i] = ra.Surface.CreateWindow(renderContext.rects[i]);
                }
                else
                {
                    renderContext.windows[i] = null;
                }
            }
            
            for (int i = 0; i < renderContext.windows.Length; ++i)
            {
                if (renderContext.windows[i] != null)
                {
                    this.threadPool.QueueUserWorkItem(renderContext.waitCallback, BoxedConstants.GetInt32(i));
                }
            }

            this.threadPool.Drain();
        }

        private Rectangle[] realUpdateRects = null;
        protected override void WndProc(ref Message m)
        {
            IntPtr preR = m.Result;

            // Ignore focus
            if (m.Msg == 7 /* WM_SETFOCUS */)
            {
                return;
            }
            else if (m.Msg == 0x000f /* WM_PAINT */)
            {
                this.realUpdateRects = UI.GetUpdateRegion(this);

                if (this.realUpdateRects != null &&
                    this.realUpdateRects.Length >= 5) // '5' chosen arbitrarily
                {
                    this.realUpdateRects = null;
                }

                base.WndProc(ref m);
            }
            else
            {
                base.WndProc (ref m);
            }
        }

        /// <summary>
        /// Converts from control client coordinates to surface coordinates
        /// This is useful when this.Bounds != surface.Bounds (i.e. some sort of zooming is in effect)
        /// </summary>
        /// <param name="clientPt"></param>
        /// <returns></returns>
        public PointF ClientToSurface(PointF clientPt)
        {
            return ScaleFactor.UnscalePoint(clientPt);
        }

        public Point ClientToSurface(Point clientPt)
        {
            return ScaleFactor.UnscalePoint(clientPt);
        }

        public SizeF ClientToSurface(SizeF clientSize)
        {
            return ScaleFactor.UnscaleSize(clientSize);
        }

        public Size ClientToSurface(Size clientSize)
        {
            return Size.Round(ClientToSurface((SizeF)clientSize));
        }

        public RectangleF ClientToSurface(RectangleF clientRect)
        {
            return new RectangleF(ClientToSurface(clientRect.Location), ClientToSurface(clientRect.Size));
        }

        public Rectangle ClientToSurface(Rectangle clientRect)
        {
            return new Rectangle(ClientToSurface(clientRect.Location), ClientToSurface(clientRect.Size));
        }

        public PointF SurfaceToClient(PointF surfacePt)
        {
            return ScaleFactor.ScalePoint(surfacePt);
        }

        public Point SurfaceToClient(Point surfacePt)
        {
            return ScaleFactor.ScalePoint(surfacePt);
        }

        public SizeF SurfaceToClient(SizeF surfaceSize)
        {
            return ScaleFactor.ScaleSize(surfaceSize);
        }

        public Size SurfaceToClient(Size surfaceSize)
        {
            return Size.Round(SurfaceToClient((SizeF)surfaceSize));
        }

        public RectangleF SurfaceToClient(RectangleF surfaceRect)
        {
            return new RectangleF(SurfaceToClient(surfaceRect.Location), SurfaceToClient(surfaceRect.Size));
        }        

        public Rectangle SurfaceToClient(Rectangle surfaceRect)
        {
            return new Rectangle(SurfaceToClient(surfaceRect.Location), SurfaceToClient(surfaceRect.Size));
        }

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
        }

        private void Renderers_Invalidated(object sender, InvalidateEventArgs e)
        {
            Rectangle rect = SurfaceToClient(e.InvalidRect);
            rect.Inflate(1, 1);
            Invalidate(rect);
        }
    }
}

www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.