4

次のパラメーターを受け入れる (CSS 3 仕様のように) ボックスの影を描画するアルゴリズムを探しているか、実装しようとしています。

  • 水平オフセット
  • 垂直オフセット
  • インセット
  • 展開する
  • ぼかし
  • (オプション: 不透明度)。

どこから始めれば。

そこから実装を引き出すことができるかどうかを確認するために、Firefox / Chrome のソース コードを探しましたが、そのような運はありません!

私は線形グラデーションアルゴリズムを調べて、ボックスで描画しました。これは、おそらくエッジの半径のために、丸みを帯びた長方形を除いて影に空のピクセルを残すことを除いて、動作します。

私はGDI +を使用して.NETでこれを行っています。私の目的は、画像のドロップ シャドウを作成することではありません。私はすでにこれに関する記事を見ました。GDI+ で描画した図形にドロップ シャドウを作成したい。

どんな助けでも大歓迎です!

4

1 に答える 1

14

内部のコントロールを処理し、コントロールのタグの必要に応じて (外側または内側の) 影を追加する DropShadowPanel をコーディングしました。

画像コントロールでわかるように、影は定義どおりに取得されます。

タグ:

テキストボックス: DropShadow:5,5,5,10,#000000,noinset

カレンダー: DropShadow:10,10,80,30,#0000FF,noinset

picturebox 左上: DropShadow:-50,20,50,10,#888888,noinset

左下のピクチャーボックス: DropShadow:10,10,20,20,#442200,inset

ピクチャボックス右下: DropShadow:0,0,50,50,#442200,noinset

ここに画像の説明を入力

