From 58629291bc145edca8fb303e465c8537bed65314 Mon Sep 17 00:00:00 2001 From: "djsollen@google.com" Date: Thu, 3 Nov 2011 13:08:29 +0000 Subject: [PATCH] Adding support to trunk for building Skia using the Android NDK. This CL depends on a subsequent CL to add the appropriate NDK toolchain and system sources to the skia repo. Review URL: http://codereview.appspot.com/5306089/ Review URL: http://codereview.appspot.com/5306089 git-svn-id: http://skia.googlecode.com/svn/trunk@2592 2bbb7eff-a529-9590-31e7-b0007b416f81 --- bench/BenchTimer.cpp | 2 +- gyp/SampleApp.gyp | 17 +- gyp/android_system.gyp | 152 +++++++ gyp/bench.gypi | 5 +- gyp/common_conditions.gypi | 44 ++ gyp/core.gyp | 18 + gyp/gpu.gyp | 17 + gyp/images.gyp | 11 +- gyp/opts.gyp | 34 +- gyp/xml.gyp | 2 +- include/core/SkOSFile.h | 4 +- include/core/SkPreConfig.h | 13 +- include/gpu/SkNativeGLContext.h | 12 + samplecode/SampleApp_android.cpp | 375 ++++++++++++++++++ ... => GrGLCreateNativeInterface_android.cpp} | 4 +- src/gpu/android/SkNativeGLContext_android.cpp | 104 +++++ src/ports/FontHostConfiguration_android.cpp | 195 +++++++++ src/ports/FontHostConfiguration_android.h | 42 ++ src/ports/SkFontHost_FreeType.cpp | 3 +- src/ports/SkFontHost_android.cpp | 266 +++++++++---- src/utils/SkOSFile.cpp | 2 +- 21 files changed, 1218 insertions(+), 104 deletions(-) create mode 100644 gyp/android_system.gyp create mode 100644 samplecode/SampleApp_android.cpp rename src/gpu/android/{GrGLDefaultInterface_android.cpp => GrGLCreateNativeInterface_android.cpp} (98%) create mode 100644 src/gpu/android/SkNativeGLContext_android.cpp create mode 100644 src/ports/FontHostConfiguration_android.cpp create mode 100644 src/ports/FontHostConfiguration_android.h diff --git a/bench/BenchTimer.cpp b/bench/BenchTimer.cpp index be24a57568..c3a1190a8c 100644 --- a/bench/BenchTimer.cpp +++ b/bench/BenchTimer.cpp @@ -10,7 +10,7 @@ #include "BenchSysTimer_windows.h" #elif defined(SK_BUILD_FOR_MAC) #include "BenchSysTimer_mach.h" -#elif defined(SK_BUILD_FOR_UNIX) +#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) #include "BenchSysTimer_posix.h" #else #include "BenchSysTimer_c.h" diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp index ba650968cb..1855fe7a32 100644 --- a/gyp/SampleApp.gyp +++ b/gyp/SampleApp.gyp @@ -247,7 +247,22 @@ '../experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib', ], }], - + [ 'skia_os == "android"', { + # TODO: This doesn't build properly yet, but it's getting there. + 'type': 'shared_library', + 'sources!': [ + '../samplecode/SampleAnimator.cpp', + '../samplecode/SampleUnitMapper.cpp', + ], + 'dependencies!': [ + 'animator.gyp:animator', + 'experimental.gyp:experimental', + ], + 'sources': [ + # TODO add support for the android sample app +# '../samplecode/SampleApp_android.cpp', + ], + }], ], 'msvs_settings': { 'VCLinkerTool': { diff --git a/gyp/android_system.gyp b/gyp/android_system.gyp new file mode 100644 index 0000000000..6818f699a9 --- /dev/null +++ b/gyp/android_system.gyp @@ -0,0 +1,152 @@ +# This GYP file stores the dependencies necessary to build Skia on the Android +# platform. The OS doesn't provide many stable libraries as part of the +# distribution so we have to build a few of them ourselves. +# +# NOTE: We tried adding the gyp file to the android/ directory at the root of +# the Skia repo, but that resulted in the generated makefiles being created +# outside of the out directory. We may be able to move the bulk of this gyp +# to the /android directory and put a simple shim here, but that has yet to be +# tested. + +{ + # Define the location of the required Android sources, allowing for override + # in GYP_DEFINES. + # + # These sources are necessary because they must be built using the Android + # toolchain and they are not expected to be present on the host OS. + # + 'variables': { + 'android_repo%': '../../android_tools', + }, + 'android_repo%': '<(android_repo)', + + 'includes': [ + 'common.gypi', + ], + + 'targets': [ + { + 'target_name': 'ft2', + 'type': 'static_library', + 'sources': [ + '<(android_repo)/external/freetype/src/base/ftbbox.c', + '<(android_repo)/external/freetype/src/base/ftbitmap.c', + '<(android_repo)/external/freetype/src/base/ftglyph.c', + '<(android_repo)/external/freetype/src/base/ftlcdfil.c', + '<(android_repo)/external/freetype/src/base/ftstroke.c', + '<(android_repo)/external/freetype/src/base/ftxf86.c', + '<(android_repo)/external/freetype/src/base/ftbase.c', + '<(android_repo)/external/freetype/src/base/ftsystem.c', + '<(android_repo)/external/freetype/src/base/ftinit.c', + '<(android_repo)/external/freetype/src/base/ftgasp.c', + '<(android_repo)/external/freetype/src/base/ftfstype.c', + '<(android_repo)/external/freetype/src/raster/raster.c', + '<(android_repo)/external/freetype/src/sfnt/sfnt.c', + '<(android_repo)/external/freetype/src/smooth/smooth.c', + '<(android_repo)/external/freetype/src/autofit/autofit.c', + '<(android_repo)/external/freetype/src/truetype/truetype.c', + '<(android_repo)/external/freetype/src/cff/cff.c', + '<(android_repo)/external/freetype/src/psnames/psnames.c', + '<(android_repo)/external/freetype/src/pshinter/pshinter.c', + ], + 'include_dirs': [ + '<(android_repo)/external/freetype/builds', + '<(android_repo)/external/freetype/include', + ], + 'cflags': [ + '-W', + '-Wall', + '-fPIC', + '-DPIC', + '-DDARWIN_NO_CARBON', + '-DFT2_BUILD_LIBRARY', + '-O2', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(android_repo)/external/freetype/include', # For ft2build.h + ], + } + }, + { + 'target_name': 'expat', + 'type': 'static_library', + 'sources': [ + '<(android_repo)/external/expat/lib/xmlparse.c', + '<(android_repo)/external/expat/lib/xmlrole.c', + '<(android_repo)/external/expat/lib/xmltok.c', + ], + 'include_dirs': [ + '<(android_repo)/external/expat', + '<(android_repo)/external/expat/lib', + ], + 'cflags': [ + '-Wall', + '-Wmissing-prototypes', + '-Wstrict-prototypes', + '-fexceptions', + '-DHAVE_EXPAT_CONFIG_H', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(android_repo)/external/expat/lib', # For expat.h + ], + } + }, + { + 'target_name': 'gif', + 'type': 'static_library', + 'sources': [ + '<(android_repo)/external/gif/dgif_lib.c', + '<(android_repo)/external/gif/gifalloc.c', + '<(android_repo)/external/gif/gif_err.c', + ], + 'include_dirs': [ + '<(android_repo)/external/gif', + ], + 'cflags': [ + '-Wno-format', + '-DHAVE_CONFIG_H', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(android_repo)/external/gif', + ], + } + }, + { + 'target_name': 'png', + 'type': 'static_library', + 'sources': [ + '<(android_repo)/external/png/png.c', + '<(android_repo)/external/png/pngerror.c', + '<(android_repo)/external/png/pnggccrd.c', + '<(android_repo)/external/png/pngget.c', + '<(android_repo)/external/png/pngmem.c', + '<(android_repo)/external/png/pngpread.c', + '<(android_repo)/external/png/pngread.c', + '<(android_repo)/external/png/pngrio.c', + '<(android_repo)/external/png/pngrtran.c', + '<(android_repo)/external/png/pngrutil.c', + '<(android_repo)/external/png/pngset.c', + '<(android_repo)/external/png/pngtrans.c', + '<(android_repo)/external/png/pngvcrd.c', + '<(android_repo)/external/png/pngwio.c', + '<(android_repo)/external/png/pngwrite.c', + '<(android_repo)/external/png/pngwtran.c', + '<(android_repo)/external/png/pngwutil.c', + ], + 'include_dirs': [ + '<(android_repo)/external/png', + ], + 'cflags': [ + '-fvisibility=hidden', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(android_repo)/external/png', + ], + } + }, + ] +} \ No newline at end of file diff --git a/gyp/bench.gypi b/gyp/bench.gypi index e993790c4d..6ac64d1dbe 100644 --- a/gyp/bench.gypi +++ b/gyp/bench.gypi @@ -40,12 +40,13 @@ '../bench/BenchSysTimer_mach.cpp', ], }], - [ 'skia_os not in ["linux", "freebsd", "openbsd", "solaris"]', { + [ 'skia_os not in ["linux", "freebsd", "openbsd", "solaris", "android"]', { 'sources!': [ '../bench/BenchSysTimer_posix.h', '../bench/BenchSysTimer_posix.cpp', ], - },{ + }], + [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', { 'link_settings': { 'libraries': [ '-lrt', diff --git a/gyp/common_conditions.gypi b/gyp/common_conditions.gypi index d627998a86..759d8952ab 100644 --- a/gyp/common_conditions.gypi +++ b/gyp/common_conditions.gypi @@ -135,6 +135,50 @@ }, }, ], + + ['skia_os == "android"', + { + 'defines': [ + 'ANDROID', # TODO: change these defines to SK_BUILD_FOR_ANDROID + 'SK_BUILD_FOR_ANDROID', + 'SK_BUILD_FOR_ANDROID_NDK', + ], + 'configurations': { + 'Debug': { + 'cflags': ['-g'] + }, + 'Release': { + 'cflags': ['-O2'] + }, + }, + 'libraries': [ + '-lstdc++', + '-lm', + + ], + 'conditions': [ + [ 'skia_target_arch == "arm" and armv7 == 1', { + 'defines': [ + '__ARM_ARCH__=7', + ], + 'cflags': [ + '-march=armv7-a', + ], + 'conditions': [ + [ 'arm_neon == 1', { + 'defines': [ + '__ARM_HAVE_NEON', + ], + 'cflags': [ + '-mfloat-abi=softfp', + '-mfpu=neon', + ], + }], + ], + }], + ], + }, + ], ], # end 'conditions' } diff --git a/gyp/core.gyp b/gyp/core.gyp index abe9d1171c..a79af6b73e 100644 --- a/gyp/core.gyp +++ b/gyp/core.gyp @@ -340,6 +340,24 @@ '../src/ports/SkTime_win.cpp', ], }], + [ 'skia_os == "android"', { + 'sources!': [ + '../src/opts/opts_check_SSE2.cpp', + ], + 'sources': [ + '../include/core/SkMMapStream.h', + '../src/core/SkMMapStream.cpp', + '../src/ports/SkThread_pthread.cpp', + '../src/ports/SkFontHost_android.cpp', + '../src/ports/SkFontHost_gamma.cpp', + '../src/ports/SkFontHost_FreeType.cpp', + '../src/ports/FontHostConfiguration_android.cpp', + ], + 'dependencies': [ + 'android_system.gyp:ft2', + 'android_system.gyp:expat', + ], + }], ], 'direct_dependent_settings': { 'include_dirs': [ diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index a919de7d15..8b30a1259b 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -114,6 +114,8 @@ '../src/gpu/SkGrTexturePixelRef.cpp', '../src/gpu/SkNullGLContext.cpp', + '../src/gpu/android/SkNativeGLContext_android.cpp', + '../src/gpu/mac/SkNativeGLContext_mac.cpp', '../src/gpu/win/SkNativeGLContext_win.cpp', @@ -270,6 +272,8 @@ '../src/gpu/unix/GrGLCreateNativeInterface_unix.cpp', + '../src/gpu/android/GrGLCreateNativeInterface_android.cpp', + '../src/gpu/mesa/GrGLCreateMesaInterface.cpp', ], 'defines': [ @@ -327,6 +331,19 @@ '../src/gpu/GrGLCreateNativeInterface_none.cpp', ], }], + [ 'skia_os == "android"', { + 'sources!': [ + '../src/gpu/GrGLDefaultInterface_none.cpp', + '../src/gpu/GrGLCreateNativeInterface_none.cpp', + ], + 'link_settings': { + 'libraries': [ + '-lGLESv2', + '-lEGL', + '-shared', + ], + }, + }], ], }, ], diff --git a/gyp/images.gyp b/gyp/images.gyp index b50dd34594..4dce2c549b 100644 --- a/gyp/images.gyp +++ b/gyp/images.gyp @@ -116,7 +116,16 @@ }, # end libpng stuff }], - + [ 'skia_os == "android"', { + 'sources!': [ + '../src/images/SkImageDecoder_libjpeg.cpp', + '../src/images/SkJpegUtility.cpp', + ], + 'dependencies': [ + 'android_system.gyp:gif', + 'android_system.gyp:png', + ], + }], ], 'direct_dependent_settings': { 'include_dirs': [ diff --git a/gyp/opts.gyp b/gyp/opts.gyp index e63b1cb1f0..ae364816d8 100644 --- a/gyp/opts.gyp +++ b/gyp/opts.gyp @@ -35,11 +35,35 @@ '-msse2', ], }], - ], - 'sources': [ - '../src/opts/SkBitmapProcState_opts_SSE2.cpp', - '../src/opts/SkBlitRow_opts_SSE2.cpp', - '../src/opts/SkUtils_opts_SSE2.cpp', + [ 'skia_target_arch != "arm"', { + 'sources': [ + '../src/opts/SkBitmapProcState_opts_SSE2.cpp', + '../src/opts/SkBlitRow_opts_SSE2.cpp', + '../src/opts/SkUtils_opts_SSE2.cpp', + ], + }], + [ 'skia_target_arch == "arm" and armv7 == 1', { + # The assembly uses the frame pointer register (r7 in Thumb/r11 in + # ARM), the compiler doesn't like that. + 'cflags!': [ + '-fno-omit-frame-pointer', + ], + 'cflags': [ + '-fomit-frame-pointer', + ], + 'sources': [ + '../src/opts/SkBitmapProcState_opts_arm.cpp', + '../src/opts/SkBlitRow_opts_arm.cpp', + '../src/opts/SkUtils_opts_none.cpp', + ], + }], + [ 'skia_target_arch == "arm" and armv7 != 1', { + 'sources': [ + '../src/opts/SkBitmapProcState_opts_none.cpp', + '../src/opts/SkBlitRow_opts_none.cpp', + '../src/opts/SkUtils_opts_none.cpp', + ], + }], ], }, ], diff --git a/gyp/xml.gyp b/gyp/xml.gyp index 5c9edb27f6..7da8666630 100644 --- a/gyp/xml.gyp +++ b/gyp/xml.gyp @@ -33,7 +33,7 @@ '../src/xml/SkXMLPullParser.cpp', #if 0 around class decl in header ], 'conditions': [ - [ 'skia_os in ["win", "mac", "linux", "freebsd", "openbsd", "solaris"]', { + [ 'skia_os in ["win", "mac", "linux", "freebsd", "openbsd", "solaris", "android"]', { 'sources!': [ # no jsapi.h by default on system '../include/xml/SkJS.h', diff --git a/include/core/SkOSFile.h b/include/core/SkOSFile.h index 2a08b8e30f..9f01ead683 100644 --- a/include/core/SkOSFile.h +++ b/include/core/SkOSFile.h @@ -13,7 +13,7 @@ #include "SkString.h" -#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) #include #endif @@ -58,7 +58,7 @@ public: #ifdef SK_BUILD_FOR_WIN HANDLE fHandle; uint16_t* fPath16; -#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) DIR* fDIR; SkString fPath, fSuffix; #endif diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h index f8f4f62c52..11d2c5287a 100644 --- a/include/core/SkPreConfig.h +++ b/include/core/SkPreConfig.h @@ -16,7 +16,7 @@ ////////////////////////////////////////////////////////////////////// -#if !defined(SK_BUILD_FOR_ANDROID_NDK) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) && !defined(SK_BUILD_FOR_BREW) +#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) && !defined(SK_BUILD_FOR_BREW) #ifdef __APPLE__ #include "TargetConditionals.h" @@ -36,7 +36,7 @@ #define SK_BUILD_FOR_IOS #elif defined(ANDROID_NDK) #define SK_BUILD_FOR_ANDROID_NDK - #elif defined(ANROID) + #elif defined(ANDROID) #define SK_BUILD_FOR_ANDROID #else #define SK_BUILD_FOR_MAC @@ -44,6 +44,15 @@ #endif +/* Even if the user only defined the NDK variant we still need to build + * the default Android code. Therefore, when attempting to include/exclude + * something from the NDK variant check first that we are building for + * Android then check the status of the NDK define. + */ +#if defined(SK_BUILD_FOR_ANDROID_NDK) && !defined(SK_BUILD_FOR_ANDROID) + #define SK_BUILD_FOR_ANDROID +#endif + ////////////////////////////////////////////////////////////////////// #if !defined(SK_DEBUG) && !defined(SK_RELEASE) diff --git a/include/gpu/SkNativeGLContext.h b/include/gpu/SkNativeGLContext.h index 4ab58fbd74..36461ba7b2 100644 --- a/include/gpu/SkNativeGLContext.h +++ b/include/gpu/SkNativeGLContext.h @@ -12,6 +12,10 @@ #if defined(SK_BUILD_FOR_MAC) #include + +#elif defined(SK_BUILD_FOR_ANDROID) + #include + #include #elif defined(SK_BUILD_FOR_UNIX) #include #include @@ -43,6 +47,10 @@ public: #elif defined(SK_BUILD_FOR_WIN32) HDC fOldHDC; HGLRC fOldHGLRC; + #elif defined(SK_BUILD_FOR_ANDROID) + EGLContext fOldEGLContext; + EGLDisplay fOldDisplay; + EGLSurface fOldSurface; #endif }; @@ -63,6 +71,10 @@ private: HDC fDeviceContext; HGLRC fGlRenderContext; static ATOM gWC; +#elif defined(SK_BUILD_FOR_ANDROID) + EGLContext fContext; + EGLDisplay fDisplay; + EGLSurface fSurface; #endif }; diff --git a/samplecode/SampleApp_android.cpp b/samplecode/SampleApp_android.cpp new file mode 100644 index 0000000000..eafd76d046 --- /dev/null +++ b/samplecode/SampleApp_android.cpp @@ -0,0 +1,375 @@ + +/* + * Copyright 2011 Skia + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include "GrContext.h" +#include "GrGLInterface.h" +#include "SampleApp.h" +#include "SkApplication.h" +#include "SkCanvas.h" +#include "SkDevice.h" +#include "SkEvent.h" +#include "SkGpuCanvas.h" +#include "SkWindow.h" + +#include +#include "android/AndroidKeyToSkKey.h" + + +/////////////////////////////////////////// +///////////////// Globals ///////////////// +/////////////////////////////////////////// + +struct ActivityGlue { + JNIEnv* m_env; + jweak m_obj; + jmethodID m_setTitle; + jmethodID m_startTimer; + jmethodID m_addToDownloads; + ActivityGlue() { + m_env = NULL; + m_obj = NULL; + m_setTitle = NULL; + m_startTimer = NULL; + m_addToDownloads = NULL; + } +} gActivityGlue; + +struct WindowGlue { + jweak m_obj; + jmethodID m_inval; + jmethodID m_queueSkEvent; + WindowGlue() { + m_obj = NULL; + m_inval = NULL; + m_queueSkEvent = NULL; + } +} gWindowGlue; + +SampleWindow* gWindow; + +/////////////////////////////////////////// +///////////// SkOSWindow impl ///////////// +/////////////////////////////////////////// + +void SkOSWindow::onSetTitle(const char title[]) +{ + if (gActivityGlue.m_env) { + JNIEnv* env = gActivityGlue.m_env; + jstring string = env->NewStringUTF(title); + env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_setTitle, + string); + env->DeleteLocalRef(string); + } +} + +void SkOSWindow::onHandleInval(const SkIRect& rect) +{ + if (!gActivityGlue.m_env || !gWindowGlue.m_inval || !gWindowGlue.m_obj) { + return; + } + gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval); +} + +void SkOSWindow::onPDFSaved(const char title[], const char desc[], + const char path[]) +{ + if (gActivityGlue.m_env) { + JNIEnv* env = gActivityGlue.m_env; + jstring jtitle = env->NewStringUTF(title); + jstring jdesc = env->NewStringUTF(desc); + jstring jpath = env->NewStringUTF(path); + + env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_addToDownloads, + jtitle, jdesc, jpath); + + env->DeleteLocalRef(jtitle); + env->DeleteLocalRef(jdesc); + env->DeleteLocalRef(jpath); + } +} + +/////////////////////////////////////////// +/////////////// SkEvent impl ////////////// +/////////////////////////////////////////// + +void SkEvent::SignalQueueTimer(SkMSec ms) +{ + if (!gActivityGlue.m_env || !gActivityGlue.m_startTimer + || !gActivityGlue.m_obj || !ms) { + return; + } + gActivityGlue.m_env->CallVoidMethod(gActivityGlue.m_obj, + gActivityGlue.m_startTimer, ms); +} + +void SkEvent::SignalNonEmptyQueue() +{ + if (!gActivityGlue.m_env || !gWindowGlue.m_queueSkEvent + || !gWindowGlue.m_obj) { + return; + } + gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, + gWindowGlue.m_queueSkEvent); +} + +/////////////////////////////////////////// +////////////////// JNI //////////////////// +/////////////////////////////////////////// + +static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], + const char signature[]) +{ + jmethodID m = env->GetMethodID(clazz, name, signature); + if (!m) SkDebugf("Could not find Java method %s\n", name); + return m; +} + +extern "C" { +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_draw( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize( + JNIEnv* env, jobject thiz, jint w, jint h); +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown( + JNIEnv* env, jobject thiz, jint keyCode, jint uni); +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp( + JNIEnv* env, jobject thiz, jint keyCode); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick( + JNIEnv* env, jobject thiz, jint owner, jfloat x, jfloat y, jint state); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow( + JNIEnv* env, jobject thiz, jobject jsampleView); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_setZoomCenter( + JNIEnv* env, jobject thiz, jfloat x, jfloat y); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_zoom( + JNIEnv* env, jobject thiz, jfloat factor); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_nextSample( + JNIEnv* env, jobject thiz, jboolean fprevious); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_toggleRendering( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_toggleSlideshow( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_toggleFps( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_processSkEvent( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_serviceQueueTimer( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_saveToPdf( + JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_postInval( + JNIEnv* env, jobject thiz); +}; + +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown( + JNIEnv* env, jobject thiz, jint keyCode, jint uni) +{ + bool handled = gWindow->handleKey(AndroidKeycodeToSkKey(keyCode)); + handled |= gWindow->handleChar((SkUnichar) uni); + return handled; +} + +JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(JNIEnv* env, + jobject thiz, jint keyCode) +{ + return gWindow->handleKeyUp(AndroidKeycodeToSkKey(keyCode)); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(JNIEnv* env, + jobject thiz, jint owner, jfloat x, jfloat y, jint jstate) +{ + SkView::Click::State state; + switch(jstate) { + case 0: // MotionEvent.ACTION_DOWN + state = SkView::Click::kDown_State; + break; + case 1: // MotionEvent.ACTION_UP + case 3: // MotionEvent.ACTION_CANCEL + state = SkView::Click::kUp_State; + break; + case 2: // MotionEvent.ACTION_MOVE + state = SkView::Click::kMoved_State; + break; + default: + SkDebugf("motion event ignored\n"); + return; + } + gWindow->handleClick(x, y, state, (void*) owner); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(JNIEnv* env, + jobject thiz, jint w, jint h) +{ + gWindow->resize(w, h); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow( + JNIEnv* env, jobject thiz, jobject jsampleView) +{ + gWindow = new SampleWindow(NULL); + jclass clazz = gActivityGlue.m_env->FindClass( + "com/skia/sampleapp/SampleView"); + gWindowGlue.m_obj = gActivityGlue.m_env->NewWeakGlobalRef(jsampleView); + gWindowGlue.m_inval = GetJMethod(gActivityGlue.m_env, clazz, + "requestRender", "()V"); + gWindowGlue.m_queueSkEvent = GetJMethod(gActivityGlue.m_env, clazz, + "queueSkEvent", "()V"); + gActivityGlue.m_env->DeleteLocalRef(clazz); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env, + jobject thiz) +{ + gActivityGlue.m_env = env; + jclass clazz = env->FindClass("com/skia/sampleapp/SampleApp"); + gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz); + gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle", + "(Ljava/lang/CharSequence;)V"); + gActivityGlue.m_addToDownloads = GetJMethod(env, clazz, "addToDownloads", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + gActivityGlue.m_startTimer = GetJMethod(gActivityGlue.m_env, clazz, + "startTimer", "(I)V"); + env->DeleteLocalRef(clazz); + + application_init(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(JNIEnv* env, + jobject thiz) +{ + delete gWindow; + gWindow = NULL; + application_term(); + if (gWindowGlue.m_obj) { + env->DeleteWeakGlobalRef(gWindowGlue.m_obj); + gWindowGlue.m_obj = NULL; + } + if (gActivityGlue.m_obj) { + env->DeleteWeakGlobalRef(gActivityGlue.m_obj); + gActivityGlue.m_obj = NULL; + } +} + + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_draw( + JNIEnv* env, jobject thiz) +{ + if (!gWindow) return; + gWindow->update(NULL); + + // Copy the bitmap to the screen in raster mode + if (!gWindow->drawsToHardware()) { + + SkBitmap bitmap = gWindow->getBitmap(); + + GrContext* context = gWindow->getGrContext(); + if (!context) { + context = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL); + if (!context || !gWindow->setGrContext(context)) { + return; + } + context->unref(); + } + + GrRenderTarget* renderTarget; + + GrPlatformSurfaceDesc desc; + desc.reset(); + desc.fSurfaceType = kRenderTarget_GrPlatformSurfaceType; + desc.fWidth = bitmap.width(); + desc.fHeight = bitmap.height(); + desc.fConfig = kRGBA_8888_GrPixelConfig; + desc.fStencilBits = 8; + GrGLint buffer; + GR_GL_GetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer); + desc.fPlatformRenderTarget = buffer; + + renderTarget = static_cast( + context->createPlatformSurface(desc)); + + SkGpuCanvas* gpuCanvas = new SkGpuCanvas(context, renderTarget); + + SkDevice* device = gpuCanvas->createDevice(SkBitmap::kARGB_8888_Config, + bitmap.width(), bitmap.height(), + false, false); + gpuCanvas->setDevice(device)->unref(); + gpuCanvas->drawBitmap(bitmap, 0, 0); + + SkSafeUnref(renderTarget); + SkSafeUnref(gpuCanvas); + } +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_setZoomCenter( + JNIEnv* env, jobject thiz, jfloat x, jfloat y) +{ + gWindow->setZoomCenter(x, y); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_zoom( + JNIEnv* env, jobject thiz, jfloat factor) +{ + gWindow->changeZoomLevel(factor); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_nextSample( + JNIEnv* env, jobject thiz, jboolean fprevious) +{ + if (fprevious) { + gWindow->previousSample(); + } else { + gWindow->nextSample(); + } +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_toggleRendering( + JNIEnv* env, jobject thiz) +{ + gWindow->toggleRendering(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_toggleSlideshow( + JNIEnv* env, jobject thiz) +{ + gWindow->toggleSlideshow(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_toggleFps( + JNIEnv* env, jobject thiz) +{ + gWindow->toggleFPS(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_processSkEvent( + JNIEnv* env, jobject thiz) +{ + if (SkEvent::ProcessEvent()) { + SkEvent::SignalNonEmptyQueue(); + } +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_serviceQueueTimer( + JNIEnv* env, jobject thiz) +{ + SkEvent::ServiceQueueTimer(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_saveToPdf( + JNIEnv* env, jobject thiz) +{ + gWindow->saveToPdf(); +} + +JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_postInval( + JNIEnv* env, jobject thiz) +{ + gWindow->postInvalDelay(); +} diff --git a/src/gpu/android/GrGLDefaultInterface_android.cpp b/src/gpu/android/GrGLCreateNativeInterface_android.cpp similarity index 98% rename from src/gpu/android/GrGLDefaultInterface_android.cpp rename to src/gpu/android/GrGLCreateNativeInterface_android.cpp index 7de4bd1a65..2bb1ce381c 100644 --- a/src/gpu/android/GrGLDefaultInterface_android.cpp +++ b/src/gpu/android/GrGLCreateNativeInterface_android.cpp @@ -13,10 +13,10 @@ #include #include -const GrGLInterface* GrGLDefaultInterface() { +const GrGLInterface* GrGLCreateNativeInterface() { static SkAutoTUnref glInterface; if (!glInterface.get()) { - GrGLInteface* interface = new GrGLInterface; + GrGLInterface* interface = new GrGLInterface; glInterface.reset(interface); interface->fBindingsExported = kES2_GrGLBinding; interface->fActiveTexture = glActiveTexture; diff --git a/src/gpu/android/SkNativeGLContext_android.cpp b/src/gpu/android/SkNativeGLContext_android.cpp new file mode 100644 index 0000000000..eb58c27eab --- /dev/null +++ b/src/gpu/android/SkNativeGLContext_android.cpp @@ -0,0 +1,104 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SkNativeGLContext.h" + +SkNativeGLContext::AutoContextRestore::AutoContextRestore() { + fOldEGLContext = eglGetCurrentContext(); + fOldDisplay = eglGetCurrentDisplay(); + fOldSurface = eglGetCurrentSurface(EGL_DRAW); + +} + +SkNativeGLContext::AutoContextRestore::~AutoContextRestore() { + if (NULL != fOldDisplay) { + eglMakeCurrent(fOldDisplay, fOldSurface, fOldSurface, fOldEGLContext); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkNativeGLContext::SkNativeGLContext() + : fContext(EGL_NO_CONTEXT) + , fDisplay(EGL_NO_DISPLAY) + , fSurface(EGL_NO_SURFACE) { +} + +SkNativeGLContext::~SkNativeGLContext() { + this->destroyGLContext(); +} + +void SkNativeGLContext::destroyGLContext() { + if (fDisplay) { + eglMakeCurrent(fDisplay, 0, 0, 0); + + if (fContext) { + eglDestroyContext(fDisplay, fContext); + fContext = EGL_NO_CONTEXT; + } + + if (fSurface) { + eglDestroySurface(fDisplay, fSurface); + fSurface = EGL_NO_SURFACE; + } + + //TODO should we close the display? + fDisplay = EGL_NO_DISPLAY; + } +} + +const GrGLInterface* SkNativeGLContext::createGLContext() { + fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + EGLint majorVersion; + EGLint minorVersion; + eglInitialize(fDisplay, &majorVersion, &minorVersion); + + EGLint numConfigs; + static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE + }; + + EGLConfig surfaceConfig; + eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs); + + static const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + fContext = eglCreateContext(fDisplay, surfaceConfig, NULL, contextAttribs); + + + static const EGLint surfaceAttribs[] = { + EGL_WIDTH, 1, + EGL_HEIGHT, 1, + EGL_NONE + }; + fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs); + + eglMakeCurrent(fDisplay, fSurface, fSurface, fContext); + + const GrGLInterface* interface = GrGLCreateNativeInterface(); + if (!interface) { + SkDebugf("Failed to create gl interface"); + this->destroyGLContext(); + return NULL; + } + return interface; +} + +void SkNativeGLContext::makeCurrent() const { + if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { + SkDebugf("Could not set the context.\n"); + } +} diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp new file mode 100644 index 0000000000..475dc4af8b --- /dev/null +++ b/src/ports/FontHostConfiguration_android.cpp @@ -0,0 +1,195 @@ +/* libs/graphics/ports/FontHostConfiguration_android.cpp +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "FontHostConfiguration_android.h" +#include +#include "SkTDArray.h" + +#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.xml" +#define FALLBACK_FONTS_FILE "/system/etc/fallback_fonts.xml" +#define VENDOR_FONTS_FILE "/vendor/etc/fallback_fonts.xml" + + +// These defines are used to determine the kind of tag that we're currently +// populating with data. We only care about the sibling tags nameset and fileset +// for now. +#define NO_TAG 0 +#define NAMESET_TAG 1 +#define FILESET_TAG 2 + +/** + * The FamilyData structure is passed around by the parser so that each handler + * can read these variables that are relevant to the current parsing. + */ +struct FamilyData { + FamilyData(XML_Parser *parserRef, SkTDArray &familiesRef) : + parser(parserRef), families(familiesRef), currentTag(NO_TAG) {}; + + XML_Parser *parser; // The expat parser doing the work + SkTDArray &families; // The array that each family is put into as it is parsed + FontFamily *currentFamily; // The current family being created + int currentTag; // A flag to indicate whether we're in nameset/fileset tags +}; + +/** + * Handler for arbitrary text. This is used to parse the text inside each name + * or file tag. The resulting strings are put into the fNames or fFileNames arrays. + */ +void textHandler(void *data, const char *s, int len) { + FamilyData *familyData = (FamilyData*) data; + // Make sure we're in the right state to store this name information + if (familyData->currentFamily && + (familyData->currentTag == NAMESET_TAG || familyData->currentTag == FILESET_TAG)) { + // Malloc new buffer to store the string + char *buff; + buff = (char*) malloc((len + 1) * sizeof(char)); + strncpy(buff, s, len); + buff[len] = '\0'; + switch (familyData->currentTag) { + case NAMESET_TAG: + *(familyData->currentFamily->fNames.append()) = buff; + break; + case FILESET_TAG: + *(familyData->currentFamily->fFileNames.append()) = buff; + break; + default: + // Noop - don't care about any text that's not in the Fonts or Names list + break; + } + } +} + +/** + * Handler for the start of a tag. The only tags we expect are family, nameset, + * fileset, name, and file. + */ +void startElementHandler(void *data, const char *tag, const char **atts) { + FamilyData *familyData = (FamilyData*) data; + int len = strlen(tag); + if (strncmp(tag, "family", len)== 0) { + familyData->currentFamily = new FontFamily(); + familyData->currentFamily->order = -1; + // The Family tag has an optional "order" attribute with an integer value >= 0 + // If this attribute does not exist, the default value is -1 + for (int i = 0; atts[i] != NULL; i += 2) { + const char* attribute = atts[i]; + const char* valueString = atts[i+1]; + int value; + int len = sscanf(valueString, "%d", &value); + if (len > 0) { + familyData->currentFamily->order = value; + } + } + } else if (len == 7 && strncmp(tag, "nameset", len)== 0) { + familyData->currentTag = NAMESET_TAG; + } else if (len == 7 && strncmp(tag, "fileset", len) == 0) { + familyData->currentTag = FILESET_TAG; + } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || + (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { + // If it's a Name, parse the text inside + XML_SetCharacterDataHandler(*familyData->parser, textHandler); + } +} + +/** + * Handler for the end of tags. We only care about family, nameset, fileset, + * name, and file. + */ +void endElementHandler(void *data, const char *tag) { + FamilyData *familyData = (FamilyData*) data; + int len = strlen(tag); + if (strncmp(tag, "family", len)== 0) { + // Done parsing a Family - store the created currentFamily in the families array + *familyData->families.append() = familyData->currentFamily; + familyData->currentFamily = NULL; + } else if (len == 7 && strncmp(tag, "nameset", len)== 0) { + familyData->currentTag = NO_TAG; + } else if (len == 7 && strncmp(tag, "fileset", len)== 0) { + familyData->currentTag = NO_TAG; + } else if ((strncmp(tag, "name", len) == 0 && familyData->currentTag == NAMESET_TAG) || + (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) { + // Disable the arbitrary text handler installed to load Name data + XML_SetCharacterDataHandler(*familyData->parser, NULL); + } +} + +/** + * This function parses the given filename and stores the results in the given + * families array. + */ +void parseConfigFile(const char *filename, SkTDArray &families) { + XML_Parser parser = XML_ParserCreate(NULL); + FamilyData *familyData = new FamilyData(&parser, families); + XML_SetUserData(parser, familyData); + XML_SetElementHandler(parser, startElementHandler, endElementHandler); + FILE *file = fopen(filename, "r"); + // Some of the files we attempt to parse (in particular, /vendor/etc/fallback_fonts.xml) + // are optional - failure here is okay because one of these optional files may not exist. + if (file == NULL) { + return; + } + char buffer[512]; + bool done = false; + while (!done) { + fgets(buffer, sizeof(buffer), file); + int len = strlen(buffer); + if (feof(file) != 0) { + done = true; + } + XML_Parse(parser, buffer, len, done); + } +} + +/** + * Loads data on font families from various expected configuration files. The + * resulting data is returned in the given fontFamilies array. + */ +void getFontFamilies(SkTDArray &fontFamilies) { + + SkTDArray fallbackFonts; + SkTDArray vendorFonts; + parseConfigFile(SYSTEM_FONTS_FILE, fontFamilies); + parseConfigFile(FALLBACK_FONTS_FILE, fallbackFonts); + parseConfigFile(VENDOR_FONTS_FILE, vendorFonts); + + // This loop inserts the vendor fallback fonts in the correct order in the + // overall fallbacks list. + int currentOrder = -1; + for (int i = 0; i < vendorFonts.count(); ++i) { + FontFamily* family = vendorFonts[i]; + int order = family->order; + if (order < 0) { + if (currentOrder < 0) { + // Default case - just add it to the end of the fallback list + *fallbackFonts.append() = family; + } else { + // no order specified on this font, but we're incrementing the order + // based on an earlier order insertion request + *fallbackFonts.insert(currentOrder++) = family; + } + } else { + // Add the font into the fallback list in the specified order. Set + // currentOrder for correct placement of other fonts in the vendor list. + *fallbackFonts.insert(order) = family; + currentOrder = order + 1; + } + } + // Append all fallback fonts to system fonts + for (int i = 0; i < fallbackFonts.count(); ++i) { + *fontFamilies.append() = fallbackFonts[i]; + } +} diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h new file mode 100644 index 0000000000..010f0efc0f --- /dev/null +++ b/src/ports/FontHostConfiguration_android.h @@ -0,0 +1,42 @@ +/* libs/graphics/ports/FontHostConfiguration_android.h +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +#ifndef FONTHOSTCONFIGURATION_ANDROID_H_ +#define FONTHOSTCONFIGURATION_ANDROID_H_ + +#include "SkTDArray.h" + +/** + * The FontFamily data structure is created during parsing and handed back to + * Skia to fold into its representation of font families. fNames is the list of + * font names that alias to a font family. fFileNames is the list of font + * filenames for the family. Order is the priority order for the font. This is + * used internally to determine the order in which to place fallback fonts as + * they are read from the configuration files. + */ +struct FontFamily { + SkTDArray fNames; + SkTDArray fFileNames; + int order; +}; + +/** + * Parses all system font configuration files and returns the results in an + * array of FontFamily structures. + */ +void getFontFamilies(SkTDArray &fontFamilies); + +#endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */ diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 09390151fb..fa3c96a02d 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -322,7 +322,8 @@ static FT_Error getAdvances(FT_Face face, FT_UInt start, FT_UInt count, } static bool canEmbed(FT_Face face) { -#ifdef FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING +// The android port of FreeType does not build +#if defined(FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING) FT_UShort fsType = FT_Get_FSType_Flags(face); return (fsType & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING | FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) == 0; diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp index 940d162e35..7f1517485e 100644 --- a/src/ports/SkFontHost_android.cpp +++ b/src/ports/SkFontHost_android.cpp @@ -1,11 +1,19 @@ - /* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ #include "SkFontHost.h" #include "SkDescriptor.h" @@ -15,6 +23,7 @@ #include "SkStream.h" #include "SkThread.h" #include "SkTSearch.h" +#include "FontHostConfiguration_android.h" #include #define FONT_CACHE_MEMORY_BUDGET (768 * 1024) @@ -138,13 +147,17 @@ static SkTypeface* find_from_uniqueID(uint32_t uniqueID) { */ static FamilyRec* remove_from_family(const SkTypeface* face) { FamilyRec* family = find_family(face); - SkASSERT(family->fFaces[face->style()] == face); - family->fFaces[face->style()] = NULL; + if (family) { + SkASSERT(family->fFaces[face->style()] == face); + family->fFaces[face->style()] = NULL; - for (int i = 0; i < 4; i++) { - if (family->fFaces[i] != NULL) { // family is non-empty - return NULL; + for (int i = 0; i < 4; i++) { + if (family->fFaces[i] != NULL) { // family is non-empty + return NULL; + } } + } else { +// SkDebugf("remove_from_family(%p) face not found", face); } return family; // return the empty family } @@ -383,61 +396,80 @@ struct FontInitRec { const char* const* fNames; // null-terminated list }; -static const char* gSansNames[] = { - "sans-serif", "arial", "helvetica", "tahoma", "verdana", NULL -}; - -static const char* gSerifNames[] = { - "serif", "times", "times new roman", "palatino", "georgia", "baskerville", - "goudy", "fantasy", "cursive", "ITC Stone Serif", NULL -}; - -static const char* gMonoNames[] = { - "monospace", "courier", "courier new", "monaco", NULL -}; - // deliberately empty, but we use the address to identify fallback fonts static const char* gFBNames[] = { NULL }; -/* Fonts must be grouped by family, with the first font in a family having the - list of names (even if that list is empty), and the following members having - null for the list. The names list must be NULL-terminated -*/ -static const FontInitRec gSystemFonts[] = { - { "DroidSans.ttf", gSansNames }, - { "DroidSans-Bold.ttf", NULL }, - { "DroidSerif-Regular.ttf", gSerifNames }, - { "DroidSerif-Bold.ttf", NULL }, - { "DroidSerif-Italic.ttf", NULL }, - { "DroidSerif-BoldItalic.ttf", NULL }, - { "DroidSansMono.ttf", gMonoNames }, - /* These are optional, and can be ignored if not found in the file system. - These are appended to gFallbackFonts[] as they are seen, so we list - them in the order we want them to be accessed by NextLogicalFont(). - */ - { "DroidSansArabic.ttf", gFBNames }, - { "DroidSansHebrew.ttf", gFBNames }, - { "DroidSansThai.ttf", gFBNames }, - { "MTLmr3m.ttf", gFBNames }, // Motoya Japanese Font - { "MTLc3m.ttf", gFBNames }, // Motoya Japanese Font - { "DroidSansJapanese.ttf", gFBNames }, - { "DroidSansFallback.ttf", gFBNames } -}; -#define DEFAULT_NAMES gSansNames +/* Fonts are grouped by family, with the first font in a family having the + list of names (even if that list is empty), and the following members having + null for the list. The names list must be NULL-terminated. +*/ +static FontInitRec *gSystemFonts; +static size_t gNumSystemFonts = 0; + +#define SYSTEM_FONTS_FILE "/system/etc/system_fonts.cfg" // these globals are assigned (once) by load_system_fonts() static FamilyRec* gDefaultFamily; static SkTypeface* gDefaultNormal; +static char** gDefaultNames = NULL; +static uint32_t *gFallbackFonts; -/* This is sized conservatively, assuming that it will never be a size issue. - It will be initialized in load_system_fonts(), and will be filled with the - fontIDs that can be used for fallback consideration, in sorted order (sorted - meaning element[0] should be used first, then element[1], etc. When we hit - a fontID==0 in the array, the list is done, hence our allocation size is - +1 the total number of possible system fonts. Also see NextLogicalFont(). - */ -static uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1]; +/* Load info from a configuration file that populates the system/fallback font structures +*/ +static void load_font_info() { +// load_font_info_xml("/system/etc/system_fonts.xml"); + SkTDArray fontFamilies; + getFontFamilies(fontFamilies); + + SkTDArray fontInfo; + bool firstInFamily = false; + for (int i = 0; i < fontFamilies.count(); ++i) { + FontFamily *family = fontFamilies[i]; + firstInFamily = true; + for (int j = 0; j < family->fFileNames.count(); ++j) { + FontInitRec fontInfoRecord; + fontInfoRecord.fFileName = family->fFileNames[j]; + if (j == 0) { + if (family->fNames.count() == 0) { + // Fallback font + fontInfoRecord.fNames = (char **)gFBNames; + } else { + SkTDArray names = family->fNames; + const char **nameList = (const char**) + malloc((names.count() + 1) * sizeof(char*)); + if (nameList == NULL) { + // shouldn't get here + break; + } + if (gDefaultNames == NULL) { + gDefaultNames = (char**) nameList; + } + for (int i = 0; i < names.count(); ++i) { + nameList[i] = names[i]; + } + nameList[names.count()] = NULL; + fontInfoRecord.fNames = nameList; + } + } else { + fontInfoRecord.fNames = NULL; + } + *fontInfo.append() = fontInfoRecord; + } + } + gNumSystemFonts = fontInfo.count(); + gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec)); + gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t)); + if (gSystemFonts == NULL) { + // shouldn't get here + gNumSystemFonts = 0; + } + for (size_t i = 0; i < gNumSystemFonts; ++i) { + gSystemFonts[i].fFileName = fontInfo[i].fFileName; + gSystemFonts[i].fNames = fontInfo[i].fNames; + } + fontFamilies.deleteAll(); +} /* Called once (ensured by the sentinel check at the beginning of our body). Initializes all the globals, and register the system fonts. @@ -448,11 +480,13 @@ static void load_system_fonts() { return; } + load_font_info(); + const FontInitRec* rec = gSystemFonts; SkTypeface* firstInFamily = NULL; int fallbackCount = 0; - for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { + for (size_t i = 0; i < gNumSystemFonts; i++) { // if we're the first in a new family, clear firstInFamily if (rec[i].fNames != NULL) { firstInFamily = NULL; @@ -490,7 +524,7 @@ static void load_system_fonts() { const char* const* names = rec[i].fNames; // record the default family if this is it - if (names == DEFAULT_NAMES) { + if (names == gDefaultNames) { gDefaultFamily = family; } // add the names to map to this family @@ -511,40 +545,85 @@ static void load_system_fonts() { /////////////////////////////////////////////////////////////////////////////// void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { - const char* name = ((FamilyTypeface*)face)->getUniqueString(); + // lookup and record if the font is custom (i.e. not a system font) + bool isCustomFont = !((FamilyTypeface*)face)->isSysFont(); + stream->writeBool(isCustomFont); - stream->write8((uint8_t)face->style()); + if (isCustomFont) { + SkStream* fontStream = ((FamilyTypeface*)face)->openStream(); + + // store the length of the custom font + uint32_t len = fontStream->getLength(); + stream->write32(len); + + // store the entire font in the serialized stream + void* fontData = malloc(len); + + fontStream->read(fontData, len); + stream->write(fontData, len); + + fontStream->unref(); + free(fontData); +// SkDebugf("--- fonthost custom serialize %d %d\n", face->style(), len); - if (NULL == name || 0 == *name) { - stream->writePackedUInt(0); -// SkDebugf("--- fonthost serialize null\n"); } else { - uint32_t len = strlen(name); - stream->writePackedUInt(len); - stream->write(name, len); -// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style()); + const char* name = ((FamilyTypeface*)face)->getUniqueString(); + + stream->write8((uint8_t)face->style()); + + if (NULL == name || 0 == *name) { + stream->writePackedUInt(0); +// SkDebugf("--- fonthost serialize null\n"); + } else { + uint32_t len = strlen(name); + stream->writePackedUInt(len); + stream->write(name, len); +// SkDebugf("--- fonthost serialize <%s> %d\n", name, face->style()); + } } } SkTypeface* SkFontHost::Deserialize(SkStream* stream) { load_system_fonts(); - int style = stream->readU8(); + // check if the font is a custom or system font + bool isCustomFont = stream->readBool(); - int len = stream->readPackedUInt(); - if (len > 0) { - SkString str; - str.resize(len); - stream->read(str.writable_str(), len); + if (isCustomFont) { - const FontInitRec* rec = gSystemFonts; - for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) { - if (strcmp(rec[i].fFileName, str.c_str()) == 0) { - // backup until we hit the fNames - for (int j = i; j >= 0; --j) { - if (rec[j].fNames != NULL) { - return SkFontHost::CreateTypeface(NULL, - rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style); + // read the length of the custom font from the stream + uint32_t len = stream->readU32(); + + // generate a new stream to store the custom typeface + SkMemoryStream* fontStream = new SkMemoryStream(len); + stream->read((void*)fontStream->getMemoryBase(), len); + + SkTypeface* face = CreateTypefaceFromStream(fontStream); + + fontStream->unref(); + +// SkDebugf("--- fonthost custom deserialize %d %d\n", face->style(), len); + return face; + + } else { + int style = stream->readU8(); + + int len = stream->readPackedUInt(); + if (len > 0) { + SkString str; + str.resize(len); + stream->read(str.writable_str(), len); + + const FontInitRec* rec = gSystemFonts; + for (size_t i = 0; i < gNumSystemFonts; i++) { + if (strcmp(rec[i].fFileName, str.c_str()) == 0) { + // backup until we hit the fNames + for (int j = i; j >= 0; --j) { + if (rec[j].fNames != NULL) { + return SkFontHost::CreateTypeface(NULL, + rec[j].fNames[0], NULL, 0, + (SkTypeface::Style)style); + } } } } @@ -627,6 +706,16 @@ size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { load_system_fonts(); + const SkTypeface* origTypeface = find_from_uniqueID(origFontID); + const SkTypeface* currTypeface = find_from_uniqueID(currFontID); + + SkASSERT(origTypeface != 0); + SkASSERT(currTypeface != 0); + + // Our fallback list always stores the id of the plain in each fallback + // family, so we transform currFontID to its plain equivalent. + currFontID = find_typeface(currTypeface, SkTypeface::kNormal)->uniqueID(); + /* First see if fontID is already one of our fallbacks. If so, return its successor. If fontID is not in our list, then return the first one in our list. Note: list is zero-terminated, and returning zero means @@ -635,10 +724,17 @@ SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { const uint32_t* list = gFallbackFonts; for (int i = 0; list[i] != 0; i++) { if (list[i] == currFontID) { - return list[i+1]; + if (list[i+1] == 0) + return 0; + const SkTypeface* nextTypeface = find_from_uniqueID(list[i+1]); + return find_typeface(nextTypeface, origTypeface->style())->uniqueID(); } } - return list[0]; + + // If we get here, currFontID was not a fallback, so we start at the + // beginning of our list. + const SkTypeface* firstTypeface = find_from_uniqueID(list[0]); + return find_typeface(firstTypeface, origTypeface->style())->uniqueID(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/utils/SkOSFile.cpp b/src/utils/SkOSFile.cpp index fd5d273920..7c2b02439a 100644 --- a/src/utils/SkOSFile.cpp +++ b/src/utils/SkOSFile.cpp @@ -132,7 +132,7 @@ bool SkOSFile::Iter::next(SkString* name, bool getDir) return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir); } -#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) #if 0 OSStatus FSPathMakeRef (