KLabGames Tech Blog

KLabは、多くのスマートフォン向けゲームを開発・提供しています。 スマートフォン向けゲームの開発と運用は、Webソーシャルゲームと比べて格段に複雑で、またコンシューマゲーム開発とも異なったノウハウが求められる領域もあります。 このブログでは、KLabのゲーム開発・運用の中で培われた様々な技術や挑戦とそのノウハウについて、広く紹介していきます。

KLabでクライアント開発基本ライブラリの開発をしている鬼塚です。

今回はKLabで使用しているクライアント開発基本ライブラリの紹介をしたいと思います。
クライアント開発基本ライブラリとはUnityを用いたスマートフォンゲーム開発において共通して必要になる機能、及び実装ノウハウを集約したライブラリ群です。

kgsdk_img

KLabではUnityを用いたスマートフォンゲームのほとんどでこのクライアント開発基本ライブラリを導入しています。
現在リリース中のアプリでも使用されている為、UnityやiOS/Androidといった各PFのバージョンアップの対応やそれよって生じた不具合等の改修が日々が行われています。
また、Unity4、Unity5どちらにも対応しているので環境を問わず利用することができます。
クライアント開発基本ライブラリの概要についてはコチラの記事をご確認ください。

提供ライブラリ一覧

(1)ストア課金処理ライブラリ

App Storeの In-App Purchase、Google Play の In-App Billing を利用してアプリ内課金を行う際に必要な機能を提供するライブラリです。
iOS/Androidの各PF間の仕様の差異を吸収した作りとなっており商品一覧の取得、 商品の購入、レシート更新(iOS)、レジューム処理の機能を提供しています。

(2)アセット管理ライブラリ

アプリ本体の起動後に必要な画像などの追加データファイル(アセットと呼びます)のダウンロード及びそれらのアセットの読み込みにまつわる様々な問題を解消したライブラリです。
なお、バックグラウンドでのダウンロード機能やDownload機構及び、Object Pooling機構をモニタリングする機能なども提供しています。

(3)チャットシステム

ゲーム内でユーザ同士のチャット機能を提供する際に必要な機能をまとめたライブラリです。
テキストだけのやりとりだけでなく、画像を用いたスタンプや投稿した内容に対するリアクション機能なども提供しています。
チャットシステムの詳細についてはコチラの記事をご確認下さい。

(4)コアユーティリティ郡

ファイル管理、オンメモリキャッシュ、ロケール取得、タイムゾーン取得といった、アプリケーション開発で必要となる基本的な機能をまとめています。
クライアント開発基本ライブラリでは全てのライブラリでこのコアユーティリティ群が含まれています。

(5)通信ライブラリ

HTTPやHTTPS通信にまつわる機能をまとめたライブラリです。
Unity標準のWWWクラスの互換性のない仕様や挙動の吸収やリクエストキューイング、リクエストタイムアウト、自動リトライ等HTTP通信を行う上で必要な機能を提供しています。

(6)WebViewライブラリ

iOS/AndroidのWebView機能をOSによらず透過的に扱えるようにするライブラリです。
WebViewで標準的に利用可能な戻る、進む、リロード、アプリ実装との相互連携などももちろん利用可能で、その他キャッシュの無効化やUserAgentの指定、iOS8から導入されたWKWebViewにも対応しています。

(7)ローカライズテキスト管理ライブラリ

ローカライズテキストを管理するライブラリです。
アプリ内テキストのローカライズを行う為に必要な表示言語、表示するテキスト、表示するテキストを参照する為のIDを簡単に管理することができます。
テキストのローカライズと言語設定管理の2つの機能を提供しています。

(8)PFアチーブメント対応ライブラリ

Google Play Game Servicesと Game Centerのアチーブメント機能を透過的に扱えるようにするライブラリです。
アチーブメントの一覧や達成状況、アンロック等各PFが提供している機能を統一されたインターフェースで利用することができます。

(9)Bluetooth Low Enargyライブラリ

iOS/AndroidのBluetooth Low Energy機能をOSによらず透過的に扱えるようにするライブラリです。
Bluetooth LE通信に必要な機能が含まれおり、Peripheral機器としての利用、Central機器としての利用どちらにも対応しています。

通信ライブラリについてもう少し詳しく

上で紹介したクライアント開発基本ライブラリの中から今回は通信ライブラリにフォーカスしてどのような機能を提供しているかいくつか紹介させて頂きます。

特徴

