Can' get rendering to work(android,SurfaceView,ndk,ANativeWindow) - android

I searched the whole day and can still not figure out, what I am missing. All Examples I found are either incomplete (only not connected snippets) or overcomplete(cannt see what is really part if the principle)
I have an Activity that has a View that extends SurfaceView that should be filled using a native method. It is currently implemented by a memset(..,0,..) but my View is white although all calls seem fine.
MyView:
public class MyView extends SurfaceView implements SurfaceHolder.Callback
{
public MyView(Context context)
{
super(context);
SurfaceHolder sh = getHolder();
sh.addCallback(this);
}
// protected void onDraw(Canvas canvas) // works to make the view red
// { canvas.drawColor(Color.RED);}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
nativeRender(holder.getSurface(), width, height);
}
public void surfaceCreated(SurfaceHolder holder)
{}
public void surfaceDestroyed(SurfaceHolder holder)
{}
private native void nativeRender(Object surface, int width, int height);
}
Native method:
void ...nativeRender(JNIEnv* env, jobject myView, jobject surface, jint width, jint height)
{
ANativeWindow* pWindow(ANativeWindow_fromSurface(env, surface));
ANativeWindow_setBuffersGeometry(pWindow, width,height,WINDOW_FORMAT_RGBX_8888);
ANativeWindow_Buffer buffer;
if (ANativeWindow_lock(pWindow, &buffer, NULL) == 0)
{
memset(buffer.bits, 0, buffer.stride*buffer.height*4);
ANativeWindow_unlockAndPost(pWindow);
}
ANativeWindow_release(pWindow);
}
Things I checked:
return values of all ANativeWindow_* are 0.
buffer size/format is as expected
Things I tried:
calling nativeRender() from onDraw()
calling nativeRender() repeatetly (Handler(mainLooper(), postDelayed())
keeping ANativeWindow* as static variable (over multiple calls of the above)
same without the ANativeWindow_release(pWindow);
WINDOW_FORMAT_RGBX_8888 vs WINDOW_FORMAT_RGBA_8888
fill with patterns
So it looks to me that I fill a buffer of the correct dimensions that is never shown. Probably I' missing something obvious because nobody else seems to have problems in that way.
Thanks in Advance
Moritz

Thanks!
fadden's comment was the answer. My code actually called setBackgroundColor() which is obviously not what I wanted...
(Although I would expect that in this context this property would have no meaning or would be what the buffer would be prefilled with...)

Related

How to change the rendered objects in a GLSurfaceView.Renderer

I'm having trouble to change my rendered mesh in my GLSurfaceView, and more precisely in the Renderer, I guess there is something I didn't understood in the workflow of the Renderer class.
So, let's consider we have this :
public class MyRenderer extends GLSurfaceView.Renderer{
private Mesh aMesh;
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config){
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
aMesh = new Mesh("TestMesh1");
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
...Creating viewport...
}
#Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(gl.GL_COLOR_BUFFER_BIT);
...all Maths to create mvpMatrix...
aMesh.draw(mvpMatrix);
}
}
So this is perfectly working, aMesh is display on the screen with all the good settings. Now, I want to change this mesh when the user is pressing a button, and to refresh my renderer I created a specific method in the renderer, as follow :
public class MyRenderer extends GLSurfaceView.Renderer{
private Mesh aMesh;
public void changeMesh(String newMeshName){
GLES20.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);
....
aMesh=new Mesh(newMeshName);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config){
....
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
...
}
#Override
public void onDrawFrame(GL10 gl) {
aMesh.draw(mvpMatrix);
}
}
So in my mind, calling changeMesh would make onDrawFrame method to redraw aMesh with the new model, right?
My problem is that the result is an empty renderer. It's not even crashing or so. When I call changeMesh(), the previously well displayed mesh disappear (as expected), but the new one is not draw.
So I'm wondering, does it requires to create a new Renderer(which seems a little bit heavy)? Is there a way to ask a pass in OnSurfaceCreated manually? I'm a bit lost on this bug because it's kinda unexpected.
Part of the problem was the call to new, I couldn't explain exactly the reasons, but it seems it's somehow breaking links between OpenGL Objects and my buffers. I solved the problem with a kind of "setter" which just change all datas in an instance of Mesh to suit the datas of an other Mesh, this way I could save the memory address of the first instance.
It's a bit strange but well this solved my problem, maybe it can help someone else.

