プレビュー機能を実装する 【QuickLook Plug-inの開発:実践的Macintoshプログラミング解説】

印刷用表示 |テキストサイズ 小 |中 |大 |

クイックルックプラグインの開発

実践的 Macintosh プログラミング解説

QuickLook Cover Flow.png

| HOME | 実際の機能を実装する | プレビュー機能を実装する |

更新日 2011-10-02 | 作成日 2008-01-04

実際の機能を実装する

displayRectIgnoringOpacity:inContext:の説明

 ここまではサンプルとして日の丸を描いてきましたが、ここからは実際にRepeating Motif Generatorのプレビュー画像とサムネイル画像を生成する機能を実装していきます。

 とはいえ、ここまでくるとQuickLookプラグインに特有の話はほとんど残っていませんので、後は一気に完成まで進んでしまう事になります。

 その前にNSViewのメソッドdisplayRectIgnoringOpacity:inContext:の説明をしておきましょう。

- (void)displayRectIgnoringOpacity:(NSRect)aRect
                         inContext:(NSGraphicsContext *)context

 このメソッドはdisplayメソッドと似た働きをしますが、描画範囲をaRectに限定します。そしてたとえビューが透明であっても、その透明度は無視して描画を開始します。

 Repeating Motif GeneratorはPDFで画像を書き出す際に、オフスクリーンバッファのようなビューを用意して、そのビューにdataWithPDFInsideRect:メッセージを送ってPDFデータを生成しています。今回はそのビューにdisplayRectIgnoringOpacity:inContext:メッセージを送って、プレビュー画像を描くグラフィックコンテキストに描画を行なわせる事にします。


プレビュー機能を実装する

 それではコードの解説に移ります。最初はインターフェイスファイルのインポートです。
 先ほど説明したオフスクリーンバッファの様なビューRepeatingMotifViewのインターフェイスファイルのインポートを追加します。

 次に文字列定数をグローバル変数として追加します。UTIはこれまで説明してきたものなのでよいでしょう。引数チェックに使います。その次の文字列はデータをエンコードする際にキーとして使った文字列です。これはRMGDocumentからコピーしました。

#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <QuickLook/QuickLook.h>

#import <Cocoa/Cocoa.h>
#import "RepeatingMotifView.h"

NSString    *RMGDocumentUTI         = @"com.novemberkou.rmgdoc";
NSString    *RMGRepeatingMotifsKey  = @"RMGRepeatingMotifArray";
NSString    *RMGPictureWidthKey     = @"RMGPictureWidth";
NSString    *RMGPictureHeightKey    = @"RMGPictureHeight";

 関数の頭では、読み込んだデータを入れておく変数を定義しています。また、アーカイブ化されているデータを非アーカイブ化するためのオブジェクトが必要なので、そのインスタンスを保持する変数を定義しました。

 オートリリースプールを作成する前にまず引数チェックをして、おかしかったら終了する様にしています。
 引数のUTIが想定しているものと違う場合は何もできないので、そのまま終了します。
 引数のURLがファイルを表すものではない場合も、やはりその後の処理ができないので終了します。

OSStatus GeneratePreviewForURL(
    void *thisInterface,
    QLPreviewRequestRef preview,
    CFURLRef url,
    CFStringRef contentTypeUTI,
    CFDictionaryRef options)
{
    int                 pictureWidth,pictureHeight;
    NSArray             *repeatingMotifs;
    NSKeyedUnarchiver   *unarchiver;

    if(![(NSString *)contentTypeUTI isEqualToString:RMGDocumentUTI])
        return noErr;
    if(![(NSURL *)url isFileURL])
        return noErr;

 オブジェクトの生成をする前にオートリリースプールを生成します。その後、NSDataを生成してファイルの中身を読み込みます。

 NSDataから必要なオブジェクトだけを非アーカイブ化して取り出します。この処理はRMGDocumentから抜粋したものです。

    NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];
    NSData  *data = [NSData dataWithContentsOfFile:[(NSURL *)url path]];

    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    repeatingMotifs = [unarchiver decodeObjectForKey:RMGRepeatingMotifsKey];
    pictureWidth = [unarchiver decodeIntForKey:RMGPictureWidthKey];
    pictureHeight = [unarchiver decodeIntForKey:RMGPictureHeightKey];

    [unarchiver finishDecoding];
    [unarchiver release];


 後は一気に最後までいきますが、骨格は「複数ページの画像を返す」で説明したものと全く同じです。違いは画像サイズが固定値からファイルから読み込んだ値になっている程度でしょうか。

 肝心な処理はwhile()節の中身ですが、ここではオフスクリーンビューにRepeatingMotifオブジェクトをセットしてからdisplayRectIgnoringOpacity:inContext:メッセージを送って、グラフィックコンテキストに描画を行なっています。

    CGRect  mediaBox = CGRectMake(0,0,pictureWidth,pictureHeight);
    NSRect  nsMediaBox = NSRectFromCGRect(mediaBox);
    RepeatingMotifView *renderingView =
        [[[RepeatingMotifView alloc] initWithFrame:nsMediaBox] autorelease];
    NSDictionary    *dict = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSNumber numberWithInt:pictureWidth],kQLPreviewPropertyWidthKey,
        [NSNumber numberWithInt:pictureHeight],kQLPreviewPropertyHeightKey,
        nil];
    CGContextRef cgContext = QLPreviewRequestCreatePDFContext(
        preview,&mediaBox,NULL,(CFDictionaryRef)dict);
    if(cgContext)
    {
        NSGraphicsContext* context =
            [NSGraphicsContext
                graphicsContextWithGraphicsPort:(void *)cgContext
                                        flipped:YES];
        if(context)
        {
            [NSGraphicsContext saveGraphicsState];
            [NSGraphicsContext setCurrentContext:context];

            NSEnumerator    *enumerator = [repeatingMotifs objectEnumerator];
            RepeatingMotif  *repeatingMotif;

            while(repeatingMotif = [enumerator nextObject])
            {
                CGPDFContextBeginPage(cgContext,NULL);
                [renderingView setRepeatingMotif:repeatingMotif];
                [renderingView displayRectIgnoringOpacity:nsMediaBox
                                                inContext:context];
                CGPDFContextEndPage(cgContext);
            }

            [NSGraphicsContext restoreGraphicsState];
        }
        QLPreviewRequestFlushContext(preview,cgContext);
        CFRelease(cgContext);
    }

    [pool release];
    return noErr;
}

 以上でプレビュー機能の実装に関する説明は終わりです。