先ずは通信ライブラリの特徴から。
通信ライブラリではUnity標準の通信クラス(UnityEngine::WWW)と.NET FrameworkのWebClientクラスの拡張クラスのどちらを利用するかを指定できるようになっています。
Unity標準のWWWクラスにはUnityのバージョンによっていくつかの互換性のない仕様や挙動があります。通信ライブラリではそれらの差異を吸収し、また機能拡張することで利用者はUnityのバージョンによる互換性のない仕様や挙動を気にすることなく開発に専念することができます。

RequestTypeによる処理の切り替え

通信ライブラリでは利用者が取得したいデータ形式に合わせて処理を切り替えています。
通信ライブラリで用意しているデータ形式はAPIレスポンス等に使用する為のテキストデータ、Unityで使用するアセットバンドル、テクスチャ用のアセットバンドルではない画像やバイナリファイル、汎用的に使用できるファイルダウンロード等があります。

リクエストキューイング

通信ライブラリではクライアントから投げられたリクエストは一度キューに積まれます。
もし、キューに何も無ければリクエストを開始しますが、前のリクエストが完了していない場合はそのリクエストが完了するまで待機します。こうすることで、複数のスレッドで通信を行った場合でもリクエストした順番に処理することができます。
一度に実行できるリクエストの上限はデフォルトでは5回に設定されていますが、もちろんこの回数等は利用者が自由に変更することができます。

リクエストタイムアウト

リクエストがタイムアウトする迄の時間を設定することができます。
モバイル通信環境で起こりがちな、電波の届かない場所での通信や、正常にレスポンスが返って来なかった場合にタイムアウトを設定することでユーザーを待たせずエラー処理を実施することが可能になります。
また、Unity標準のWWWクラスではこのリクエストタイムアウト処理が提供されていない為自前で実装する必要がありますが、通信ライブラリではリクエストキューイング同様自由にタイムアウト時間を設定することができます。

自動リトライ

通信失敗時にリクエストのリトライ回数を設定することができます。
一時的なネットワーク上のエラーでリクエストが失敗した場合に自動的にリトライされるのでライブラリ利用者のリトライ実装の負担と対アプリユーザへの通信失敗通知によるストレスを軽減することができます。

その他

その他にも通信ライブラリでは便利な機能を提供しています。
例えば、HTTPRequestを同期/非同期に処理するかを切り替えることができ、この機能はUnityEditor拡張実装において複雑になりがちなHTTP通信処理実装の手助けになります。
加えて、ダウンロード時の進捗状況を表示できたり、デバッグ用にダミーのレスポンスを受け取れるように設定できたりなどネットワークを扱う上で必要となる機能を提供しています。

最後に

このようにクライアント開発基本ライブラリではスマートフォンゲームの開発を行う上で必須となる機能を機能単位で利用者が必要な物を取捨選択して導入できるようになっています。
KLabでは、開発パートナー様と共同開発したゲームをKLabにてパブリッシング、プロモーションを行うというモデルを積極的に進めており、開発パートナー様にKG SDKの提供もしています。

パブリッシング事業につきましてはコチラ
KG SDKに関するお問い合わせにつきましてはコチラ
をご覧ください。

@tenntennです。

現地時間の2016年7月11日〜13日にアメリカのコロラド州デンバーにて開催されたGopherConにて発表をしてきました。

GopherConとは?

GopherConは、毎年デンバーで開催される世界でもっとも大きいGo(Go言語)のカンファレンスです。
今年で3回目の開催になります。
GopherConと名前の付くカンファレンスは、デンバーで開催されるGopherConの他にもGopherCon IndiaやGopherCon Chinaがあります。
GoのWikiに世界各地で行われているカンファレンスまとめてあるのでそちらを見ると分かりやすいかなと思います。

今年のGopherConの参加者は1500名を超えているらしく、3トラックに分けて発表が行われました。
他のカンファレンスと比べても非常に規模が大きいことが分かります。

GopherConで発表するまで

今年のGopherConも2種類の形式のセッションが募集されました。
通常セッション(発表30分・質疑なし)とチュートリアルセッション(発表50分・質疑込み)です。
通常セッションは午前中にメインステージ(1500人席)で行われ、チュートリアルセッションは3トラックに分かれて行われました。

GopherConで発表を行うためには、プロポーザルを提出する必要があります。
プロポーザルは、タイトル、概要、詳細などを英語で書きます。
タイトルと概要は採択後にWeb上で公開されるもので、詳細は公開されません。
そのため、詳細の方はネタバレの情報を含んでいても構わなく、できるだけ詳細に具体的なことを書きます。

