Nicer GUI elements with NinePatches
A 9-patch is a special type of bitmap that is very useful for GUI elements. When it is blitted larger than the original size, only the inner part gets scaled in two dimensions, while borders are scaled in one dimension (horizontal or vertical) and the corners are not scaled at all. Therefore, it will look good in all resolutions without getting blurry on the corners, even when using a very small source image (which saves memory).
There is no built-in support in JPCT-AE for it, but you can use the implementation below. It's not a 1:1 adaptation of Android's 9Patch though (only scaleable area is supported, not fill area).
First, you will need a source bitmap like this.
Important are the black pixels on the upper and left edge. They need to be on the first horizontal and vertical stripe of the image and show where the scale zone begins and ends. The black borders themselves do not get blitted.
I made the button translucent inside, but that is not mandatory.
Here is the code:
class NinePatch { private int[]X = new int[2]; private int[]Y = new int[2]; //These hold the scale zone coordinates private int W, H; // Input bitmap dimensions private Texture tex; private String name; public Space9Patch(Bitmap b, String name) { // Make sure b is not scaled! If you use a white picture you can blit it in any color later on this.name = name; W = b.getWidth(); H = b.getHeight(); X[0] = X[1] = Y[0] = Y[1] = -1; //Start and end positions of inner scale zone for (int x=0; x<W; x++) { // Analyze first horizontal stripe of bitmap if (b.getPixel(x, 0) == Color.BLACK && X[0] == -1) { // Look for black pixel X[0] = x; //Save position of first black pixel } else if (b.getPixel(x, 0) == Color.TRANSPARENT && X[0]>=0) { //Look for first transparent pixel after black stripe X[1] = x-1; //Save position of last black pixel break; } } for (int y=0; y<H; y++) { // Analyze first vertical stripe if (b.getPixel(0, y) == Color.BLACK && Y[0] == -1) { //same as above but in vertical direction Y[0] = y; } else if (b.getPixel(0, y) == Color.TRANSPARENT && Y[0]>=0) { Y[1] = y-1; break; } } //Create texture from bitmap TextureManager tm = TextureManager.getInstance(); if (!tm.containsTexture(name)) tm.addTexture(name, new Texture(b, true)); tex = tm.getTexture(name); } //Draw 9-patch in abitrary size and additional color public void blit(FrameBuffer fb, int x, int y, int w, int h, int transp, RGBColor col) { fb.blit(tex, 1, 1, x, y, X[0]-1, Y[0]-1, X[0], Y[0], transp, false, col); fb.blit(tex, X[0], 1, x+X[0], y, X[1] - X[0]+1, Y[0]-1, w- X[0]- (W-X[1])+1, Y[0], transp, false, col); fb.blit(tex, X[1]+1, 1, x+w-(W-X[1])+1, y, W-X[1]-1, Y[0]-1, W-X[1]-1, Y[0], transp, false, col); fb.blit(tex, 1, Y[0], x, y+Y[0], X[0]-1, Y[1] - Y[0]+1, X[0], h - Y[0] - (H-Y[1]) + 1, transp, false, col); fb.blit(tex, X[0], Y[0], x+X[0], y+Y[0], X[1]-X[0]+1, Y[1]-Y[0]+1, w-X[0] - (W-X[1])+1, h - Y[0] - (H-Y[1])+1, transp, false, col); fb.blit(tex, X[1]+1, Y[0], x+w-(W-X[1])+1, y+Y[0], W-X[1]-1, Y[1] - Y[0]+1, W-X[1]-1, h - Y[0] - (H-Y[1])+1, transp, false, col); fb.blit(tex, 1, Y[1]+1, x, y+h-(H-Y[1])+1, X[0], H-Y[1]-1, X[0], H-Y[1]-1, transp, false, col); fb.blit(tex, X[0], Y[1]+1, x+X[0], y+h-(H-Y[1])+1, X[1] - X[0]+1, H-Y[1]-2, w - X[0] - (W-X[1])+1, H-Y[1]-1, transp, false, col); fb.blit(tex, X[1]+1, Y[1]+1, x+w-(W-X[1])+1, y+h-(H-Y[1])+1, W-X[1]-1, H-Y[1]-2, W-X[1]-1, H-Y[1]-1, transp, false, col); } //Blit using original color public void blit(FrameBuffer fb, int x, int y, int w, int h, int transp) { blit(fb, x, y, w, h, transp, RGBColor.WHITE); } }