2010年4月4日日曜日

カラーピッカーを作るその3(RGB変換とAIRイベント)



前回カラーピッカーを作るための画面をMXMLで作成したのですが、これにイベントをつけました。
イベントの付け方なんですが、通常は


<mx:Button x="31" y="36" label="ウィンドウ作成" click="createWindow();" />


のようにmxmlファイルのタグでイベントと呼び出すメソッドの紐付けをするのですが、これをするとビューとロジックの分離が上手く行かないのが嫌で、こちらのサイトを参考にビューとロジックを分けて見ました。




このサイトでは、<helper>タグを利用しているようですが、自分は<logic>タグを利用しました。使い方は一緒です。



  1. IMXMLObjectインタフェースを継承したオブジェクトを生成する
  2. イベントハンドラを設定するメソッドを作成する
  3. 作成したメソッドを initialized メソッドの中でFlexEvent.CREATION_COMPLETEイベントに紐づくようにする

という形になります。


HSB(色相、彩度、明度)で設定した色をRGBに変更するロジックは




から拝借しました。


ソースを全部のっけます。


colorPicker.ColorHSB.as ― HSBからRGBへの変換ロジックのあるクラス


package colorPicker {
public final class ColorHSB
{
public var h:uint = 0;    // hue
public var s:uint = 0;    // saturation
public var b:uint = 0;    // brightness

public function ColorHSB(hue:uint = 0, saturation:uint = 0, brightness:uint = 0)
{
h = hue;
s = saturation;
b = brightness;
}

public function to_object():Object
{
return {h:h, s:s, b:b };
}

public static function rgb_to_hsb(color:uint):ColorHSB
{
var r:int = (color >> 16) & 0xFF;
var g:int = (color >> 8) & 0xFF;
var b:int = color & 0xFF;

var min:int = Math.min(Math.min(r, g), b);
var max:int = Math.max(Math.max(r, g), b);

var delta:int = max - min;

var brightness:int = max;
var saturation:Number = (max == 0) ? 0 : Number(delta) / max;

var hue:Number = 0;
if (saturation != 0) {
if (r == brightness) hue = (60 * (g - b)) / delta;
else if (g == brightness) hue = 120 + (60 * (b - r)) / delta;
else    hue = 240 + (60 * (r - g)) / delta;

if (hue < 0) hue += 360;
}

return new ColorHSB(Math.round(hue), Math.round(saturation * 100), Math.round((brightness / 255) * 100) );
}

public static function hsb_to_rgb(hsb:ColorHSB):uint
{
var brightness:Number = hsb.b / 100;
if (brightness == 0) return 0;
var hue:Number = (hsb.h % 360) / 60;
var saturation:Number = hsb.s / 100;

var i:Number = Math.floor(hue);
var p:Number = (1 - saturation);
var q:Number = (1 - (saturation * (hue - i)));
var t:Number = (1 - (saturation * (1 - (hue - i))));

var r:Number, g:Number, b:Number;
switch (i) {
case 0: r = 1; g = t; b = p; break;
case 1: r = q; g = 1; b = p; break;
case 2: r = p; g = 1; b = t; break;
case 3: r = p; g = q; b = 1; break;
case 4: r = t; g = p; b = 1; break;
case 5: r = 1; g = p; b = q; break;
}

return ((Math.round(r * 255 * brightness) & 0xFF) << 16) | ((Math.round(g * 255 * brightness) & 0xFF) << 8) | (Math.round(b * 255 * brightness) & 0xFF);
}
}
}


colorPicker.ColorPickerEvent.as ― イベントハンドラを登録しているクラス


