Related
I'm trying to draw something use OpenGL ES 1.0, the rendering to screen works good. Then I want to use ImageReader to get the image data from surface, but its callback ImageReader.OnImageAvailableListener is not called at all while rendering, what's wrong about my code?
public class ImageReaderActivity extends ActionBarActivity implements SurfaceHolder.Callback, Runnable {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Surface mSurface;
private int mWidth, mHeight;
private boolean mRunning = false;
private ImageReader mImageReader;
private OpenGLDrawer mOpenGLDrawer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_imagereader);
mSurfaceView = findViewById(R.id.surface_view);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mWidth = width;
mHeight = height;
mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
mSurface = mImageReader.getSurface();
new Thread(this).start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mRunning = false;
}
#Override
public void run() {
EGL10 egl = (EGL10) EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
egl.eglInitialize(dpy, version);
int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
EGLConfig config = configs[0];
EGLContext context = egl.eglCreateContext(dpy, config,
EGL10.EGL_NO_CONTEXT, null);
EGLSurface drawSurface = egl.eglCreateWindowSurface(dpy, config, mSurfaceHolder, null);
EGLSurface readSurface = egl.eglCreateWindowSurface(dpy, config, mSurface, null);
egl.eglMakeCurrent(dpy, drawSurface, readSurface, context);
GL10 gl = (GL10)context.getGL();
mOpenGLDrawer = new OpenGLDrawer();
mOpenGLDrawer.setSize(gl, mWidth, mHeight);
mRunning = true;
while (mRunning) {
SystemClock.sleep(333);
synchronized (mSurfaceHolder) {
render(gl);
egl.eglSwapBuffers(dpy, drawSurface);
}
}
egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(dpy, drawSurface);
egl.eglDestroySurface(dpy, readSurface);
egl.eglDestroyContext(dpy, context);
egl.eglTerminate(dpy);
}
private void render(GL10 gl) {
gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
mOpenGLDrawer.draw(gl);
}
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
if (image != null) {
image.close();
}
}
};
}
After long time debugging, here is the final working code:
public class ImageReaderActivity extends ActionBarActivity
implements SurfaceHolder.Callback, Runnable {
private static final int MSG_SHOW_SCREENSHOT = 0;
private static final int MSG_HIDE_SCREENSHOT = 1;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Surface mSurface;
private int mWidth, mHeight;
private boolean mRunning = false;
private ImageReader mImageReader;
private OpenGLDrawer mOpenGLDrawer;
private EGLSurface mDrawSurface;
private EGLSurface mImageReaderSurface;
private EGLDisplay mEGLDisplay;
private EGLContext mEGLContext;
private FrameLayout mFrameView;
private ImageView mScreenshotView;
private volatile boolean mIsCapturing = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_imagereader);
mSurfaceView = findViewById(R.id.surface_view);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mFrameView = findViewById(R.id.frame);
mScreenshotView = findViewById(R.id.screenshot);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#SuppressLint("WrongConstant")
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mWidth = width;
mHeight = height;
mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,1);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
mSurface = mImageReader.getSurface();
new Thread(this).start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mRunning = false;
}
#Override
public void run() {
// 创建一个EGL实例
EGL10 egl = (EGL10) EGLContext.getEGL();
// 传教一个EGLDisplay实例
mEGLDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// 初始化EGLDisplay实例
int[] version = new int[2];
egl.eglInitialize(mEGLDisplay, version);
int[] configSpec = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
EGL_RECORDABLE_ANDROID, 1,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] num_config = new int[1];
// 选择config创建OpenGL运行环境
egl.eglChooseConfig(mEGLDisplay, configSpec, configs, 1, num_config);
EGLConfig config = configs[0];
int ctxAttr[] = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 1,// 0x3098
EGL14.EGL_NONE
};
mEGLContext = egl.eglCreateContext(mEGLDisplay, config,
EGL10.EGL_NO_CONTEXT, ctxAttr);
// 创建新的surface
mDrawSurface = egl.eglCreateWindowSurface(mEGLDisplay, config, mSurfaceHolder, null);
mImageReaderSurface = egl.eglCreateWindowSurface(mEGLDisplay, config, mSurface, null);
// 将OpenGL环境设置为当前
egl.eglMakeCurrent(mEGLDisplay, mDrawSurface, mDrawSurface, mEGLContext);
// 获取当前OpenGL画布
GL10 gl = (GL10)mEGLContext.getGL();
mOpenGLDrawer = new OpenGLDrawer();
mOpenGLDrawer.setSize(gl, mWidth, mHeight);
mRunning = true;
while (mRunning) {
synchronized (mSurfaceHolder) {
render(gl);
// 显示绘制结果到屏幕上
egl.eglSwapBuffers(mEGLDisplay, mDrawSurface);
egl.eglMakeCurrent(mEGLDisplay, mImageReaderSurface, mImageReaderSurface, mEGLContext);
render(gl);
egl.eglSwapBuffers(mEGLDisplay, mImageReaderSurface);
egl.eglMakeCurrent(mEGLDisplay, mDrawSurface, mDrawSurface, mEGLContext);
}
SystemClock.sleep(333);
}
egl.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(mEGLDisplay, mDrawSurface);
egl.eglDestroyContext(mEGLDisplay, mEGLContext);
egl.eglTerminate(mEGLDisplay);
}
public void onTakeScreenshotClick(View view) {
synchronized (this) {
mIsCapturing = true;
}
}
private void render(GL10 gl) {
gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
mOpenGLDrawer.draw(gl);
}
private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Log.d("reader", "onImageAvailable(ImageReader reader)");
Image image = reader.acquireNextImage();
if (image != null) {
Log.d("reader", "acquireLatestImage()");
synchronized (this) {
if (mIsCapturing) {
mIsCapturing = false;
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
final Rect src = new Rect(0,0, bitmap.getWidth(), bitmap.getHeight());
final RectF dst = new RectF(0,0, width, height);
canvas.drawBitmap(bitmap, src, dst, null);
mScreenshotView.post(() -> mScreenshotView.setImageBitmap(bmp));
mHandler.sendEmptyMessage(MSG_SHOW_SCREENSHOT);
}
}
image.close();
}
}
};
private Handler mHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(#NonNull Message msg) {
switch (msg.what) {
case MSG_SHOW_SCREENSHOT:
mFrameView.setVisibility(View.VISIBLE);
mHandler.sendEmptyMessageDelayed(MSG_HIDE_SCREENSHOT, 3000);
break;
case MSG_HIDE_SCREENSHOT:
mFrameView.setVisibility(View.GONE);
break;
default:
break;
}
return false;
}
});
}
I wanted to make some water ripples in my android application so I used this javascript code http://jsfiddle.net/esteewhy/5Ht3b/6/ converted into C code like it is shown here Live Wallpaper Water Ripple Effect (by esteewhy).
I have a main activity, which calls the water ripples activity after clicking a button. The water ripples are working great with my mutable bitmap, the problem is when I clicked the "back" button of the android device, the system crashes throwing this error:
FATAL EXCEPTION: Thread-2556
java.lang.NullPointerException
at com.wheelly.whater.WaterView.onDraw (line 149)
at com.wheelly.whater.WaterView$GameThread.run (line 255)
I guess there is some problem handling the Thread or the activities themselves.
Here is the code of the Water Ripples:
public class WaterView extends SurfaceView implements SurfaceHolder.Callback {
GameThread thread;
public static Bitmap icon;
//Measure frames per second.
long now;
int framesCount=0;
int framesCountAvg=0;
long framesTimer=0;
Paint fpsPaint=new Paint();
//Frame speed
long timeNow;
long timePrev = 0;
long timePrevFrame = 0;
long timeDelta;
private int width = 480;
private int height = 800;
private short riprad = 6;
boolean flip;
private short[] ripplemap, last_map;
Bitmap ripple;
private static final String TAG = null;
private Rippler rippler;
public WaterView(Context context) {
super(context);
initialize();
}
void initialize() {
//CB
icon = BitmapFactory.decodeResource(getResources(), R.drawable.sand100);
icon = convertToMutable(icon);
//--
rippler = new NativeRippler();
reinitgGlobals();
fpsPaint.setTextSize(30);
//Set thread
getHolder().addCallback(this);
setFocusable(true);
}
void reinitgGlobals() {
int size = width * (height + 2) * 2;
ripplemap = new short[size];
last_map = new short[size];
Bitmap texture = createBackground(width, height); // this creates a MUTABLE bitmap
ripple = texture;
_td = new int[width * height];
texture.getPixels(_td, 0, width, 0, 0, width, height);
_rd = new int[width * height];
}
void randomizer() {
final Random rnd = new Random();
final Handler disHAndler = new Handler();
final Runnable disturbWater = new Runnable() {
#Override
public void run() {
disturb(rnd.nextInt(width), rnd.nextInt(height));
disHAndler.postDelayed(this, 7000);
}
};
disHAndler.post(disturbWater);
}
private static Bitmap createBackground(int width, int height) {
Canvas cb = new Canvas (icon);
cb.save();
cb.restore();
return icon;
}
private Bitmap convertToMutable(Bitmap bitmap) {
try {
File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
int width_mutable = bitmap.getWidth();
int height_mutable = bitmap.getHeight();
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width_mutable*height_mutable*4);
bitmap.copyPixelsToBuffer(map);
bitmap.recycle();
bitmap = Bitmap.createBitmap(width_mutable, height_mutable, Config.ARGB_8888);
map.position(0);
bitmap.copyPixelsFromBuffer(map);
channel.close();
randomAccessFile.close();
} catch (FileNotFoundException e) {
Log.i(TAG, "::onActivityResult:" + ""+Log.getStackTraceString(e));
} catch (IOException e) {
Log.i(TAG, "::onActivityResult:" + ""+Log.getStackTraceString(e));
}
return bitmap;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
reinitgGlobals();
}
#Override
protected void onDraw(android.graphics.Canvas canvas) {
super.onDraw(canvas);
newframe();
canvas.drawBitmap(ripple, 0, 0, null);
//Measure frame rate (unit: frames per second).
now=System.currentTimeMillis();
canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
framesCount++;
if(now-framesTimer>1000) {
framesTimer=now;
framesCountAvg=framesCount;
framesCount=0;
}
}
/**
* Disturb water at specified point
*/
private void disturb(int dx, int dy) {
rippler.disturb(dx, dy, width, height, riprad, ripplemap, flip);
}
int[] _td;
int[] _rd;
/**
* Generates new ripples
*/
private void newframe() {
System.arraycopy(_td, 0, _rd, 0, width * height);
flip = !flip;
rippler.transformRipples(height, width, ripplemap, last_map, _td, _rd, flip);
ripple.setPixels(_rd, 0, width, 0, 0, width, height);
}
#Override
public synchronized boolean onTouchEvent(MotionEvent event) {
disturb((int)event.getX(), (int)event.getY());
return true;
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
thread = new GameThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class GameThread extends Thread {
private SurfaceHolder surfaceHolder;
private WaterView gameView;
private boolean run = false;
public GameThread(SurfaceHolder surfaceHolder, WaterView gameView) {
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
public void setRunning(boolean run) {
this.run = run;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
//limit frame rate to max 60fps
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
if ( timeDelta < 16) {
try {
Thread.sleep(16 - timeDelta);
}
catch(InterruptedException e) {
}
}
timePrevFrame = System.currentTimeMillis();
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
//call methods to draw and process next fame
gameView.onDraw(c);
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
I found the solution:
I had to interrupt the thread in the onPause method of the activity instead of doing it in the onStop method. Now it is just working fine.
Thanks a LOT to all of you for helping me with that problem.
you should shut down your working thread in the method Activity.onStop
#Override
protected void onStop() {
thread.interrupt();
super.onStop();
}
in the run method of GameThread check if thread was interrupted right before you call gameView.onDraw(c); like this:
if(isInterrupted()) {
break;
}
gameView.onDraw(c);
BTW, probably it would be better if you replace gameView.onDraw(c) with gameView.invalidate();
Don't call onDraw directly on a view, but rather call gameView.invalidate()
synchronized (surfaceHolder) {
//call methods to draw and process next fame
gameView.invalidate();
}
If it doesn't work, try using debugger with a breakpoint to see which variable is null exactly
Recently I worked on a 2D Game using SurfaceView and I learned a lot about game loops and everything. But now I want to make the same game using OpenGL. I read that the GLSurfaceView is the class that would be relative to what SurfaceView was. But I'm not sure about other things as:
Using SurfaceView, I used the Bitmap class to load a image resource that would be lets say a character. And that bitmap would be a property of my Character class. And for the gameloop I used a different Thread
I'm new to OpenGL so I'd like to know how to load image resources (do I load them using the Bitmap class?), or what to use instead of a Thread for the game loop?
do I load them using the Bitmap class?
Yes, you can load bitmap and use it as textImage2D.
Example:
public void loadTextures(GL10 gl, Context context) {
Log.e(LOG_TAG, "ExplosionSprite :: loadTextures");
mFrame = 0;
InputStream is;
Bitmap bitmap;
is = context.getResources().openRawResource(DRAW_SOURCE);
bitmap = BitmapFactory.decodeStream(is);
try {
is.close();
is = null;
} catch (IOException e) {
}
gl.glGenTextures(TEXTURE_COUNT, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
Before 4 month I did the same, because of performance switched to OpenGL ES 2D.
Its a bit complicated but after some time seems easy enough.
I don't have right now example for single image but have pretty good example for sprite sheet where I animate my Image. I'm sure you can remove not relevant data to make it for one static image. Google has enough resources "how to" so I'll point what I used for my purposes:
link 1
link 2
link 3
Cast_001_Sprite_.java
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import net.obviam.droidz.R;
import net.obviam.droidz.model.components.ESpriteDirection;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;
public class Cast_001_Sprite_ {
private static final String LOG_TAG = Cast_001_Sprite.class.getSimpleName();
/** Sprite sheet definition */
private static final int SPRITE_WIDTH = 5;
private static final int SPRITE_HEIGHT = 4;
private int DRAW_SOURCE = R.drawable.a1;
private int mX = Location._1[0]; // 100;
private int mY = Location._1[1]; // 100;
private float mScreenWidth, mScreenHeight, wRatio, hRatio;
private int mFrame = 0;
private int mSwitcher = 0;
private final static int TEXTURE_COUNT = 1; // for sprite sheet we use 1 image all the time.
private int[] textures = new int[TEXTURE_COUNT]; // frame animation
protected FloatBuffer vertexBuffer;
private final static ESpriteDirection mDirection = ESpriteDirection.TOP_TO_DOWN_LEFT_TO_RIGHT;
public float x, y, initPos, finalPos, initSpeed, currentPos;
private ByteBuffer bb1;
private final static int TOTAL_IMAGE_COUNT_IN_SPRITE = SPRITE_WIDTH * SPRITE_HEIGHT;
private FloatBuffer[] floatBufferArray = new FloatBuffer[TOTAL_IMAGE_COUNT_IN_SPRITE];
private float xOffset = 1.0f/SPRITE_WIDTH;
private float yOffset = 1.0f/SPRITE_HEIGHT;
float[] vertices = new float[] {
-1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f };
private float storage[][] = new float[TOTAL_IMAGE_COUNT_IN_SPRITE][];
private int[] sprite_X_Indexes = new int[SPRITE_WIDTH];//{1,2,3,4};
private int[] sprite_Y_Indexes = new int[SPRITE_HEIGHT];//{1,2,3,4};
public Cast_001_Sprite_(float screenWidth, float screenHeight){
generateSpriteIndexes();
updateScreenData(screenWidth, screenHeight);
int index = 0;
switch (mDirection) {
case TOP_TO_DOWN_LEFT_TO_RIGHT:
for(int row = 0; row<SPRITE_HEIGHT; row++){
for(int column = 0; column<SPRITE_WIDTH; column++){
storage[index] = generateTextures(column, row);
index++;
}
}
break;
case DOWN_TO_TOP_LEFT_TO_RIGHT:
//TODO
// for(int row = spriteLength; row>0; row--){
// for(int column = 0; column<spriteHeight; column++){
// storage[index] = generateTextures( row-1, column);
// index++;
// }
// }
break;
default:
break;
}
// vertices buffer
bb1 = ByteBuffer.allocateDirect(vertices.length * 4);
bb1.order(ByteOrder.nativeOrder());
vertexBuffer = bb1.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
for(int i=0; i<TOTAL_IMAGE_COUNT_IN_SPRITE; i++){
bb1 = ByteBuffer.allocateDirect(storage[i].length * 4);
bb1.order(ByteOrder.nativeOrder());
FloatBuffer textureBuffer = bb1.asFloatBuffer();
textureBuffer.put(storage[i]);
textureBuffer.position(0);
floatBufferArray[i] = textureBuffer;
}
}
private void generateSpriteIndexes() {
for(int indexX = 0; indexX<SPRITE_WIDTH; indexX++){
sprite_X_Indexes[indexX] = indexX+1;
}
for(int indexY = 0; indexY<SPRITE_HEIGHT; indexY++){
sprite_Y_Indexes[indexY] = indexY+1;
}
}
public void updateScreenData(float screenWidth, float screenHeight){
// takes screen Height and Width
this.mScreenWidth = (screenWidth > 0) ? screenWidth : 1f;
this.mScreenHeight = screenHeight;
wRatio = 10f/mScreenWidth;
hRatio = mScreenHeight/10f;
addExplosion(mX,mY);
}
public void addExplosion(float x, float y) {
this.x = x;
this.y = y;
this.initPos = y;
}
/**
* Generates texture by location
*
* #param texture - fill current texture
* #param placeX - image place in sprite scale X
* #param placeY - image place in sprite scale Y
* #return
*/
private float[] generateTextures(int placeX, int placeY) {
float texture[] = new float[8];
/*
V1 _____ V3
| |
| |
V2|_____|V4
*/
//StringBuffer buff = new StringBuffer();
/** V1 */
texture[0] = (placeX == 0)?0.0f : xOffset*sprite_X_Indexes[placeX-1];
texture[1] = yOffset*sprite_Y_Indexes[placeY];
/** V2 */
texture[2] = (placeX == 0)?0.0f : xOffset*sprite_X_Indexes[placeX-1];
texture[3] = (placeY == 0)?0.0f : yOffset*sprite_Y_Indexes[placeY-1];
/** V3 */
texture[4] = xOffset*sprite_X_Indexes[placeX];
texture[5] = yOffset*sprite_Y_Indexes[placeY];
/** V4 */
texture[6] = xOffset*sprite_X_Indexes[placeX];
texture[7] = (placeY == 0)?0.0f : yOffset*sprite_Y_Indexes[placeY-1];
return texture;
}
private void update() {
if(mSwitcher == 1){
mFrame = ++mFrame % TOTAL_IMAGE_COUNT_IN_SPRITE;
mSwitcher = 0;
// Log.e(LOG_TAG, "DevQuestSpriteBase :: " + mFrame);
}
else{
mSwitcher++;
}
}
public void reset(){
mFrame = 0;
}
public void loadTextures(GL10 gl, Context context) {
Log.e(LOG_TAG, "ExplosionSprite :: loadTextures");
mFrame = 0;
InputStream is;
Bitmap bitmap;
is = context.getResources().openRawResource(DRAW_SOURCE);
bitmap = BitmapFactory.decodeStream(is);
try {
is.close();
is = null;
} catch (IOException e) {
}
gl.glGenTextures(TEXTURE_COUNT, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
public void draw(GL10 gl){
// if(mFrame == TOTAL_IMAGE_COUNT_IN_SPRITE - 1){
// return;
// }
gl.glPushMatrix();
try {
float transx = + (wRatio * x);
float transy = + (mScreenHeight*wRatio) - (wRatio * y) - 1/hRatio;
// Log.e(LOG_TAG, "transx: " + transx + "; transy: " + transy + "; sprite.x: "+ sprite.x + "; sprite.y: " + sprite.y);
gl.glTranslatef(transx, transy, 0.0f);
//########### draw ##############
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, floatBufferArray[mFrame]);
update();
gl.glColor4f(1f, 1f, 1f, 0.2f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//###############################
} catch (NullPointerException e) {
}
gl.glPopMatrix();
}
}
DevQuest1Activity.java
public class DevQuest1Activity extends Activity {
private DevQuestGLSurfaceView mGLView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mGLView = new DevQuestGLSurfaceView(this);
setContentView(mGLView);
}
#Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}
#Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}
}
DevQuestGLRenderer.java
public class DevQuestGLRenderer implements GLSurfaceView.Renderer {
private static final String LOG_TAG = "Fess";//DevQuestGLRenderer.class.getSimpleName();
private Context context;
private float ratio;
private int screenWidth, screenHeight;
public Cast_001_Sprite Cast_001_Sprite;
public DevQuestGLRenderer(Context context){
this.context = context;
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig confid) {
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.glClearDepthf(1.0f);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
gl.glEnable(GL10.GL_BLEND);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, /*GL10.GL_REPLACE*/ GL10.GL_MODULATE);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.e(LOG_TAG, "onSurfaceChanged");
// prevent 0 divise
if(height == 0) { height=1;}
screenWidth = width; screenHeight = height;
ratio = (float) width/height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, width, 0, height, -10f, 10f);
gl.glViewport(0, 0, screenWidth, screenHeight);
Cast_001_Sprite = new Cast_001_Sprite(width, height);
Cast_001_Sprite.loadTextures(gl, context);
}
#Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glScalef((screenWidth)/10, (screenHeight*ratio)/10, 1.0f);
Cast_001_Sprite.draw(gl);
gl.glPopMatrix();
}
}
DevQuestGLSurfaceView.java
public class DevQuestGLSurfaceView extends GLSurfaceView {
private DevQuestGLRenderer mRenderer;
private int count = 0;
public DevQuestGLSurfaceView(Context context) {
super(context);
mRenderer = new DevQuestGLRenderer(context);
setRenderer(mRenderer);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}
ESpriteDirection.java
public enum ESpriteDirection {
TOP_TO_DOWN_LEFT_TO_RIGHT,
DOWN_TO_TOP_LEFT_TO_RIGHT,
TOP_TO_DOWN_RIGHT_TO_LEFT,
DOWN_TO_TOP_RIGHT_TO_LEFT
}
And this is an image I used:
USE THIS CODE TO LOAD YOUR IMAGE..
public static int loadTexture(Context context, int resourceId) {
final int[] textureObjectIds = new int[1];
glGenTextures(1, textureObjectIds, 0);
if (textureObjectIds[0] == 0) {
if (LoggerConfig.ON) {
Log.w(TAG, "Could not generate a new OpenGL texture object.");
}
return 0;
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
// Read in the resource
final Bitmap bitmap = BitmapFactory.decodeResource(
context.getResources(), resourceId, options);
if (bitmap == null) {
if (LoggerConfig.ON) {
Log.w(TAG, "Resource ID " + resourceId + " could not be decoded.");
}
glDeleteTextures(1, textureObjectIds, 0);
return 0;
}
// Bind to the texture in OpenGL
glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);
// Set filtering: a default must be set, or the texture will be
// black.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Load the bitmap into the bound texture.
texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
// Note: Following code may cause an error to be reported in the
// ADB log as follows: E/IMGSRV(20095): :0: HardwareMipGen:
// Failed to generate texture mipmap levels (error=3)
// No OpenGL error will be encountered (glGetError() will return
// 0). If this happens, just squash the source image to be
// square. It will look the same because of texture coordinates,
// and mipmap generation will work.
glGenerateMipmap(GL_TEXTURE_2D);
// Recycle the bitmap, since its data has been loaded into
// OpenGL.
bitmap.recycle();
// Unbind from the texture.
glBindTexture(GL_TEXTURE_2D, 0);
return textureObjectIds[0];
}
I'm trying to render a simple textured quad on Android 2.2 using GLSurfaceView. I'm loading a BMP image (128x128) with BitmapFactory.decodeResource() - this seems to work. But whenever I try to put this bitmap into an OpenGL texture using GLUtils.glTexImage2D I get an OpenGL error: glGetError() returns 1280, GL_INVALID_ENUM. What am I doing wrong?
This is the code for my Renderer:
public class MyRenderer implements GLSurfaceView.Renderer {
Context context;
int texId;
public MyRenderer(Context c) {
this.context = c;
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_TEXTURE_2D);
this.texId = loadTexture(gl);
}
int loadTexture(GL10 gl) {
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
int id = tmp[0];
Bitmap bmp = BitmapFactory.decodeResource(this.context.getResources(), R.drawable.myimage);
gl.glGetError();
GLUtils.texImage2D(id, 0, bmp, 0);
int err = gl.glGetError();
if (err != 0) {
// err == 1280, prints "invalid enum":
System.err.println(GLU.gluErrorString(err));
}
return id;
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluOrtho2D(gl, 0, width, height, 0);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
#Override
public void onDrawFrame(GL10 gl) {
// ...
}
}
It should probably be something like:
gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
I'm trying to render a 640x480 RGB565 image using OpenGL ES on Android using GLSurfaceView and Native C code.
Initially I had a 0x0501 error with glTexImage2D, which I was able to resolve by changing the image dimensions.
But now, in the "drawFrame" call, when I do glDrawTexiOES to resnder the texture, I'm getting the following error on the Logs:
drawtex.c:89: DrawTexture: No textures enabled
I'm already doing glEnable(GL_TEXTURE_2D), is there anything else I should do?
Is there a complete example showing GLSurfaceView with native code using textures?
Thanks in advance!
I have had the same problem,you can either make a custom renderer like this:
or (a do it little bit complicated by subclassing SurfaceView directly)
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.util.Log;
import com.TIEmulator.JNI.NLib;
class EmulatorRenderer implements GLSurfaceView.Renderer, NLib.EventsInterface {
public static interface RenderCb {
public boolean dismissStartupDialog();
public void updateStartupDialog(String msg);
}
private static int mViewHeight;
private static int mViewWidth;
private static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
private static int TEX_BUF_HEIGHT = 128;
private static int TEX_BUF_WIDTH = 256;
private final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4
* EmulatorRenderer.TEX_BUF_HEIGHT * EmulatorRenderer.TEX_BUF_WIDTH);
private boolean emulation_running = false;
private Context mContext = null;
private final int[] mCropWorkspace;
private final GLSurfaceView mGLView;
protected int mTex = -1;
protected int[] mtexBuf = new int[1];
private final int[] mTextureNameWorkspace;
public EmulatorRenderer(final Context ctx, final GLSurfaceView v) {
// Pre-allocate and store these objects so we can use them at runtime
// without allocating memory mid-frame.
mTextureNameWorkspace = new int[1];
mCropWorkspace = new int[4];
byteBuffer.order(ByteOrder.BIG_ENDIAN);
// Set our bitmaps to 16-bit, 565 format.
EmulatorRenderer.sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
mGLView = v;
mContext = ctx;
try {
NLib.setListener(this);
} catch (final RuntimeException exc) {
exc.printStackTrace();
callFinishOnce();
return;
}
if (!NLib.TryLoadLib()) {
callFinishOnce();
return;
}
Log.v(this.getClass().toString(),">>>>>>>>>>>>>>>>>>>>>>> init successfull! <<<<<<<<<<<<<<<<<<<<<<");
}
private void callFinishOnce() {
if (mContext != null) {
((Activity) mContext).finish();
mContext = null;
}
}
#Override
protected void finalize() throws Throwable {
NLib.stopEmu();
super.finalize();
}
protected int loadBB(final GL10 gl) {
int textureName = -1;
if (mContext != null && gl != null) {
gl.glGenTextures(1, mTextureNameWorkspace, 0);
textureName = mTextureNameWorkspace[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_NEAREST);// GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
mCropWorkspace[0] = 0; // u
// mCropWorkspace[1] = EMU_HEIGHT; // v
mCropWorkspace[1] = 0; // v
mCropWorkspace[2] = NLib.getWidth(); // w
// mCropWorkspace[3] = -EMU_HEIGHT; // h -EMU_HEIGHT;
mCropWorkspace[3] = NLib.getHeight(); // h -EMU_HEIGHT;
byteBuffer.order(ByteOrder.BIG_ENDIAN);
final int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
Log.e("SpriteMethodTest", "Texture Load GLError: " + error);
}
}
return textureName;
}
public void onDrawFrame(final GL10 gl) {
// Log.v(this.toString(),"onDrawFrame called");
gl.glActiveTexture(mTex);
gl.glClientActiveTexture(mTex);
byteBuffer.position(0);
// this two lines bind and create the texture!
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTex);
((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D,
GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0);
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA,
EmulatorRenderer.TEX_BUF_WIDTH,
EmulatorRenderer.TEX_BUF_HEIGHT, 0, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, byteBuffer);
((GL11Ext) gl).glDrawTexiOES(0, 0, 0, EmulatorRenderer.mViewWidth,
EmulatorRenderer.mViewHeight);
/** gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL);
*/
}
public void OnFatalError(final String text) {
Log.d(toString(), "FATAL ERROR CALLBACK raised: " + text
+ " ===> Activity calls finish()");
}
public void onFinish() {
onPause();
}
/*
* JNI interface
*
*/
#Override
public void OnBufferUpdate() {
mGLView.requestRender();
}
// TODO Auto-generated method stub
#Override
public void OnWarning(String msg) {
// TODO Auto-generated method stub
}
public void onPause() {
mGLView.onPause();
// Log.v("onPause", "NILib.stopEmulate()");
emulation_running = false;
//startupDialogDismiss = false;
NLib.stopEmu();
}
public void onResume() {
// Log.v(this.toString(),"EmulatorRenderer:onResume called");
NLib.startEmu(byteBuffer);
mGLView.onResume();
//callFinishOnce();
emulation_running = true;
}
public void onSurfaceChanged(final GL10 gl, final int w, final int h) {
EmulatorRenderer.mViewWidth = w;
EmulatorRenderer.mViewHeight = h;
Log.v(toString(), "onSurfaceChanged: ==> New View Size: [" + w + ","
+ h + "]");
}
public void onSurfaceCreated(final GL10 gl, final EGLConfig config) {
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0.5f, 0.5f, 0.5f, 1);
gl.glShadeModel(GL10.GL_FLAT);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_TEXTURE_2D);
/*
* By default, OpenGL enables features that improve quality but reduce
* performance. One might want to tweak that especially on software
* renderer.
*/
gl.glDisable(GL10.GL_DITHER);
gl.glDisable(GL10.GL_LIGHTING);
gl.glDisable(GL10.GL_BLEND);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST); // Set Line
// Antialiasing
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// create the one and only texture here...
mTex = loadBB(gl);
}
public void OnTIEmuStopped() {
System.out.println("OnTIEmuStopped callback called! ");
callFinishOnce();
}
/* Called when the size of the window changes. */
public void sizeChanged(final GL10 gl, final int w, final int h) {
// Log.v(this.toString(),"sizeChanged: ==> new Viewport: ["+w+","+h+"]");
gl.glViewport(0, 0, w, h);
/*
* Set our projection matrix. This doesn't have to be done each time we
* draw, but usually a new projection needs to be set when the viewport
* is resized.
*/
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
// Set up the orthographic projection
// gl.glOrthof(0.0f, w, 0.0f, h, 0.0f, 1.0f);
gl.glOrthof(0.0f, w, 0.0f, 0.0f, h, 1.0f);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glMatrixMode(GL10.GL_MODELVIEW);
}
}
yes i did...
gl.glGenTextures(1, mTextureNameWorkspace, 0);
textureName = mTextureNameWorkspace[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_NEAREST);// GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
mCropWorkspace[0] = 0; // u
// mCropWorkspace[1] = EMU_HEIGHT; // v
mCropWorkspace[1] = 0; // v
mCropWorkspace[2] = NLib.getWidth(); // w
// mCropWorkspace[3] = -EMU_HEIGHT; // h -EMU_HEIGHT;
mCropWorkspace[3] = NLib.getHeight(); // h -EMU_HEIGHT;
static_test_debug_if_gl_error( gl , "loadBB time 1");
gl.glActiveTexture(mTex);
gl.glClientActiveTexture(mTex);
static_test_debug_if_gl_error( gl , "loadBB time 2");
Did you generate a texture id and bind a texture first?
glGenTexture()/glBindTexture()
The following will get the texture all set up and ready to use on GL_TEXTURE0:
When loading the texture:
// in your native onSurfaceCreated function:
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
// setup texture parameters if you want:
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // etc.
glTexImage2D(GL_TEXTURE_2D, ... );
see here for how to fill out the TexImage2D parameters.
I've unfortunately not been able to get the glDrawTex_OES functions to work properly, but the texture does work if you render it onto a quad:
// in your native onRender function:
glBindTexture(GL_TEXTURE_2D, sGlTexture.texID);
// quad drawing:
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, quadVerts[i]);
glTexCoordPointer(2, GL_FLOAT, 0, quadTexCoords[i]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);