UIImage から縮小画像を生成する (2) iPhone4 高解像度対応

2010年7月1日木曜日 | Published in | 2 コメント

このエントリーをはてなブックマークに追加

[前回] Cocoaの日々: UIImage から縮小画像を生成する

前回作成したメソッドに手を入れて iPhone4向け高解像度対応を行う。

デバイス毎の解像度に対応した縮小


例えば 960x300 の縮小画像を作成する場合、iPhone 3G/3GS で横幅ぴったりに表示できる画像のサイズは 320 x 100 となる。一方、iPhone4の場合は 640 x 200 が望ましい。どちらか一方のデバイスをターゲットにするアプリの場合は特に問題ないが、両方のデバイスをターゲットとする場合はデバイスの解像度に合わせた縮小率を適用する必要がある。


UIGraphicsBeginImageContextWithOptions


iOS4 からは UIGraphicsBeginImageContextWithOptions() が導入されて、これを使うとデバイス毎の解像度の違いを吸収することができる。
UIKit Function Reference - UIGraphicsBeginImageContextWithOptions

この関数ではビットマップに適用する倍率を指定することができる。
(例)UIGraphicsBeginImageContextWithOptions(CGRectMake(100, 100), NO, 2.0);

この場合、実際に作成されるビットマップのサイズは 200x200(ピクセル)となる。この倍率(scale)は 0 を指定することができて、その場合はデバイスに適した倍率が自動的い採用される。現在のところ iPhone3G/3GS は 1.0、iPhone4 は 2.0 となる。


高解像度対応版 縮小処理


前回のコードに手を入れて UIGraphicsBeginImageContext() の代わりに UIGraphicsBeginImageContextWithOptions()を使うようにする。
#import <CoreGraphics/CoreGraphics.h>

@implementation UIImage (extension)

- (UIImage*)imageByShrinkingWithSize:(CGSize)size
{
 CGFloat widthRatio  = size.width  / self.size.width;
 CGFloat heightRatio = size.height / self.size.height;
 
 CGFloat ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio;

 if (ratio >= 1.0) {
  return self;
 }

 CGRect rect = CGRectMake(0, 0,
        self.size.width  * ratio,
        self.size.height * ratio);
 
 UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); // 変更

 [self drawInRect:rect];

 UIImage* shrinkedImage = UIGraphicsGetImageFromCurrentImageContext();
 
 UIGraphicsEndImageContext();
 
 return shrinkedImage;
}


実行例


次の画像を縮小してみた。サイズ 320x480 ピクセル。
縮小後のサイズは iPhone3G/3GS で 80x120 ピクセルとする。

生成されるビットマップ画像のサイズがそれぞれのデバイスで異なることをiPhoneシミュレータを使って確認してみる。

検証に使ったコードの一部
UIImage* image = [UIImage imageNamed:@"sample1.png"];
 CGSize size = CGSizeMake(80, 120);
 
 UIImage* shrinkedImage = [image imageByShrinkingWithSize:size];
 NSLog(@"image.scale: %f, size:[%f, %f]",
    shrinkedImage.scale,
    shrinkedImage.size.width,
    shrinkedImage.size.height);
 
 NSLog(@"cgimage.size:[%d, %d]",
    CGImageGetWidth(shrinkedImage.CGImage),
    CGImageGetHeight(shrinkedImage.CGImage));
UIImage.size と CGImageGetWidth/Height をコンソールへ出力して見比べてみた。

iPhone3の結果(iPhoneシミュレータ)

image.scale: 1.000000, size:[80.000000, 120.000000]
cgimage.size:[80, 120]

iPhone4の結果(iPhoneシミュレータ)

image.scale: 2.000000, size:[80.000000, 120.000000]
cgimage.size:[160, 240]

シミュレータでは見た目の違いはわからないが、コンソールの出力からは予想通りiPhone4では scaleが 2.0 となっており、ビットマップのサイズも iPhone3の縦横2倍となっているのがわかる。一方で、UIImage.size は両者とも同じになっている。実は iOS4から UIImage.sizeの定義が変わっている。リファレンスによると iOS4 からはピクセルではなく、ポイントを返すようになっている。
In iPhone OS 4.0 and later, this value reflects the logical size of the image and is measured in points. In iPhone OS 3.x and earlier, this value always reflects the dimensions of the image measured in pixels.

UIImage Class Reference

単位が変わるなんてちょっとドラスティックな変更な気もするが、従来のコードの修正を最小限に抑えるという意味ではうまい方法かもしれない。

参考情報


iPhone Application Programming Guide: Supporting High-Resolution Screens
アプリを高解像度対応させる方法についての解説

