質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.38%

  • C#

    9490questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

【C#】画像にリッチテキストを合成したい

受付中

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 4,244
退会済みユーザー

退会済みユーザー

photoshopのように画像の上にテキストを編集できるようなWindows Formアプリを作りたいと思っています。

  1. pictureBoxに画像を表示
  2. 画像をクリックするとテキスト(richText)を入力できる
  3. 最終的に画像と文字を合成してファイルに出力する【合成処理】

というような処理をしたいのですが、どのような方法で実現できるのでしょうか?
Bitmapに文字を入れ込むことができるのはわかります。

しかしrichTextを合成する方法がわかりません。

GUI側(編集モード)では画像をクリックしたときにpictureBoxの上にrichTextBoxを配置して文字を入力するようにすれば良いと思います(ただしbackgroundをtransparencyにする方法は未解決です)。

3.の【合成処理】は、画像にrichTextを合成できればと思っていますが方法があればお教えください。

※キャプチャ以外の方法を模索しています
・画面キャプチャで合成だとフォームの上に別の描画があった場合に一緒にキャプチャされるのでこれを回避できる方法を探しています。
・また、Form上で文字追加をする場合は画像が縮小表示されて編集します。元画像が2000x2000pxの場合、例えば500x500pxに縮小表示して文字を入力し、3.の画像と文字を合成する処理の場合は元画像の大きさの2000x2000pxで合成するといった感じです。

※今回の質問はwindows form アプリですが、WPFは未経験ですが検討したいと思っています。

どうぞよろしくお願い致します。

開発環境:
.Net Framework 4.0
windows8 64bit
visualstudio 2012 express

アプリの動作環境:
windows Vista以上(32/64bit)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • lib

    2016/03/26 16:21

    windows formの開発は、.Net frameworkのバージョンによって、挙動の差異、ご使用のフレームワークによって異なり想定されない動きをするため、せめてご使用のバージョン、使用OS等の明記はお願いします。また、最近の傾向からWPFを使った設計もあるので合わせてご検討いただければ。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2016/03/27 12:26

    ご指摘ありがとうございます。追記させて頂きました。

    キャンセル

回答 3

+2

フォーム上で描写されているRichTextBoxのイメージ取る形でよければこんな感じでしょうか?
やってることは、RichTextBox(Control)の座標を元にキャプチャしてるだけです。
PictureBoxを指定すれば、RichTextBoxを埋め込んだ感じのイメージがとれるかと。

public static Bitmap ToBmp(Control control)
{
    control.Update();
    Bitmap bmp = new Bitmap(control.Width, control.Height);
    using (Graphics graphics = Graphics.FromImage(bmp))
    {
        graphics.CopyFromScreen(control.PointToScreen(Point.Empty), Point.Empty, control.Size);
    }
    return bmp;
}

注意:フォームに描画されていないものはこの方法でとれなかったはず。

追記

要件が追加されたので違うサンプルを。gdi32 BitBltを使う方法になります。
PrintWindowあたりでもいーんですが取れないことがあるんでこっちで。
BitBlt
PrintWindow

