Added utility functions and iOS ports of SkWindow, SkOSFile, SkFontHost, and SkImageDecoder

the iOSSampleApp in experimental currently includes all the following files but only uses SkOSWindow_iOS and SkOSFile_iOS
http://codereview.appspot.com/4657047/


git-svn-id: http://skia.googlecode.com/svn/trunk@1706 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
yangsu@google.com 2011-06-24 17:20:50 +00:00
Родитель 1c18ca3ca2
Коммит 1a2b4c1586
7 изменённых файлов: 743 добавлений и 0 удалений

Просмотреть файл

@ -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

35
include/views/SkOSWindow_iOS.h Executable file
Просмотреть файл

@ -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

266
src/utils/ios/SkFontHost_iOS.mm Executable file
Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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;
}

94
src/utils/ios/SkOSFile_iOS.mm Executable file
Просмотреть файл

@ -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;
}

208
src/utils/ios/SkOSWindow_iOS.mm Executable file
Просмотреть файл

@ -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();
}

Просмотреть файл

@ -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));
}