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

More than 1 year has passed since last update.


最初に

初投稿です

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