diff options
-rwxr-xr-x | include/utils/ios/SkStream_NSData.h | 34 | ||||
-rwxr-xr-x | include/views/SkOSWindow_iOS.h | 35 | ||||
-rwxr-xr-x | src/utils/ios/SkFontHost_iOS.mm | 266 | ||||
-rwxr-xr-x | src/utils/ios/SkImageDecoder_iOS.mm | 65 | ||||
-rwxr-xr-x | src/utils/ios/SkOSFile_iOS.mm | 94 | ||||
-rwxr-xr-x | src/utils/ios/SkOSWindow_iOS.mm | 208 | ||||
-rwxr-xr-x | src/utils/ios/SkStream_NSData.mm | 41 |
7 files changed, 743 insertions, 0 deletions
diff --git a/include/utils/ios/SkStream_NSData.h b/include/utils/ios/SkStream_NSData.h new file mode 100755 index 0000000000..af0004f991 --- /dev/null +++ b/include/utils/ios/SkStream_NSData.h @@ -0,0 +1,34 @@ +#ifndef SkStream_NSData_DEFINED +#define SkStream_NSData_DEFINED + +#import <UIKit/UIKit.h> +#include "SkStream.h" + +/** Returns an NSData with a copy of the stream's data. The caller must call + retain if it intends to keep the data object beyond the current stack-frame + (i.e. internally we're calling [NSData dataWithBytes...] + */ +NSData* NSData_dataWithStream(SkStream* stream); + +/** Returns an NSData from the named resource (from main bundle). + The caller must call retain if it intends to keep the data object beyond + the current stack-frame + (i.e. internally we're calling [NSData dataWithContentsOfMappedFile...] + */ +NSData* NSData_dataFromResource(const char name[], const char suffix[]); + +/** Wrap a stream around NSData. + */ +class SkStream_NSData : public SkMemoryStream { +public: + SkStream_NSData(NSData* data); + virtual ~SkStream_NSData(); + + static SkStream_NSData* CreateFromResource(const char name[], + const char suffix[]); + +private: + NSData* fNSData; +}; + +#endif diff --git a/include/views/SkOSWindow_iOS.h b/include/views/SkOSWindow_iOS.h new file mode 100755 index 0000000000..4f4205b592 --- /dev/null +++ b/include/views/SkOSWindow_iOS.h @@ -0,0 +1,35 @@ +#ifndef SkOSWindow_iOS_DEFINED +#define SkOSWindow_iOS_DEFINED + +#include "SkWindow.h" +#include "SkMatrix.h" +class SkOSWindow : public SkWindow { +public: + SkOSWindow(void* hwnd); + ~SkOSWindow(); + void* getHWND() const { return fHWND; } + + static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay); + + void detachGL(); + bool attachGL(); + void presentGL(); + +protected: + // overrides from SkEventSink + virtual bool onEvent(const SkEvent& evt); + // overrides from SkWindow + virtual void onHandleInval(const SkIRect&); + // overrides from SkView + virtual void onAddMenu(const SkOSMenu*); + virtual void onSetTitle(const char[]); + +private: + void* fHWND; + bool fInvalEventIsPending; + void* fNotifier; + typedef SkWindow INHERITED; +}; + +#endif + diff --git a/src/utils/ios/SkFontHost_iOS.mm b/src/utils/ios/SkFontHost_iOS.mm new file mode 100755 index 0000000000..0cde81e741 --- /dev/null +++ b/src/utils/ios/SkFontHost_iOS.mm @@ -0,0 +1,266 @@ +#import <UIKit/UIKit.h> + +#include "SkStream_NSData.h" +#include "SkTypeface.h" +#include "SkFontHost.h" +#include "SkThread.h" +#include "SkTemplates.h" + +enum FontDesign { + kUnknown_Design, + kSans_FontDesign, + kSerif_FontDesign, + + kIllegal_FontDesign, // never use with a real font +}; + +// returns kIllegal_FontDesign if not found +static FontDesign find_design_from_name(const char name[]) { + static const struct { + const char* fName; + FontDesign fDesign; + } gRec[] = { + { "sans-serif", kSans_FontDesign }, + { "serif", kSerif_FontDesign }, + }; + + for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) { + if (!strcasecmp(name, gRec[i].fName)) { + return gRec[i].fDesign; + } + } + return kIllegal_FontDesign; +} + +struct FontRes { + const char* fName; + SkTypeface::Style fStyle; + FontDesign fDesign; +}; + +static const FontRes gFontRes[] = { + { "DroidSans", SkTypeface::kNormal, kSans_FontDesign }, + { "DroidSans", SkTypeface::kBold, kSans_FontDesign }, + { "DroidSerif-Regular", SkTypeface::kNormal, kSerif_FontDesign }, + { "DroidSerif-Bold", SkTypeface::kBold, kSerif_FontDesign }, +// { "PescaderoPro", SkTypeface::kNormal, kSerif_FontDesign }, +// { "PescaderoPro-Bold", SkTypeface::kBold, kSerif_FontDesign }, +}; +#define FONTRES_COUNT SK_ARRAY_COUNT(gFontRes) + +#define DEFAULT_INDEX_REGULAR 1 +#define DEFAULT_INDEX_BOLD 2 + +/////////////////////////////////////////////////////////////////////////////// + +class SkTypeface_Stream : public SkTypeface { +public: + SkTypeface_Stream(SkStream* stream, Style style); + virtual ~SkTypeface_Stream(); + + SkStream* refStream() { + fStream->ref(); + return fStream; + } + +private: + SkStream* fStream; +}; + +static int32_t gUniqueFontID; + +SkTypeface_Stream::SkTypeface_Stream(SkStream* stream, Style style) +: SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) { + fStream = stream; + fStream->ref(); +} + +SkTypeface_Stream::~SkTypeface_Stream() { + fStream->unref(); +} + +static SkTypeface_Stream* create_from_fontres(const FontRes& res) { + SkStream* stream = SkStream_NSData::CreateFromResource(res.fName, "ttf"); + SkAutoUnref aur(stream); + + return SkNEW_ARGS(SkTypeface_Stream, (stream, res.fStyle)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int compute_style_distance(SkTypeface::Style a, SkTypeface::Style b) { + int dist = 0; + int diff = a ^ b; + if (diff & SkTypeface::kBold) { + dist += 2; + } + if (diff & SkTypeface::kItalic) { + dist += 1; + } + return dist; +} + +static SkTypeface_Stream* gFonts[FONTRES_COUNT]; + +static void assure_init_fonts() { + static bool gOnce; + if (!gOnce) { + for (size_t i = 0; i < FONTRES_COUNT; i++) { + gFonts[i] = create_from_fontres(gFontRes[i]); + gOnce = true; + } + } +} + +static SkTypeface_Stream* get_default_font(SkTypeface::Style style) { + assure_init_fonts(); + + if (style & SkTypeface::kBold) { + return gFonts[DEFAULT_INDEX_BOLD]; + } else { + return gFonts[DEFAULT_INDEX_REGULAR]; + } +} + +static SkTypeface_Stream* find_by_id(SkFontID fontID) { + assure_init_fonts(); + + for (size_t i = 0; i < FONTRES_COUNT; i++) { + if (gFonts[i]->uniqueID() == fontID) { + return gFonts[i]; + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +template <typename T> T* ref_and_return(T* obj) { + obj->ref(); + return obj; +} + +SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, + const char familyName[], + const void* data, size_t bytelength, + SkTypeface::Style style) { + assure_init_fonts(); + + if (familyName) { + FontDesign design = find_design_from_name(familyName); + if (kIllegal_FontDesign != design) { + familyName = "$#@*&%*#$@ never match any name"; + } + + int bestDistance = 999; + int bestIndex = -1; + for (size_t i = 0; i < FONTRES_COUNT; i++) { + if (design == gFontRes[i].fDesign || !strcmp(gFontRes[i].fName, familyName)) { + int dist = compute_style_distance(style, gFontRes[i].fStyle); + if (dist < bestDistance) { + bestDistance = dist; + bestIndex = i; + } + } + } + if (bestIndex >= 0) { + return ref_and_return(gFonts[bestIndex]); + } + } + + return ref_and_return(get_default_font(style)); +} + +SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { + SkASSERT(!"SkFontHost::CreateTypeface unimplemented"); + return NULL; +} + +SkTypeface* SkFontHost::CreateTypefaceFromFile(char const*) { +// SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented"); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SkFontHost::ValidFontID(uint32_t uniqueID) { + return true; +} + +SkStream* SkFontHost::OpenStream(uint32_t uniqueID) { + SkTypeface_Stream* tf = find_by_id(uniqueID); + SkASSERT(tf); + return tf->refStream(); +} + +size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, + int32_t* index) { + SkDebugf("SkFontHost::GetFileName unimplemented\n"); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { + SkASSERT(!"SkFontHost::Serialize unimplemented"); +} + +SkTypeface* SkFontHost::Deserialize(SkStream* stream) { + int style = stream->readU8(); + int len = stream->readPackedUInt(); + const char* name = NULL; + if (len > 0) { + SkString str; + str.resize(len); + stream->read(str.writable_str(), len); + + if (str.startsWith("DroidSans")) { + name = "sans-serif"; + } else if (str.startsWith("DroidSerif")) { + name = "serif"; + } + SkDebugf("---- deserialize typeface <%s> %d %s\n", str.c_str(), style, name); + } +// name = NULL; style = 0; + return SkFontHost::CreateTypeface(NULL, name, NULL, NULL, + (SkTypeface::Style)style); +} + +SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { + return 0; +} + +#define FONT_CACHE_MEMORY_BUDGET 1 * 1024 * 1024 + +size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { + if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) + return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; + else + return 0; // nothing to do +} + +/////////////////////////////////////////////////////////////////////////////// +int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { + return 0; +} + +void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { + tables[0] = NULL; // black gamma (e.g. exp=1.4) + tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) +} + +// static +SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( + uint32_t fontID, + SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) { + SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); + return NULL; +} + +void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { +} + +SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { + SkASSERT(!"SkFontHost::CreateScalarContext unimplemented"); + return NULL; +}
\ No newline at end of file diff --git a/src/utils/ios/SkImageDecoder_iOS.mm b/src/utils/ios/SkImageDecoder_iOS.mm new file mode 100755 index 0000000000..fc4316a56e --- /dev/null +++ b/src/utils/ios/SkImageDecoder_iOS.mm @@ -0,0 +1,65 @@ +/* + Copyright 2010, Tetrark Inc. +*/ + +#import <CoreGraphics/CoreGraphics.h> +#include <CoreGraphics/CGColorSpace.h> +#import <UIKit/UIKit.h> + +#include "SkImageDecoder.h" +#include "SkImageEncoder.h" +#include "SkMovie.h" +#include "SkStream_NSData.h" + +class SkImageDecoder_iOS : public SkImageDecoder { +protected: + virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); +}; + +#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) + +bool SkImageDecoder_iOS::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { + + NSData* data = NSData_dataWithStream(stream); + + UIImage* uimage = [UIImage imageWithData:data]; + + const int width = uimage.size.width; + const int height = uimage.size.height; + bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); + if (SkImageDecoder::kDecodeBounds_Mode == mode) { + return true; + } + + if (!this->allocPixelRef(bm, NULL)) { + return false; + } + + bm->lockPixels(); + bm->eraseColor(0); + + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); + CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, + 8, bm->rowBytes(), cs, BITMAP_INFO); + CGContextDrawImage(cg, CGRectMake(0, 0, width, height), uimage.CGImage); + CGContextRelease(cg); + CGColorSpaceRelease(cs); + + bm->unlockPixels(); + return true; +} + +///////////////////////////////////////////////////////////////////////// + +SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { + return new SkImageDecoder_iOS; +} + +SkMovie* SkMovie::DecodeStream(SkStream* stream) { + return NULL; +} + +SkImageEncoder* SkImageEncoder::Create(Type t) { + return NULL; +} + diff --git a/src/utils/ios/SkOSFile_iOS.mm b/src/utils/ios/SkOSFile_iOS.mm new file mode 100755 index 0000000000..33c572ac3a --- /dev/null +++ b/src/utils/ios/SkOSFile_iOS.mm @@ -0,0 +1,94 @@ +/* + Copyright 2010, Tetrark Inc. +*/ + +#include "SkOSFile.h" +#include "SkString.h" + +struct SkFILE { + NSData* fData; + size_t fOffset; + size_t fLength; +}; + +SkFILE* sk_fopen(const char cpath[], SkFILE_Flags flags) { + if (flags & kWrite_SkFILE_Flag) { + return NULL; + } + + SkString cname, csuffix; + + const char* start = strrchr(cpath, '/'); + if (NULL == start) { + start = cpath; + } else { + start += 1; + } + const char* stop = strrchr(cpath, '.'); + if (NULL == stop) { + return NULL; + } else { + stop += 1; + } + + cname.set(start, stop - start - 1); + csuffix.set(stop); + + NSBundle* bundle = [NSBundle mainBundle]; + NSString* name = [NSString stringWithUTF8String:cname.c_str()]; + NSString* suffix = [NSString stringWithUTF8String:csuffix.c_str()]; + NSString* path = [bundle pathForResource:name ofType:suffix]; + NSData* data = [NSData dataWithContentsOfMappedFile:path]; + + if (data) { + [data retain]; + SkFILE* rec = new SkFILE; + rec->fData = data; + rec->fOffset = 0; + rec->fLength = [data length]; + return reinterpret_cast<SkFILE*>(rec); + } + return NULL; +} + +size_t sk_fgetsize(SkFILE* rec) { + SkASSERT(rec); + return rec->fLength; +} + +bool sk_frewind(SkFILE* rec) { + SkASSERT(rec); + rec->fOffset = 0; + return true; +} + +size_t sk_fread(void* buffer, size_t byteCount, SkFILE* rec) { + if (NULL == buffer) { + return rec->fLength; + } else { + size_t remaining = rec->fLength - rec->fOffset; + if (byteCount > remaining) { + byteCount = remaining; + } + memcpy(buffer, (char*)[rec->fData bytes] + rec->fOffset, byteCount); + rec->fOffset += byteCount; + SkASSERT(rec->fOffset <= rec->fLength); + return byteCount; + } +} + +size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f) { + SkASSERT(!"Not supported yet"); + return 0; +} + +void sk_fflush(SkFILE* f) { + SkASSERT(!"Not supported yet"); +} + +void sk_fclose(SkFILE* rec) { + SkASSERT(rec); + [rec->fData release]; + delete rec; +} + diff --git a/src/utils/ios/SkOSWindow_iOS.mm b/src/utils/ios/SkOSWindow_iOS.mm new file mode 100755 index 0000000000..3611ba6377 --- /dev/null +++ b/src/utils/ios/SkOSWindow_iOS.mm @@ -0,0 +1,208 @@ +#import <UIKit/UIKit.h> +#import <OpenGLES/EAGL.h> +#import <OpenGLES/ES1/gl.h> +#include "SkTypes.h" +#include "SkWindow.h" +#include "SkCanvas.h" +#include "SkOSMenu.h" +#include "SkTime.h" +#include "SkGraphics.h" + +#define kINVAL_UIVIEW_EventType "inval-uiview" +#include "SkIOSNotifier.h" +#import "SkUIView_shell.h" + +SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd) { + fInvalEventIsPending = false; + fNotifier = [[SkIOSNotifier alloc] init]; +} +SkOSWindow::~SkOSWindow() { + [(SkIOSNotifier*)fNotifier release]; +} + +void SkOSWindow::onHandleInval(const SkIRect& r) { + if (!fInvalEventIsPending) { + fInvalEventIsPending = true; + (new SkEvent(kINVAL_UIVIEW_EventType))->post(this->getSinkID()); + } +} + +bool SkOSWindow::onEvent(const SkEvent& evt) { + if (evt.isType(kINVAL_UIVIEW_EventType)) { + fInvalEventIsPending = false; + const SkIRect& r = this->getDirtyBounds(); + [(SkUIView_shell*)fHWND postInvalWithRect:&r]; + return true; + } + if ([(SkUIView_shell*)fHWND onHandleEvent:evt]) { + return true; + } + return INHERITED::onEvent(evt); +} + +void SkOSWindow::onSetTitle(const char title[]) { + [(SkUIView_shell*)fHWND setSkTitle:title]; +} + +void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) +{ + +} + +/////////////////////////////////////////////////////////////////////////////////////// +/* + #if 1 + static void NonEmptyCallback(CFRunLoopTimerRef timer, void*) { + // printf("------- event queue depth = %d\n", SkEvent::CountEventsOnQueue()); + + if (!SkEvent::ProcessEvent()) { + CFRunLoopTimerInvalidate(timer); + } + } + + void SkEvent::SignalNonEmptyQueue() { + double tinyDelay = 1.0 / 60; + CFRunLoopTimerRef timer; + + timer = CFRunLoopTimerCreate(NULL, + CACurrentMediaTime() + tinyDelay, + tinyDelay, + 0, + 0, + NonEmptyCallback, + NULL); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), + timer, + kCFRunLoopCommonModes); + CFRelease(timer); + } + #elif 1 + #if 0 + #define NONE_EMPTY_CODE(code) code + #else + #define NONE_EMPTY_CODE(code) + #endif + static CFRunLoopSourceRef gNonEmptySource; + static CFRunLoopRef gNoneEmptyRunLoop; + static bool gAlreadySignaled; + + static void signal_nonempty() { + if (!gAlreadySignaled) { + NONE_EMPTY_CODE(printf("--- before resignal\n");) + gAlreadySignaled = true; + CFRunLoopSourceSignal(gNonEmptySource); + CFRunLoopWakeUp(gNoneEmptyRunLoop); + NONE_EMPTY_CODE(printf("--- after resignal\n");) + } + } + + static void NonEmptySourceCallback(void*) { + gAlreadySignaled = false; + NONE_EMPTY_CODE(printf("---- service NonEmptySourceCallback %d\n", SkEvent::CountEventsOnQueue());) + if (SkEvent::ProcessEvent()) { + signal_nonempty(); + } + NONE_EMPTY_CODE(printf("----- after service\n");) + } + + void SkEvent::SignalNonEmptyQueue() { + if (NULL == gNonEmptySource) { + gNoneEmptyRunLoop = CFRunLoopGetMain(); + + CFIndex order = 0; // should this be lower, to not start UIEvents? + CFRunLoopSourceContext context; + sk_bzero(&context, sizeof(context)); + // give it a "unique" info, for the default Hash function + context.info = (void*)NonEmptySourceCallback; + // our perform callback + context.perform = NonEmptySourceCallback; + gNonEmptySource = CFRunLoopSourceCreate(NULL, order, &context); + + CFRunLoopAddSource(gNoneEmptyRunLoop, + gNonEmptySource, + kCFRunLoopCommonModes); + } + signal_nonempty(); + } + #elif 1 + @interface NonEmptyHandler : NSObject {} + - (void)signalNonEmptyQ; + @end + + @implementation NonEmptyHandler + + - (void)callProccessEvent { + // printf("----- callProcessEvent\n"); + if (SkEvent::ProcessEvent()) { + [self signalNonEmptyQ]; + } + } + + - (void)signalNonEmptyQ { + [self performSelectorOnMainThread:@selector(callProccessEvent) withObject:nil waitUntilDone:NO]; + } + + void SkEvent::SignalNonEmptyQueue() { + static id gNonEmptyQueueObject; + if (nil == gNonEmptyQueueObject) { + gNonEmptyQueueObject = [[NonEmptyHandler alloc] init]; + } + [gNonEmptyQueueObject signalNonEmptyQ]; + } + + @end + + #endif + + /////////////////////////////////////////////////////////////////////////////// + + static CFRunLoopTimerRef gTimer; + + static void TimerCallback(CFRunLoopTimerRef timer, void* info) { + gTimer = NULL; + SkEvent::ServiceQueueTimer(); + } + + void SkEvent::SignalQueueTimer(SkMSec delay) + { + //We always release the timer right after we've added it to our RunLoop, + //thus we don't worry about freeing it later: if it fires our callback, + //it gets automatically freed, as it does if we call invalidate() + + if (gTimer) { + // our callback wasn't called, so invalidate it + CFRunLoopTimerInvalidate(gTimer); + gTimer = NULL; + } + + if (delay) { + gTimer = CFRunLoopTimerCreate(NULL, + CACurrentMediaTime() + delay/1000.0, + // CFAbsoluteTimeGetCurrent() + delay/1000.0, + 0, + 0, + 0, + TimerCallback, + NULL); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), + gTimer, + kCFRunLoopCommonModes); + CFRelease(gTimer); + } + } + */ +/////////////////////////////////////////////////////////////////////////////////////// + +bool SkOSWindow::attachGL() +{ + bool success = true; + return success; +} + +void SkOSWindow::detachGL() { +} + +void SkOSWindow::presentGL() { + glFlush(); +} + diff --git a/src/utils/ios/SkStream_NSData.mm b/src/utils/ios/SkStream_NSData.mm new file mode 100755 index 0000000000..0586cd6fd2 --- /dev/null +++ b/src/utils/ios/SkStream_NSData.mm @@ -0,0 +1,41 @@ +/* + Copyright 2010, Tetrark Inc. + */ + +#include "SkStream_NSData.h" + +NSData* NSData_dataWithStream(SkStream* stream) { + size_t length = stream->getLength(); + void* src = malloc(length); + size_t bytes = stream->read(src, length); + SkASSERT(bytes == length); + return [NSData dataWithBytesNoCopy:src length:length freeWhenDone:YES]; +} + +NSData* NSData_dataFromResource(const char cname[], const char csuffix[]) { + NSBundle* bundle = [NSBundle mainBundle]; + NSString* name = [NSString stringWithUTF8String:cname]; + NSString* suffix = [NSString stringWithUTF8String:csuffix]; + NSString* path = [bundle pathForResource:name ofType:suffix]; + return [NSData dataWithContentsOfMappedFile:path]; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkStream_NSData::SkStream_NSData(NSData* data) { + fNSData = data; + [fNSData retain]; + + this->setMemory([fNSData bytes], [fNSData length], false); +} + +SkStream_NSData::~SkStream_NSData() { + [fNSData release]; +} + +SkStream_NSData* SkStream_NSData::CreateFromResource(const char name[], + const char suffix[]) { + NSData* data = NSData_dataFromResource(name, suffix); + return SkNEW_ARGS(SkStream_NSData, (data)); +} + |