package colorPicker {
import mx.core.*;
import mx.events.*;
import colorPicker.*;
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.desktop.*;

public class ColorPickerEvent implements IMXMLObject
{
// 新しく生成するウィンドウを格納する変数
public var win:NativeWindow;

private var view:ColorPicker;

/**
* インタフェースから継承したメソッド
*
* @param Object document
* @param String id
*/
public function initialized(document:Object, id:String):void
{
view = document as ColorPicker
view.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
}


/**
* アプリケーション生成完了時に呼び出されるメソッド
*
* @param FlexEvent event
*/
private function creationCompleteHandler(event:FlexEvent):void
{
// 初期値設定
redraw(0xFFFFFF);

// イベント設定
view.colorText.addEventListener(FlexEvent.ENTER, colorTextEnterEvent);
view.tx_r.addEventListener(NumericStepperEvent.CHANGE, rgbStepperEvent);
view.tx_g.addEventListener(NumericStepperEvent.CHANGE, rgbStepperEvent);
view.tx_b.addEventListener(NumericStepperEvent.CHANGE, rgbStepperEvent);
view.rSlider.addEventListener(SliderEvent.CHANGE, rgbSliderEvent);
view.gSlider.addEventListener(SliderEvent.CHANGE, rgbSliderEvent);
view.bSlider.addEventListener(SliderEvent.CHANGE, rgbSliderEvent);
view.tx_h.addEventListener(NumericStepperEvent.CHANGE, hsvStepperEvent);
view.tx_s.addEventListener(NumericStepperEvent.CHANGE, hsvStepperEvent);
view.tx_v.addEventListener(NumericStepperEvent.CHANGE, hsvStepperEvent);
view.hSlider.addEventListener(SliderEvent.CHANGE, hsvSliderEvent);
view.sSlider.addEventListener(SliderEvent.CHANGE, hsvSliderEvent);
view.vSlider.addEventListener(SliderEvent.CHANGE, hsvSliderEvent);
}


/**
* HSVのNumericStepperが変更されたときのイベント
*
* @param NumericStepperEvent event
*/
private function hsvStepperEvent(event:NumericStepperEvent):void
{
var hsb:ColorHSB = new ColorHSB(view.tx_h.value,
view.tx_s.value,
view.tx_v.value);
var color:uint = ColorHSB.hsb_to_rgb(hsb);
redraw(color);
}


/**
* RGBのNumericStepperが変更された時のイベント
*
* @param NumericStepperEvent event
*/
private function rgbStepperEvent(event:NumericStepperEvent):void
{
var r:uint     = view.tx_r.value;
var g:uint     = view.tx_g.value;
var b:uint     = view.tx_b.value;
var color:uint = r << 16 | g << 8 | b;
redraw(color);
}


/**
* HSVのスライダーが変更された場合のイベント
*
* @param SliderEvent event
*/
private function hsvSliderEvent(event:SliderEvent):void
{
var hsb:ColorHSB = new ColorHSB(view.hSlider.value,
view.sSlider.value,
view.vSlider.value);
var color:uint = ColorHSB.hsb_to_rgb(hsb);
redraw(color);
}

/**
* RGBのスライダーが変更された場合のイベント
*
* @param SliderEvent event
*/
private function rgbSliderEvent(event:SliderEvent):void
{
var r:uint     = view.rSlider.value;
var g:uint     = view.gSlider.value;
var b:uint     = view.bSlider.value;
var color:uint = r << 16 | g << 8 | b;
redraw(color);
}


/**
* 色指定テキストに値を設定する
*
* @param uint color
*/
private function updateColorText(color:uint):void
{
var colorStr:String = rightAlign(color.toString(16), 6, "0");
view.colorText.text = "#" + colorStr.toUpperCase();
}

/**
* 指定した桁数になるまで、指定した文字列を右詰する
*
* @param String str
* @param uint   digit
* @param String fill
* @reutrn String
*/
private function rightAlign(str:String, digit:uint, fill:String):String
{
var i:int = digit - str.length;
while(i-- > 0) str = fill + str;

return str;
}

/**
* エンタキー押下時のイベント
*
*/
private function colorTextEnterEvent(event:FlexEvent):void
{
var s:String = view.colorText.text.toUpperCase();
var d:String = "";
for (var i:int = 0; i < s.length; i++) {
var c:int = s.charCodeAt(i);
if ( ( c < 48 || c > 57) && (c < 65 || c > 70 ) ) continue;
d += String.fromCharCode(c);
}
var color:uint = uint(d == '' ? 0 : '0x' + d);
redraw(color);
}


/**
* 指定された色にあわせてバーと数値を変更する
*
* @param uint color
*/
private function redrawBars(color:uint):void
{
var r:uint = (color >> 16) & 0xFF;
var g:uint = (color >> 8) & 0xFF;
var b:uint = color & 0xFF;

var hsb:ColorHSB = ColorHSB.rgb_to_hsb(color);

// 値を設定する
view.rSlider.value = r;
view.tx_r.value = r;
view.gSlider.value = g;
view.tx_g.value = g;
view.bSlider.value = b;
view.tx_b.value = b;
view.tx_h.value = hsb.h;
view.hSlider.value = hsb.h;
view.tx_s.value = hsb.s;
view.sSlider.value = hsb.s;
view.tx_v.value = hsb.b;
view.vSlider.value = hsb.b;
}


/**
* 指定した色を元に画面を再描画する
*
* @param uint color
*/
private function redraw(color:uint):void {
updateColorText(color);
view.colorCanvas.setStyle("backgroundColor", color);
redrawBars(color);
}
}
}


