HSV カラーマップから色選択
前回では、RGB バーを付けて色選択ができるようにしましたが、ペイントソフトでは、HSV や HLS などの色空間でカラーマップを用意して、そこから選択できるようにすると便利です。
今回は、よく使われる HSV 色空間のカラーマップを作って、色を選択できるようにします。
HSV は、H (色相)、S (彩度)、V (明度) から成ります。
今回は、よく使われる HSV 色空間のカラーマップを作って、色を選択できるようにします。
HSV は、H (色相)、S (彩度)、V (明度) から成ります。
スクリーンショット
左の色相部分をドラッグで、現在の色相を変更。
右の SV 部分をドラッグで、現在の色を下の色プレビュー上に表示。
ソースコード
009_hsvmap.c
#include "sptk.h" #define HSVMAP_SIZE 150 #define HSVMAP_H_W 15 #define WIDTH (10 + HSVMAP_H_W + 10 + HSVMAP_SIZE + 10) #define HEIGHT (10 + HSVMAP_SIZE + 10 + 50 + 10) #define DRAGF_H 1 #define DRAGF_SV 2 SPTK_IMAGE *image; int hsv_h = 0, /* 現在のH: 0-359 */ hsv_h_y = 0, /* H のカーソル y 位置 */ dragflag = 0; SPTK_POINT hsv_sv_pos; /** HSV -> RGB * h:0-359 s,v:0.0-1.0 */ uint32_t hsv_to_rgb(int h,double s,double v) { double r,g,b,c1,c2,c3,t; int rr,gg,bb; if(s == 0) r = g = b = v; else { t = ((h * 6) % 360) / 360.0; c1 = v * (1 - s); c2 = v * (1 - s * t); c3 = v * (1 - s * (1 - t)); switch(h / 60) { case 0: r = v; g = c3; b = c1; break; case 1: r = c2; g = v; b = c1; break; case 2: r = c1; g = v; b = c3; break; case 3: r = c1; g = c2; b = v; break; case 4: r = c3; g = c1; b = v; break; case 5: r = v; g = c1; b = c2; break; } } rr = (int)(r * 255 + 0.5); gg = (int)(g * 255 + 0.5); bb = (int)(b * 255 + 0.5); return (rr << 16) | (gg << 8) | bb; } /** H バーの初期描画 */ void draw_hsv_h() { int i; uint32_t col; for(i = 0; i < HSVMAP_SIZE; i++) { col = hsv_to_rgb(i * 359 / (HSVMAP_SIZE - 1), 1, 1); sptk_image_hline(image, 10, 10 + i, HSVMAP_H_W, col); } /* XOR カーソル */ sptk_image_hline(image, 10, 10, HSVMAP_H_W, SPTK_COL_XOR); } /** SV 部分の描画 */ void draw_hsv_sv() { int ix,iy; uint32_t col; for(iy = 0; iy < HSVMAP_SIZE; iy++) { for(ix = 0; ix < HSVMAP_SIZE; ix++) { col = hsv_to_rgb(hsv_h, (double)ix / (HSVMAP_SIZE - 1), 1 - (double)iy / (HSVMAP_SIZE - 1)); sptk_image_setpixel(image, 10 + HSVMAP_H_W + 10 + ix, 10 + iy, col); } } } /** SV カーソル描画 */ void draw_sv_cursor() { int x,y; x = 10 + HSVMAP_H_W + 10 + hsv_sv_pos.x - 3; y = 10 + hsv_sv_pos.y - 3; sptk_image_box(image, x, y, 7, 7, SPTK_COL_XOR); sptk_update(NULL, x, y, 7, 7, 5); } void winhandle(SPTK_EVENT *ev) { uint32_t col; int x,y; switch(ev->type) { case SPTK_EVENT_BTTDOWN: if(ev->mouse.btt == SPTK_MOUSEBTT_LEFT && dragflag == 0) { if(ev->mouse.x >= 10 && ev->mouse.x < 10 + HSVMAP_H_W && ev->mouse.y >= 10 && ev->mouse.y < 10 + HSVMAP_SIZE) { /* H 部分 */ dragflag = DRAGF_H; sptk_grab(NULL); } else if(ev->mouse.x >= 10 + HSVMAP_H_W + 10 && ev->mouse.x < 10 + HSVMAP_H_W + 10 + HSVMAP_SIZE && ev->mouse.y >= 10 && ev->mouse.y < 10 + HSVMAP_SIZE) { /* SV 部分 */ dragflag = DRAGF_SV; hsv_sv_pos.x = ev->mouse.x - (10 + HSVMAP_H_W + 10); hsv_sv_pos.y = ev->mouse.y - 10; draw_sv_cursor(); sptk_grab(NULL); } } break; case SPTK_EVENT_BTTUP: if(ev->mouse.btt == SPTK_MOUSEBTT_LEFT && dragflag) { if(dragflag == DRAGF_SV) draw_sv_cursor(); dragflag = 0; sptk_ungrab(); } break; case SPTK_EVENT_MOUSEMOVE: if(dragflag == DRAGF_H) { /*---- H 部分 ----*/ /* H の XOR バーを移動 */ sptk_image_hline(image, 10, 10 + hsv_h_y, HSVMAP_H_W, SPTK_COL_XOR); hsv_h_y = ev->mouse.y - 10; if(hsv_h_y < 0) hsv_h_y = 0; else if(hsv_h_y >= HSVMAP_SIZE) hsv_h_y = HSVMAP_SIZE - 1; hsv_h = hsv_h_y * 359 / (HSVMAP_SIZE - 1); sptk_image_hline(image, 10, 10 + hsv_h_y, HSVMAP_H_W, SPTK_COL_XOR); sptk_update(NULL, 10, 10, HSVMAP_H_W, HSVMAP_SIZE, 5); /* SV を再描画 */ draw_hsv_sv(); sptk_update(NULL, 10 + HSVMAP_H_W + 10, 10, HSVMAP_SIZE, HSVMAP_SIZE, 5); } else if(dragflag == DRAGF_SV) { /*---- SV 部分 ----*/ x = ev->mouse.x - (10 + HSVMAP_H_W + 10); y = ev->mouse.y - 10; if(x < 0) x = 0; else if(x >= HSVMAP_SIZE) x = HSVMAP_SIZE - 1; if(y < 0) y = 0; else if(y >= HSVMAP_SIZE) y = HSVMAP_SIZE - 1; /* XOR カーソル移動 */ draw_sv_cursor(); hsv_sv_pos.x = x; hsv_sv_pos.y = y; draw_sv_cursor(); /* 色プレビューを更新 */ col = hsv_to_rgb(hsv_h, (double)x / (HSVMAP_SIZE - 1), 1 - (double)y / (HSVMAP_SIZE - 1)); sptk_image_fillbox(image, 11, 10 + HSVMAP_SIZE + 11, 48, 48, col); sptk_update(NULL, 11, 10 + HSVMAP_SIZE + 11, 48, 48, 5); } break; case SPTK_EVENT_WINDOW_CLOSE: sptk_quit(); break; } } int main() { sptk_init("test", WIDTH, HEIGHT); sptk_window_set_handle(winhandle); image = sptk_window_get_image(); draw_hsv_h(); draw_hsv_sv(); sptk_image_box(image, 10, 10 + HSVMAP_SIZE + 10, 50, 50, 0); sptk_run(); return 0; }
解説
HSV -> RGB
HSV の色を RGB 値に変換する関数として、hsv_to_rgb() を用意してあります。
H は 0〜359、S,V は 0.0〜1.0 の値で指定します。
H は 0〜359、S,V は 0.0〜1.0 の値で指定します。
H バー部分
左側の色相を選択する部分では、縦に H の値を変化させ、SV は最大値、の色をサンプルとしています。
現在選択されている色相の位置には、XOR で横線のカーソルを描画しています。
現在選択されている色相の位置には、XOR で横線のカーソルを描画しています。
SV マップ部分
右側の色一覧は、現在の色相に、x 方向は彩度、y 方向は明度を変化させたものです。
この部分で左ドラッグすると、カーソル位置の色を取得し、下の色プレビューに現在色を表示します。
この部分で左ドラッグすると、カーソル位置の色を取得し、下の色プレビューに現在色を表示します。
他の形
今回は、作りやすいように四角形の形で HSV マップを作りましたが、他によく見る形としては、H を円状にしたものがあります。
それらは今やるにはちょっと難しいので、また後で説明します。
それらは今やるにはちょっと難しいので、また後で説明します。