ANDROID: OpenGL White Screen Weirdness

For some reason the G1 was displaying a white screen in GLSurfaceView when the NexusOne was displaying images just fine.

Note that my GLSurfaceView is initialized to be ARGB 8888 with DEPTH BUFFER 16
Also, it is set with pixelformat.Translucent

Turns out, this is caused by texture loading, not the EGL. textures in GLES 1.0 needs to be in powers of 2! NexusOne solves that issue by using GLES 2.0, without letting the users know about it.

So, convert your bitmaps to powers of 2, before using the GLUtils method to create your textures!!!

ANDROID: Overriding OnMeasure

When generating a custom layout view, you will often need to override the onMeasure method. sometimes, you just want the width or height to 'fill_parent'... in this case we cant simply do:

setMeasuredDimension(fill_parent, fill_parent);

NOR can we pass in the width & height values received by onMeasure like so:

setMeasuredDimension(width, height);

INSTEAD, here's what you need to do:

super.onMeasure(w, h);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());

This will get you the parent's width and height, assuming you've set its layout-params to be (w=fill_parent, h=fill_parent)

ANDROID: Alpha Masking

Lets say we had 2 bg images, and wanted to use an alpha mask (black = invisible pixels, transparent = visible pixels) to mask those 2 bg images composite together...


(1) create default paint
(2) set filter bitmap to false
(3) draw bg #1
(4) draw bg #2
(5) set paint's setXfermode to dstOut
(6) draw mask
(7) set paint's setXfermode to null

however, if you are putting this View object on top of another view, the mask will ALSO mask out the pixels in the PARENT VIEW... this is obviously not wanted behavior. to resolve this, you could simply render everything to a mutable-bitmap, and in onDraw, just draw that bitmap instead. See below:


inside public void onDraw(Canvas onDrawCanvas);

mutable = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(mutable);
paint = new Paint();
paint.setFilterBitmap(false);

canvas.drawBitmap(bg1, 0, 0, paint);
canvas.drawBitmap(bg2, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
canvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);

onDrawCanvas.drawBitmap(bitmap, 0, 0, paint);

ANDROID: Image Scaling

I was encountering a very bizarre error where images were all being displayed larger than actual pixelxpixel size.

Finally I figured out it was because anything placed in /drawable directory gets scaled automatically by the density value!

So the fix are as follows:

- Use drawable-ldpi, -mdpi, -hdpi directories for drawables instead!
- Use BitmapFactory.Options(); options.inScaled = false;
- Place any assets that should not be scaled in drawable-nodpi

ANDROID: Starting another Activity

From an activity...

Intent i = new Intent();
i.setClass(this, NewClass.class);
startActivity(i);

Where this is the main context...

ANDROID: Camera

When using the camera, you might see a weird error where in portrait, the camera preview shows 2 screens instead of one. This is because the camera orientation is LANDSCAPE by default. To fix this issue, you can do one of 2 things...

(1) Force your application to be in landscape by using the following in the manifest.xml



(2) Alter the camera parameters to account for the orientation differences:

Camera.Parameters parameters = camera.getParameters();
parameters.set("orientation", "portrait");
camera.setParameters(parameters);

NOTE: If your application is not of fixed-orientation, you will have to detect the orientation of the application and adjust the 2nd parameter string accordingly between "portrait" and "landscape"

Android: Custom XML Layout

main.xml usually takes a 'TextView'. But what if you wanted to use a 'custom' view instead?

to do this:

1. have a class that extends a View
2. define its constructor: public CONSTR(Context, AttributeSet, int)
3. in xml type in manually

< com.domain.package.CONSTR >

etc

< / com.domain.package.CONSTR >

DID

Again nearly forgot to report the progress last night. Spent the majority of time creating the project estimate for the Bear Bear project she wants to hire me to do.

The original estimate was actually approximately 200 hours, but at $45/hr, that amounted to over $9000. Knowing she wouldnt have it, I had to skim down some hours and take out any paddings.

All in all, part the $6500 from this project would go towards the iPad I would be developing with.

Secondly, I began redo-ing the danny-yu.com website. By using separate text files for containing the data, it's way easier to manage when new items need to be added.

Regex Look-Ahead/Back

This is a tricky topic, but here's the best explanation I found:

* (?=) - Positive look ahead assertion foo(?=bar) matches foo when followed by bar

* (?!) - Negative look ahead assertion foo(?!bar) matches foo when not followed by bar