android camera access from delphi

I'm building a simple application which acts similar to built-in camera application - takes a photo and saves it locally, with some additional aсtions. First I took Firemonkeys' TCameraComponent, placed it to the form and added transfered image from it to TImage. It works OK, I can change resolution, control flash power and so on. The problem is that preview framerate is very low while running at good quality. If I switch camera to 320x240 resolution, it works fast enough, but quality is poor and I have to switch to some higher resolution to take a good photo - loosing time on re-focusing and light adaptation. If I set high resolution (such as 1280x720) for preview, it slows down to about 3-5 fps.
So I need to use some another technique to access camera. App interface must still unchanged during usage, so I can't use TakePhotoFromCameraAction. I'm looking to JNI interfaces. So I need, using JNI calls, access camera and get a preview at some image object at my form. I use the such code to get preview (I'm a bit lack of understanding native android so it may look crazy):
J.Cam := TJCamera.JavaClass.open(0); // J.Cam is JCamera object
J.View := TJView.JavaClass.init( TAndroidHelper.Context ); // JView
J.ViewParams := TJViewGroup_LayoutParams.JavaClass.init(Width, Height); // i got Width and Height from J.Cam.getParameters()
TAndroidHelper.Activity.addContentView( J.View, J.ViewParams );
J.SurfaceView := TJSurfaceView.JavaClass.init( J.View.getContext );
J.LayoutParameters := TJViewGroup_LayoutParams.JavaClass.init(Width, Height);
J.SurfaceView.setLayoutParams( J.LayoutParameters );
J.Cam.setPreviewDisplay( J.SurfaceView.getHolder );
J.Cam.startPreview;
and it doesn't show anything except black screen. Google developer reference says that "If you are using SurfaceView, you will need to register a SurfaceHolder.Callback with addCallback(SurfaceHolder.Callback) and wait for surfaceCreated(SurfaceHolder) before calling setPreviewDisplay() or starting preview."
So the next step I should create the callback class, extending SurfaceView and implementing SurfaceHolder.Callback. There are many samples can be found like this:
public class TestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new MySurfaceView(this));
}
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
public MySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
#Override
public void surfaceCreated(SurfaceHolder holder) { }
#Override
public void surfaceDestroyed(SurfaceHolder holder) { }
}
}
Now a have to translate this code to Delphi. Java code is classes only, but Delphi's JNI is interfaces AND classes so I can't figure out how to represent this:
class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback
since both JNI's JSurfaceView and JSurfaceHolder_Callback are interfaces.
So, does anybody knows how do do this? (or completely another way to access camera)

Change GlSurfaceView renderer

