ホーム > 製品情報 > SophiaFramework UNIVERSE > BREW 文字認識アプリ "Recog" > -4-

BREW 文字認識アプリ "Recog"

Bitmap クラスの作成

Bitmap クラス作成の意図

ビットマップが読み込める Bitmap クラスを作成します。

Bitmap クラスのヘッダ ファイル

- Bitmap.hpp -

#if !defined(__BITMAP_HPP)
#define __BITMAP_HPP
#include <SophiaFramework.hpp>

SFMTYPEDEFCLASS(Bitmap)
class Bitmap {
private:
  SFBBitmapSmp _pbitmap;  // ビットマップへのポインタ
  SFBDIBSmp _pdib;        // DIB 形式のビットマップへのポインタ

  SFCError GetDib(Void);  // DIB 形式のビットマップを取得する
  SFCError Copy(BitmapConstRef bitmap);  // ビットマップをコピーする

public:
  // コンストラクタ
  Bitmap(Void);
  Bitmap(ACharConstPtr file);
  Bitmap(WCharConstPtr file);
  Bitmap(SFXAnsiStringConstRef file);
  Bitmap(SFXWideStringConstRef file);
  Bitmap(ACharConstPtr resFile, UInt16 id);
  Bitmap(WCharConstPtr resFile, UInt16 id);
  Bitmap(SFXAnsiStringConstRef resFile, UInt16 id);
  Bitmap(SFXWideStringConstRef resFile, UInt16 id);
  Bitmap(SFBBitmapSmpConstRef pbitmap);
  Bitmap(BitmapConstRef bitmap);
  ~Bitmap(Void);  // ディストラクタ
  // ファイルからのビットマップの読み込み
  SFCError LoadBitmap(ACharConstPtr file);
  SFCError LoadBitmap(WCharConstPtr file);
  SFCError LoadBitmap(SFXAnsiStringConstRef file);
  SFCError LoadBitmap(SFXWideStringConstRef file);
  // リソース ファイルからのビットマップの読み込み
  SFCError LoadResBitmap(ACharConstPtr resFile, UInt16 id);
  SFCError LoadResBitmap(WCharConstPtr resFile, UInt16 id);
  SFCError LoadResBitmap(SFXAnsiStringConstRef resFile, UInt16 id);
  SFCError LoadResBitmap(SFXWideStringConstRef resFile, UInt16 id);
  // ビットマップのコピーを作成
  SFCError Create(SFBBitmapSmpConstRef pbitmap);
  // 代入演算子
  BitmapConstRef operator =(SFBBitmapSmpConstRef pbitmap);
  BitmapConstRef operator =(BitmapConstRef bitmap);
  // 画素の色情報を取得
  SFXRGBColor GetPixel(SFXPixelConstRef pixel) const;
  SFXRGBColor GetPixel(SInt16 x, SInt16 y) const {
    return GetPixel(SFXPixel(x, y));
  }
  SFXSize GetSize(Void) const;   // ビットマップの大きさを取得
  UInt16 GetWidth(Void) const;   // ビットマップの幅を取得
  UInt16 GetHeight(Void) const;  // ビットマップの高さを取得
  UInt08 GetDepth(Void) const;   // ビットマップの色深度を取得
};

#endif

コンストラクタ、LoadBitmap 関数、LoadResBitmap 関数は、何れもファイルからビットマップを読み込めます。

GetDib 関数 : DDB から DIB を作成する

SFCError Bitmap::GetDib(Void)
{
  SFCError error(SFERR_NO_ERROR);
  _pdib.Release();
  if(_pbitmap != null) {
    SFBDIBPtr pdib;
    error = _pbitmap->QueryInterface(AEECLSID_BITMAP,
                                reinterpret_cast<VoidHandle>(&pdib));
    if (error == SFERR_NO_ERROR) {
      _pdib.Attach(pdib);
    } else {
      _pbitmap.Release();
    }
  } else {
    error = SFERR_FAILED;
  }
  return error;
}