//using System.Drawing;
//using System.Windows.Forms;
//using System.Runtime.InteropServices;
[DllImport("gdi32.dll")]
private static extern int BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public static Bitmap CaptureControl(Control target)
{
    Bitmap caputuredBitmap;
    using (Graphics targetGraphic = target.CreateGraphics())
    {
        caputuredBitmap = new Bitmap(target.Width, target.Height);
        using (Graphics capturedGraphics = Graphics.FromImage(caputuredBitmap))
        {
            IntPtr targetDc = targetGraphic.GetHdc();
            IntPtr capturedDc = capturedGraphics.GetHdc();
            BitBlt(capturedDc, 0, 0, target.Width, target.Height, targetDc, 0, 0, 0xCC0020);
            targetGraphic.ReleaseHdc(targetDc);
            capturedGraphics.ReleaseHdc(capturedDc);
        }
    }

    return caputuredBitmap;
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/26 12:52

    ありがとうございます!
    これからコード実行を確認させていただきますが、キャプチャということは画面が他のフォームとかで隠れていたらダメでしょうか?
    もしダメならキャプチャ以外の方法を知りたいです。
    どうぞよろしくお願い致します。

    キャンセル

  • 2016/03/26 14:00

    横から失礼します。
    すいませんアイディアを募集するのは質問の姿勢としていかがでしょうか?
    そもそも、いただいた内容を検証した上でよりよい案と試行錯誤の上で実装していくことが設計なので、Aがだめだから他はありませんか。ということを聞いていることになります。もし質問が不適切であれば見直した上で再質問していただいたらどうでしょうか。
    定番のリンクを掲載します。https://teratail.com/blog/article/ba10https://teratail.com/help/avoid-asking もう一度質問を考え直していただけますか?案外その中で解決策がみえてくるものです。

    キャンセル

  • 2016/03/26 14:45 編集

    すみませんがここでその類の議論をここでやらないとダメでしょうか?
    質問内容は要点も書きましたし悪気は全くないです。
    補足は追加させていただきましたが、せっかく質問させていただいたのに回答がつかなくなると困るので別の議論で荒らさないでほしいのですが・・・。

    キャンセル

  • 2016/03/26 16:19

    設計の議論であれば、歓迎ですが、誤解を招きそうな表現がありましたため指摘しました。
    また、質問の改変内容から総合的に判断した上で、よい質問の方向とすることにしました。

    キャンセル

+1

フォームあるいはコントロールの表示イメージをファイルにする場合は、画面キャプチャーではなくDrawToBitmapメソッドの利用をお勧めします。DrawToBitmapメソッドでフォームやコントロールの表示イメージをビットマップにすることができます。
詳しい使い方はDrawToBitmapのリファレンスを参照してください。

しかしrichTextを合成する方法がわかりません。 

これについては、フォームアプリでは難しいかもしれません。RichTextBoxは背景色にTransparentを選択できないので、いい感じに合成できません。WPFアプリで作ることも検討された方が良いでしょう。

どうしてもフォームアプリで作るなら、リッチテキスト部分は別フォームにして、TransparencyKeyにリッチテキストの背景色を設定して重ねることで文字部分だけを合成することができますが、1枚絵への合成は自力で行う必要があり、親フォームの移動やサイズ変更に追従させる必要もあるので、面倒ではあります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/27 12:22

    ありがとうございます。
    Stack Overflowや海外サイトでも探していますが、どれもtextBoxやrichTextの背景を透明にしたり合成することはできないようなことしか書いていませんでした。
    解決策のコードが書いてあっても、コメントで「それは実行できない」という投稿が多く、自分も試しましたがダメでした。

    WebBrowserオブジェクトでWebサイトとして画像を表示させて画像の上のcontenteditable=trueで文字入力も検討しましたが、そもそもForm上の画像が100%の大きさで表示されるわけではなく、例えば2000x2000pixelの画像を500x500pixelで表示させて文字を入力し、実際の文字と画像の合成は2000x2000pixelでやりたいのでキャプチャ自体が使えません。

    それとWPFで作るほうがよいのですね。WPFは未経験ですがちょっと挑戦してみようと思います。
    アドバイスありがとうございました。

    キャンセル

0

ほぼ同じようなアプリ開発の経験があります。

Wordアートのように選択して再編集が無いのであればPictureBoxでもいいですが、
私なら、この手のお絵かきアプリはUserControlを使い、

this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);


というお決まりの設定をしておきます。

2000,2000のBitamapをメンバ変数m_BmpSourceで保持し、

protected override void OnPaint(PaintEventArgs e)
{
  base.OnPaint (e);
 
 //ここでm_BmpSourceをDrawImageで描画
}

と、あくまでも、m_BmpSourceを描画する処理だけにしておきます。
このm_BmpSource.Save()でキャプチャと同等ですね。

では、文字の合成ですが、
m_BmpSourceからGraphicsオブジェクトgBmpを取得し、
gBmp.DrawStringでメンバ変数の画像に描画してしまえばいいのです。
ブラシをカスタムすれば、色々なデザインに対応できますね。

最後にTextBoxの背景ですが、私が作ったアプリでは透過しないでOKの仕様でしたが
「FormのTransparencyKeyは子コントロールにも有効」というルールに乗っかってみましょう。

つまり、Form2をつくりそこにRitchTextBoxを全面に貼り付けます。
Form2は枠もコントロールBoxも無い丸坊主スタイルで。
RitchTextBoxの背景をWhiteにして、Form2のTransparencyKeyにWhiteを設定します。
Form2は枠なしで同じサイズのRitchTextBoxが乗っている訳なので、あたかも透明のRitchTextBoxになる訳です。

元のFormでクリックされた位置をPointToScreenで計算し、Form2を上に表示されてしまえば、
まるで透明のRitchTextBoxが乗っているようになります。

Form2がフォーカスを失ったイベントを確定とし、文字を取得しm_BmpSourceに描画、
Update(true)で画面に反映させる流れです。

ちょっと面倒で難しそうな事を書いていますが、ただの画面の組み合わせだけで
Graphicsの概念が分かっていればすぐに完成できると思いますよ。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/30 23:45

    ありがとうございます。とても参考になります!
    なかなかロジックが面倒ですがそのような方法があるのですね!
    ちょっと返信が遅れましたが、作業していたPCが故障したので実験していたものが中途半端になりましたが、結局BitmapにDrawStringでテキストを入力するようにしてみました。
    入力位置は、ただの四角の枠なので何でもpictureBoxの上に乗せればいいのでそれで入力位置とサイズを取得してGraphics.DrawStringする場所を割り出そうと思っています(まだやっていません)。

    Graphics.DrawStringだとrichTextのように文字の色や大きさを個別にできないのですが、もしかしたら文字のピクセル長が取れるようなのでそこでがんばって対応しようかと思っています。

    そしてなぜGraphics.DrawStringにしたかというと、pictureBoxで表示する画像の大きさが、pictureBoxのサイズ変更(ウインドウサイズ変更などで)に追従するようにしたかったからです。なのでキャプチャの方法だと実際の画像サイズと表示している画像サイズが違うので無理かなと思って採用しませんでした(もしかしたら可能なのかもしれませんが・・・)。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    9490questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。