ColorPicker.mxml


<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" height="380" width="560" backgroundColor="#ffffff" xmlns:logic="colorPicker.*">
<logic:ColorPickerEvent id="logic" />
<mx:HBox backgroundColor="#d4d0c8" horizontalScrollPolicy="off" verticalScrollPolicy="off" height="90%" width="100%">
<mx:VBox backgroundColor="#d4d0c8" paddingLeft="10" paddingRight="10" paddingTop="10">
<mx:HBox>
<mx:TextInput id="colorText" maxChars="7" restrict="#a-fA-F0-9"/>
</mx:HBox>
<mx:Canvas id="colorCanvas" width="200" height="200"/>
</mx:VBox>
<mx:VRule strokeColor="#808080" shadowColor="#FFFFFF" height="100%"/>
<mx:VBox paddingLeft="10" paddingTop="10">
<mx:HBox>
<mx:Label text="R" fontSize="12" fontWeight="bold" />
<mx:NumericStepper id="tx_r" maxChars="3" minimum="0" maximum="255" stepSize="1"/>
<mx:HSlider id="rSlider" minimum="0" maximum="255"
showDataTip="false"
tickColor="black"
snapInterval="1" tickInterval="10"
allowTrackClick="true"
liveDragging="true"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="G" fontSize="12" fontWeight="bold" />
<mx:NumericStepper id="tx_g" maxChars="3" minimum="0" maximum="255" stepSize="1"/>
<mx:HSlider id="gSlider" minimum="0" maximum="255"
showDataTip="false"
tickColor="black"
snapInterval="1" tickInterval="10"
allowTrackClick="true"
liveDragging="true"/>
</mx:HBox>
<mx:HBox paddingBottom="20">
<mx:Label text="B" fontSize="12" fontWeight="bold" />
<mx:NumericStepper id="tx_b" maxChars="3" minimum="0" maximum="255" stepSize="1"/>
<mx:HSlider id="bSlider" minimum="0" maximum="255"
showDataTip="false"
tickColor="black"
snapInterval="1" tickInterval="10"
allowTrackClick="true"
liveDragging="true"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="H" fontSize="12" fontWeight="bold" />
<mx:NumericStepper id="tx_h" maxChars="3" minimum="0" maximum="360" stepSize="1"/>
<mx:HSlider id="hSlider" minimum="0" maximum="360"
showDataTip="false"
tickColor="black"
snapInterval="1" tickInterval="10"
allowTrackClick="true"
liveDragging="true"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="S" fontSize="12" fontWeight="bold" />
<mx:NumericStepper id="tx_s" maxChars="3" minimum="0" maximum="100" stepSize="1"/>
<mx:HSlider id="sSlider" minimum="0" maximum="100"
showDataTip="false"
tickColor="black"
snapInterval="1" tickInterval="10"
allowTrackClick="true"
liveDragging="true"/>
</mx:HBox>
<mx:HBox>
<mx:Label text="V" fontSize="12" fontWeight="bold" />
<mx:NumericStepper id="tx_v" maxChars="3" minimum="0" maximum="100" stepSize="1"/>
<mx:HSlider id="vSlider" minimum="0" maximum="100"
showDataTip="false"
tickColor="black"
snapInterval="1" tickInterval="10"
allowTrackClick="true"
liveDragging="true"/>
</mx:HBox>
</mx:VBox>
</mx:HBox>
</mx:WindowedApplication>