I looked all over the net in order to find out if its possible to change the renderer of a GLSurfaceView on the flight. The reason is that I want to change the OpenGl program, and initiate all the attributes and unified params from its vertex and fragment shader and I don't want the any change would require to create a brand new GLSurfaceView with a brand new Renderer.
It seems like reasonable operation that should be doable.
Note: I haven't implemented the following.
GLSurfaceView.Renderer is an interface. Implement it three times. Twice for your different OpenGL renderers, and one time attached to the GLSurfaceView. The latter only dispatches to one of the former, and allows to change the renderer to which it dispatches. The code must hold a reference to this renderer, and eventually must be synchronized to the draw calls (though I don't know).
Be aware that you cannot easily switch OpenGLES context data. It is shared between all renderer instances.
class DispatchingRenderer implements GLSurfaceView.Renderer {
private class Renderer1 implements GLSurfaceView.Renderer {
...
}
private class Renderer2 implements GLSurfaceView.Renderer {
...
}
public DispatchingRenderer() {
this.r1 = new Renderer1();
this.r2 = new Renderer2();
this.currentRenderer = this.r1;
}
public void ToggleRenderer() {
if(this.currentRenderer == this.r1) {
this.currentRenderer = this.r2;
} else if (this.currentRenderer == this.r2) {
this.currentRenderer = this.r1;
}
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// do one-time setup
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
this.currentRenderer.onSurfaceChanged(gl, w, h);
}
public void onDrawFrame(GL10 gl) {
this.currentRenderer.onDrawFrame(gl);
}
}

Android: Declaring static bitmaps? Yay or nay?

Somebody recently commented on my code where I declare the following:
private Bitmap splashBackground;
private Bitmap lightDot;
private static Bitmap scaledBackground;
private static Bitmap scaledLightDot;
They advised me against declaring satic Bitmaps.
However, I've tried everything and my code doesn't work unless I declare them as static.
Also, "public static Bitmap createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)" seems to appear on the official Android Developer site so I'm a bit confused about what I should and shouldn't be doing.
Any pointers would be appreciated - thank you
Edit: For clarification:
When I remove the static from my declaration, then by the time I get to my onDraw() method, the scaled bitmap is null. (I am creating the scaled bitmap object in an initialise() method and once it's been created, it is valid (ie, not null) - but then seems to become null at onDraw unless I declare it as static.
I am calling my initialise() method from my activity class.
Edit: More code as requested.
My OnCreate method: As you can see, I'm passing my screen height and width over so I can create my scaled bitmaps
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
displaySplashScreen= new SplashScreen(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// set View
setContentView(new SplashScreen(this));
WindowManager w = getWindowManager();
Display d = w.getDefaultDisplay();
int width=d.getWidth();
int height=d.getHeight();
displaySplashScreen.initialise(width, height);
}
My initalise method:
public void initialise(int w, int h)
{
//Get width and height (passed in from Activity)
vwidth=w;
vheight=h;
//Create pre-scaled bitmaps
scaledBackground = Bitmap.createScaledBitmap(splashBackground, vwidth, vheight, true);
scaledLightDot = Bitmap.createScaledBitmap(lightDot, vwidth, vheight, true);
}
I could add also that if I use a standard variable in the same way (say int number;) and set it in initalise (number = 5;), then number is only equal to 5in my initialise method. If I log it from onDraw() it will always repeatedly return '0'!! It's baffling.
Thanks everyone so far, please let me know if more code is required......
In general, utilizing static for Bitmaps is a very bad idea. There are a lot of good reasons for this, mostly having to do with avoiding memory leaks.
Also, "public static Bitmap createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)" seems to appear on the official Android Developer site ...
This is not a static Bitmap. This is a method call to a class method. Static does not work the same and the return type (Bitmap) is not static. What this means is that the method is static and does not require an instance to be called. It will return a Bitmap to be placed in an appropriate variable of your choice.
I am calling my initialise() method from my activity class.
This statement is quite unhelpful. From where in the class is it being called? Is it in onCreate(), onStart(), onResume(), some other custom method? Where and when you choose to do certain things can have a huge effect as to how successful they are.
... but then seems to become null at onDraw unless I declare it as static.
This could be for several possible reasons, and since we don't have any of your code there really isn't a qualified answer. Here are some things to look at.
This could be because the Activity is getting recreated.
This could also be because some method that seems unrelated is actually getting called. This would probably be somewhere that you are manually setting it to null.
This might be due to improper use of the createScaledBitmap() method.
The Bitmap might be getting recycled due to low memory (this actually happens more often than one would think)
EDIT: After reviewing your code
This looks like it may be the culprit. Above, you have...
displaySplashScreen= new SplashScreen(this);
Below, you add...
setContentView(new SplashScreen(this));
This means that you are creating two Splashscreens. One reason why you are getting a null pointer when you are not using static may be because further down the line you use...
displaySplashScreen.initialise(width, height);
But since your contentView is set to a new SplashScreen, you are not actually utilizing that View. To resolve this, make sure you are talking to the same view object. I.E.
setContentView(displaySplashScreen);
This will at least make sure you are looking at the same object. It is possible that you may have to reorganize a bit depending upon what other things are happening. For instance,
setContentView(displaySplashScreen);
... may have to appear below ...
displaySplashScreen.initialise(width, height);
This is something that you might have to toy with, but I don't see anything else that gives any other immediate indication. Be aware that resolving nullpointerexceptions will often result in revealing more errors in code, at first. Stay the course and resolve each in order.
This line is wrong:
// set View
setContentView(new SplashScreen(this)); // This line is wrong.
Should be this:
// set View
setContentView(displaySplashScreen); // displaySplashScreen is created earlier.
You are created two instances of SplashScreen. You should keep using the same instance.
I vote for nay,
this one is a static variable
private Bitmap splashBackground;
private Bitmap lightDot;
private static Bitmap scaledBackground;
private static Bitmap scaledLightDot;
and this one is a static method
public static Bitmap createScaledBitmap (Bitmap src,
int dstWidth, int dstHeight, boolean filter)
static variable is usually declared for a constant and the variable belongs to class not object as an example if you have a class
public class car {
private static colorOfCar;
private numberOfDoor;
}
let's say you have a class car and have 2 variable colorOfCar and numberOfDoor when you create an object porsche and ferrari from car class if you change the number of door it's OK the numberOfDoor in your porsche object and ferrari object will be different, but if you change the colorOfCar both porsche object and ferrari object colorOfCar will be changed because the colorOfCar is a static object that belong to the class not the object.
I hope you understand my explanation. If you find my answer helping you please vote and accept my answer and if you have any other question feel free to ask in the comment, thank you :)
Without your code, it seems you are using your view is being created more than once. Maybe in two instances of the view, or maybe the same view being recreated (Activity restarting). In the first time around, initialize() is being called before onDraw(), making your static Drawable initialized and valid. The second time around, onDraw() is running before initialize() (which works when Drawable is static). This is most likely due to you inflating the view and calling initialize() afterwards (this means the view is already in the layout). i.e.
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.mylayout); //the view is added to layout, onDraw() may be called
MyView view = (MyView)findViewById(R.id.myview);
view.initialize(); //initializing the drawable from null
//no guarentee that initialize was called before onDraw()
}
This runs ok when your Drawable is static because when the second view is drawn, it is using the same Drawable which was initialized by the first view. When you remove the static, you need to make sure initialize is always called before onDraw().
Instead of calling initialize() from your activity, why not invoke it from the View's constructor? I often use the following pattern:
public class MyView extends View {
private Bitmap splashBackground;
private Bitmap lightDot;
private Bitmap scaledBackground;
private Bitmap scaledLightDot;
public MyView(Context context) {
super(context);
init();
}
public MyView(AttributeSet attr, Context context) {
super(attr, context);
//parse attr for xml attributes
init();
}
private void init() {
splashBackground = getResources().getDrawable(R.drawable.splash_background);
lightDot = getResources().getDrawable(R.drawable.light_dot);
scaledLightDot = Bitmap.createScaledBitmap(lightDot, getDPI(32), getDPI(32), false);
}
public void onSizeChanged(int width, int height) {
scaledBackground = Bitmap.createScaledBitmap (splashBackground, width, height, false);
}
/**
* Convert pixel value to device independent pixels (DPI)
* #params pixels Value for pixel size for MDPI screens
*/
private int getDPI(int pixels) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, pixels, getResources().getDisplayMetrics());
}
public void onDraw(Canvas canvas) {
//non-static drawables are guaranteed to be not-null
canvas.draw(scaledBackground, 0, 0, null);
canvas.draw(scaledLightDot, 10, 10, null);
}
}
You'll be all set

