Bitmap > Bitmap の使い方 > ドラッグによるトリミング |
クライアント側で画像を完全にトリミングできれば、とても便利なタスクになります。Silverlight では、C1Bitmap または WritableBitmap クラス(Silverlight 3 で導入)を使用してこれを実現できます。C1Bitmap コンポーネントには、あらゆるビットマップ関連操作を実行する際に簡単に使用できる API が用意されています。この API は、単純に色を取得および設定することができ、GetPixel および SetPixel メソッドを使ってピクセルに直接アクセスできます。
C1Bitmap は、画像をトリミングするために必要な API を提供しますが、UI は提供しません。画像トリミングの UI を実装する方法は、数え切れないほどあります。ほとんどの開発者や画像編集者は、装飾付きのボックスをドラッグする方法がよいと考えるでしょう。これは、Adobe Photoshop などの市販の画像編集ソフトウェアでよく使用されている方法です。ここでも、このようなボックスを作成します。
次の XAML は、トリミングボックスを作成するために必要な要素を定義します。このボックスは、ユーザーがドラッグできる4つのつまみ(Thumb)と、トリミング領域をマスクする4つの影付き四角形(Rectangle)で構成されます。これらの要素の位置をコードで簡単に調整できるように Canvas 内に配置します。
XAML |
コードのコピー
|
---|---|
<Canvas Name="cropCanvas"> <Rectangle Name="topMask" Fill="{StaticResource MaskBrush}" Canvas.Top="0" Canvas.Left="0" /> <Rectangle Name="bottomMask" Fill="{StaticResource MaskBrush}" /> <Rectangle Name="leftMask" Fill="{StaticResource MaskBrush}" Canvas.Left="0"/> <Rectangle Name="rightMask" Fill="{StaticResource MaskBrush}" Canvas.Top="0" /> <Thumb Name="cropUL" Width="10" Height="10" DragDelta="cropUL_DragDelta" Cursor="SizeNWSE" /> <Thumb Name="cropUR" Width="10" Height="10" DragDelta="cropUR_DragDelta" Cursor="SizeNESW" /> <Thumb Name="cropBL" Width="10" Height="10" DragDelta="cropBL_DragDelta" Cursor="SizeNESW" /> <Thumb Name="cropBR" Width="10" Height="10" DragDelta="cropBR_DragDelta" Cursor="SizeNWSE" /> </Canvas> |
トリミングボックスの操作に必要なコードは、かなり複雑です。ドラッグ可能なトリミングボックスは、動作と Visual State Manager を使って実装することもできますが、Silverlight の初級開発者にとっては、コードを使用した方が間違いなく理解しやすいソリューションになります。トリミングボックス UI の目的は、C1Bitmap がトリミング座標を決定するために使用する単純な四角形(Rect)を作成することです。ツールバーの[Crop]ボタンをクリックすると、トリミングボックスがフルサイズで表示されます。ユーザーがつまみをドラッグした場合は、各 Thumb の DragDelta イベントを使用して、垂直および水平方向の移動をキャプチャします。次に、少しのロジックと単純な計算を使用して、ユーザーがドラッグしていないつまみの動作を操作します。トリミングアクションを完了するには、[Crop]ボタン(C1ToolbarToggleButton)を再度クリックします。
C# |
コードのコピー
|
---|---|
private void cropUL_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { double left = Canvas.GetLeft(cropUL) + e.HorizontalChange; double top = Canvas.GetTop(cropUL) + e.VerticalChange; if (left > 0 && left < bitmap.Width && cropBox.Width > e.HorizontalChange) { cropBox = new Rect(left, cropBox.Top, cropBox.Width - e.HorizontalChange, cropBox.Height); } if (top > 0 && top < bitmap.Height && cropBox.Height > e.VerticalChange) { cropBox = new Rect(cropBox.Left, top, cropBox.Width, cropBox.Height - e.VerticalChange); } UpdateCropBox(); } private void cropUR_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { double left = Canvas.GetLeft(cropUR) + e.HorizontalChange; double top = Canvas.GetTop(cropUR) + e.VerticalChange; if (left > 0 && left < bitmap.Width && left > cropBox.Left) { cropBox = new Rect(cropBox.Left, cropBox.Top, left - cropBox.Left, cropBox.Height); } if (top > 0 && top < bitmap.Height && cropBox.Height > e.VerticalChange) { cropBox = new Rect(cropBox.Left, top, cropBox.Width, cropBox.Height - e.VerticalChange); } UpdateCropBox(); } private void cropBL_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { double left = Canvas.GetLeft(cropBL) + e.HorizontalChange; double top = Canvas.GetTop(cropBL) + e.VerticalChange; if (left > 0 && left < bitmap.Width && cropBox.Width > e.HorizontalChange) { cropBox = new Rect(left, cropBox.Top, cropBox.Width - e.HorizontalChange, cropBox.Height); } if (top > 0 && top < bitmap.Height && top > cropBox.Top) { cropBox = new Rect(cropBox.Left, cropBox.Top, cropBox.Width, top - cropBox.Top); } UpdateCropBox(); } private void cropBR_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { double left = Canvas.GetLeft(cropBR) + e.HorizontalChange; double top = Canvas.GetTop(cropBR) + e.VerticalChange; if (left > 0 && left < bitmap.Width && left > cropBox.Left) { cropBox = new Rect(cropBox.Left, cropBox.Top, left - cropBox.Left, cropBox.Height); } if (top > 0 && top < bitmap.Height && cropBox.Height + e.VerticalChange > 0) { cropBox = new Rect(cropBox.Left, cropBox.Top, cropBox.Width, cropBox.Height + e.VerticalChange); } UpdateCropBox(); } |
上の "if" 文で、つまみの範囲に対していくつかのロジックを適用します。たとえば、右下のつまみを左下のつまみより左にドラッグできないようにします。また、どのつまみも画像の外までドラッグできないようにします。
C# |
コードのコピー
|
---|---|
private void UpdateCropBox() { Canvas.SetLeft(cropUL, cropBox.Left); Canvas.SetTop(cropUL, cropBox.Top); Canvas.SetLeft(cropUR, cropBox.Left + cropBox.Width); Canvas.SetTop(cropUR, cropBox.Top); Canvas.SetLeft(cropBL, cropBox.Left); Canvas.SetTop(cropBL, cropBox.Top + cropBox.Height); Canvas.SetLeft(cropBR, cropBox.Left + cropBox.Width); Canvas.SetTop(cropBR, cropBox.Top + cropBox.Height); UpdateMask(); cropping = true; } |
UpdateCropBox メソッドは、cropBox Rect の Left、Top、Width、および Height プロパティに基づいて、すべての cropCanvas 要素の位置を更新します。最終的にトリミングを適用([Crop]ボタンを再クリック)する段階になると、C1Bitmap を使用します。ここで、境界 Rect 内のピクセルを取得して新しい C1Bitmap にコピーし、元の画像を新しい画像に置き換えます。
C# |
コードのコピー
|
---|---|
private void CropImage() { bitmap2 = new C1Bitmap((int)cropBox.Width, (int)cropBox.Height); bitmap2.BeginUpdate(); for (int x = 0; x < cropBox.Width; ++x) { for (int y = 0; y < cropBox.Height; ++y) { bitmap2.SetPixel(x, y, bitmap.GetPixel(x + (int)cropBox.X, y + (int)cropBox.Y)); } } bitmap2.EndUpdate(); bitmap.Copy(bitmap2, false); UpdateImage(true); InitCropHandles(); } |