前回はPhoneGapを支える各種APIの紹介と、
ユニバーサルアプリのビルド方法、実機テスト準備、外部プラグインの導入手順
今回はPhoneGapのCamera APIを使って、
- iPhone/
iPadのユニバーサルアプリケーションであること - iPhone/
iPadともに横向き (Landscape) 利用を前提とする - カメラで写真を撮るか、
アルバム内の写真を選択する - 選択した写真に対して
「グレースケール」 「セピア」 のフィルタ処理が行えるように - 画面数は
「写真を取る/アルバムからの選択メニュー」 「写真の確認/フィルタ処理/保存」 の2画面 - jQuery Mobileを利用して、
index. html1ファイルで完結させる - 加工した写真はiOSのフォトアルバムに保存ができること
第1回で紹介した手順で新しいプロジェクトを作成します。プロジェクト名は
- ①ユニバーサルアプリケーションをビルドするには
- ②デバイスの向き
(Device Orientations) とアプリのアイコン (App Icons)、 起動画面 (Launch Images) のカスタマイズ - ③iPhone/
iPad実機でテストする手順 - ④外部プラグインの導入手順
①ユニバーサルアプリケーションをビルドするには
iPhone/

これでユニバーサルアプリケーションをビルドする準備が整いました。アプリケーション内部ではユーザエージェントやDevice APIを用いて、
②デバイスの向き(Device Orientations)とアプリのアイコン(App Icons)、起動画面(Launch Images)のカスタマイズ
デバイスの向きとアプリのアイコン、
- Portrait: 縦向き
- Upside Down: 縦向き
(逆さま) - Landscape Left: 左横向き
- Landscape Right: 右横向き

Supported Device Orientationsは複数選択することが可能です。今回はLandScape Rightのみを選択しました。
アプリのアイコンは
- 通常アイコン:
(PhoneGapプロジェクト名)/Resources/ icons/ icon. png - Retinaディスプレイアイコン:
(PhoneGapプロジェクト名)/Resources/ icons/ icon@2x. png
パスのアイコンファイルを直接編集するか、

起動画面は
- 通常:
(PhoneGapプロジェクト名)/Resources/ splash/ Default. png - Retinaディスプレイ
(PhoneGapプロジェクト名)/Resources/ splash/ Default@2x. png
アイコン同様、

③iPhone/iPad実機でテストする手順
iPhoneシミュレータではカメラを利用することができないので、
Xcodeを起動した状態でiOSデバイスを接続すると、

「Usr for Development」

認証を行ってしばらくすると、

