[AS3] RGB←→HSV を劣化なしで変換。あとカラーピッカー

2009/07/30

こんばんは。きんくまです。

えーと先週はエヴァの破とお台場のガンダム見てきました。
どっちも面白かったです。特に破はすごいです。すごすぎです。
画面の密度が濃いです。あと、TV版はもう関係ないんですね。
話は完全にオリジナルになってました。だから序よりも断然面白いですね。

ガンダムの方は良かったんですが、なんというか「思ったよりも小さかった。」
というのが正直な感想です。いや、決して悪かったわけではありません。
むしろ細かいところまでよくできていまして、ガンプラをそのままひきのばしたような精巧さ。といいますか。
あの大きさだったら、次回はもっと大胆に動かして欲しいなあ、と。
無理か、、。

さて、RGBとHSVの変換です。

 
クラゲ時計ではRGBとHSVの変換を下記の本から移植したクラスを使っています。

>> 詳解 画像処理プログラミング / 昌達 慶仁 著 / ソフトバンククリエイティブ

このクラスを使うとRGBとHSVの変換をしても情報が劣化しないです。
そのかわりHSVのとる値が変わっていて
H : 0 ~ 3066
S : 0 ~ 511
V : 0 ~ 511
となっています。すべてint型です。

デモは単純なカラーピッカーだけだとさみしいので、モザイク処理もつけてみました。
モザイク処理はもととなる画像をMatrixをつかって拡大するとできます。

以下ソースです。

package
{
  import com.kuma_de.ColorUtil;
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.MovieClip;
  import flash.display.Sprite;
  import flash.display.Stage;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.geom.Matrix;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import flash.text.TextField;
  import flash.ui.Mouse;
  import flash.utils.getDefinitionByName;

  public class Main extends Sprite
  {
    public var spoit:MovieClip;
    public var photo:MovieClip;
    public var photoBmd:BitmapData;
    public var isSpoitCursor:Boolean = false;
    public var currentColorBmd:BitmapData;
    public var currentX:Number;
    public var currentY:Number;
    public var currentColor:int;
    public var h:TextField;
    public var s:TextField;
    public var v:TextField;
    public var r:TextField;
    public var g:TextField;
    public var b:TextField;
    public var mozaicBmd:BitmapData;
    public var mozaicSrcBmd:BitmapData;

    public function Main()
    {
      init();
      spoit.visible = false;
      this.stage.addEventListener(Event.ENTER_FRAME, update);
    }

    private function update(e:Event):void
    {
      var t:Stage = e.currentTarget as Stage;
      currentX = t.mouseX;
      currentY = t.mouseY;

      var hitTestFlag:Boolean = photoBmd.hitTest(new Point(5, 5), 0, new Point(currentX, currentY));
      if (hitTestFlag) {
        setCurrentColor();
        setColorText();
        setMozaic();
        setSpoitCursor(true);
      }else {
        setSpoitCursor(false);
      }
    }

    private function setSpoitCursor(isSpoit:Boolean):void
    {
      if (isSpoit) {
        Mouse.hide();
        spoit.visible = true;
        isSpoitCursor = true;
        spoit.x = currentX;
        spoit.y = currentY;
      }else {
        Mouse.show();
        spoit.visible = false;
        isSpoitCursor = false;
      }
    }

    private function setCurrentColor():void
    {
      var pt:Point = photo.globalToLocal(new Point(currentX, currentY));
      currentColor = photoBmd.getPixel(pt.x, pt.y);
      currentColorBmd.fillRect(currentColorBmd.rect, currentColor);
    }

    private function init():void
    {
      var myc:Class = getDefinitionByName("photo") as Class;
      var b:BitmapData = new myc(0, 0);
      photoBmd = new BitmapData(b.width + 10, b.height + 10, true, 0);
      photoBmd.copyPixels(b, b.rect, new Point(5, 5));
      photo = new MovieClip();
      photo.addChild(new Bitmap(photoBmd));
      photo.x = 5;
      photo.y = 5;
      this.addChild(photo);

      currentColorBmd = new BitmapData(25, 25, false, 0xff000000);
      var curBm:Bitmap = new Bitmap(currentColorBmd);
      this.addChild(curBm);
      curBm.x = 227;
      curBm.y = 59;

      mozaicBmd = new BitmapData(70, 70, true, 0xffffffff);
      var moB:Bitmap = new Bitmap(mozaicBmd);
      moB.x = 309;
      moB.y = 19;
      this.addChild(moB);
      mozaicSrcBmd = new BitmapData(7, 7);


      initColorText();
      setChildIndex(spoit, numChildren - 1);
    }

    private function initColorText():void
    {
      h.text = "";
      s.text = "";
      v.text = "";
      r.text = "";
      g.text = "";
      b.text = "";
    }

    private function setColorText():void
    {
      var rgb:Vector.<int> = ColorUtil.HextoRGB(currentColor);
      var hsv:Vector.<int> = ColorUtil.RGBtoHSV(rgb[0], rgb[1], rgb[2]);
      h.text = hsv[0].toString();
      s.text = hsv[1].toString();
      v.text = hsv[2].toString();
      r.text = rgb[0].toString();
      g.text = rgb[1].toString();
      b.text = rgb[2].toString();
    }

    private function setMozaic():void
    {
      var pt:Point = photo.globalToLocal(new Point(currentX, currentY));
      mozaicSrcBmd.fillRect(mozaicSrcBmd.rect, 0xffffffff);
      var b:BitmapData = mozaicSrcBmd.clone();
      b.fillRect(b.rect, 0xff000000);
      mozaicSrcBmd.copyPixels(
        photoBmd,
        new Rectangle(pt.x - 3, pt.y - 3, 7, 7),
        new Point(0, 0),
        b,
        new Point(0, 0),
        true
        );
      var mtx:Matrix = new Matrix();
      mtx.scale(10, 10);
      mozaicBmd.draw(mozaicSrcBmd, mtx);
    }
  }

}