User to decide which class to load Android

EDIT: I'll try and make it clearer sorry for the confusion.
I have two livewallpapers one is called the Past one is called the Future, what I want to do is put both into one livewallpaper (sort of a two for one deal) but let the user decide which one they want to load.
How I had it set up for one (let's say the Past) I had the onDraw method running in a class called the Past (it did not impliment anything) just past the onDraw and put the whole livewallpaper togeather.
In the livewallpaper engine I had this.
public class ThePastActivity extends WallpaperService {
#Override
public Engine onCreateEngine() {
return new ThePastActivityEngine();
}
class ThePastActivityEngine extends Engine {
private Past _past;
public ThePastActivityEngine() {
this._past = new Past();
this._past.initialize(getBaseContext(), getSurfaceHolder());
}
#Override
public void onVisibilityChanged(boolean visible) {
if(visible){
this._past.render();
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
this._past.start();
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this._past.stop();
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,float xStep, float yStep, int xPixels, int yPixels) {
this._past.drawXOff = Math.round((this._blimp.theBackgroundImage.getWidth() - initFrameParamsWidth()) * -xOffset);
this._past.drawYOff = Math.round((this._blimp.theBackgroundImage.getHeight() - initFrameParams()) * -yOffset);
this._past.render();
}
}
Now I have two instead of one. The new one is called Future so now I have it like so:
public class ThePastActivity extends WallpaperService {
public static final String SHARED_PREFS_NAME = "livewallpapersettings";
public static final String PREFERENCE_BACK = "livewallpaper_back";
#Override
public Engine onCreateEngine() {
return new ThePastActivityEngine();
}
class ThePastActivityEngine extends Engine implements SharedPreferences.OnSharedPreferenceChangeListener{
private SharedPreferences prefs;
private String whichEra;
private Past _past;
private Future _future;
//make a new name ChooseEra and let it become either Past or Future
private ChooseEra _chooseEra;
public ThePastActivityEngine() {
this._chooseEra = new ChooseEra();
this._past.initialize(getBaseContext(), getSurfaceHolder());
prefs = TheSteampunkCityActivity.this.getSharedPreferences(SHARED_PREFS_NAME, 0);
prefs.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(prefs, null);
}
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
whichEra=(prefs.getString(PREFERENCE_BACK, "past"));
// from here I want to get either the "past" or "future"
// make the ChooseEra to be either Past or Future
// and use that for this livewallpaper engine instead
}
#Override
public void onVisibilityChanged(boolean visible) {
if(visible){
this._chooseEra.render();
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
this._past.start();
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this._chooseEra.stop();
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,float xStep, float yStep, int xPixels, int yPixels) {
this._chooseEra.drawXOff = Math.round((this._chooseEra.theBackgroundImage.getWidth() - initFrameParamsWidth()) * -xOffset);
this._chooseEra.drawYOff = Math.round((this._chooseEra.theBackgroundImage.getHeight() - initFrameParams()) * -yOffset);
this._chooseEra.render();
}
}
So chooseEra has to become either Future or Past so it reads one of the two classes only and passes the arguments along through the engine.
The issue I am having is making ChooseEra to become either Past or Future. Normally using a method is easy to do but this is the first time I am trying to make it change the class name so when I put
private ChooseEra _chooseEra;
it makes no sense at all, I tried ChooseEra = Past and ChooseEra = Future in an if else statement comparing the prefs of "past" and "future" but no luck.
Again any help is greatly appreciated.
Sam
Your having typing issues. If you want to return the implementation to use (either Future or Past) then they need a shared interface or base class that you can use. I'll pick the name to be Era. Define all of your variables of that shared type and it will work. For example:
public Era readTheEra(SharedPreferences prefs) {
String whichEra = prefs.getString(PREFERENCE_BACK, "past");
Era era = whichEra.equals("past") ? new Past() : new Future();
return era;
}
Notice Era is marked as the return type of the method (you had void which won't work). Also notice I simply passed the SharedPreferences to the method, and encapsulated the code to extract the preference value in the method. That way you aren't writing to instance variables (that you don't need), then reading in other methods. Just pass the information to the method, and don't save the intermediate steps. The only thing you need is the Era reference to use. The value of the preference isn't needed after you instantiate the correct class.
You'll need to mark the two concrete implementations as implementing the Era Interface:
public interface Era {
// put methods here you need both implementations to
// have so you can work from Era interface and not the
// individual concrete clases.
}
public class Past implements Era {
...
}
public class Future implements Era {
...
}
public class Engine {
private Era era;
...
private Era readTheEra(SharedPreferences prefs) {
String whichEra = prefs.getString(PREFERENCE_BACK, "past");
Era era = whichEra.equals("past") ? new Past() : new Future();
return era;
}
}
I picked to use Interfaces because your question isn't clear enough to know if you need classes or can use simply Interfaces. But all of the same thing applies if you need to use some class like Activity or whatever. Subclass Activty with an abstract base class, and Past and Future should subclass the abstract base class.

Categories

Resources