/* * RegionData.cs - Implementation of the * "System.Drawing.Drawing2D.RegionData" class. * * Copyright (C) 2003 Southern Storm Software, Pty Ltd. * Copyright (C) 2005 Free Software Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ using System.Collections; namespace System.Drawing.Drawing2D { public sealed class RegionData { // Public interface public byte[] Data { get { return data; } set { data = value; } } // Internal state. private byte[] data; /* * Flags & Constants */ // Basic regions private const Int32 REG_RECT = 0x10000000; private const Int32 REG_PATH = 0x10000001; private const Int32 REG_EMPTY = 0x10000002; private const Int32 REG_INF = 0x10000003; // Operation codes private const Int32 OP_INTERSECT = 0x00000001; private const Int32 OP_UNION = 0x00000002; private const Int32 OP_XOR = 0x00000003; private const Int32 OP_EXCLUDE = 0x00000004; private const Int32 OP_COMPLEMENT = 0x00000005; // Magic number private const Int32 _MAGIC_ = unchecked( (Int32) 0xDBC01001 ); // Format qualifiers for path data private const Int32 FMT_SHORT = 0x00004000; private const Int32 FMT_SINGLE = 0x00000000; // Array handling private const Int32 HEADER_SIZE = 0x00000010; private const Int32 CAP_MIN = 0x00000080; private const Int32 CAP_MAX = 0x7FFFFFFF; private const Int32 CAP_INC = 0x00000100; /* * Constructors, for internal use ( used by System.Drawing.Region ) */ // Construct region data for the infinite region internal RegionData() { InitializeInfiniteRegionData() ; } // Construct region data for a given rectangle internal RegionData( RectangleF rect ) : this() { Intersect( rect ); } // Construct region data for a given graphic path internal RegionData( GraphicsPath path ) : this() { Intersect( path ); } // Construct region data using another region's data. // The incoming region data is parsed and operations are evaluated // on the parent region. internal RegionData( Region region, RegionData regData ) { region = ParseEvaluate( region, regData.Data ); } /* * */ // Creational methods private void InitializeRegionData( RectangleF rect ) { int size = AddRegionRectangle( rect , StartRegionData() ) ; WriteHeader( size , 0 ); Resize( size ) ; } private void InitializeRegionData( GraphicsPath path ) { int size = AddRegionPath( path , StartRegionData() ) ; WriteHeader( size , 0 ); Resize( size ) ; } private void InitializeEmptyRegionData() { int size = AddRegionEmpty( StartRegionData() ) ; WriteHeader( size , 0 ); Resize( size ) ; } private void InitializeInfiniteRegionData() { int size = AddRegionInfinite( StartRegionData() ) ; WriteHeader( size , 0 ); Resize( size ) ; } /* * Unary Region operations */ internal void Transform ( Matrix matrix ) { } internal void Translate ( float dx, float dy ) { } /* * Binary Region operations */ internal void Intersect ( RectangleF rect ) { if ( IsEmpty() ) // bail out return; else if ( IsInfinite() ) // return the other region InitializeRegionData( rect ) ; else RegionOperation( OP_INTERSECT, REG_RECT, rect ); } internal void Union ( RectangleF rect ) { if ( IsEmpty() ) // return the other region InitializeRegionData( rect ) ; else if ( IsInfinite() ) // bail out return; else RegionOperation( OP_UNION, REG_RECT, rect ); } internal void Xor ( RectangleF rect ) { if ( IsEmpty() ) // return the other region InitializeRegionData( rect ) ; else RegionOperation( OP_XOR, REG_RECT, rect ); } internal void Exclude ( RectangleF rect ) { if ( IsEmpty() ) // bail out return ; else RegionOperation( OP_EXCLUDE, REG_RECT, rect ); } internal void Complement ( RectangleF rect ) { if ( IsEmpty() ) // return the other region InitializeRegionData( rect ) ; else if ( IsInfinite() ) // return the empty region InitializeEmptyRegionData() ; else RegionOperation( OP_COMPLEMENT, REG_RECT, rect ); } internal void Intersect ( GraphicsPath path ) { if ( IsEmpty() ) // bail out return; else if ( IsInfinite() ) // return the other region InitializeRegionData( path ) ; else RegionOperation( OP_INTERSECT, REG_PATH, path ); } internal void Union ( GraphicsPath path ) { if ( IsEmpty() ) // return the other region InitializeRegionData( path ) ; else if ( IsInfinite() ) // bail out return; else RegionOperation( OP_UNION, REG_PATH, path ); } internal void Xor ( GraphicsPath path ) { if ( IsEmpty() ) // return the other region InitializeRegionData( path ) ; else RegionOperation( OP_XOR, REG_PATH, path ); } internal void Exclude ( GraphicsPath path ) { if ( IsEmpty() ) // bail out return ; else RegionOperation( OP_EXCLUDE, REG_PATH, path ); } internal void Complement ( GraphicsPath path ) { if ( IsEmpty() ) // return the other region InitializeRegionData( path ) ; else if ( IsInfinite() ) // return the empty region InitializeEmptyRegionData() ; else RegionOperation( OP_COMPLEMENT, REG_PATH, path ); } internal void Intersect ( Region region ) { if ( IsEmpty() ) // bail out return; else if ( IsInfinite() ) // return the other region data = region.GetRegionData().Data ; else RegionOperation( OP_INTERSECT, -1, region ); } internal void Union ( Region region ) { if ( IsEmpty() ) // return the other region data = region.GetRegionData().Data ; else if ( IsInfinite() ) // bail out return; else RegionOperation( OP_UNION, -1, region ); } internal void Xor ( Region region ) { if ( IsEmpty() ) // return the other region data = region.GetRegionData().Data ; else RegionOperation( OP_XOR, -1, region ); } internal void Exclude ( Region region ) { if ( IsEmpty() ) // bail out return ; else if ( region.GetRegionData().IsEmpty() ) // bail out return; else if ( region.GetRegionData().IsInfinite() ) // return the empty region InitializeEmptyRegionData() ; else RegionOperation( OP_EXCLUDE, -1, region ); } internal void Complement ( Region region ) { if ( IsEmpty() ) // return the other region data = region.GetRegionData().Data ; else if ( region.GetRegionData().IsEmpty() ) // return the empty region InitializeEmptyRegionData() ; else if ( IsInfinite() ) // return the empty region InitializeEmptyRegionData() ; else RegionOperation( OP_COMPLEMENT, -1, region ); } // check if we are the infinite region internal bool IsInfinite() { return ( GetInt32( data, 12 ) == 0 ) && ( GetInt32( data, HEADER_SIZE ) == REG_INF ) ; } // check if we are the empty region internal bool IsEmpty() { return ( GetInt32( data, 12 ) == 0 ) && ( GetInt32( data, HEADER_SIZE ) == REG_EMPTY ) ; } private void RegionOperation( int opcode, int regtype, Object regionObject ) { // get actual number of operations int nrOps = GetInt32( data, 12 ) ; // truncate header from actual data int size = data.Length - HEADER_SIZE; byte[] bytes = new byte[size]; Array.Copy( data , HEADER_SIZE, bytes , 0, size ); // create a fresh region data int index = StartRegionData() ; // insert the new op code PutInt32( opcode, index ); index+=4; // add old data EnsureCapacity( index + size ) ; Array.Copy( bytes, 0, data, index, size ); index+=size; // insert new data, update number of operations switch (regtype) { case REG_RECT: index = AddRegionRectangle( (RectangleF) regionObject , index ); nrOps += 2 ; break; case REG_PATH: index = AddRegionPath( (GraphicsPath) regionObject , index ); nrOps += 2 ; break; default: index = AddRegion( (Region) regionObject ,index ); nrOps += GetInt32( ((Region) regionObject).GetRegionData().Data , 12 ) ; break; } WriteHeader( index, nrOps ) ; Resize ( index ) ; } /* * Private methods for updating * and reading internal state */ // Start a new region data. // Returns next free write index. private int StartRegionData() { data = new byte[CAP_MIN]; return HEADER_SIZE ; } /* * Tha data header format: * index description format * ----------------------------------------------------- * 00-03 size after checksum Int32 * 04-07 checksum ?? ?? TODO * 08-11 magic number Int32 * 12-15 2 * (nr. of bin. operations) Int32 */ // Call this procedure after all region data manipulations private void WriteHeader( int size, Int32 _2nrOps ) { PutInt32( size , 0 ) ; PutInt32( CheckSum() , 4 ) ; PutInt32( _MAGIC_ , 8 ) ; PutInt32( _2nrOps , 12 ) ; } private void Resize( int size ) { if ( size != data.Length ) { byte[] bytes = new byte[ size ]; Array.Copy( data , bytes , size ); data = bytes ; } } private void EnsureCapacity( int newsize ) { uint oldcap = (uint) data.Length ; uint newcap = (uint) newsize + (uint) CAP_INC - (uint) ( newsize % CAP_INC ) ; if ( newcap <= oldcap ) return; if ( newcap > CAP_MAX ) throw new OverflowException( "array too large" ); byte[] bytes = new byte[ newcap ]; Array.Copy( data , bytes , oldcap ); data = bytes ; } /* * Read/Write primitive data types */ // Put a byte at specified index private void PutByte( Byte value, int index ) { data[index] = value ; } // Read a byte from specified index private Byte GetByte( byte[] bts, int index ) { return bts[index] ; } // Put an unsigned short at specified index private void PutInt16( Int16 value, int index ) { byte[] bytes = BitConverter.GetBytes( value ) ; for ( ushort s = 0; s<2 ; s++ ) data[index+s] = bytes[s] ; } // Read an unsigned short from specified index private Int16 GetInt16( byte[] bts, int index ) { return BitConverter.ToInt16( bts , index ) ; } // Put an unsigned integer at specified index private void PutInt32( Int32 value, int index ) { byte[] bytes = BitConverter.GetBytes( value ) ; for ( ushort s = 0; s<4 ; s++ ) data[index+s] = bytes[s] ; } // Read an unsigned integer from specified index private Int32 GetInt32( byte[] bts, int index ) { return BitConverter.ToInt32( bts , index ) ; } // Put a single precision floating point at specified index using // IEEE floating point standard representation private void PutSingle( Single value, int index ) { byte[] bytes = BitConverter.GetBytes( value ) ; for ( ushort s = 0; s<4 ; s++ ) data[index+s] = bytes[s] ; } // Read a single precision floating point from specified index private Single GetSingle( byte[] bts, int index ) { return BitConverter.ToSingle( bts , index ) ; } // It looks like a checksum, but it could be everything else ... // Still an open issue [TODO] private void ValidateCheckSum() { // TODO // compute checksum and throw ArgumentException if not valid return ; } [TODO] private Int32 CheckSum() { // TODO return unchecked( (Int32) 0xFFFFFFFF ); } /* * Basic regions */ // Add a rectangle region at the given index. // Returns next free write index. private int AddRegionRectangle( RectangleF rect , int index ) { EnsureCapacity( index + 20 ); PutInt32 ( REG_RECT , index ); PutSingle( rect.X , index+=4 ); PutSingle( rect.Y , index+=4 ); PutSingle( rect.Width , index+=4 ); PutSingle( rect.Height , index+=4 ); return index+=4 ; } // Add a path region at s given index. // Returns next free write index. private int AddRegionPath( GraphicsPath path , int index ) { bool fmtShort = HasSimpleFormat( path ); int nrPoints = path.PointCount ; int pathSize = nrPoints * ( fmtShort ? 5 : 9 ) + 12 ; EnsureCapacity( index + 8 + pathSize ); PutInt32( REG_PATH , index ); PutInt32( pathSize , index+=4 ); PutInt32( _MAGIC_ , index+=4 ); PutInt32( nrPoints , index+=4 ); PutInt32( fmtShort ? FMT_SHORT : FMT_SINGLE , index+=4 ); if ( fmtShort ) { index+=2; for ( int i=0; i 0 ) { t = NextToken( regData ); if ( t.Type == Token.OP ) stack.Push( t ); else { if ( ((Token) stack.Peek()).Type == Token.OP ) stack.Push( t ); else { while ( ( numbOp > 0 ) && ( ((Token) stack.Peek()).Type == Token.DATA ) ) { Token s = (Token) stack.Pop() ; Token o = (Token) stack.Pop() ; t = o.Eval( s, t ); numbOp-- ; } stack.Push( t ); } } } return t.region ; } private class Token { public const int OP = 1; public const int DATA = 2; public byte[] rawData; public int Type; public Region region = new Region(); public Token Eval( Token a, Token b ) { if ( ! ( ( this.Type == Token.OP ) && ( a.Type == Token.DATA ) && ( b.Type == Token.DATA ) ) ) throw new InvalidOperationException(); Int32 whichOperation = BitConverter.ToInt32( this.rawData, 0 ); switch ( whichOperation ) { case OP_INTERSECT : a.region.Intersect( b.region ) ; break; case OP_UNION : a.region.Union( b.region ) ; break; case OP_XOR : a.region.Xor( b.region ) ; break; case OP_EXCLUDE : a.region.Exclude( b.region ) ; break; case OP_COMPLEMENT : a.region.Complement( b.region ) ; break; default: throw new InvalidOperationException(); } Token c = new Token(); c.Type = Token.DATA; int datSize = BitConverter.ToInt32( a.region.GetRegionData().Data , 0 ) - 4 ; byte[] dat = new byte[datSize] ; Array.Copy( a.region.GetRegionData().Data, HEADER_SIZE, dat, 0, datSize ) ; c.rawData = dat ; return c; } } private uint readPointer; private Token NextToken( byte[] regdat ) { Token t = new Token(); int length = 4; Int32 lookahead = GetInt32( regdat, (int)readPointer ); switch (lookahead) { case REG_RECT: t.Type = Token.DATA; length = 20 ; break; case REG_PATH : t.Type = Token.DATA; length = GetInt32( regdat, (int)(readPointer+4) ); break; case REG_EMPTY : t.Type = Token.DATA; break; case REG_INF : t.Type = Token.DATA; break; case OP_INTERSECT : t.Type = Token.OP; break; case OP_UNION : t.Type = Token.OP; break; case OP_XOR : t.Type = Token.OP; break; case OP_EXCLUDE : t.Type = Token.OP; break; case OP_COMPLEMENT : t.Type = Token.OP; break; default: throw new ArgumentException( "invalid token" ); break; } t.rawData = new byte[ length ]; Array.Copy( regdat, readPointer, t.rawData, 0, length ); readPointer += length ; return t; } }; // class RegionData }; // namespace System.Drawing.Drawing2D