重要なのは、

  SFBDIBPtr pdib;
  error = _pbitmap->QueryInterface(AEECLSID_BITMAP,
                              reinterpret_cast<VoidHandle>(&pdib));

の 2 行です。

_pbitmap は SFBBitmapSmp 型のメンバ変数です。第 1 引数に AEECLSID_BITMAP を渡し、第 2 引数に作成した DIB へのポインタを保持する変数へのポインタを渡して SFBBitmap::QueryInterface 関数を呼び出すことで、DIB が作成されます。

スマート ポインタを使うと、不必要になった時点で SophiaFramework UNIVERSE が自動的に解放してくれて便利です。

今回のコードでは

  _pdib.Attach(pdib);

として、SFBBitmap::QueryInterface 関数で作成した pdib をスマート ポインタの _pdib に管理させています。

勿論、スマート ポインタの Release メンバ関数を呼んで、明示的にオブジェクトの解放することも可能です。

元のビットマップを変更したい場合、SFBBitmap::QueryInterface 関数で新たに DIB を作成します。

GetPixel 関数 : 各画素の色を取得する

IDIB 構造体のメンバと SFBDIB クラスのメンバ関数の対応関係です。

IDIB 構造体のメンバSFBDIB クラスのメンバ
cxGetWidth(Void)
cyGetHeight(Void)
nDepthGetDepth(Void)
nPitchGetPitch(Void)
pBmpGetBuffer(Void)

画像上の点 (x, y) のデータの場所は、pBmp + y * nPitch から数えて x * nDepth ビット目なので、コーディングすると次のようになります。

SFXRGBColor Bitmap::GetPixel(SFXPixelConstRef pixel) const
{
  if (_pdib == null) {
    return SFXRGBColor(0, 0, 0, 0);
  }

  UInt32 bitsIndex(_pdib->GetDepth());
  bitsIndex *= pixel.GetX();
  BytePtr p(_pdib->GetBuffer() +
              pixel.GetY() * _pdib->GetPitch() + (bitsIndex >> 3));
                // bitsIndex >> 3 は bitsIndex / 8 と等価
                // これで、指定された点の値が格納されている
                // 先頭のメモリ アドレスが取得できる
                // (高速化のためビット演算を使用)
  SInt08 localBitsIndex(8 - static_cast<SInt08>(bitsIndex & 7));
                // bitsIndex & 7 は bitsIndex % 8 と等価
                // これで p の下から何ビット目に
                // 目的のデータがあるのか取得できる
                // (高速化のためビット演算を使用)
  SInt16 len(_pdib->GetDepth());
  NativeColor color(0);
  BytePtr pcolor(reinterpret_cast<BytePtr>(&color));
  SInt08 leftBits(8);  // pcolor にあと何ビット書き込めるか
  UInt08 getbits;
  UInt16 mask;

  while (len > 0) {
    // len と localBitsIndex と leftBits のうち最小のものを
    // getbits に代入
    getbits = (localBitsIndex < leftBits)
                  ? localBitsIndex : leftBits;
    if (getbits > len) {
      getbits = static_cast<UInt08>(len);
    }

    mask = static_cast<UInt16>(1 << localBitsIndex) - 1;
    *pcolor = (*pcolor << getbits)
                | ((static_cast<UInt08>(mask) & *p)
                     >> ((getbits < localBitsIndex)
                           ? (localBitsIndex - getbits) : 0));
    len -= getbits;
    leftBits -= getbits;
    localBitsIndex -= getbits;
    if (localBitsIndex <= 0) {
      ++p;
      localBitsIndex = 8;
    }
    if (leftBits <= 0) {
      ++pcolor;
      leftBits = 8;
    }
  }
  return _pdib->NativeToColor(color);
}

このコードでは、目的の画素の NativeColor を SFXRGBColor に変換しているだけです。

ビット単位でバッファにアクセスする汎用的な処理なので、煩雑なコードになっています。

※ 国内の端末はカメラの画像は 16 ビット色に統一されていますので、バイト単位しか扱わないようにするともっと簡単になります。