Collapse All Expand All Code: All Code: Multiple Code: C# Code: Visual Basic

C#
Visual Basic

Azuki 1.7 Documentation
Developper's Notes
Send Feedback

This page is "developper's notes."

These notes are written by developper for developper so basically worthy for only the developper but may worth for someone who is facing same problem as I've met.

(In this note, a term CF is used to indicate .NET Compact Framework, and a term FF is used to indicate .NET Framework.)

This topic contains the following sections.

- Optimization

- Buffer.BlockCopy and Array.Copy

- Resizing array on .NET Compact Framework
- Measuring string on .NET Compact Framework
- Drawing Selection
- Invalidation system in Windows.Forms GUI Toolkit
- Overwriting window procedure
- Line height and line spacing

# Optimization

These notes are about optimization.

### Buffer.BlockCopy and Array.Copy

I found some people is saying that Buffer.BlockCopy is faster than Array.Copy. But as far as I tested, Buffer.BlockCopy is not significantly faster than Array.Copy so Azuki uses Array.Copy.

# Resizing array on .NET Compact Framework

Array<T<.Resize does not exists on .NET Compact Framework so Azuki creates original resize logic for CF version. Note that if compared with Array<T>.Resize, my original logic is slightly slower so Azuki uses Array<T<.Resize for full version, and uses original logic for CF version.

# Measuring string on .NET Compact Framework

System.Drawing.Graphics.DrawString / MeasureString automatically adds padding before drawing/measuring so I can not get exact drawing/calculation result from them. Although specifiying System.Drawing.StringFormat.GenericTypographic to them removes the padding, CF does not support GenericTypographic so I cannot use it.

test code | Copy
--- | ---
                          using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;

    class Form1 : Form
    {
        static void Main()
        {
            Application.Run( new Form1() );
        }

        public Form1()
        {
            this.Load += Form_Load;
        }

        void Form_Load( object sender, EventArgs e )
        {
            this.Font = new Font( "MS Gothic", 14 );
            unsafe
            {
                SIZE nativeSize;

                using( Graphics g = CreateGraphics() )
                {
                    string[] strs = new string[]{"A", " ", "|", ".", "あ", ""};
                    foreach( string str in strs )
                    {
                        GetTextExtentExPointW(
                                GetDC(Handle), str, str.Length, 0, null, null, &nativeSize
                            );
                        Size s1 = new Size( nativeSize.width, nativeSize.height );
                        Size s2 = g.MeasureString( str, Font ).ToSize();
                        SizeF s3 = new SizeF();
                        StringFormat sf = StringFormat.GenericTypographic;
                        CharacterRange range = new CharacterRange( 0, str.Length );
                        sf.SetMeasurableCharacterRanges( new CharacterRange[]{range} );
                        Region[] regions = g.MeasureCharacterRanges( str, Font, new RectangleF(0,0,1000,1000), sf );
                        if( 0 < regions.Length )
                            s3 = regions[0].GetBounds(g).Size;

                        if( str == "あ" )
                            Console.WriteLine( "[{0}], {1}\t{2}\t{3}", str, s1, s2, s3 );
                        else if( str == "" )
                            Console.WriteLine( "[{0}]  , {1}\t{2}\t{3}", str, s1, s2, s3 );
                        else
                            Console.WriteLine( "[{0}] , {1}\t{2}\t{3}", str, s1, s2, s3 );
                    }
                }
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        struct SIZE
        {
            public Int32 width, height;
        }

        [DllImport("user32.dll")]
        static extern IntPtr GetDC( IntPtr hWnd );

        [DllImport("gdi32.dll", CharSet=CharSet.Unicode)]
        unsafe static extern Int32 GetTextExtentExPointW(
            IntPtr hdc, string text, int textLen, int maxWidth,
            int* out_fitLength, int* out_x, SIZE* out_size
        );
    }

The result when using "MS Gothic (Japanese fixed width font)" 14pt on Windows Vista is next.

result | Copy
--- | ---
    [A] , {Width=11, Height=18}     {Width=15, Height=21}   {Width=9.333334, Height=18.66667}
    [ ] , {Width=6, Height=18}      {Width=6, Height=21}    {Width=0, Height=0}
    [|] , {Width=3, Height=18}      {Width=15, Height=21}   {Width=9.333334, Height=18.66667}
    [.] , {Width=5, Height=18}      {Width=15, Height=21}   {Width=9.333334, Height=18.66667}
    [あ], {Width=16, Height=18}     {Width=25, Height=21}   {Width=18.66667, Height=18.66667}
    []  , {Width=0, Height=0}       {Width=0, Height=0}     {Width=0, Height=0}

There is no portable way to determine exact metric of drawn text in C#. Therefore I must use platform drawing APIs through P/Invoke.

# Drawing Selection

To represent "selection", there are two ways. First one is simply drawing selected text with specified background color. Second one is inverting the selected text area. To use specific background color for selected text has no impact on performance and easy to implement. To invert text area, selection rectangle must be calculated by measuring selected text once so there may be slight effect on performance. I have not tested the difference but it's obvious that first choice will not cause trouble. Therefore Azuki took first one.

# Invalidation system in Windows.Forms GUI Toolkit

For unknown reason, calling Control.Invalidate with invalid rectangle DOES NOT affect to the ClipBounds property of Graphics object given at Control.Paint method without enabling double buffering by using Control.SetStyle. Control.SetStyle is not included in Compact Framework so Azuki avoids to use framework's invalidation system.

# Overwriting window procedure

Control.WndProc does not exist in Compact Framework so I overwrite window procedure by calling SetWindowLong API in CF build. On the other hand, in x64 Vista, overwriting window procedure of .NET Control windows by SetWindowLong with GWL_WNDPROC always fail for unknown reason as far as I tested. Therefore I override Control.WndProc in desktop build (not CF build).

# Line height and line spacing

Line height is calculated as height of '\x3000' [px]. ('\x3000' is full-width space char.) Line spacing is the distance between each lines. The underline for highlighting current line is drawn 1px under the text so line spacing is larger than line height if highlighting is enabled.