Android: OpenGL textures showing up on emulator and some devices, but not others

Rule #1, RTFM. I'm afraid I break this rule quite often and when it comes to learning something as arcane as OpenGL, you're gonna have to even if the docs suck.

I ran into this problem after testing on the emulator and my Sony Xperia x10 mini fine, but surprisingly my Samsung Galaxy S failed to render the textures.

I soon discovered that for OpenGL 1.x, the dimensions of texture bitmaps have to be in the power of 2. That means the width or height can be any combination of 1, 2, 4, 8, 16, 32, 64, 128, 256, etc.

2x512, 16x64, 8x256, whatever. If any of those were a non-power of two (NPOT), you run the risk of it not loading on some devices.

Obvious fixes

Two ways of dealing with it. First method is obvious, resize your bitmap so it's in the right size in the power of 2's. If you're still running into errors, see the section below.

Second way to use OpenGL 2.x, which allows for NPOT texture files to be loaded. This is a choice between dumping support for older devices or using a newer API.

I believe you'll need at least SDK API level 8 and ensure all devices have OpenGL 2.x and need to set something in the Android manifest.

Not so obvious fixes

Alright, so you've tried the first two methods. But there are many other possible issues you will run into.

Other people had suggestions of putting the drawables into the "/res/raw" folder instead. That isn't an issue if you're using the InputStream method of loading bitmaps.

Others had the solution of putting it into /res/drawable-nodpi. That didn't work for me either.

Why all this hassle? The answer is simple, it's because the Android framework can't be trusted to load the bitmaps accurately. Manufacturers may tweak the source, image compression upon loading works differently across devices, etc etc.

To ensure that it loads exactly the way you've created it, ensure that this function isn't changing your bitmaps:

Bitmap bm = BitmapFactory.decodeResource(context.getResources(), resID)

For the textures to load properly, swap it for this longer snippet to prevent bitmap transformation:

InputStream is = context.getResources().openRawResource(resID);

try {
bitmap = BitmapFactory.decodeStream(is);
}
finally {
//Always clear and close
try {
is.close();
is = null;
}
catch (IOException e) {
}
}

Give it a shot, it'll probably work. Tested on Android 1.6 (Sony Xperia x10 mini) and Android 2.3.6 (Samsung Galaxy S).

1oxBB
Problem solved, FUCK YEAH!

Sources

Android: Scalable background images with clickable hotmaps

Every once in a while I decide to fire up the Android SDK.

Once I start hitting up the GUI development... I wonder what the hell am I doing it for? It's frustrating.

Anyway whilst working on my Diablo 3 Max Stats app, I stumbled upon a problem. A problem of detecting "hit-boxes" on a scalable background image.

Displaying the image itself isn't too bad, as long as it's centered in the middle of the screen. Adjusting the invisible overlay views on top in the right position, pain in the ass.

So I gave up. Fuck it, why waste my time?

Instead, I stumbled upon a post by Bill Lahti which made me think in a different way. Using old-school Duck Hunt technology to determine my coordinates.

Why not? Worth a shot and it looks quick and easy to implement.

Setting up the layout

  • First, we make a FrameLayout the root for our layout.
  • On the FrameLayout, I set:
    image
  • Then we add an ImageView (img_bg) on it. The FrameLayout automatically scales and centers the ImageView.
  • We add an ImageView rather than setting the "Background" option of the Layout, since we can choose the scaling type.
  • For the ImageView, set the width/height to fill_parent, adjust view bounds to True and scaling type to "centerInside".

image
You should now have something like this (ImageView selected)

  • Now add another ImageView (img_hitbox) into the FrameLayout, but this time choose the "hit-box" image (see below).
  • Important! Set the same settings as the background image. They have to be the same size positioned in the same place for this to work!

Your current structure should now look like this.

image

image

  • Finally but most importantly, set the Visibility of the hitbox to be "invisible". If you select "gone", it won't be added to the UI at all.

That's the WYSIWYG side of things done.

Setting up the hit-box code

Now for some code trickery. Because the img_hitbox ImageView visibility is set to invisible, it can't receive touch events.

So, we have to add the event handler to our Activity instead.

Ideally, what will happen is:

  • User touches screen.
  • Activity receives onTouchEvent() signal.
  • We translate the X/Y of that touch into a pixel colour in the hit-box.
  • Depending on the colour, we do various things.

And here's the source that'll do just that!

