Compact Frameworkで非矩形ウインドウ 2010

気がついたんだけど、Compact Frameworkで非矩形ウインドウで紹介していたShape.csはリンクが切れちゃってますね(´・ω・`)
っということで、同等品のソースをのせておきます(・∀・)


処理としてはDIBから矩形を作って、それをウインドウに適用する形になります。
っというわけで、まずはDIBを扱うクラスについて。

public struct DIBSection
{
    // BITMAPFILEHEADER
    public int OffBits;

    // BITMAPINFOHEADER
    public int Width;
    public int Height;
    public int BitCount;

    public int scLine;
    public byte[] Data;

    public int GetPixel(int x, int y)
    {
        switch( BitCount )
        {
            case 1:
            case 2:
            case 4:
            case 16:
            case 32:
                throw new NotSupportedException( "Supported only 8 or 24 bpp" );

            case 8:
                return Data[( Height - y - 1 ) * scLine + x];

            case 24:
                int pos = ( ( Height - y - 1 ) * scLine + x * 3 );
                return ( Data[ pos ] << 16 ) + ( Data[ pos + 1 ] << 8 ) + ( Data[ pos + 2 ] );

            default:
                throw new NotSupportedException( "Supported only 8 or 24 bpp" );
        }
    } 

    public static DIBSection GetDibData(Stream stream)
    {
        DIBSection data = new DIBSection();

        BinaryReader reader = new BinaryReader( stream );
        reader.BaseStream.Seek( 0, SeekOrigin.Begin );
        if( reader.ReadByte() != 'B' || reader.ReadByte() != 'M' )
        {
            throw new ArgumentException( "Invalid bitmap." ); 
        }

        reader.BaseStream.Seek( 10, SeekOrigin.Begin );
        data.OffBits = reader.ReadInt32();

        reader.ReadInt32(); // biSize

        data.Width = reader.ReadInt32();
        data.Height = reader.ReadInt32();

        reader.ReadInt16(); // biPlanes

        data.BitCount = reader.ReadInt16();

        reader.BaseStream.Seek( data.OffBits, SeekOrigin.Begin );
        data.scLine = ( data.Width * ( data.BitCount >> 3 ) + 3 >> 2 ) << 2;
        data.Data = reader.ReadBytes( data.scLine * data.Height ); 

        return data;
    }
}

っで、次に使用するAPIの定義をしておきます。

internal static class NativeMethods
{
    [DllImport( "coredll.dll", SetLastError = true )]
    internal static extern bool DeleteObject(IntPtr hObject);

    [DllImport( "coredll.dll", SetLastError = true )]
    internal static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);

    [DllImport( "coredll.dll", SetLastError = true )]
    internal static extern int CombineRgn(IntPtr hrgnDest, IntPtr hrgnSrc1, IntPtr hrgnSrc2, RgnCombineMode fnCombineMode);

    [DllImport( "coredll.dll", SetLastError = true )]
    internal static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, int bRedraw);
}

っということで、準備が出来たので矩形クラス。

public class Shape : IDisposable
{
    private IntPtr hRgn;

    public IntPtr Region
    {
        get { return this.hRgn; }
    } 

    public Shape(Stream stream)
    {
        RebuildRegion( stream, Color.Transparent );
    }

    public Shape(Stream stream, Color color)
    {
        RebuildRegion( stream, color );
    }

    ~Shape()
    {
        Dispose( false );
    }

    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    public void Dispose(bool disposing)
    {
        if( this.hRgn != IntPtr.Zero )
        {
            NativeMethods.DeleteObject( this.hRgn );
            this.hRgn = IntPtr.Zero;
        }
    }

    private void RebuildRegion(Stream stream, Color color)
    {
        if( stream == null )
        {
            throw new ArgumentNullException( "stream" ); 
        }

        if( this.hRgn != IntPtr.Zero )
        {
            NativeMethods.DeleteObject( this.hRgn ); 
        }

        DIBSection data = DIBSection.GetDibData( stream );

        if( data.BitCount != 24 )
        {
            throw new NotSupportedException( "Supported only 24 bpp" );
        }

        int mask;
        if( color == Color.Transparent )
        {
            mask = data.GetPixel( 0, 0 );
        }
        else
        {
            mask = ( color.B << 16 ) | ( color.G << 8 ) | color.R;
        }

        this.hRgn = NativeMethods.CreateRectRgn( 0, 0, 0, 0 ); 

        for( int y = 0; y < data.Height; y ++ ) 
        { 
            for( int x = 0; x < data.Width; x++ )
            {
                while( ( x < data.Width ) && ( data.GetPixel( x, y ) == mask ) )
                {
                    x++;
                }

                int left = x;

                while( ( x < data.Width ) && ( data.GetPixel( x, y ) != mask ) )
                {
                    x++;
                }

                if ( ( x - left ) > 0 )
                {
                    IntPtr hRgnRect = NativeMethods.CreateRectRgn( left, y, x, y + 1 );
                    NativeMethods.CombineRgn( this.hRgn, this.hRgn, hRgnRect, RgnCombineMode.RGN_OR );
                    NativeMethods.DeleteObject( hRgnRect );
                }
            }
        }
    }

    public void Apply(Control c)
    {
        if( c == null )
        {
            throw new ArgumentNullException( "c" );
        }

        NativeMethods.SetWindowRgn( c.Handle, this.Region, 1 );
    }
}

やっていることはDIBの各Pixelから矩形をCombineRgn()して作成。
あとはApply()で対象のControlに対して矩形を適用します。


っということで、丸ボタンやマスコットはこれで出来ますね(・∀・)


Mix10での情報を待ちながら〜(´∀`)