これでこのデバイス上に開発中のアプリケーションをインストールすることが可能になりました。
④外部プラグインの導入手順
PhoneGap APIには
iOSで写真をフォトアルバムに保存するには、
window.plugins.SaveImage.saveImage('(base64エンコードされた文字列)');
SaveImageプラグインを利用するための手順は次のとおりです。
- (1)
www/以下にSaveImage. jsをデプロイ - (2)
Plugins/以下にSaveImage. h, SaveImage. mをデプロイ後、 修正 - (3)
Supporting Files/ PhoneGap. plistを編集
④-(1)www/以下にSaveImage.jsをデプロイ
wwwディレクトリ以下にSaveImage.
<script type="text/javascript" charset="utf-8" src="SaveImage.js"></script>
④-(2)Plugins/以下にSaveImage.h, SaveImage.mをデプロイ後、修正
Plugins以下にSaveImage.
--- SaveImage.h.orig 2010-09-08 11:40:12.000000000 +0900
+++ SaveImage.h 2011-09-10 21:07:57.000000000 +0900
@@ -7,9 +7,9 @@
//
#import <foundation>
-#import "NSData+Base64.h"
-#import "PhoneGapCommand.h"
-@interface SaveImage : PhoneGapCommand {
+#import "PhoneGap/NSData+Base64.h"
+#import "PhoneGap/PGPlugin.h"
+@interface SaveImage : PGPlugin {
}
- (void)saveImage:(NSMutableArray*)sdata withDict:(NSMutableDictionary*)options;
--- SaveImage.m.orig 2010-09-08 11:40:12.000000000 +0900
+++ SaveImage.m 2011-09-10 21:07:43.000000000 +0900
@@ -6,7 +6,7 @@
// MIT licensed
//
-#import "NSData+Base64.h"
+#import "PhoneGap/NSData+Base64.h"
#import "SaveImage.h"
@implementation SaveImage

リリースされて時間のたっているPhoneGapプラグインの場合、
④-(3)Supporting Files/PhoneGap.plistを編集
最後にSupporting FilesのPhoneGap. なお、 それではいよいよカメラアプリの開発を行ってみましょう。wwwディレクトリ以下にjquery Mobileの成果物を一式デプロイします。今回はCreative Commonsライセンスの画像ファイルを用いました。 Xcodeを開き、 これをiPhoneで実行してみましょう。 アプリケーションを起動すると、 アプリケーションの起動時にdevice. 「Capture Photo」 「From Photo Library」 写真データがコールバック関数に渡されると、 撮影または選択した写真を閲覧・ プルダウンが変更されると、 右上の 全4回の PhoneGapを使うことで、 同様の開発環境としてTitanium Mobileがあります それでは、
Camera API/
The Noun Project作成、
Deziner Folio氏作成、<!DOCTYPE html>
<html>
<head>
<title>Capture Photo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="jquery.mobile-1.0b2.min.css" />
<script type="text/javascript" charset="utf-8" src="jquery-1.6.2.min.js"></script>
<script type="text/javascript" charset="utf-8" src="jquery.mobile-1.0b2.min.js"></script>
<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
<script type="text/javascript" charset="utf-8" src="SaveImage.js"></script>
<script type="text/javascript" charset="utf-8">
<!--
var pictureSource;
var destinationType;
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady()
{
pictureSource = navigator.camera.PictureSourceType;
destinationType = navigator.camera.DestinationType;
// デバイス判定。画像の幅/高さの調節に使用
if ( -1 < device.platform.indexOf('iPad') )
{
photoWidth = 1024;
photoHeight = 768;
}
else
{
photoWidth = 450;
photoHeight = 180;
}
$('.rawImage').css('width', photoWidth+'px').css('height', photoHeight+'px');
$('.resultImage').css('width', photoWidth+'px').css('height', photoHeight+'px');
// カメラ起動
$('.capturePhoto').live
(
'click',
function()
{
navigator.camera.getPicture
(
onPhotoDataSuccess, onFail,
{
quality: 50,
encodingType: Camera.EncodingType.PNG,
targetWidth: photoWidth,
targetHeight: photoHeight,
allowEdit: true
}
);
}
);
// フォトアルバムから画像を選択
$('.getPhoto').live
(
'click',
function()
{
navigator.camera.getPicture
(
onPhotoDataSuccess, onFail,
{
quality: 50,
destinationType: destinationType.DATA_URL,
encodingType: Camera.EncodingType.PNG,
sourceType: pictureSource.PHOTOLIBRARY,
targetWidth: photoWidth,
targetHeight: photoHeight
}
)
}
);
// 成功時のコールバック関数
function onPhotoDataSuccess(imageData)
{
$('.rawImage').attr('src', 'data:image/png;base64,' + imageData).show();
$('.resultImage').attr('src', 'data:image/png;base64,' + imageData).hide();
location.href='#edit';
$('select#filterValue option:first-child').attr('selected', 'false');
$('select#filterValue').selectmenu('refresh');
}
// 失敗時のコールバック関数
function onFail(message)
{
alert('Failed because: ' + message);
}
// Canvasでの再描画
function reDrawImage()
{
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var imgObj = new Image();
imgObj.src = $('.rawImage').attr('src');
canvas.width = imgObj.width;
canvas.height = imgObj.height;
ctx.drawImage(imgObj, 0, 0);
// フィルタ処理
switch($('#filterValue').val())
{
case 'grayscale':
var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
for(var y = 0; y < imgPixels.height; y++)
{
for(var x = 0; x < imgPixels.width; x++)
{
var i = (y * 4) * imgPixels.width + x * 4;
// via Web Designer Wall - HTML5 Grayscale Image Hover
// http://webdesignerwall.com/tutorials/html5-grayscale-image-hover
var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
imgPixels.data[i] = avg;
imgPixels.data[i + 1] = avg;
imgPixels.data[i + 2] = avg;
}
}
ctx.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);
break;
case 'sepia':
var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
for(var y = 0; y < imgPixels.height; y++)
{
for(var x = 0; x < imgPixels.width; x++)
{
var i = (y * 4) * imgPixels.width + x * 4;
var r = imgPixels.data[i];
var g = imgPixels.data[i+1];
var b = imgPixels.data[i+2];
// via CamanJS - sepia
// https://github.com/meltingice/CamanJS
var adjust = 80 / 100;
r = Math.min(255, (r * (1 - (0.607 * adjust))) + (g * (0.769 * adjust)) + (b * (0.189 * adjust)));
g = Math.min(255, (r * (0.349 * adjust)) + (g * (1 - (0.314 * adjust))) + (b * (0.168 * adjust)));
b = Math.min(255, (r * (0.272 * adjust)) + (g * (0.534 * adjust)) + (b * (1- (0.869 * adjust))));
imgPixels.data[i] = r;
imgPixels.data[i + 1] = g;
imgPixels.data[i + 2] = b;
}
}
ctx.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);
break;
default:
break;
}
$('.rawImage').hide();
$('.resultImage').attr('src', canvas.toDataURL()).show();
}
// フィルタのプルダウンが操作された場合に、画像の再描画を実施
$('#filterValue').live
(
'change', reDrawImage
);
// 画像をフォトアルバムに保存
$('#save').live
(
'click',
function ()
{
var src = $('.resultImage').attr('src');
window.plugins.SaveImage.saveImage(src.replace('data:image/png;base64,', ''));
}
);
}
-->
</script>
<style>
<!--
.rawImage, .resultImage
{
display: none;
}
.menuColumn
{
width:49%;
float:left;
}
.menuColumn p
{
text-align: center;
}
-->
</style>
</head>
<body>
<div id="home" data-role="page">
<div data-role="header" data-theme="b">
<h1>Camera</h1>
</div>
<div data-role="content">
<div style="width:100%">
<div class="menuColumn">
<p>
<img src="Black-Camera-Symbol-128.png" class="capturePhoto" />
</p>
<input type="button" class="capturePhoto" value="Capture Photo">
</div>
<div class="menuColumn">
<p>
<img src="Album_128.png" class="getPhoto" />
</p>
<input type="button" class="getPhoto" value="From Photo Library">
</div>
</div>
</div>
</div>
<div id="edit" data-role="page">
<div data-role="header" data-theme="b">
<a data-rel="back" href="#home" data-direction="reverse" data-role="button" data-icon="arrow-l">Back</a>
<h1>Result</h1>
<a id="save" href="javascript:void(0)" data-direction="reverse" data-role="button" data-icon="check">Save</a>
</div>
<div data-role="content">
<img class="rawImage" src="" />
<img class="resultImage" src="" />
</div>
<div data-role="footer" data-position="fixed" data-theme="b" class="ui-bar">
<label for="filterValue">Filter:</label>
<select id="filterValue">
<option value="No filter">No fileter</option>
<option value="grayscale">Gray scale</option>
<option value="sepia">Sepia</option>
</select>
</div>
</div>
</body>
</html>
最後に