* (?<=) - Positive look behind assertion (?<=foo)bar matches bar when preceded by foo * (? - Negative look behind assertion (? * (?>) - Once-only subpatterns (?>\d+)bar Performance enhancing when bar not present

* (?(x)) - Conditional subpatterns

* (?(3)foo|fu)bar - Matches foo if 3rd subpattern has matched, fu if not

* (?#) - Comment (?# Pattern does x y or z)


Example: I was looking for ABCabc123 in "apple/banana/ABCabc123"

regular expression: (?<=apple/banana/)\\w+(?=\")

AJAX: Internal Server Error 500

First lesson learned today: Always check the error logs whenever you get a weird server error with obscure message describing what actually transpired...

While attempting to use AJAX & PHP, I kept running into a weird Internal Server Error. After spending lots of time on google, it turns out that the problem was the PERMISSION (chmod) given to the PHP file being requested.

I had the php file set at PERM 0777, which apparently causes the server to freak out, because PHP files being writable can be a very, very dangerous thing.

Lesson #2: Set the permission for PHP on server to 0644.

MySQL on OS X

Seems like everytime I revisit mysql I'm confused about login & user creation, so here it is again:

log in using:

mysql -u USERNAME

create user using:

use mysql;
insert into user(host,user,password,select_priv) values
('localhost','USERNAME',password('PASSWORD'),'Y');

grant privileges to user:

grant all privileges on *.* to 'USERNAME'@'localhost' with grant option;

make sure to flush privileges to enable new accounts:

flush privileges;

to log in:

mysql --user=USERNAME --password=PASSWORD (optional: db name)

Maximizing windows in OS X

So my mouse was acting crazy, and basically it caused me to shrink a window down to like 2x10 pixels... impossible to drag back to maximum size. After some research, I found an AppleScript that can be used to maximize windows:
tell application "System Events"
if UI elements enabled then
set FrontApplication to (get name of every process whose frontmost is true) as string
tell process FrontApplication
click button 2 of window 1
--button 2 is the green "zoom" button for all applications
--window 1 is always the frontmost window.
end tell
else
tell application "System Preferences"
activate
set current pane to pane "com.apple.preference.universalaccess"
display dialog "UI element scripting is not enabled. Check 'Enable access for assistive devices'"
end tell
end if
end tell

I then downloaded QuickSilver (by blacktree), and bound the script to the keys COMMAND+Z. Now I can just select any window, hit COMMAND+Z, and the window will maximize again. WHEW! What a life-saver.

To add the script to QS, you do the following:
1. Goto the Triggers tab
2. Hit the small + icon at the bottom left
3. Select (add) hotkey
4. Click on the Command-Key column, and set your Hot Key
5. Double-click on the Command column
6. For Select an Item, select the apple-script you just created
7. The Action should automatically be 'Run'
8. Save and exit

Test out your script by pressing the hotkey... it worked for me :)

OpenGL: Copy PixelBuffers to GL Texture

NOTE: some source is omitted, but should be relatively easy to re-enter.

1. Create the pixel buffer

EGLint configAttributes[] = {
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
//EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 0,
EGL_LUMINANCE_SIZE, EGL_DONT_CARE,
EGL_SURFACE_TYPE, SURFACE_TYPE,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE,
EGL_NONE
};

EGLint pbufferAttributes[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_COLORSPACE, COLOR_SPACE,
EGL_ALPHA_FORMAT, ALPHA_FORMAT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE
};

eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(eglDisplay, &major, &minor);
eglChooseConfig(eglDisplay, configAttributes,
&eglConfig, 1, &numConfigs);
eglContext = eglCreateContext(eglDisplay,
eglConfig, NULL, NULL);
eglSurface = eglGetCurrentSurface(EGL_DRAW);
eglPbuffer = eglCreatePbufferSurface(eglDisplay,
eglConfig, pbufferAttributes);

2. Create empty pbuffer texture (theSource) to render GL onto

glGenTextures(1, &theSource);
glBindTexture(GL_TEXTURE_2D, theSource);
glTexImage2D(GL_TEXTURE_2D, GL_RGB, width, height,
0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

3. Drawing to the pixel buffer surface

eglMakeCurrent(eglDisplay, pbuffer, pbuffer, eglContext);
glBindTexture(GL_TEXTURE_2D, theSource);
// draw code, glDraw*, etc
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

4. Creating the target texture (theCopy)

glGenTextures(1, &theCopy);
glBindTexture(GL_TEXTURE_2D, theCopy);
glTexImage2D(GL_TEXTURE_2D, GL_RGB, width, height,
0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

5. Copying the pbuffer to the tagret texture (theCopy)

eglBindTexImage(eglDisplay, pbuffer, EGL_BACK_BUFFER);
glActiveTexture(GL_TEXTURE0 + theCopy);
glBindTexture(GL_TEXTURE_2D, theCopy);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);

UTF8 in Java

Java seems to already assume UTF8 encoding.

String test = "ÀÁÂÃÄ";
System.out.println(test); // prints out ÀÁÂÃÄ
System.out.println(test.length()); // prints out 5

Here's how to convert the above UTF8 string into series of bytes:

byte[] bytes = test.getBytes("UTF8");

And here's how to convert those bytes back into a Java string:

String utf8 = new String(bytes, "UTF8");

Both of these must be wrapped by try-catches in case your system doesn't recognize UTF8 encoding, which would be pretty rare...

UTF8 Encoding

in utf8, words can be 1-4 bytes long.

each word uses the first couple bits of each byte to signify that it's a command. there's probably some clever mathematics involved, but I'm not going to spend the time to go figure it out.

see http://en.wikipedia.org/wiki/UTF-8

U+0000 - U+007F stands for the simplest case, ascii values 0-127. all these values are a single byte, and the way you can tell is its first bit will be 0.

VALUE: 0xxxxxxx

U+0080 - U+07ff are the 2-byte characters. the first byte will begin with 110, and the second byte will begin with 10.

VALUE: 110yyyxx 10xxxxxx

U+0800 - 0xFFFF are the 3-byte characters. the first byte will begin with 1110, the second with 10, and the third again with 10.

VALUE: 1110yyyy 10yyyyxx 10xxxxxx

U+10000 - U+10FFFF are the 4-byte characters. the first byte will begin with 11110, and each byte thereafter begins with the 10 bits.

VALUE: 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx

Where x: lowest 8-bits
Where y: middle 8-bits
Where z: highest 5-bits

The maximum value that can be represented is x^(5+8+8). However, for some reason, the UTF8 standard only uses 0x00 - 0x10 for the highest bits. meaning the maximum value for any UTF8 (right now) is 0x10FFFF.

See the next post for how to handle these values in Java.

ANDROID: Install apk on emulator

You can view a list of emulators via:
android list avd

Start the emulator with:
emulator -avd [emu name]

Place the apk into the android/tools directory, and install it with:
adb install [apk name].apk

Resubmit the previous command if you get this message:
* daemon not running. starting it now *
* daemon started successfully *
error: device offline

BASH: Running bash script from dos

Lets say you have something that ONLY runs in cygwin... ie. the Android NDK makefile

usually you'd call this with cygwin like so:
make APP=[app name]

instead, you could create a bash script (we'll call it _build) file that contains the following:
#!/bin/bash
make APP=$1 $2;

then call this bash script with the following:
bash _build [app name] [-B]

oh, to make sure this works, you'll have to set windows env. 'path' to include c:\cygwin\bin directory.

Android: adb shell

To manually kill a process that is running:

adb shell


In the shell, type:

ps


This will produce something like the following:

app_23 --- 19333 --- 19283 --- 840 --- 312 --- ffffffff --- com.sample.app


Kill the process with the PID:

kill 19333

Android: Dirty build JNI

*EDIT
actually the problem was the JNI shared libraries in eclipse were out of sync with the actual. To solve this issue, simply do a refresh on the entire tree.
*EDIT

I ran into a weird dirty-build bug. Where even after I rebuilt the JNI shared library, it wouldn't load my new library onto the phone.

To solve:

- delete the application from the handset (settings > applications > manage...)
- delete the bin directory
- while you're at it delete the gen directory
- close the project
- delete the project
- import the project as new

at this point you will probably see a very annoying error that states:

"gen directory is missing" fix missing paths before continuing...

- build the application
- if android glitches and doesn't remove the error messages, make sure gen/bin were created by the previous step
- close the project
- delete the project
- import the project as new (AGAIN)

this time the error message should be gone.

Regex: Eclipse Search & Replace

to convert #defines into java final vars...

#define DEFINE_VARIABLE 0x12AB

Search and replace:
#define -> public static final int
(0x[\dA-F]{4}) -> = \1;

Result:
public static final int DEFINE_VARIABLE = 0x12AB;