今年の1月1日にCall For Proposal (CFP) がオープンし、締め切りは1月末でした。
私は、Droid Kaigi 2016のCFPが通った勢いでGopherConの方にもえいやと応募してみることにしました。
応募の動機は、GopherConに参加したかったことと、今年は英語能力をのばそうと考えていたので、その具体的な目標にちょうどいいかなと思ったからです。

発表するネタをあれこれ考えている間に、とうとう締め切りの日が近づいてきていました。
Droid Kaigiが50分の発表でしたので、GopherConも同じく50分のチュートリアルセッションに応募することにしました。
発表するネタは、Droid Kaigiでも発表したGo Mobileに決めました。

そこからプロポーザルを書いていきました。
はじめは日本語を書いて英語に翻訳するという作業でプロポーザルを考えていましたが、締め切り間際になると英語で直接文章を考えるようにしました。
これは英語の文章を書くいい練習になりました。

無事締め切りギリギリでプロポーザルを提出し、ひと安心しました。
しばらくすると、思いがけないプロポーザル通過の案内がきて大変驚きました。
それはGopherConに参加できる喜びと、最大規模のGoのカンファレンスで発表する緊張の入り混じった気持ちでした。

そこからは具体的な内容を詰めたり、新しい話のネタを探したりして、発表資料づくりを行いました。

Go for Mobile Games

私のセッションでは、「Go for Mobile Games」というタイトルで、Go MobileというGoのモバイルアプリ開発向けのツールキットについて発表を行いました。

発表資料は、GoogleスライドとSlide Shareにアップロードしてあります。

発表に使った原稿についても以下のURLから閲覧できるようにしてあります。

当日の発表の様子は録画されており、後日Youtubeにて公開されるとのことなので、Gopher Academyの再生リストに追加されるでしょう。

今回の発表では、このブログでも取り上げている、Go MobileのNativeアプリの開発について扱っています。

具体的には、

  • SDKアプリとNativeアプリについて
  • Nativeアプリでゲームを作る方法
  • Google Playへの公開
  • JavaのAPIをGoから呼ぶ方法

についてお話しました。

資料には記載していますが、Unityのネイティブプラグインを作る方法については、時間が足りずお話することができませんでした。
興味のある方はQiitaに記事を書いていますので、そちらをご覧ください。

当日の発表の様子

当日の発表は、写真のような500名ほど入る部屋で行われました。
緊張していてあまり覚えていませんが、それなりに人は入っていたような気がします。
プレゼンの様子

発表ギリギリまで資料を調整し、原稿を手直しました。
発表は原稿を読みながらやろうと割りきっていたので、そこまで不安はありませんでした。
ちなみに、発表原稿は紙に印刷して持って行きましたが、滞在中に修正を加えたため、結局はGoogle DocsをNexus 9で開いて発表しました。

しかし、その時になると機器トラブルが起こり、3台PCを取り替えてもスクリーンに映らず、4台目でようやく映りました。
プレゼンできないんじゃないかと不安になりましたが、カンファレンススタッフの方たちのおかげでどうにか発表を開始できました。

発表の途中に気づいたのですが、機器トラブルに加えて、別の問題も起きました。
自分のMacで発表できない場合に備えて、Googleスライドの原稿をpptxやPDFに変換したものを用意していました。
しかし、pptxまでは直前の調整の結果を反映していたのですが、PDFに反映を忘れており、ようやくスクリーンに映せた4台目のMacにはパワーポイントが入っておらず、結局古い状態の資料で発表することになりました。
PDFなのでスライド中に貼っていたGIFが動かなかったのは非常に残念でした。

また、発表時間は50分だったのですが、発表開始と同時にカウントダウンタイマーが動く予定でしたが、動いておらず、今自分が何分話しているのか分からないまま発表するという事態になりました。
発表練習では、だいたい時間どおりに発表できていたので、うまく時間どおりに発表できたと信じたいところです。

発表した感触としては、英語はまだまだ改善の余地があり、内容についてはもう少し盛り上がる要素を入れればよかったなと思いました。
それでも50分の英語の発表は、これまでに経験したことのなかったのでいい経験になりました。
質問も発表時間内は時間がなくて受けれませんでしたが、発表終了後にいくつか頂いたので興味をもって頂けたのかなと思いました。
発表資料については、最後まで調整するのは仕方がないことだと思いますが、PDFへの反映を忘れたり、GIFのことを忘れたりしないようにしたいです。