public class D3minmaxActivity extends Activity {
@Override
public boolean onTouchEvent(MotionEvent event) {
// We only care about the ACTION_UP event
if (event.getAction() != MotionEvent.ACTION_UP) {
return super.onTouchEvent(event);
}

// Get the colour of the clicked coordinates
// And yes, I spell it coloUr.
int x = (int) event.getX();
int y = (int) event.getY();
int touchColour = getHitboxColour(x, y);

StringBuilder sb = new StringBuilder();
sb.append("ARGB(");
sb.append(Color.alpha(touchColour));
sb.append(",");
sb.append(Color.red(touchColour));
sb.append(",");
sb.append(Color.green(touchColour));
sb.append(",");
sb.append(Color.blue(touchColour));
sb.append(")");

Log.e("Clicked", sb.toString());

// We do this because the pixel colour returned isn't an exact match due to scaling + cache quality
for (Integer col : colorMap.keySet()) {
if (closeMatch(col, touchColour)) {
Log.e("SuCESS!", colorMap.get(col).toString());
Intent data = new Intent(this, ModifiersActivity.class);
data.putExtra("slot", colorMap.get(col));
startActivity(data);
return true;
}
}

// No close matches found
Log.e("clicked", "nothing");
return false;
}

/**
* This is where the magic happens.
* @return Color The colour of the clicked position.
*/
public int getHitboxColour(int x, int y) {
ImageView iv = (ImageView) findViewById(R.id.img_hitbox);
Bitmap bmpHotspots;
int pixel;

// Fix any offsets by the positioning of screen elements such as Activity titlebar.
// This part was causing me issues when I was testing out Bill Lahti's code.
int[] location = new int[2];
iv.getLocationOnScreen(location);
x -= location[0];
y -= location[1];

// Prevent crashes, return background noise
if ((x < 0) || (y < 0)) {
return Color.WHITE;
}

// Draw the scaled bitmap into memory
iv.setDrawingCacheEnabled(true);
bmpHotspots = Bitmap.createBitmap(iv.getDrawingCache());
iv.setDrawingCacheEnabled(false);

pixel = bmpHotspots.getPixel(x, y);
bmpHotspots.recycle();
return pixel;
}

public boolean closeMatch(int color1, int color2) {
int tolerance = 25;

if ((int) Math.abs (Color.red (color1) - Color.red (color2)) > tolerance) {
return false;
}
if ((int) Math.abs (Color.green (color1) - Color.green (color2)) > tolerance) {
return false;
}
if ((int) Math.abs (Color.blue (color1) - Color.blue (color2)) > tolerance) {
return false;
}

return true;
}
}

Best part about this solution is that it's orientation friendly!

Extra consideration

The main issue I had with implementing Bill's solution is that I had the Activity toolbar showing. This caused the getPixel() function to bug out, as it was trying to fetch pixels for coordinates from outside of the bitmap.

I've fixed that issue by determining the location of the ImageView and adjusting it accordingly.

Another difference is that his code had a colour tolerance level. My version wants precise colours. Colour me foolish, I didn't realise until I was testing on the actual device that the pixel colours returned are not exact. I've added the tolerance code back in.

Also fixed a memory leak issue when not recycling bitmaps.

Sources

Django: Form.clean() is being called even though required fields are empty

So I decided to randomly check some code by submitting an empty form. To my surprise, Form.clean() was being called even though the required fields weren't valid. I was certain it was a core Django bug.

69E6e
Hello Twig, I'd like to play a game ...

It took a bit of thinking but I finally understand now. It's meant to work like that.

Wait, hear me out!

Most people would do this:

def clean(self):
data = self.cleaned_data
country = data['country']

Assuming that the required field "country" is set, but that's not true.

The framework is so lenient that it allows you to raise custom error messages even if the required fields aren't filled in, just in case you wanted it to.

That's ok, Uncle Twig has a fix for you and it's only 2 lines long.

To remedy this, make sure you do this on every custom Form.clean() function!

def clean(self):
if self.errors:
return self.cleaned_data

data = self.cleaned_data
# ...

That's it! At the start, if there's ANY errors at all, simply don't bother. No more invalid key exceptions.

Android: How to play button click sound

This stupid snippet was harder to find than expected!

Little did I know that it's part of the base functionality of a View.

All you need to do is call:

yourView.playSoundEffect(SoundEffectConstants.CLICK);

And you'll have yourself some annoying clickity click!

Best thing is you don't have to worry about silent mode either, system handles all that for you.

kprywnakn7 
Enjoy your success!

Source

 
Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog