2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【C# + SDL2】色調を用いて、画像の色違いを表現してみるサンプル

Last updated at Posted at 2018-01-15

#最初に
初投稿です
Qiitaの使い方はよくわかりません

・2018/01/18
修正案がありました
コードに色がつきました

#内容

 タイトルの通りです
C#とSDL2を使いました

 コードが複雑かも
まだ改善の余地はありそう

 白い画像なら色調で自由に色を変えられる、という点を使った小技です
(白以外だと、別の色に変えられない場合があるし、
黒だと色を変えること自体ができないため)

 この方法なら、色違いのキャラクターとかを表現できるはずです
おそらくSDL2でなくてもできる

 果たしてこれは誰かの役に立つのか?

#ソースコード

/*

使用言語 C#

SDL2の色調を用いて、
画像の色違いを表現してみるサンプル


画像が8ビットの時専用?の方法なことに注意

画像が32ビットの時はできない?らしいので注意


〇ソースコードを少し変更しました

8ビット形式以外の画像を読み込んだ時に例外を出すように変更

また、ブログにコードを載せた際に、一部のコードが抜け落ちていたので、
少しコードを修正

*/

using System;
using System.Collections.Generic;
using System.IO;
using SDL2;

namespace Test {
	static class Program {
		static void Main() {
			try {
				InitSDL();
				Start();
				QuitSDL();
			}
			catch {
				QuitSDL();
			}
		}

		static void InitSDL() {
			SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING);
			SDL_image.IMG_Init(SDL_image.IMG_InitFlags.IMG_INIT_PNG);
		}
		static void QuitSDL() {
			SDL.SDL_Quit();
			SDL_image.IMG_Quit();
		}

		static void Start() {
			var window = SDL.SDL_CreateWindow("画像色変えテスト",
			SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED,
			300, 300,
			SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN);
			var renderer = SDL.SDL_CreateRenderer(window,
			-1,
			SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED);

			var textureAry = CreateTextureList(renderer);
			var colorArgs = CreateArrayColorArgs(textureAry.Length);

			while (true) {
				var e = new SDL.SDL_Event();

				SDL.SDL_PollEvent(out e);

				if (e.type == SDL.SDL_EventType.SDL_QUIT) break;

				//描画内容の初期化
				SDL.SDL_RenderClear(renderer);
				//描画の開始
				for (var i = 0; i < textureAry.Length; i++) {
					//テクスチャの描画
					RenderTexture(renderer, textureAry[i], colorArgs[i]);
				}
				//描画の初期化
				SDL.SDL_RenderPresent(renderer);

				SDL.SDL_Delay(1000 / 30);
			}
		}

		//画像が8ビット形式かをチェック
		static unsafe void CheckImageTypeBit8(SDL.SDL_PixelFormat* format) {
			if (format->BitsPerPixel != 8) {
				throw new Exception("画像が8ビットでありません");
			}
		}

		//描画用テクスチャのリストを作る
		static unsafe IntPtr[] CreateTextureList(IntPtr renderer) {
			//テクスチャ用配列の宣言
			var texture_ary = default(IntPtr[]);

			//画像サーフェスを読み込み
			//(今回は、「test.png」を読み込みます)
			var surface_p = SDL_image.IMG_Load("test.png");

			//画像サーフェスのデータを取得
			var surface = (SDL.SDL_Surface*)surface_p;
			var format = (SDL.SDL_PixelFormat*)surface->format;

			//画像が8ビット形式かをチェック
			CheckImageTypeBit8(format);

			//画像サーフェスのカラーパレットデータを取得
			var pallete = (SDL.SDL_Palette*)format->palette;
			var colors = (SDL.SDL_Color*)pallete->colors;
			
			//テクスチャ用配列を作成
			texture_ary = new IntPtr[pallete->ncolors];
			//サーフェスの色データのコピー
			var colorArray = CreateColorDataCopy(pallete);


			//画像サーフェスで使われている色の数だけループが実行される
			for (var i = 0; i < pallete->ncolors; i++) {
				//サーフェスを色ごとに分割するためのループ
				for (var n = 0; n < pallete->ncolors; n++) {
					//サーフェスの色データをすべて白にする
					ColorPtrWhiteOut(colors, n);
					//違う色の部分は、透明にしておく
					//こうして、サーフェスを色ごとに分割できる
					if (i != n) ColorPtrHide(colors, n);
				}

				//切り分けたテクスチャの追加
				texture_ary[i] = SDL.SDL_CreateTextureFromSurface(renderer, surface_p);
				//サーフェスの色データを元に戻す
				ResetColorPtr(colorArray, pallete);
			}

			//使い終わったサーフェスは削除しよう
			SDL.SDL_FreeSurface(surface_p);

			return texture_ary;
		}
		static unsafe void ColorPtrWhiteOut(SDL.SDL_Color* colorPtr, int index) {
			//テクスチャの色を白にしておく
			//こうすることで、色調を使うことでどんな色にも変化させられる
			colorPtr[index].r = byte.MaxValue;
			colorPtr[index].g = byte.MaxValue;
			colorPtr[index].b = byte.MaxValue;
		}
		static unsafe void ColorPtrHide(SDL.SDL_Color* colorPtr, int index) {
			//違う色の部分は透明にしておく
			//そうしないと、画像の全域が同じ色で表示されてしまう
			colorPtr[index].a = byte.MinValue;
		}

		//サーフェスの色データのコピーを作る
		//
		//サーフェスの色データをいじるので、
		//元に戻すための配列コピーは必要
		static unsafe SDL.SDL_Color[] CreateColorDataCopy(SDL.SDL_Palette* pallete) {
			var colors = new SDL.SDL_Color[pallete->ncolors];

			for (var i = 0; i < colors.Length; i++) {
				colors[i] = ((SDL.SDL_Color*)pallete->colors)[i];
			}

			return colors;
		}
		//サーフェスのパレットカラーを元に戻す
		static unsafe void ResetColorPtr(SDL.SDL_Color[] colors, SDL.SDL_Palette* pallete) {

			for (var n = 0; n < pallete->ncolors; n++) {
				((SDL.SDL_Color*)pallete->colors)[n] = colors[n];
			}
		}

		//テクスチャの描画
		static void RenderTexture(IntPtr renderer, IntPtr texture, SDL.SDL_Color color) {
			//必ずブレンドモードにすること!
			//(ブレンドモードにしないと、正しく表示されないことがあるため)
			SDL.SDL_SetTextureBlendMode(texture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
			//テクスチャの色を指定する
			SDL.SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
			//テクスチャの描画
			SDL.SDL_RenderCopy(renderer, texture, IntPtr.Zero, IntPtr.Zero);
		}
		//乱数を使って色を指定しています
		//
		//もちろん、乱数を使わずに色指定してもよい
		static SDL.SDL_Color[] CreateArrayColorArgs(int length) {
			var rand = new Random();

			var colorArgs = new SDL.SDL_Color[length];

			for (var i = 0; i < colorArgs.Length; i++) {
				//テクスチャの色はかならず指定すること
				//指定しないで描画すると、真っ黒な画像が表示されてしまう
				//
				//色指定に必要なのは、RGB値の3つだけ
				//(A値は指定しても変化がないので不要)
				colorArgs[i].r = (byte)rand.Next(byte.MaxValue);
				colorArgs[i].g = (byte)rand.Next(byte.MaxValue);
				colorArgs[i].b = (byte)rand.Next(byte.MaxValue);
			}

			return colorArgs;
		}
	}
}

#最後に

ブログにのせたコードが読みにくいので、試しにこの記事を作りました
コードが読みやすかったらうれしい

↓ブログにのせたコード(一応)
http://mini09memo.blog.fc2.com/blog-entry-348.html

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?