// Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2004 Novell, Inc. // // Authors: // Peter Bartok pbartok@novell.com // // // $Revision: 1.1 $ // $Modtime: $ // $Log: ControlPaint.cs,v $ // Revision 1.1 2004/07/09 05:21:25 pbartok // - Initial check-in // // // NOT COMPLETE using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; namespace System.Windows.Forms { public sealed class ControlPaint { #region Local Variables static int RGBMax=255; static int HLSMax=255; #endregion // Local Variables #region Private Enumerations private enum DrawFrameControlTypes { Caption = 1, Menu = 2, Scroll = 3, Button = 4 } private enum DrawFrameControlStates { ButtonCheck = 0x0000, ButtonRadioImage = 0x0001, ButtonRadioMask = 0x0002, ButtonRadio = 0x0004, Button3State = 0x0008, ButtonPush = 0x0010, CaptionClose = 0x0000, CaptionMin = 0x0001, CaptionMax = 0x0002, CaptionRestore = 0x0004, CaptionHelp = 0x0008, MenuArrow = 0x0000, MenuCheck = 0x0001, MenuBullet = 0x0002, MenuArrowRight = 0x0004, ScrollUp = 0x0000, ScrollDown = 0x0001, ScrollLeft = 0x0002, ScrollRight = 0x0003, ScrollComboBox = 0x0005, ScrollSizeGrip = 0x0008, ScrollSizeGripRight = 0x0010, Inactive = 0x0100, Pushed = 0x0200, Checked = 0x0400, Transparent = 0x0800, Hot = 0x1000, AdjustRect = 0x2000, Flat = 0x4000, Mono = 0x8000 } #endregion // Private Enumerations #region Helpers private static Color Win32ToColor(int Win32Color) { return(Color.FromArgb( (int)(Win32Color) & 0xff0000 >> 16, // blue (int)(Win32Color) & 0xff00 >> 8, // green (int)(Win32Color) & 0xff // red )); } internal static void Color2HBS(Color color, out int h, out int l, out int s) { int r; int g; int b; int cMax; int cMin; int rDelta; int gDelta; int bDelta; r=color.R; g=color.G; b=color.B; cMax = Math.Max(Math.Max(r, g), b); cMin = Math.Min(Math.Min(r, g), b); l = (((cMax+cMin)*HLSMax)+RGBMax)/(2*RGBMax); if (cMax==cMin) { // Achromatic h=0; // h undefined s=0; l=r; return; } /* saturation */ if (l<=(HLSMax/2)) { s=(((cMax-cMin)*HLSMax)+((cMax+cMin)/2))/(cMax+cMin); } else { s=(((cMax-cMin)*HLSMax)+((2*RGBMax-cMax-cMin)/2))/(2*RGBMax-cMax-cMin); } /* hue */ rDelta=(((cMax-r)*(HLSMax/6))+((cMax-cMin)/2))/(cMax-cMin); gDelta=(((cMax-g)*(HLSMax/6))+((cMax-cMin)/2))/(cMax-cMin); bDelta=(((cMax-b)*(HLSMax/6))+((cMax-cMin)/2))/(cMax-cMin); if (r == cMax) { h=bDelta - gDelta; } else if (g == cMax) { h=(HLSMax/3) + rDelta - bDelta; } else { /* B == cMax */ h=((2*HLSMax)/3) + gDelta - rDelta; } if (h<0) { h+=HLSMax; } if (h>HLSMax) { h-=HLSMax; } } private static int HueToRGB(int n1, int n2, int hue) { if (hue<0) { hue+=HLSMax; } if (hue>HLSMax) { hue -= HLSMax; } /* return r,g, or b value from this tridrant */ if (hue<(HLSMax/6)) { return(n1+(((n2-n1)*hue+(HLSMax/12))/(HLSMax/6))); } if (hue<(HLSMax/2)) { return(n2); } if (hue<((HLSMax*2)/3)) { return(n1+(((n2-n1)*(((HLSMax*2)/3)-hue)+(HLSMax/12))/(HLSMax/6))); } else { return(n1); } } private static Color HBS2Color(int hue, int lum, int sat) { int R; int G; int B; int Magic1; int Magic2; if (sat == 0) { /* Achromatic */ R=G=B=(lum*RGBMax)/HLSMax; // FIXME : Should throw exception if hue!=0 } else { if (lum<=(HLSMax/2)) { Magic2=(lum*(HLSMax+sat)+(HLSMax/2))/HLSMax; } else { Magic2=sat+lum-((sat*lum)+(HLSMax/2))/HLSMax; } Magic1=2*lum-Magic2; R = Math.Min(255, (HueToRGB(Magic1,Magic2,hue+(HLSMax/3))*RGBMax+(HLSMax/2))/HLSMax); G = Math.Min(255, (HueToRGB(Magic1,Magic2,hue)*RGBMax+(HLSMax/2))/HLSMax); B = Math.Min(255, (HueToRGB(Magic1,Magic2,hue-(HLSMax/3))*RGBMax+(HLSMax/2))/HLSMax); } return(Color.FromArgb(R, G, B)); } #endregion // Helpers #region Public Static Properties public static Color ContrastControlDark { get { return(SystemColors.ControlDark); } } #endregion // Public Static Properties #region Public Static Methods public static IntPtr CreateHBitmap16Bit(Bitmap bitmap, Color background){ throw new NotImplementedException (); } public static IntPtr CreateHBitmapColorMask(Bitmap bitmap, IntPtr monochromeMask){ throw new NotImplementedException (); } public static IntPtr CreateHBitmapTransparencyMask(Bitmap bitmap){ throw new NotImplementedException (); } public static Color Light(Color baseColor) { return Light( baseColor, 10.0f); } public static Color Light(Color baseColor,float percOfLightLight) { int H, I, S; ControlPaint.Color2HBS(baseColor, out H, out I, out S); int NewIntensity = Math.Min( 255, I + ((255*(int)percOfLightLight)/100)); return ControlPaint.HBS2Color(H, NewIntensity, S); } public static Color LightLight(Color baseColor) { return Light( baseColor, 20.0f); } public static Color Dark(Color baseColor) { return Dark(baseColor, 10.0f); } public static Color Dark(Color baseColor,float percOfDarkDark) { int H, I, S; ControlPaint.Color2HBS(baseColor, out H, out I, out S); int NewIntensity = Math.Max(0, I - ((255*(int)percOfDarkDark) / 100)); return ControlPaint.HBS2Color(H, NewIntensity, S); } public static Color DarkDark(Color baseColor) { return Dark(baseColor, 20.0f); } public static void DrawBorder(Graphics graphics, Rectangle bounds, Color color, ButtonBorderStyle style) { DrawBorder(graphics, bounds, color, 1, style, color, 1, style, color, 1, style, color, 1, style); } public static void DrawBorder( Graphics graphics, Rectangle bounds, Color leftColor, int leftWidth, ButtonBorderStyle leftStyle, Color topColor, int topWidth, ButtonBorderStyle topStyle, Color rightColor, int rightWidth, ButtonBorderStyle rightStyle, Color bottomColor, int bottomWidth, ButtonBorderStyle bottomStyle) { DrawBorderInternal(graphics, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom-1, leftWidth, leftColor, leftStyle, Border3DSide.Left); DrawBorderInternal(graphics, bounds.Left, bounds.Top, bounds.Right-1, bounds.Top, topWidth, topColor, topStyle, Border3DSide.Top); DrawBorderInternal(graphics, bounds.Right-1, bounds.Top, bounds.Right-1, bounds.Bottom-1, rightWidth, rightColor, rightStyle, Border3DSide.Right); DrawBorderInternal(graphics, bounds.Left, bounds.Bottom-1, bounds.Right-1, bounds.Bottom-1, bottomWidth, bottomColor, bottomStyle, Border3DSide.Bottom); } private static void DrawBorderInternal(Graphics graphics, int startX, int startY, int endX, int endY, int width, Color color, ButtonBorderStyle style, Border3DSide side) { Pen pen=new Pen(color, 1); switch(style) { case ButtonBorderStyle.Solid: { pen.DashStyle=DashStyle.Solid; break; } case ButtonBorderStyle.Dashed: { pen.DashStyle=DashStyle.Dash; break; } case ButtonBorderStyle.Dotted: { pen.DashStyle=DashStyle.Dot; break; } case ButtonBorderStyle.Inset: { pen.DashStyle=DashStyle.Solid; break; } case ButtonBorderStyle.Outset: { pen.DashStyle=DashStyle.Solid; break; } default: case ButtonBorderStyle.None: { pen.Dispose(); return; } } switch(style) { case ButtonBorderStyle.Outset: { Color colorGrade; int hue, brightness, saturation; int brightnessSteps; int brightnessDownSteps; Color2HBS(color, out hue, out brightness, out saturation); brightnessDownSteps=brightness/width; if (brightness>127) { brightnessSteps=Math.Max(6, (160-brightness)/width); } else { brightnessSteps=(127-brightness)/width; } for (int i=0; i127) { brightnessSteps=Math.Max(6, (160-brightness)/width); } else { brightnessSteps=(127-brightness)/width; } for (int i=0; i127) { foreColor=Color.Black; } else { foreColor=Color.White; } #if false /* Commented out until I take the time and figure out which HatchStyle will match requirements. The code below is only correct for Percent50. */ if (pixelsBetweenDots.Width==pixelsBetweenDots.Height) { HatchBrush brush=null; switch(pixelsBetweenDots.Width) { case 2: brush=new HatchBrush(HatchStyle.Percent50, foreColor, backColor); break; case 4: brush=new HatchBrush(HatchStyle.Percent25, foreColor, backColor); break; case 5: brush=new HatchBrush(HatchStyle.Percent20, foreColor, backColor); break; default: { /* Have to do it the slow way */ break; } } if (brush!=null) { graphics.FillRectangle(brush, area); pen.Dispose(); brush.Dispose(); return; } } #endif /* Slow method */ Bitmap bitmap = new Bitmap(area.Width, area.Height, graphics); for (int x=0; x 256 colors on the display. */ ImageAttributes imageAttributes=new ImageAttributes(); ColorMatrix colorMatrix=new ColorMatrix(new float[][] { // This table would create a perfect grayscale image, based on luminance // new float[]{0.3f,0.3f,0.3f,0,0}, // new float[]{0.59f,0.59f,0.59f,0,0}, // new float[]{0.11f,0.11f,0.11f,0,0}, // new float[]{0,0,0,1,0,0}, // new float[]{0,0,0,0,1,0}, // new float[]{0,0,0,0,0,1} // This table generates a image that is grayscaled and then // brightened up. Seems to match MS close enough. new float[]{0.2f,0.2f,0.2f,0,0}, new float[]{0.41f,0.41f,0.41f,0,0}, new float[]{0.11f,0.11f,0.11f,0,0}, new float[]{0.15f,0.15f,0.15f,1,0,0}, new float[]{0.15f,0.15f,0.15f,0,1,0}, new float[]{0.15f,0.15f,0.15f,0,0,1} }); imageAttributes.SetColorMatrix(colorMatrix); graphics.DrawImage(image, new Rectangle(x, y, image.Width, image.Height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes); imageAttributes.Dispose(); } public static void DrawLockedFrame(Graphics graphics, Rectangle rectangle, bool primary) { Pen penBorder; Pen penInside; if (primary) { penBorder=new Pen(Color.White, 2); penInside=new Pen(Color.Black, 1); } else { penBorder=new Pen(Color.Black, 2); penInside=new Pen(Color.White, 1); } penBorder.Alignment=PenAlignment.Inset; penInside.Alignment=PenAlignment.Inset; graphics.DrawRectangle(penBorder, rectangle); graphics.DrawRectangle(penInside, rectangle.X+2, rectangle.Y+2, rectangle.Width-5, rectangle.Height-5); penBorder.Dispose(); penInside.Dispose(); } public static void DrawMenuGlyph(Graphics graphics, Rectangle rectangle, MenuGlyph glyph) { Rectangle rect; int lineWidth; // MS seems to draw the background white graphics.FillRectangle(new SolidBrush(Color.White), rectangle); switch(glyph) { case MenuGlyph.Arrow: { Point[] arrow = new Point[3]; Point P1; Point P2; Point P3; int centerX; int centerY; int shiftX; int shiftY; rect=new Rectangle(rectangle.X+rectangle.Width/4, rectangle.Y+rectangle.Height/4, rectangle.Width/2, rectangle.Height/2); centerX=rect.Left+rect.Width/2; centerY=rect.Top+rect.Height/2; shiftX=Math.Max(1, rect.Width/8); shiftY=Math.Max(1, rect.Height/8); rect.X-=shiftX; centerX-=shiftX; P1=new Point(centerX, rect.Top-1); P2=new Point(centerX, rect.Bottom); P3=new Point(rect.Right, centerY); arrow[0]=P1; arrow[1]=P2; arrow[2]=P3; graphics.FillPolygon(SystemBrushes.ControlText, arrow, FillMode.Winding); return; } case MenuGlyph.Bullet: { SolidBrush sb; lineWidth=Math.Max(2, rectangle.Width/3); rect=new Rectangle(rectangle.X+lineWidth, rectangle.Y+lineWidth, rectangle.Width-lineWidth*2, rectangle.Height-lineWidth*2); sb=new SolidBrush(SystemColors.MenuText); graphics.FillEllipse(sb, rect); sb.Dispose(); return; } case MenuGlyph.Checkmark: { int Scale; lineWidth=Math.Max(2, rectangle.Width/6); Scale=Math.Max(1, rectangle.Width/12); rect=new Rectangle(rectangle.X+lineWidth, rectangle.Y+lineWidth, rectangle.Width-lineWidth*2, rectangle.Height-lineWidth*2); for (int i=0; i127) { foreColor=SystemColors.ControlDark; } else { foreColor=SystemColors.ControlLight; } transparent=Color.FromArgb(0, backColor); if (active==true) { brush=new HatchBrush(HatchStyle.LightUpwardDiagonal, foreColor, transparent); } else { brush=new HatchBrush(HatchStyle.Percent25, foreColor, transparent); } container=graphics.BeginContainer(); graphics.ExcludeClip(insideRect); graphics.FillRectangle(brush, outsideRect); graphics.EndContainer(container); brush.Dispose(); } public static void DrawSizeGrip(Graphics graphics, Color backColor, Rectangle bounds) { int h; int b; int s; Pen pen1; Pen pen2; Color2HBS(backColor, out h, out b, out s); pen1=new Pen(HBS2Color(h, Math.Min(255, (b*166)/100), s), 1); pen2=new Pen(HBS2Color(h, (b*33)/100, s), 1); for (int i=0; i