こっちが移植したクラスです。あと、16進数の変換のあたりは勝手につけといた。

/*
 * Cの書籍からの移植
 * 詳解 画像処理プログラミング / 昌達 慶仁 著 / ソフトバンククリエイティブ
 * http://www.sbcr.jp/books/products/detail.asp?sku=4797344370
 */
package com.kuma_de
{
  public class ColorUtil
  {
    private static const SCALE:Number = 255;
    private static const hSCALE:Number = 256;
    private static const GETA:Number = 2;
    private static const hGETA:Number = 2;

    /**
     * RGBからHSVを得る
     *
     * @param r Red 0~255
     * @param g Green 0~255
     * @param b Blue 0~255
     * @return Vector.<int> H:0~3066, S:0~511, V:0~511
     */
    public static function RGBtoHSV(r:int, g:int, b:int):Vector.<int>
    {
      var rr:Number, gg:Number, bb:Number;
      var hh:Number, ss:Number, vv:Number;
      var cmax:Number, cmin:Number, cdes:Number;

      rr = Number(r);
      gg = Number(g);
      bb = Number(b);

      cmax = (rr > gg) ? rr : gg;
      if (bb > cmax) {
        cmax = bb;
      }

      cmin = (rr < gg) ? rr : gg;
      if (bb < cmin) {
        cmin = bb;
      }

      cdes = cmax - cmin;
      vv = cmax;
      if (cdes != 0) {
        ss = cdes * SCALE / cmax;
        if (cmax == rr) {
          hh = (gg - bb) * SCALE / cdes;
        }else if (cmax == gg) {
          hh = (bb - rr) * SCALE / cdes + 2 * hSCALE;
        }else {
          hh = (rr - gg) * SCALE / cdes + 4 * hSCALE;
        }
      }else if (cmax != 0) {
        ss = cdes * SCALE / cmax;
        hh = 0;
      }else {
        ss = 0;
        hh = 0;
      }
      if (hh < 0) {
        hh += 6 * hSCALE;
      }

      return Vector.<int>([int(hh * hGETA), int(ss * hGETA), int(vv * hGETA)]);
    }

    /**
     * RGBからHSVを得る
     *
     * @param h Hue 0~3066
     * @param s Saturation 0~511
     * @param v Value 0~511
     * @return Vector.<int> R:0~255, G:0~255, B:0~255
     */
    public static function HSVtoRGB(h:int, s:int, v:int):Vector.<int>
    {
      var rr:Number, gg:Number, bb:Number;
      var hh:Number, ss:Number, vv:Number;
      var r:Number, g:Number, b:Number;

      if (h == 6 * hGETA * hSCALE) {
        h = 0;
      }
      hh = Number(h / hGETA);
      ss = Number(s / GETA);
      vv = Number(v / GETA);

      switch(int(hh / hSCALE)) {
      case 0:
        rr = SCALE;
        gg = hh;
        bb = 0;
        break;
      case 1:
        rr = 2 * hSCALE - hh;
        gg = SCALE;
        bb = 0;
        break;
      case 2:
        rr = 0;
        gg = SCALE;
        bb = hh - 2 * hSCALE;
        break;
      case 3:
        rr = 0;
        gg = 4 * hSCALE - hh;
        bb = SCALE;
        break;
      case 4:
        rr = hh - 4 * hSCALE;
        gg = 0;
        bb = SCALE;
        break;
      case 5:
        rr = SCALE;
        gg = 0;
        bb = 6 * hSCALE - hh;
        break;
      }

      rr = (rr + (SCALE - rr) * (SCALE - ss) / SCALE) * vv / SCALE;
      gg = (gg + (SCALE - gg) * (SCALE - ss) / SCALE) * vv / SCALE;
      bb = (bb + (SCALE - bb) * (SCALE - ss) / SCALE) * vv / SCALE;

      r = int(rr);
      g = int(gg);
      b = int(bb);
      if (r > 255) { r = 255 }
      if (g > 255) { g = 255 }
      if (b > 255) { b = 255 }
      return Vector.<int>([r, g, b]);
    }

    private static function sgn(x:Number):Number
    {
      return (x < 0) ? -1 : 1;
    }

    private static function CINT(x:Number):int
    {
      return int(x + sgn(x) * 0.5);
    }

    private static var FL:Number;

    //ここから追加した分

    /**
     * RGBから16進数を得る
     *
     * @param r Red 0~255
     * @param g Green 0~255
     * @param b Blue 0~255
     * @return int 0~0xffffff
     */
    public static function RGBtoHex(r:int, g:int, b:int):int
    {
      return r << 16 | g << 8 | b;
    }

    /**
     * ARGBから16進数を得る
     *
     * @param a Alpha 0~255
     * @param r Red 0~255
     * @param g Green 0~255
     * @param b Blue 0~255
     * @return int 0~0xffffffff
     */
    public static function ARGBtoHex(a:int, r:int, g:int, b:int):int
    {
      return a << 24 | r << 16 | g << 8 | b;
    }

    /**
     * 16進数からRGBを得る
     *
     * @param hex 16進数
     * @return Vector.<int> R:0~255, G:0~255, B:0~255
     */
    public static function HextoRGB(hex:int):Vector.<int>
    {
      var r:int = hex >> 16;
      var g:int = hex >> 8 & 0xff;
      var b:int = hex & 0xff;
      return Vector.<int>([r, g, b]);
    }

    /**
     * 16進数からRGBを得る
     *
     * @param hex 16進数
     * @return Vector.<int> A:0~255, R:0~255, G:0~255, B:0~255
     */
    public static function HextoARGB(hex:int):Vector.<int>
    {
      var a:int = hex >> 24;
      var r:int = hex >> 16 & 0xff;
      var g:int = hex >> 8 & 0xff;
      var b:int = hex & 0xff;
      return Vector.<int>([a, r, g, b]);
    }
  }
}
LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る