パネルのコードは次のとおりです: (コントロール gdi オブジェクトに描画する前に中間描画を画像に使用して、フォームがクロールしないようにします - これは実際にはかなり高速に動作します)

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication4
{
    public class DropShadowPanel : Panel
    {
        protected override void OnControlAdded(ControlEventArgs e)
        {
            e.Control.Paint += new PaintEventHandler(Control_Paint);
            base.OnControlAdded(e);
        }

    void Control_Paint(object sender, PaintEventArgs e)
    {
        CheckDrawInnerShadow(sender as Control, e.Graphics);
    }

    private void CheckDrawInnerShadow(Control sender, Graphics g)
    {
        var dropShadowStruct = GetDropShadowStruct(sender);

        if (dropShadowStruct == null || !dropShadowStruct.Inset)
        {
            return;
        }

        DrawInsetShadow(sender as Control, g);

    }

    protected override void  OnControlRemoved(ControlEventArgs e)
    {
        e.Control.Paint -= new PaintEventHandler(Control_Paint);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        DrawShadow(Controls.OfType<Control>().Where(c => c.Tag != null && c.Tag.ToString().StartsWith("DropShadow")), e.Graphics);
    }

    void DrawInsetShadow(Control control, Graphics g)
    {
        var dropShadowStruct = GetDropShadowStruct(control);

        var rInner = new Rectangle(Point.Empty, control.Size);

        var img = new Bitmap(rInner.Width, rInner.Height, g);
        var g2 = Graphics.FromImage(img);

        g2.CompositingMode = CompositingMode.SourceCopy;
        g2.FillRectangle(new SolidBrush(dropShadowStruct.Color), 0, 0, control.Width, control.Height);

        rInner.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow);
        rInner.Inflate(dropShadowStruct.Blur, dropShadowStruct.Blur);
        rInner.Inflate(-dropShadowStruct.Spread, -dropShadowStruct.Spread);

        double blurSize = dropShadowStruct.Blur;
        double blurStartSize = blurSize;

        do
        {
            var transparency = blurSize/blurStartSize;
            var color = Color.FromArgb(((int)(255 * (transparency * transparency))), dropShadowStruct.Color);               
            rInner.Inflate(-1,-1);
            DrawRoundedRectangle(g2, rInner, (int)blurSize, Pens.Transparent, color);
            blurSize--;
        } while (blurSize > 0);

        g.DrawImage(img, 0, 0);
        g.Flush();

        g2.Dispose();
        img.Dispose();
    }

    void DrawShadow(IEnumerable<Control> controls, Graphics g)
    {
        foreach (var control in controls)
        {
            var dropShadowStruct = GetDropShadowStruct(control);

            if (dropShadowStruct.Inset)
            {
                continue; // must be handled by the control itself
            }

            DrawOutsetShadow(g, dropShadowStruct, control);
        }
    }

    // drawing the loop on an image because of speed
    private void DrawOutsetShadow(Graphics g, dynamic dropShadowStruct, Control control)
    {
        var rOuter = control.Bounds;
        var rInner = control.Bounds;
        rInner.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow);
        rInner.Inflate(-dropShadowStruct.Blur, -dropShadowStruct.Blur);
        rOuter.Inflate(dropShadowStruct.Spread, dropShadowStruct.Spread);
        rOuter.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow);
        var originalOuter = rOuter;

        var img = new Bitmap(originalOuter.Width, originalOuter.Height, g);
        var g2 = Graphics.FromImage(img);

        var currentBlur = 0;

        do
        {
            var transparency = (rOuter.Height - rInner.Height)/(double) (dropShadowStruct.Blur*2 + dropShadowStruct.Spread*2);
            var color = Color.FromArgb(((int)(255 * (transparency * transparency))), dropShadowStruct.Color);
            var rOutput = rInner;
            rOutput.Offset(-originalOuter.Left, -originalOuter.Top);
            DrawRoundedRectangle(g2, rOutput, currentBlur, Pens.Transparent, color);
            rInner.Inflate(1, 1);
            currentBlur = (int) ((double) dropShadowStruct.Blur*(1 - (transparency*transparency)));
        } while (rOuter.Contains(rInner));

        g2.Flush();
        g2.Dispose();

        g.DrawImage(img, originalOuter);

        img.Dispose();
    }

    private static dynamic GetDropShadowStruct(Control control)
    {
        if (control.Tag == null || !(control.Tag is string) || !control.Tag.ToString().StartsWith("DropShadow"))
            return null;

        string[] dropShadowParams = control.Tag.ToString().Split(':')[1].Split(',');
        var dropShadowStruct = new
                                {
                                    HShadow = Convert.ToInt32(dropShadowParams[0]),
                                    VShadow = Convert.ToInt32(dropShadowParams[1]),
                                    Blur = Convert.ToInt32(dropShadowParams[2]),
                                    Spread = Convert.ToInt32(dropShadowParams[3]),
                                    Color = ColorTranslator.FromHtml(dropShadowParams[4]),
                                    Inset = dropShadowParams[5].ToLowerInvariant() == "inset"
                                };
        return dropShadowStruct;
    }

    private void DrawRoundedRectangle(Graphics gfx, Rectangle bounds, int cornerRadius, Pen drawPen, Color fillColor)
    {
        int strokeOffset = Convert.ToInt32(Math.Ceiling(drawPen.Width));
        bounds = Rectangle.Inflate(bounds, -strokeOffset, -strokeOffset);

        var gfxPath = new GraphicsPath();
        if (cornerRadius > 0)
        {
            gfxPath.AddArc(bounds.X, bounds.Y, cornerRadius, cornerRadius, 180, 90);
            gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y, cornerRadius, cornerRadius, 270, 90);
            gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y + bounds.Height - cornerRadius, cornerRadius,
                           cornerRadius, 0, 90);
            gfxPath.AddArc(bounds.X, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
        }
        else
        {
            gfxPath.AddRectangle(bounds);
        }
        gfxPath.CloseAllFigures();

        gfx.FillPath(new SolidBrush(fillColor), gfxPath);
        if (drawPen != Pens.Transparent)
        {
            var pen = new Pen(drawPen.Color);
            pen.EndCap = pen.StartCap = LineCap.Round;
            gfx.DrawPath(pen, gfxPath);
        }
    }
    }
}

コードはあまりレビューしなくても高速に記述されるため、特にコントロールに wring タグを設定するとバグが発生する可能性があります)。

PS。一部のコントロールではインナー シャドウが機能しないことに気付くかもしれません。これは、Windows システム コントロールのラッパーであるためです。パネルだけでこれを克服することはできませんが、次のようにすることができます: http://www.codeproject.com/Articles/4548/Generating-missing-Paint-event-for-TreeView-and-Li

于 2012-11-30T21:05:29.407 に答える