Responses

  1. 匿名
    2011年9月2日 20:18

    いつもお世話になってます。

    下記のコードを入力すると
    UIImage* shrinkedImage = [image imageByShrinkingWithSize:size];

    以下のように注意が出て、
    "UIImage" may not respond to "-imageByShrinkingWithSize:"

    ビルドをして該当部分まで進むとアプリが落ちてします。

    原因などお分かりになりますでしょうか?

    ちなみにコードは以下通りです。imageViewからUIImageを作ってます。


    - (UIImage*)imageByShrinkingWithSize:(CGSize)size
    {
    CGFloat widthRatio = size.width / self.imageView.image.size.width;
    CGFloat heightRatio = size.height / self.imageView.image.size.height;

    CGFloat ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio;

    if (ratio >= 1.0) {
    return self.imageView.image;
    }

    CGRect rect = CGRectMake(0, 0,
    self.imageView.image.size.width * ratio,
    self.imageView.image.size.height * ratio);

    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); // 変更

    [self.imageView.image drawInRect:rect];

    UIImage* shrinkedImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return shrinkedImage;
    }

    検証コード(NSLogのところも%luに変更しろと注意されたところは変更しました)

    CGSize size = CGSizeMake(80, 120);

    UIImage* shrinkedImage = [imageView.image imageByShrinkingWithSize:size];

    NSLog(@"image.scale: %f, size:[%f, %f]",
    shrinkedImage.scale,
    shrinkedImage.size.width,
    shrinkedImage.size.height);

    NSLog(@"cgimage.size:[%lu, %lu]",
    CGImageGetWidth(shrinkedImage.CGImage),
    CGImageGetHeight(shrinkedImage.CGImage));


    どうぞ宜しくお願い致します。

  2. 匿名
    2011年9月2日 20:18

    いつもお世話になってます。

    下記のコードを入力すると
    UIImage* shrinkedImage = [image imageByShrinkingWithSize:size];

    以下のように注意が出て、
    "UIImage" may not respond to "-imageByShrinkingWithSize:"

    ビルドをして該当部分まで進むとアプリが落ちてします。

    原因などお分かりになりますでしょうか?

    ちなみにコードは以下通りです。imageViewからUIImageを作ってます。


    - (UIImage*)imageByShrinkingWithSize:(CGSize)size
    {
    CGFloat widthRatio = size.width / self.imageView.image.size.width;
    CGFloat heightRatio = size.height / self.imageView.image.size.height;

    CGFloat ratio = (widthRatio < heightRatio) ? widthRatio : heightRatio;

    if (ratio >= 1.0) {
    return self.imageView.image;
    }

    CGRect rect = CGRectMake(0, 0,
    self.imageView.image.size.width * ratio,
    self.imageView.image.size.height * ratio);

    UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); // 変更

    [self.imageView.image drawInRect:rect];

    UIImage* shrinkedImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return shrinkedImage;
    }

    検証コード(NSLogのところも%luに変更しろと注意されたところは変更しました)

    CGSize size = CGSizeMake(80, 120);

    UIImage* shrinkedImage = [imageView.image imageByShrinkingWithSize:size];

    NSLog(@"image.scale: %f, size:[%f, %f]",
    shrinkedImage.scale,
    shrinkedImage.size.width,
    shrinkedImage.size.height);

    NSLog(@"cgimage.size:[%lu, %lu]",
    CGImageGetWidth(shrinkedImage.CGImage),
    CGImageGetHeight(shrinkedImage.CGImage));


    どうぞ宜しくお願い致します。

  3. xcatsan says:
    2011年9月5日 21:53

    こんばんは。

    > "UIImage" may not respond to "-imageByShrinkingWithSize:"

    > ビルドをして該当部分まで進むとアプリが落ちてします。

    サンプルでは、- imageByShrinkingWithSize: は UIImage のカテゴリとして実装しているのでこのメッセージが出るのは

     (a) カテゴリでメソッドが定義されていない
     (b) カテゴリの定義を import していない

    のどちらかだと思います。

    カテゴリは例えば次のように定義します。

    ファイル "UIImage+extension.h"
    @interface UIImage (extension)
    - (UIImage*)imageByShrinkingWithSize:(CGSize)size;
    @end

    ファイル "UIImage+extension.m"
    @implementation UIImage (extension)

    - (UIImage*)imageByShrinkingWithSize:(CGSize)size
    {
    :
    }

    @end

    その上でメソッドを利用するコード内で上記のヘッダファイルをインポートします。
    #import "UIImage+extension.h"
    :
    UIImage* shrinkedImage = [image imageByShrinkingWithSize:size];


    では。

  4. xcatsan says:
    2011年9月5日 21:53

    こんばんは。

    > "UIImage" may not respond to "-imageByShrinkingWithSize:"

    > ビルドをして該当部分まで進むとアプリが落ちてします。

    サンプルでは、- imageByShrinkingWithSize: は UIImage のカテゴリとして実装しているのでこのメッセージが出るのは

     (a) カテゴリでメソッドが定義されていない
     (b) カテゴリの定義を import していない

    のどちらかだと思います。

    カテゴリは例えば次のように定義します。

    ファイル "UIImage+extension.h"
    @interface UIImage (extension)
    - (UIImage*)imageByShrinkingWithSize:(CGSize)size;
    @end

    ファイル "UIImage+extension.m"
    @implementation UIImage (extension)

    - (UIImage*)imageByShrinkingWithSize:(CGSize)size
    {
    :
    }

    @end

    その上でメソッドを利用するコード内で上記のヘッダファイルをインポートします。
    #import "UIImage+extension.h"
    :
    UIImage* shrinkedImage = [image imageByShrinkingWithSize:size];


    では。

Leave a Response

人気の投稿(過去 30日間)