次も機会があれば英語での発表に積極的にチャレンジできればと思います。

他のセッションについて

11日と12日は、通常セッション(30分)とチュートリアルセッション(50分)、そしてLT(6分)の3種類のセッションが行われました。
各セッションのスライドについては、以下のgithubのリポジトリに順次アップロードされると思います。

私が特に面白かったと思う発表は以下の通りです。
括弧の中は発表者の名前です。

Understanding nil (Francesc Campoy Flores)

nilについて理解を深めることのできるセッションでした。
各型におけるnilの意味、特にインタフェースのnilの話などを改めて復習できてよかったです。
またレシーバをnilにしてもメソッドが動くことを利用して、nilをうまく使う方法についても説明されていました。

Visualizing Concurrency in Go (Ivan Daniluk)

このセッションでは、ゴールーチンのビジュアライズの話がされてました。
ゴールーチンをビジュアライズすることで、ゴールーチンのリークや並列度の有効性などを視覚的に分かりやすくなるという話でした。

Inside the Map Implementation (Keith Randall)

マップがどう実装されているのかという話でした。
コアな話でしたが、面白かったです。

The Design of the Go Assembler (Rob Pike)

こちらはまだ完全に理解が追いついてないのですが、1.5で導入されたGoのアセンブラの話でした。
中間コードのようなものを吐き出して、そこから各アーキテクチャごとのバイナリを生成するというような話だったかと思います。
Go 1.5からクロスコンパイルが簡単になったのもこの機能のおかげのようです。
ビデオが公開されたらもう一度見なおしてみようと思います。

Go Mobile as the Backbone of Lantern for Android (José Carlos Nieto)

私のセッションと同じく、Go Mobileの話でした。
こちらのセッションでは主にSDKアプリの話がされていました。
LanternというサービスでどうGo Mobileを使っているかという話でした。

The Go Gopher: A Character Study (Renee French)

このセッションではGoのマスコットのGopherのャラクターデザインについて原作者自ら話すというものでした。
Gopherがどう生まれたのか、Gopherの体の仕組みやデザインのルールなどを知ることができました。
また、Goのコミュニティで描かれているGopherのイラストが紹介され、私もGopherアーティストとして紹介してもらいました(笑)
Gopherの原作者に私の描いたGopherが気に入ってもらえているようで、とても光栄です。

Hack Day

13日は、セッションが開催されず、かわりにHack Dayといういわゆるもくもく会が行われました。
いくつかテーマごとに部屋に分かれて、もくもくと手を動かすというものでした。
私は、GoBotのワークショップに参加しました。

Edisonのスターターキット(写真参照)を使ってLEDをチカチカさせたり、音をならしたりと一通りのセンサーを使ってみるというワークショップでした。
Edison上でもクロスコンパイルしたバイナリが簡単に動いて、Goのクロスコンパイルの偉大さが改めて身にしみました。
GoBotもお手軽につかえて楽しかったです。
今度はドローンの操作もやってみたいと思います。

ワークショップで使ったキット

ワークショップで使用した資料は以下のリポジトリで公開されています。

After PartyとSpeaker's Dinnerについて

1日目の夜は、お店を貸し切ってAfter Partyが行われました。
また2日目の夜は、スピーカーとカンファレンスオーガナイザー、Goチームが参加するSpeaker's Dinnerが行われました。
どちらも普段話せないような著名な方々と話す機会ができて、非常に嬉しかったです。
これだけでも十分GopherConで発表する意義があるかなと思います。

全体を通した感想

Goのみを扱っているということで内容の濃い3日間でした。
緊張と時差ボケで一部の発表があまり聞けなかったのが残念でしたが、聞けたものはどの発表も内容が濃いものばかりでした。
発表内容は、業務上で得たノウハウを基にしたものか、Goのコアな実装の話が多かったのではないかなと思います。

カンファレンスの運営という点から見ても、有料とはいえGopherConの運営はしっかりしているなと思いました。
スピーカーへ発表内容へのアドバイス、参加者に対する気配りなどが随所に見られて関心しました。

また来年もスピーカーとして参加できたら良いなと思っています。

しばらく前に、Windows上でTextureMovie機能の実装をしました。TextureMovieというのは動画形式のファイルからフレームを取得し、デコードした結果をOpenGLやDirectX用のテクスチャへ焼きこんでこれらのレンダリングシステムを使って描画するものです。

