C# Graphics.DrawImageの使い方完全ガイド|画像の描画・拡大縮小・切り抜きまで解説
はじめに
C#で画像を画面に表示したり、拡大縮小したり、画像の一部だけを切り抜いて描画したりしたい場合によく使われるのが、Graphics.DrawImageです。
特にWindows Formsアプリケーションでは、Paintイベント内で画像を描画したり、Bitmapに対して加工結果を書き込んだりする場面で頻繁に登場します。
Graphics.DrawImageを使えるようになると、次のような処理をC#で実装できます。
画像ファイルをフォーム上に描画する
画像を指定サイズに拡大・縮小する
画像の一部だけを切り抜いて表示する
複数画像を重ねて合成する
透明PNGを重ねて描画する
加工した画像を別ファイルとして保存する
この記事では、c# graphics drawimageで調べている方に向けて、Graphics.DrawImageの基本から、拡大縮小、切り抜き、回転、反転、合成、画質調整までを順番に解説します。
1. C#のGraphics.DrawImageとは?できることと使いどころ
1-1. Graphics.DrawImageの役割
Graphics.DrawImageは、C#のSystem.Drawingで画像を描画するためのメソッドです。
Graphicsオブジェクトに対して、ImageやBitmapを指定し、画面・コントロール・別の画像などに描画します。Microsoftのドキュメントでも、Graphicsクラスは線、図形、画像などを描画するための中心的なクラスとして説明されています。Microsoft Learn
たとえば、次のように書くと、読み込んだ画像を座標(10, 20)の位置に描画できます。
C#using System.Drawing;
using Image image = Image.FromFile("sample.jpg");
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(image, 10, 20);
}
ただし、実際のアプリでは、画像の読み込みタイミングや解放処理に注意が必要です。基本的には、Paintイベントのたびに画像ファイルを読み込むのではなく、あらかじめ読み込んだ画像を使い回す設計にします。
1-2. DrawImageでできること
Graphics.DrawImageでは、主に次のような画像描画ができます。
| できること | 内容 |
|---|---|
| 通常描画 | 画像を指定座標に表示する |
| リサイズ描画 | 幅・高さを指定して拡大縮小する |
| トリミング描画 | 元画像の一部だけを切り抜いて表示する |
| 合成 | 背景画像の上に別画像を重ねる |
| 透過描画 | 透明PNGや半透明画像を重ねる |
| 回転・反転 | Transform系メソッドと組み合わせて描画する |
| 保存 | Bitmapに描画して画像ファイルとして保存する |
DrawImageは単なる画像表示だけでなく、「描画しながら加工する」ための機能として使える点が大きな特徴です。
1-3. PictureBox.ImageやBackgroundImageとの違い
Windows Formsで画像を表示するだけなら、PictureBox.ImageやBackgroundImageでも十分です。
C#pictureBox1.Image = Image.FromFile("sample.jpg");
一方、Graphics.DrawImageは、より細かく描画を制御したい場合に向いています。
| 方法 | 向いている用途 |
|---|---|
PictureBox.Image | 画像をそのまま表示したい |
BackgroundImage | フォームやコントロールの背景に画像を設定したい |
Graphics.DrawImage | 座標、サイズ、切り抜き、合成、画質を細かく制御したい |
たとえば、画像の一部だけを切り抜いて表示する、複数の画像を重ねる、半透明で合成するといった処理は、PictureBox.Imageだけでは柔軟に扱いにくいため、Graphics.DrawImageを使うのが一般的です。
1-4. Windows Forms/GDI+で画像描画するときの基本的な考え方
Windows Formsでの描画処理は、GDI+を通じて行われます。GDI+では、画像や図形、文字列などをオブジェクトとして扱い、フォームやコントロールに描画できます。MicrosoftのWindows Formsドキュメントでも、GDI+はグラフィック画像のレンダリングに使われる仕組みとして説明されています。Microsoft Learn
基本的な考え方は次のとおりです。
描画先の
Graphicsを取得する描画したい
ImageまたはBitmapを用意するDrawImageで位置やサイズを指定して描画する不要になったリソースを解放する
フォームやコントロールへ描画する場合は、基本的にPaintイベントで描画します。CreateGraphics()で取得したGraphicsに描画する方法もありますが、再描画時に消えることがあるため、継続的に表示したい描画にはPaintイベントを使うのが安全です。
2. Graphics.DrawImageを使う前に知っておきたい基本知識
2-1. Graphicsクラスとは
Graphicsクラスは、描画先に対して線、図形、文字、画像などを描くためのクラスです。
Windows Formsでは、主に次のような方法でGraphicsを取得します。
C#// Paintイベントで取得
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
}
// Bitmapに対して取得
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(bitmap);
フォームやコントロールに描画する場合はPaintEventArgsのe.Graphicsを使い、画像ファイルとして保存する場合はGraphics.FromImageを使ってBitmapに描画します。Graphics.FromImageは、指定したImageからGraphicsを作成して、その画像に描画するためのメソッドです。Microsoft Learn
2-2. Image/Bitmapクラスとは
Imageは画像を表す抽象的な基本クラスで、JPEG、PNG、BMPなどの画像を扱うときに使います。
BitmapはImageを継承したクラスで、ピクセル単位の画像データを扱うときによく使われます。Microsoftのドキュメントでは、Bitmapはグラフィック画像のピクセルデータと属性で構成されるGDI+ビットマップをカプセル化するクラスと説明されています。Microsoft Learn
使い分けの目安は次のとおりです。
| クラス | 主な用途 |
|---|---|
Image | 画像ファイルを読み込んで描画する |
Bitmap | 新しい画像を作成する、加工する、保存する |
Graphics | ImageやBitmapを描画先に描く |
画像を読み込むだけならImage.FromFile、新しく画像を作って描画結果を保存したいならnew Bitmap(...)を使います。
2-3. 座標・サイズ・Rectangleの考え方
DrawImageでは、座標やサイズを指定する場面が多くあります。
C#のGDI+描画では、基本的に左上が原点です。
X座標:右方向に増える
Y座標:下方向に増える
Width:幅
Height:高さ
Rectangleを使うと、位置とサイズをまとめて指定できます。
C#Rectangle rect = new Rectangle(10, 20, 200, 100);
この場合、意味は次のとおりです。
| 値 | 意味 |
|---|---|
10 | X座標 |
20 | Y座標 |
200 | 幅 |
100 | 高さ |
注意したいのは、第3引数と第4引数は「右下の座標」ではなく「幅」と「高さ」だという点です。
2-4. usingでリソースを解放する重要性
Image、Bitmap、Graphicsなどは、内部で画像データやGDI+リソースを使用します。そのため、不要になったらDispose()で解放する必要があります。
C#ではusingを使うと、安全にリソースを解放できます。
C#using Image image = Image.FromFile("sample.jpg");
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(bitmap);
g.DrawImage(image, 0, 0);
bitmap.Save("output.png");
古い書き方では、次のようにも書けます。
C#using (Image image = Image.FromFile("sample.jpg"))
using (Bitmap bitmap = new Bitmap(300, 200))
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(image, 0, 0);
bitmap.Save("output.png");
}
特に画像処理を繰り返すアプリでは、解放漏れがあるとメモリ使用量が増えたり、ファイルがロックされたままになったりすることがあります。
2-5. System.Drawingを使う際の注意点
System.DrawingはWindows Formsアプリでは使いやすい画像描画APIですが、.NET 6以降ではSystem.Drawing.CommonがWindows固有のライブラリとして扱われ、Windows以外のOSではサポート対象外です。Microsoftの互換性ドキュメントでも、.NET 6以降のSystem.Drawing.CommonはWindows固有であり、非Windows環境では警告や例外の対象になることが説明されています。Microsoft Learn
そのため、用途によって選択肢を分けるのがおすすめです。
| 用途 | おすすめ |
|---|---|
| Windows Formsアプリ | System.Drawing |
| Windows専用のデスクトップツール | System.Drawing |
| ASP.NET Coreのサーバー処理 | ImageSharp、SkiaSharp、Magick.NETなど |
| LinuxやmacOSを含むクロスプラットフォーム | ImageSharp、SkiaSharpなど |
この記事では、Windows FormsやWindows環境でのC#画像描画を前提に、Graphics.DrawImageの使い方を解説します。
3. Graphics.DrawImageの基本的な使い方
3-1. 画像ファイルを読み込んで描画する基本コード
まずは、画像ファイルを読み込み、Bitmapに描画して保存する基本コードです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Image image = Image.FromFile("sample.jpg");
using Bitmap canvas = new Bitmap(500, 300);
using Graphics g = Graphics.FromImage(canvas);
g.Clear(Color.White);
g.DrawImage(image, 50, 30);
canvas.Save("output.png", ImageFormat.Png);
このコードでは、sample.jpgを読み込み、500×300ピクセルの白いキャンバスに描画して、output.pngとして保存しています。
ポイントは次の3つです。
Image.FromFileで画像を読み込むGraphics.FromImageでBitmapに描画するためのGraphicsを作るDrawImageで画像を描画する
3-2. 指定した座標に画像をそのまま描画する
画像を指定した位置に描画するには、DrawImage(Image, int, int)を使います。
C#g.DrawImage(image, 100, 50);
この場合、画像の左上が(100, 50)の位置に描画されます。
フォームのPaintイベントで使うと、次のようになります。
C#private Image _image;
private void Form1_Load(object sender, EventArgs e)
{
_image = Image.FromFile("sample.jpg");
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_image, 100, 50);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
_image?.Dispose();
base.OnFormClosed(e);
}
Paintイベントの中で毎回Image.FromFileを呼ぶと、描画のたびにファイル読み込みが発生します。実用的なアプリでは、Loadイベントなどで画像を読み込み、Paintイベントでは描画だけを行うのが基本です。
3-3. FormのPaintイベントで描画する方法
フォーム上に画像を描画する場合は、Paintイベントを使います。
C#using System;
using System.Drawing;
using System.Windows.Forms;
public partial class Form1 : Form
{
private Image _image;
public Form1()
{
InitializeComponent();
_image = Image.FromFile("sample.jpg");
this.Paint += Form1_Paint;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_image, 20, 20, 300, 200);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
_image?.Dispose();
base.OnFormClosed(e);
}
}
この例では、フォームに画像を300×200ピクセルで描画しています。
Paintイベントを使う理由は、フォームが最小化・復元されたり、別ウィンドウに隠れて再表示されたりしたときに、Windows Formsが再描画を行うためです。CreateGraphics()で一時的に描いた内容は、再描画時に消えることがあります。
3-4. PictureBoxにGraphicsで画像を描画する方法
PictureBoxにGraphics.DrawImageで描画したい場合は、主に2つの方法があります。
1つ目は、PictureBoxのPaintイベントで描画する方法です。
C#private Image _image;
private void Form1_Load(object sender, EventArgs e)
{
_image = Image.FromFile("sample.jpg");
pictureBox1.Paint += pictureBox1_Paint;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_image, 10, 10, 200, 150);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
_image?.Dispose();
base.OnFormClosed(e);
}
2つ目は、Bitmapに描画してからPictureBox.Imageに設定する方法です。
C#using Image source = Image.FromFile("sample.jpg");
Bitmap canvas = new Bitmap(300, 200);
using (Graphics g = Graphics.FromImage(canvas))
{
g.Clear(Color.White);
g.DrawImage(source, 20, 20, 200, 150);
}
pictureBox1.Image?.Dispose();
pictureBox1.Image = canvas;
後者の方法は、描画結果を画像として保持できるため、加工結果を保存したい場合や、何度も再描画したくない場合に便利です。
3-5. Bitmapに描画して画像として保存する方法
Graphics.DrawImageは、画面表示だけでなく、画像ファイルの生成にも使えます。
C#using System.Drawing;
using System.Drawing.Imaging;
using Image source = Image.FromFile("sample.jpg");
using Bitmap output = new Bitmap(400, 300);
using Graphics g = Graphics.FromImage(output);
g.Clear(Color.White);
g.DrawImage(source, 0, 0, 400, 300);
output.Save("resized.png", ImageFormat.Png);
このコードでは、元画像を400×300ピクセルにリサイズしてresized.pngとして保存しています。
画像加工ツール、サムネイル作成、帳票画像生成、ウォーターマーク合成などでは、このようにBitmapへ描画して保存するパターンがよく使われます。
4. Graphics.DrawImageの主なオーバーロードを理解する
Graphics.DrawImageには多くのオーバーロードがあります。Microsoftのドキュメントでも、座標指定、サイズ指定、切り抜き元範囲指定、ImageAttributes指定など、複数の形式が用意されています。Microsoft Learn
ここでは、よく使う代表的なオーバーロードを解説します。
4-1. DrawImage(Image, int, int)の使い方
C#g.DrawImage(image, 10, 20);
画像を指定した座標に描画します。
最もシンプルな使い方で、画像の左上を(10, 20)に合わせて描画します。
使いどころは次のとおりです。
画像をそのまま表示したい
アイコンや小さな画像を指定位置に置きたい
リサイズや切り抜きが不要な場合
4-2. DrawImage(Image, Rectangle)の使い方
C#Rectangle destRect = new Rectangle(10, 20, 300, 200);
g.DrawImage(image, destRect);
Rectangleで描画先の位置とサイズを指定します。
画像はdestRectのサイズに合わせて拡大・縮小されます。
C#Rectangle destRect = new Rectangle(
x: 50,
y: 30,
width: 400,
height: 250
);
g.DrawImage(image, destRect);
位置とサイズをまとめて扱えるため、UI部品の領域に画像を収めたいときに便利です。
4-3. DrawImage(Image, int, int, int, int)の使い方
C#g.DrawImage(image, 10, 20, 300, 200);
X座標、Y座標、幅、高さを個別に指定して描画します。
Rectangleを作らずに書けるため、簡単なリサイズ描画ではよく使われます。
C#int x = 10;
int y = 20;
int width = 300;
int height = 200;
g.DrawImage(image, x, y, width, height);
ただし、引数が増えると意味が分かりにくくなるため、複雑な処理ではRectangleを使ったほうが読みやすくなります。
4-4. DrawImage(Image, Rectangle, Rectangle, GraphicsUnit)の使い方
画像の一部だけを切り抜いて描画する場合は、次のオーバーロードを使います。
C#Rectangle destRect = new Rectangle(0, 0, 200, 150);
Rectangle sourceRect = new Rectangle(100, 50, 200, 150);
g.DrawImage(image, destRect, sourceRect, GraphicsUnit.Pixel);
この形式では、次の2つの範囲を指定します。
| 引数 | 意味 |
|---|---|
destRect | 描画先の範囲 |
sourceRect | 元画像から切り抜く範囲 |
sourceRectで元画像の一部を指定し、destRectに描画します。
たとえば、元画像の(100, 50)から幅200、高さ150の範囲を切り抜き、出力先の(0, 0)から幅200、高さ150で描画する、という処理になります。
4-5. 目的別に選ぶべきオーバーロード早見表
| 目的 | おすすめの書き方 |
|---|---|
| 指定座標にそのまま描画 | DrawImage(image, x, y) |
| 指定サイズにリサイズ | DrawImage(image, x, y, width, height) |
| 位置とサイズをまとめて指定 | DrawImage(image, destRect) |
| 画像の一部を切り抜いて描画 | DrawImage(image, destRect, sourceRect, GraphicsUnit.Pixel) |
| 透明度や色補正を指定 | DrawImage(..., ImageAttributes) |
最初は、次の3つを覚えておけば十分です。
C#g.DrawImage(image, x, y);
g.DrawImage(image, x, y, width, height);
g.DrawImage(image, destRect, sourceRect, GraphicsUnit.Pixel);
通常描画、リサイズ、切り抜きの3パターンを押さえると、Graphics.DrawImageの基本的な使い方はほぼ理解できます。
5. 画像を拡大・縮小して描画する方法
5-1. 幅と高さを指定してリサイズ描画する
画像を指定サイズに拡大・縮小するには、幅と高さを指定してDrawImageを呼び出します。
C#using Image image = Image.FromFile("sample.jpg");
using Bitmap output = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(output);
g.Clear(Color.White);
g.DrawImage(image, 0, 0, 300, 200);
output.Save("resized.png");
このコードでは、元画像のサイズに関係なく、300×200ピクセルに変形して描画します。
ただし、元画像の縦横比と異なるサイズを指定すると、画像が横長または縦長に歪みます。歪ませたくない場合は、縦横比を維持してサイズを計算する必要があります。
5-2. 縦横比を維持して拡大縮小する
縦横比を維持して指定範囲内に収めるには、幅方向と高さ方向の縮小率を比較し、小さいほうを採用します。
C#using Image image = Image.FromFile("sample.jpg");
int maxWidth = 300;
int maxHeight = 200;
double ratioX = maxWidth / (double)image.Width;
double ratioY = maxHeight / (double)image.Height;
double ratio = Math.Min(ratioX, ratioY);
int newWidth = (int)(image.Width * ratio);
int newHeight = (int)(image.Height * ratio);
using Bitmap output = new Bitmap(maxWidth, maxHeight);
using Graphics g = Graphics.FromImage(output);
g.Clear(Color.White);
int x = (maxWidth - newWidth) / 2;
int y = (maxHeight - newHeight) / 2;
g.DrawImage(image, x, y, newWidth, newHeight);
output.Save("fit.png");
この方法では、画像を300×200の範囲内に収めつつ、元画像の縦横比を維持できます。
中央寄せにしているため、余白が上下または左右に入る場合があります。
5-3. 画質を落とさず高品質にリサイズする
画像を縮小するとき、何も設定しないとジャギーが目立ったり、ぼやけたりすることがあります。
高品質にリサイズしたい場合は、Graphicsの描画品質に関するプロパティを設定します。
C#using System.Drawing.Drawing2D;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
特に画像の拡大縮小では、InterpolationModeが重要です。
HighQualityBicubicを指定すると、縮小時に比較的きれいな結果になりやすく、サムネイル作成にも向いています。
5-4. InterpolationModeの設定方法
InterpolationModeは、画像を拡大・縮小するときの補間方法を指定するプロパティです。
代表的な値は次のとおりです。
| 値 | 特徴 |
|---|---|
NearestNeighbor | ドット絵向き。ギザギザが出やすい |
Bilinear | 軽めの補間 |
Bicubic | 滑らかな補間 |
HighQualityBicubic | 高品質なリサイズ向き |
HighQualityBilinear | 高品質な線形補間 |
設定例は次のとおりです。
C#using System.Drawing.Drawing2D;
using Image image = Image.FromFile("sample.jpg");
using Bitmap output = new Bitmap(300, 200);
using Graphics g = Graphics.FromImage(output);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, 0, 0, 300, 200);
output.Save("high-quality.png");
写真のサムネイルを作るならHighQualityBicubic、ドット絵やピクセルアートを拡大するならNearestNeighborのように、画像の種類に合わせて選ぶとよいでしょう。
5-5. サムネイル画像を作成するサンプルコード
次は、元画像の縦横比を維持したまま、指定サイズ内に収めるサムネイル作成コードです。
C#using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
public static void CreateThumbnail(string inputPath, string outputPath, int maxWidth, int maxHeight)
{
using Image source = Image.FromFile(inputPath);
double ratioX = maxWidth / (double)source.Width;
double ratioY = maxHeight / (double)source.Height;
double ratio = Math.Min(ratioX, ratioY);
int width = (int)(source.Width * ratio);
int height = (int)(source.Height * ratio);
using Bitmap thumbnail = new Bitmap(maxWidth, maxHeight);
thumbnail.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using Graphics g = Graphics.FromImage(thumbnail);
g.Clear(Color.White);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
int x = (maxWidth - width) / 2;
int y = (maxHeight - height) / 2;
g.DrawImage(source, x, y, width, height);
thumbnail.Save(outputPath, ImageFormat.Jpeg);
}
呼び出し例は次のとおりです。
C#CreateThumbnail("sample.jpg", "thumbnail.jpg", 300, 200);
このコードでは、300×200のキャンバス内に元画像を収め、余白は白で塗りつぶしています。
6. 画像を切り抜いて描画する方法
6-1. DrawImageで画像の一部だけを描画する仕組み
Graphics.DrawImageでは、元画像の一部だけを指定して描画できます。
このとき使うのが、次の形式です。
C#g.DrawImage(sourceImage, destRect, sourceRect, GraphicsUnit.Pixel);
考え方はシンプルです。
sourceRect:元画像のどの部分を使うかdestRect:描画先のどこに、どのサイズで描くか
たとえば、元画像の中央部分だけを切り抜いて、別のBitmapに描画するといった処理ができます。
6-2. sourceRectとdestRectの違い
sourceRectとdestRectは混同しやすいポイントです。
C#Rectangle sourceRect = new Rectangle(100, 50, 300, 200);
Rectangle destRect = new Rectangle(0, 0, 300, 200);
それぞれの意味は次のとおりです。
| Rectangle | 意味 |
|---|---|
sourceRect | 元画像から切り抜く範囲 |
destRect | 描画先に配置する範囲 |
上の例では、元画像の(100, 50)から幅300、高さ200の範囲を切り抜き、描画先の(0, 0)に300×200で描画します。
もしsourceRectが300×200で、destRectが600×400なら、切り抜いた範囲を2倍に拡大して描画することになります。
6-3. 指定範囲をトリミングして表示するサンプル
次のコードは、元画像の一部を切り抜いて、フォーム上に表示する例です。
C#private Image _image;
private void Form1_Load(object sender, EventArgs e)
{
_image = Image.FromFile("sample.jpg");
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Rectangle sourceRect = new Rectangle(100, 50, 300, 200);
Rectangle destRect = new Rectangle(20, 20, 300, 200);
e.Graphics.DrawImage(_image, destRect, sourceRect, GraphicsUnit.Pixel);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
_image?.Dispose();
base.OnFormClosed(e);
}
このコードでは、元画像の(100, 50)から300×200ピクセルの範囲だけを切り抜き、フォーム上の(20, 20)に表示しています。
6-4. 切り抜いた画像を別ファイルとして保存する方法
切り抜き結果を画像ファイルとして保存したい場合は、Bitmapに描画して保存します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Image source = Image.FromFile("sample.jpg");
Rectangle sourceRect = new Rectangle(100, 50, 300, 200);
using Bitmap cropped = new Bitmap(sourceRect.Width, sourceRect.Height);
using Graphics g = Graphics.FromImage(cropped);
Rectangle destRect = new Rectangle(0, 0, sourceRect.Width, sourceRect.Height);
g.DrawImage(source, destRect, sourceRect, GraphicsUnit.Pixel);
cropped.Save("cropped.png", ImageFormat.Png);
このコードでは、元画像から指定範囲を切り抜き、cropped.pngとして保存しています。
destRectを(0, 0, sourceRect.Width, sourceRect.Height)にしているため、切り抜いた範囲がそのまま新しい画像全体になります。
6-5. よくある切り抜き位置のズレと対処法
切り抜き処理でよくある失敗は、Rectangleの指定ミスです。
特に次の点に注意しましょう。
| 原因 | 対処法 |
|---|---|
| 幅と右端座標を勘違いしている | Rectangle(x, y, width, height)の第3・第4引数は幅と高さ |
sourceRectとdestRectを逆にしている | 元画像側がsourceRect、描画先がdestRect |
| 拡大縮小されて位置がズレて見える | sourceRectとdestRectのサイズを確認する |
| DPIや表示倍率の影響を受けている | GraphicsUnit.Pixelを指定してピクセル単位で扱う |
| 画像の範囲外を指定している | sourceRectが画像サイズ内に収まっているか確認する |
安全に切り抜くなら、事前に範囲チェックを入れるとよいでしょう。
C#Rectangle imageBounds = new Rectangle(0, 0, source.Width, source.Height);
Rectangle cropRect = new Rectangle(100, 50, 300, 200);
cropRect.Intersect(imageBounds);
if (cropRect.Width > 0 && cropRect.Height > 0)
{
g.DrawImage(source, destRect, cropRect, GraphicsUnit.Pixel);
}
Intersectを使うと、元画像の範囲内に収まるように切り抜き範囲を補正できます。
7. 画像を回転・反転・合成して描画する応用テクニック
7-1. RotateTransformで画像を回転して描画する
画像を回転して描画するには、Graphics.RotateTransformを使います。
ただし、単純に回転すると原点を中心に回転するため、通常はTranslateTransformで回転中心を移動してから描画します。
C#using System.Drawing;
using System.Drawing.Drawing2D;
using Image image = Image.FromFile("sample.png");
using Bitmap canvas = new Bitmap(500, 500);
using Graphics g = Graphics.FromImage(canvas);
g.Clear(Color.White);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
GraphicsState state = g.Save();
float centerX = 250;
float centerY = 250;
g.TranslateTransform(centerX, centerY);
g.RotateTransform(30);
g.DrawImage(
image,
-image.Width / 2,
-image.Height / 2,
image.Width,
image.Height
);
g.Restore(state);
canvas.Save("rotated.png");
このコードでは、キャンバスの中央を基準に画像を30度回転して描画しています。
g.Save()とg.Restore()を使うことで、回転設定が他の描画処理に影響しないようにしています。
7-2. ScaleTransformで反転して描画する
画像を左右反転して描画するには、ScaleTransform(-1, 1)を使います。
C#using Image image = Image.FromFile("sample.png");
using Bitmap canvas = new Bitmap(image.Width, image.Height);
using Graphics g = Graphics.FromImage(canvas);
g.Clear(Color.Transparent);
GraphicsState state = g.Save();
g.TranslateTransform(image.Width, 0);
g.ScaleTransform(-1, 1);
g.DrawImage(image, 0, 0, image.Width, image.Height);
g.Restore(state);
canvas.Save("flipped.png");
ポイントは、先にTranslateTransform(image.Width, 0)を呼ぶことです。
左右反転ではX方向がマイナスになるため、そのまま描画すると表示範囲の外に出てしまいます。そこで、画像幅の分だけ原点を移動してから反転します。
上下反転したい場合は、次のようにします。
C#g.TranslateTransform(0, image.Height);
g.ScaleTransform(1, -1);
g.DrawImage(image, 0, 0, image.Width, image.Height);
7-3. 複数画像を重ねて描画する
DrawImageは、呼び出した順番に画像を描画します。
そのため、背景画像を先に描画し、その上にロゴやアイコンを描画すると、画像合成ができます。
C#using Image background = Image.FromFile("background.jpg");
using Image logo = Image.FromFile("logo.png");
using Bitmap canvas = new Bitmap(background.Width, background.Height);
using Graphics g = Graphics.FromImage(canvas);
g.DrawImage(background, 0, 0, background.Width, background.Height);
Rectangle logoRect = new Rectangle(
background.Width - logo.Width - 20,
background.Height - logo.Height - 20,
logo.Width,
logo.Height
);
g.DrawImage(logo, logoRect);
canvas.Save("combined.png");
このコードでは、背景画像の右下にロゴ画像を重ねています。
ウォーターマーク、バナー生成、SNS用画像生成などでよく使うパターンです。
7-4. 透明PNGをDrawImageで描画する
透明部分を持つPNG画像も、DrawImageでそのまま描画できます。
C#using Image background = Image.FromFile("background.jpg");
using Image icon = Image.FromFile("icon.png");
using Bitmap canvas = new Bitmap(background.Width, background.Height);
using Graphics g = Graphics.FromImage(canvas);
g.DrawImage(background, 0, 0);
g.DrawImage(icon, 50, 50, icon.Width, icon.Height);
canvas.Save("transparent-result.png");
PNGのアルファチャンネルが保持されていれば、透明部分は背景に自然に重なります。
透明な出力画像を作りたい場合は、Bitmapを32bit ARGB形式で作成すると扱いやすくなります。
C#using System.Drawing.Imaging;
using Bitmap canvas = new Bitmap(500, 300, PixelFormat.Format32bppArgb);
using Graphics g = Graphics.FromImage(canvas);
g.Clear(Color.Transparent);
g.DrawImage(icon, 50, 50);
canvas.Save("transparent-output.png", ImageFormat.Png);
JPEGは透明度を保持できないため、透明な画像として保存したい場合はPNG形式を使います。
7-5. 透過・アルファブレンドを使った合成方法
画像全体を半透明にして合成したい場合は、ImageAttributesとColorMatrixを使います。
C#using System.Drawing;
using System.Drawing.Imaging;
public static void DrawImageWithOpacity(Graphics g, Image image, Rectangle destRect, float opacity)
{
ColorMatrix matrix = new ColorMatrix
{
Matrix33 = opacity
};
using ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(
image,
destRect,
0,
0,
image.Width,
image.Height,
GraphicsUnit.Pixel,
attributes
);
}
呼び出し例は次のとおりです。
C#using Image background = Image.FromFile("background.jpg");
using Image watermark = Image.FromFile("watermark.png");
using Bitmap canvas = new Bitmap(background.Width, background.Height);
using Graphics g = Graphics.FromImage(canvas);
g.DrawImage(background, 0, 0);
Rectangle rect = new Rectangle(50, 50, watermark.Width, watermark.Height);
DrawImageWithOpacity(g, watermark, rect, 0.4f);
canvas.Save("watermarked.png");
opacityには、0.0から1.0の値を指定します。
0.0f:完全に透明0.5f:50%の透明度1.0f:不透明
ウォーターマークや半透明の装飾画像を重ねたい場合に便利です。
8. DrawImageの画質を調整する方法
8-1. SmoothingModeの設定
SmoothingModeは、線や図形のアンチエイリアスに関係する設定です。
画像の拡大縮小そのものにはInterpolationModeのほうが大きく影響しますが、画像と一緒に図形や線を描く場合はSmoothingModeも設定しておくとよいでしょう。
C#using System.Drawing.Drawing2D;
g.SmoothingMode = SmoothingMode.AntiAlias;
高品質設定にする場合は、次のようにも書けます。
C#g.SmoothingMode = SmoothingMode.HighQuality;
図形、線、円、角丸矩形などを画像と一緒に描く場合に効果があります。
8-2. InterpolationModeの設定
画像の拡大縮小で最も重要なのがInterpolationModeです。
C#g.InterpolationMode = InterpolationMode.HighQualityBicubic;
用途別の目安は次のとおりです。
| 用途 | 推奨設定 |
|---|---|
| 写真の縮小 | HighQualityBicubic |
| 写真の拡大 | HighQualityBicubicまたはBicubic |
| ドット絵の拡大 | NearestNeighbor |
| 速度優先 | LowまたはNearestNeighbor |
| 品質優先 | HighQualityBicubic |
サムネイル作成では、まずHighQualityBicubicを試すのがおすすめです。
8-3. PixelOffsetModeの設定
PixelOffsetModeは、描画時のピクセル位置の扱いに関する設定です。
高品質なリサイズや細かい描画をしたい場合は、次のように設定します。
C#g.PixelOffsetMode = PixelOffsetMode.HighQuality;
画像の端がわずかにぼやける、1ピクセルずれて見える、といった場合には、PixelOffsetModeの設定が影響していることがあります。
ただし、すべてのケースで見た目が大きく変わるわけではないため、InterpolationModeやSmoothingModeとセットで調整するとよいでしょう。
8-4. CompositingQualityの設定
CompositingQualityは、複数の画像や半透明要素を合成するときの品質に関係します。
C#g.CompositingQuality = CompositingQuality.HighQuality;
透明PNGを重ねる、半透明のウォーターマークを描画する、複数の画像を合成する、といった処理では設定しておくと安心です。
高品質な画像処理を行う場合は、次のようにまとめて設定することが多いです。
C#using System.Drawing.Drawing2D;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
ただし、高品質設定は処理速度が遅くなる場合があります。大量の画像を一括処理する場合は、画質と速度のバランスを見ながら調整しましょう。
まとめ
Graphics.DrawImageは、C#で画像を描画・加工するための非常に重要なメソッドです。
基本的な使い方は、Graphicsを取得し、ImageまたはBitmapを用意して、DrawImageで描画するという流れです。
特に重要な書き方は次の3つです。
C#// 指定座標に描画
g.DrawImage(image, x, y);
// 指定サイズにリサイズして描画
g.DrawImage(image, x, y, width, height);
// 元画像の一部を切り抜いて描画
g.DrawImage(image, destRect, sourceRect, GraphicsUnit.Pixel);
画像をきれいに拡大縮小したい場合は、次のような品質設定を使います。
C#g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
また、Bitmapに描画すれば、加工した画像をPNGやJPEGとして保存できます。
Graphics.DrawImageを使いこなすと、Windows Formsアプリでの画像表示だけでなく、サムネイル作成、トリミング、画像合成、ウォーターマーク追加、透明PNGの描画など、幅広い画像処理を実装できます。
一方で、System.Drawing.Commonは.NET 6以降でWindows固有のライブラリとして扱われるため、LinuxやmacOS、サーバーサイドでの画像処理では、ImageSharpやSkiaSharpなどの利用も検討しましょう。Windows Formsで画像を扱う場合は、Graphics.DrawImageの基本を押さえておくことで、多くの描画処理に対応できるようになります。

