├── jni ├── Application.mk ├── Android.mk ├── glbuffer.h ├── register_natives.c └── glbuffer.c ├── res ├── drawable-hdpi │ └── icon.png ├── drawable-ldpi │ └── icon.png ├── drawable-mdpi │ └── icon.png ├── values │ └── strings.xml └── layout │ └── main.xml ├── .gitignore ├── src └── com │ └── example │ └── glbuffer │ ├── GlBufferActivity.java │ └── GlBufferView.java ├── .travis.yml ├── README.mkd ├── custom_rules.xml ├── prepare-android-build.sh ├── AndroidManifest.xml └── LICENSE /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a 2 | APP_PLATFORM := android-4 3 | 4 | -------------------------------------------------------------------------------- /res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richq/glbuffer/HEAD/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richq/glbuffer/HEAD/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richq/glbuffer/HEAD/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | GL Buffer Example 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | tags 3 | cscope.files 4 | cscope.in.out 5 | cscope.out 6 | cscope.po.out 7 | bin/ 8 | libs/ 9 | obj/ 10 | build.xml 11 | gen/ 12 | local.properties 13 | proguard-project.txt 14 | project.properties 15 | -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | BUILD_PATH := $(LOCAL_PATH)/$(SCONS_BUILD_ROOT) 4 | 5 | include $(CLEAR_VARS) 6 | 7 | LOCAL_LDLIBS := -llog -lGLESv1_CM 8 | LOCAL_MODULE := glbuffer 9 | LOCAL_SRC_FILES := glbuffer.c register_natives.c 10 | 11 | include $(BUILD_SHARED_LIBRARY) 12 | -------------------------------------------------------------------------------- /jni/glbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef glbuffer_h_seen 2 | #define glbuffer_h_seen 3 | 4 | #include 5 | #define UNUSED __attribute__((unused)) 6 | void native_start(JNIEnv *env, jclass clazz); 7 | void native_gl_resize(JNIEnv *env, jclass clazz, jint w, jint h); 8 | void native_gl_render(JNIEnv *env, jclass clazz); 9 | #endif 10 | -------------------------------------------------------------------------------- /src/com/example/glbuffer/GlBufferActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.glbuffer; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class GlBufferActivity extends Activity { 7 | @Override 8 | public void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | setContentView(R.layout.main); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | android: 3 | components: 4 | - platform-tools 5 | - tools 6 | before_install: 7 | - wget --timeout=30 http://dl.google.com/android/ndk/android-ndk-r8d-linux-x86.tar.bz2 -O ndk.tar.bz2 8 | - tar -xf ndk.tar.bz2 9 | - which android 10 | install: ./prepare-android-build.sh $(dirname $(dirname $(which android))) $(pwd)/android-ndk-r8d 11 | script: ant debug 12 | -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | This is a complete example of how to use GLSurfaceView and native code to draw 2 | old-skool pixel buffer graphics on the Android platform. It is related to this 3 | [blog post][] about the Android NDK. 4 | 5 | To compile the code: 6 | 7 | ./prepare-android-build.sh SDK [NDK] 8 | ant debug 9 | 10 | * SDK - path to the SDK (or can be set in `ANDROID_SDK`) 11 | * NDK - path to the NDK. If not given, then assumes the ndk is next to the SDK. 12 | 13 | 14 | [blog post]: http://quirkygba.blogspot.com/2010/10/android-native-coding-in-c.html 15 | -------------------------------------------------------------------------------- /custom_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /prepare-android-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # prepare a build env for android in the android-build directory 4 | 5 | top=$(dirname $(readlink -f $0)) 6 | cd $top 7 | 8 | if test $# -eq 0 ; then 9 | SDK=$ANDROID_SDK 10 | elif test $# -eq 1 ; then 11 | SDK=$1 12 | else 13 | SDK=$1 14 | NDK=$2 15 | fi 16 | 17 | if test x"$SDK" = x ; then 18 | echo "No SDK given - either set ANDROID_SDK or pass it as an argument" 19 | exit 1 20 | fi 21 | 22 | if test x"$NDK" = x ; then 23 | NDK=$(ls -1d $(dirname $SDK)/android-ndk-* | tail -1) 24 | fi 25 | 26 | exampletarget=$($SDK/tools/android list targets | awk '/android-[0-9]/{gsub("\"", ""); split($4, a, "-"); if (a[2] >= 4) { print $4}}' | head -n1) 27 | $SDK/tools/android update project -n glbuffer -p . --target $exampletarget 28 | 29 | echo "ndk.dir=$NDK" >> local.properties 30 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 14 | 17 | 18 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Richard Quirk 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 19 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 20 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 21 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /jni/register_natives.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "glbuffer.h" 5 | 6 | #ifndef NELEM 7 | #define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0]))) 8 | #endif 9 | 10 | static const char *s_class_path_name = "com/example/glbuffer/GlBufferView"; 11 | 12 | static JNINativeMethod s_methods[] = { 13 | {"native_start", "()V", (void*) native_start}, 14 | {"native_gl_resize", "(II)V", (void*) native_gl_resize}, 15 | {"native_gl_render", "()V", (void*) native_gl_render}, 16 | }; 17 | 18 | static int register_native_methods(JNIEnv* env, 19 | const char* class_name, 20 | JNINativeMethod* methods, 21 | int num_methods) 22 | { 23 | jclass clazz; 24 | 25 | clazz = (*env)->FindClass(env, class_name); 26 | if (clazz == NULL) { 27 | fprintf(stderr, "Native registration unable to find class '%s'\n", 28 | class_name); 29 | return JNI_FALSE; 30 | } 31 | if ((*env)->RegisterNatives(env, clazz, methods, num_methods) < 0) { 32 | fprintf(stderr, "RegisterNatives failed for '%s'\n", class_name); 33 | return JNI_FALSE; 34 | } 35 | 36 | return JNI_TRUE; 37 | } 38 | 39 | static int register_natives(JNIEnv *env) 40 | { 41 | return register_native_methods(env, 42 | s_class_path_name, 43 | s_methods, 44 | NELEM(s_methods)); 45 | } 46 | 47 | jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved UNUSED) 48 | { 49 | JNIEnv* env = NULL; 50 | jint result = -1; 51 | 52 | if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { 53 | fprintf(stderr, "ERROR: GetEnv failed\n"); 54 | goto bail; 55 | } 56 | assert(env != NULL); 57 | 58 | if (register_natives(env) < 0) { 59 | fprintf(stderr, "ERROR: Exif native registration failed\n"); 60 | goto bail; 61 | } 62 | 63 | /* success -- return valid version number */ 64 | result = JNI_VERSION_1_4; 65 | bail: 66 | return result; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/com/example/glbuffer/GlBufferView.java: -------------------------------------------------------------------------------- 1 | package com.example.glbuffer; 2 | 3 | import javax.microedition.khronos.opengles.GL10; 4 | import javax.microedition.khronos.egl.EGLConfig; 5 | import android.util.Log; 6 | import android.opengl.GLSurfaceView; 7 | import android.os.SystemClock; 8 | import android.content.Context; 9 | import android.util.AttributeSet; 10 | 11 | public class GlBufferView extends GLSurfaceView { 12 | private static native void native_start(); 13 | private static native void native_gl_resize(int w, int h); 14 | private static native void native_gl_render(); 15 | 16 | public GlBufferView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | (new Thread() { 19 | @Override 20 | public void run() { 21 | native_start(); 22 | } 23 | }).start(); 24 | setRenderer(new MyRenderer()); 25 | requestFocus(); 26 | setFocusableInTouchMode(true); 27 | } 28 | 29 | class MyRenderer implements GLSurfaceView.Renderer { 30 | @Override 31 | public void onSurfaceCreated(GL10 gl, EGLConfig c) { /* do nothing */ } 32 | 33 | @Override 34 | public void onSurfaceChanged(GL10 gl, int w, int h) { 35 | native_gl_resize(w, h); 36 | } 37 | 38 | @Override 39 | public void onDrawFrame(GL10 gl) { 40 | time = SystemClock.uptimeMillis(); 41 | 42 | if (time >= (frameTime + 1000.0f)) { 43 | frameTime = time; 44 | avgFPS += framerate; 45 | framerate = 0; 46 | } 47 | 48 | if (time >= (fpsTime + 3000.0f)) { 49 | fpsTime = time; 50 | avgFPS /= 3.0f; 51 | Log.d("GLBUFEX", "FPS: " + Float.toString(avgFPS)); 52 | avgFPS = 0; 53 | } 54 | framerate++; 55 | native_gl_render(); 56 | } 57 | public long time = 0; 58 | public short framerate = 0; 59 | public long fpsTime = 0; 60 | public long frameTime = 0; 61 | public float avgFPS = 0; 62 | } 63 | 64 | static { 65 | System.loadLibrary("glbuffer"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /jni/glbuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "glbuffer.h" 7 | 8 | #define TEXTURE_WIDTH 512 9 | #define TEXTURE_HEIGHT 256 10 | #define LOG_TAG "GLBUFEX" 11 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 12 | #define S_PIXELS_SIZE (sizeof(s_pixels[0]) * TEXTURE_WIDTH * TEXTURE_HEIGHT) 13 | #define RGB565(r, g, b) (((r) << (5+6)) | ((g) << 6) | (b)) 14 | 15 | static uint16_t *s_pixels = 0; 16 | static pthread_cond_t s_vsync_cond; 17 | static pthread_mutex_t s_vsync_mutex; 18 | static GLuint s_texture = 0; 19 | static int s_x = 10; 20 | static int s_y = 50; 21 | 22 | 23 | static void check_gl_error(const char* op) 24 | { 25 | GLint error; 26 | for (error = glGetError(); error; error = glGetError()) 27 | LOGI("after %s() glError (0x%x)\n", op, error); 28 | } 29 | 30 | /* wait for the screen to redraw */ 31 | static void wait_vsync() 32 | { 33 | pthread_mutex_lock(&s_vsync_mutex); 34 | pthread_cond_wait(&s_vsync_cond, &s_vsync_mutex); 35 | pthread_mutex_unlock(&s_vsync_mutex); 36 | } 37 | 38 | static void render_pixels(uint16_t *pixels) 39 | { 40 | int x, y; 41 | /* fill in a square of 5 x 5 at s_x, s_y */ 42 | for (y = s_y; y < s_y + 5; y++) { 43 | for (x = s_x; x < s_x + 5; x++) { 44 | int idx = x + y * TEXTURE_WIDTH; 45 | pixels[idx++] = RGB565(31, 31, 0); 46 | } 47 | } 48 | } 49 | 50 | int s_w = 0; 51 | int s_h = 0; 52 | 53 | /* disable these capabilities. */ 54 | static GLuint s_disable_caps[] = { 55 | GL_FOG, 56 | GL_LIGHTING, 57 | GL_CULL_FACE, 58 | GL_ALPHA_TEST, 59 | GL_BLEND, 60 | GL_COLOR_LOGIC_OP, 61 | GL_DITHER, 62 | GL_STENCIL_TEST, 63 | GL_DEPTH_TEST, 64 | GL_COLOR_MATERIAL, 65 | 0 66 | }; 67 | 68 | void native_gl_resize(JNIEnv *env UNUSED, jclass clazz UNUSED, jint w, jint h) 69 | { 70 | LOGI("native_gl_resize %d %d", w, h); 71 | glDeleteTextures(1, &s_texture); 72 | GLuint *start = s_disable_caps; 73 | while (*start) 74 | glDisable(*start++); 75 | glEnable(GL_TEXTURE_2D); 76 | glGenTextures(1, &s_texture); 77 | glBindTexture(GL_TEXTURE_2D, s_texture); 78 | glTexParameterf(GL_TEXTURE_2D, 79 | GL_TEXTURE_MIN_FILTER, GL_LINEAR); 80 | glTexParameterf(GL_TEXTURE_2D, 81 | GL_TEXTURE_MAG_FILTER, GL_LINEAR); 82 | glShadeModel(GL_FLAT); 83 | check_gl_error("glShadeModel"); 84 | glColor4x(0x10000, 0x10000, 0x10000, 0x10000); 85 | check_gl_error("glColor4x"); 86 | int rect[4] = {0, TEXTURE_HEIGHT, TEXTURE_WIDTH, -TEXTURE_HEIGHT}; 87 | glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, rect); 88 | check_gl_error("glTexParameteriv"); 89 | s_w = w; 90 | s_h = h; 91 | } 92 | 93 | void native_gl_render(JNIEnv *env UNUSED, jclass clazz UNUSED) 94 | { 95 | memset(s_pixels, 0, S_PIXELS_SIZE); 96 | render_pixels(s_pixels); 97 | glClear(GL_COLOR_BUFFER_BIT); 98 | glTexImage2D(GL_TEXTURE_2D, /* target */ 99 | 0, /* level */ 100 | GL_RGB, /* internal format */ 101 | TEXTURE_WIDTH, /* width */ 102 | TEXTURE_HEIGHT, /* height */ 103 | 0, /* border */ 104 | GL_RGB, /* format */ 105 | GL_UNSIGNED_SHORT_5_6_5,/* type */ 106 | s_pixels); /* pixels */ 107 | check_gl_error("glTexImage2D"); 108 | glDrawTexiOES(0, 0, 0, s_w, s_h); 109 | check_gl_error("glDrawTexiOES"); 110 | /* tell the other thread to carry on */ 111 | pthread_cond_signal(&s_vsync_cond); 112 | } 113 | 114 | void native_start(JNIEnv *env UNUSED, jclass clazz UNUSED) 115 | { 116 | /* init conditions */ 117 | pthread_cond_init(&s_vsync_cond, NULL); 118 | pthread_mutex_init(&s_vsync_mutex, NULL); 119 | int incr = 1; 120 | s_pixels = malloc(S_PIXELS_SIZE); 121 | while (1) { 122 | /* draw a square going backwards and forwards */ 123 | s_x += incr; 124 | if (s_x > 200) 125 | incr = -1; 126 | if (s_x < 20 && incr < 0) 127 | incr = 1; 128 | /* wait on screen sync */ 129 | wait_vsync(); 130 | } 131 | } 132 | --------------------------------------------------------------------------------