ここで出たデコードを処理するのはデコーダーで、つまり、何かしらの形式の色情報をRGB(A)に変換するものです。世の中には既成のデコーダーが幾つかありますが、いろいろ事情があって自作という選択肢に至りました。

また、Windowsのことですから、MSが提供している低レベルAPIで何とかなるのかもしれないと考えました。そこで、ほぼRGBとCMYKしか知らない私がいろいろ調べていきました。もし、 色空間の変換やYUV形式に興味がありましたら 、是非お読みください。

まずやりたいことを一言でまとめると、movie.mp4 -> YUVバッファ -> RGBバッファ -> テクスチャという流れです。

最初にWindowsの標準機能であるMicrosoft Media Foundation Transform (MFT)についていろいろ調べました。悪戦苦闘の末、やっと「YUV」でのデータ出力までたどり着きました(MFTに関してはサンプルや資料が極めて少ないため、結構試行錯誤の連続で.mp4からYUVバッファーの出力までできました。それは別の回に改めて紹介することとし、ここでは割愛します)。

いま振り返ると、そこで現れた見知らぬ「YUV」という単語が実に意味深いです。
「YUVって何ぞや、RGBの従兄弟なの?」と思いながら、Wikipediaを開きました。

YUVYCbCrYPbPrとは、輝度信号Yと、2つの色差信号を使って表現される色空間。

とWikipedia先生がこう答えてくれました。

まだよくわからないため、さらにGoogle先生へ答えを求めました。

「人間の目は明るさの変化には敏感だが, 色の変化に は鈍感である」というわけで,色度を抑え、輝度により広い帯域やビット数を割くことにより、少ない損失で効率の良い伝送や圧縮を実現するフォーマット.
デジタル画像の圧縮CODECにおけるフォーマットという観点でまとめる.

つまり、人間の目をうまく欺いて、データの容量を削減するための圧縮フォーマットと考えればいいでしょう。

エンコード(圧縮をかける)したから、デコード(圧縮を解除する)と呼ばれた処理なんだとふっと思いながら、YUVからRGBに変換する方法を探し始めました。

ところが、YUVというのは一つの形式ではなく、幾つかの形式の総称だということに気づきました。MSDNから各形式についてこんな説明があります(以下、図を引用します)。

image04

正直、ぱっと見ただけでは最初に文字も図も今ひとつ分からなかったので、読み飛ばして分かるところまで先に進みました。

まず、そのページの後半にとても重要な計算式が書いてあります(同ページより引用)。

Converting 8-bit YUV to RGB888

From the original RGB-to-YUV formulas, one can derive the following relationships for BT.601.

Y = round( 0.256788 * R + 0.504129 * G + 0.097906 * B) +  16
U = round(-0.148223 * R - 0.290993 * G + 0.439216 * B) + 128
V = round( 0.439216 * R - 0.367788 * G - 0.071427 * B) + 128

Therefore, given:

C = Y - 16
D = U - 128
E = V - 128

the formulas to convert YUV to RGB can be derived as follows:

R = clip( round( 1.164383 * C                   + 1.596027 * E  ) )
G = clip( round( 1.164383 * C - (0.391762 * D) - (0.812968 * E) ) )
B = clip( round( 1.164383 * C +  2.017232 * D                   ) )

where clip() denotes clipping to a range of [0..255]. We believe these formulas can be reasonably approximated by the following:

R = clip(( 298 * C + 409 * E + 128) >> 8)
G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
B = clip(( 298 * C + 516 * D + 128) >> 8)

上記の変換から得たのをもう少し整理すると

