├── 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 |
--------------------------------------------------------------------------------