R = clip(( 298 * (Y - 16) + 409 * (V - 128) + 128) >> 8)
G = clip(( 298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8)
B = clip(( 298 * (Y - 16) + 516 * (V - 128) + 128) >> 8)

になります。

これを手に入れたら、あとはYUVの構造さえ分かれば、変換が可能になります。

次に、代表的なフォーマット毎に詳しい説明があります(同ページより引用)。

4:4:4 Formats, 32 Bits per Pixel

AYUV

image06

このフォーマットは輝度毎に色差信号をサンプリングし、アルファ情報もついています。最高クォリティの画質です。

4:2:2 Formats, 16 Bits per Pixel

YUY2

image09

UYVY

image08

このフォーマットはY0とY1が同じUV(U0、V0)を使っています。YUY2とUYVYの差はY、U、Vの並び順だけです。

4:2:0 Formats, 16 Bits per Pixel

※以下のメモリイメージ図はビデオフレームの横幅がY配列の列数と等しく、ビデオフレームの縦幅はY行列の行数と同じです(実際にメモリ上は一次配列扱い。同ページより引用)。

IMC1

image10

IMC3

image05

このフォーマットは四つのYが同じUVを使います。

特徴的な構造として、メモリ上配列でYの値を全部格納してから、Uの配列、Vの配列を置いていることが挙げられます。ただし、UとVの配列はYと同じ幅(行と列の比率、ここはややこしい)なので、ややメモリ利用効率が悪そうです。

また、IMC1とIMC3のメモリ構造はほぼ一緒です。唯一異なるのはUの配列とVの配列どちらを先に並べるかです。

例:

352 * 240のビデオフレームだと

Yの値は352個/行 * 240行(行:列 = 352 : 240 = 22/15)

UとVの値それぞれは176個/行 * 120行(行:列 = 176 : 120 = 22/15)

4:2:0 Formats, 12 Bits per Pixel

これらのフォーマットは全部四つのYが同じUVを使用しています。違いはメモリ上でのデータ格納方法だけです。

IMC2、IMC4

image03
(IMC2)

この形式の特徴はYの値を全部格納してからUとVの値を交替に格納することです。つまり一行の半分はUの値、残り半分はVの値になります。メモリをより効率的に利用できます。IMC2とIMC4の違いはU配列とV配列どちらが先に置かれるかです。また、IMC2はNV12以外で最も使われる形式だと書いてあります(つまり、NV12は最も使われているということで、これは扱いやすさゆえでしょう)。

YV12

image01

正直なところ最初にこの図を見て、IMC1とは何か違うのが分かりませんでした。その後に分かりやすい説明を見つけました(下図は英語版Wikipediaより引用)。

image00

なお、YUV420はYV12の別名です。

簡単にいうと、メモリ上でみた場合にY、U、Vの配列が全部繋がっています。

NV12

image07

同じく、分かりやすい説明が見つかりました(同上)。

image02

NV12のメモリ構造を見ると、最も頻繁に使われる理由が分かります。UVがセットで来るため、ループでとてもまわしやすい構造です。

Yが四つに対し、UVセット一つを使うので、とても自然なプログラムが書けます。

ここまで全部読んだら、一番最初の〇と×の図もちゃんと理解できるようになりました。×はluma = 輝度 = Y、〇はChroma = 色差 = UVということでした。

これで.mp4からテクスチャへの色変換の基礎知識が全部揃いました。計算式とメモリ上のデータ取得部分をプログラムへ組み込み、またMFTで試行錯誤して完成にこぎつけました。

最後に変換部分のコードを抜粋したものを掲載します:

#define CLIP(x) do{if(x < 0){x = 0;} else if(x > 255){x = 255;}} while(0)
#define CONVERT_R(Y, V)    ((298 * (Y - 16) + 409 * (V - 128) + 128) >> 8)
#define CONVERT_G(Y, U, V) ((298 * (Y - 16) - 100 * (U - 128) - 208 * (V - 128) + 128) >> 8)
#define CONVERT_B(Y, U)    ((298 * (Y - 16) + 516 * (U - 128) + 128) >> 8)

void ImplementationMovie::NV12ToRGB(u8* rgbBuffer, u8* yuvBuffer, int width, int height)
{
    u8* uvStart = yuvBuffer + width * height;
    u8 y[2] = { 0, 0 };
    u8 u = 0;
    u8 v = 0;
    int r = 0;
    int g = 0;
    int b = 0;
    for (int rowCnt = 0; rowCnt < height; rowCnt++)
    {
        for (int colCnt = 0; colCnt < width; colCnt += 2)
        {
            u = *(uvStart + colCnt + 0);
            v = *(uvStart + colCnt + 1);

            for (int cnt = 0; cnt < 2; cnt++)
            {
                y[cnt] = yuvBuffer[rowCnt * width + colCnt + cnt];

                r = CONVERT_R(y[cnt], v);
                CLIP(r);
                g = CONVERT_G(y[cnt], u, v);
                CLIP(g);
                b = CONVERT_B(y[cnt], u);
                CLIP(b);
                rgbBuffer[(rowCnt * width + colCnt + cnt) * 3 + 0] = (u8)r;
                rgbBuffer[(rowCnt * width + colCnt + cnt) * 3 + 1] = (u8)g;
                rgbBuffer[(rowCnt * width + colCnt + cnt) * 3 + 2] = (u8)b;
            }
        }

        uvStart += width * (rowCnt % 2);
    }
}

Kyo Shinyuu

↑このページのトップヘ