Loading nine-patch image as a Libgdx Scene2d Button background looks awful
Asked Answered
G

1

5

I'm trying to use a Nine Patch as a background for a Libgdx Scene2d UI button. It is loading, buts it is really ugly. I can see the "meta-data" pixels, and its being stretched as if it were just a regular image (the text on the button is "Continue"):

ugly image of a nine-patch button

I'm loading the .9.png files directly into a (libgdx) NinePatchDrawable via a (libgdx) NinePatch like this:

this.dialogButtonUp = new NinePatchDrawable(
   new NinePatch(new Texture(Gdx.files.internal("data/button-round.9.png"))));
this.dialogButtonDown  = new NinePatchDrawable(
   new NinePatch(new Texture(Gdx.files.internal("data/button-round-down.9.png"))));

Then I make a TextButtonStyle that describes the button, and references the two NinePatch drawables:

TextButton.TextButtonStyle buttonStyle = new TextButton.TextButtonStyle();
buttonStyle.font =  aValidFontReally;
buttonStyle.fontColor = Color.BLACK;
buttonStyle.up = this.dialogButtonUp;
buttonStyle.down = this.dialogButtonDown;
buttonStyle.pressedOffsetX = -2;

I'm building the button indirectly, via a Dialog box:

new Dialog( ... ).button("Continue", null, buttonStyle);

I've checked the .9.png files to make sure that:

  • that the asset files were refreshed in Eclipse
  • that the meta-data border pixels are either fully-invisible or fully-visible-black
  • that the Android draw9patch tool can load the images and verify them

Any other suggestions on what to check or change?

Grus answered 12/3, 2013 at 7:40 Comment(4)
Try drawing each NinePatch with a SpriteBatch using NinePatch.draw() as that will at least isolate where the problem is, ie, if it works then the problem isn't the NinePatch.Servo
Good point! I just tried it and also see the same ugliness with a direct NinePatch.draw(...). So the Dialog and Button are not botching it.Grus
Looking at the NinePatch source (github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/…) I don't think it does much if you use the constructor that takes a single Texture parameter. Try this constructor instead: public NinePatch (Texture texture, int left, int right, int top, int bottom)Servo
Yeah, it looks like there are "pre-processed" (i.e., ".9.png") and "post-processed" ninepatch textures, and NinePatch is for the post-processed ones. Creating a TextureAtlas looks like it will do the processing. I don't see any run-time support for ".9.png", but maybe I'm not looking in the right place yet ...Grus
G
11

Thanks to some pointers from @RodHyde, it looks like the libgdx NinePatch class is designed to accept a "post-processed" nine patch texture (i.e., with separate integer values that describe how to cut the single texture into patches). This "processing" usually happens as a side-effect of packing a ".9.png" file into a TextureAtlas (see https://github.com/libgdx/libgdx/wiki/Texture-packer#ninePatches). A texture atlas is a really good idea (especially when your UI includes a bunch of different texture elements), so this makes sense, but is a bit surprising when developing and trying to get something running.

To work-around this so I can directly include ".9.png" files I wrote this:

private static NinePatch processNinePatchFile(String fname) {
    final Texture t = new Texture(Gdx.files.internal(fname));
    final int width = t.getWidth() - 2;
    final int height = t.getHeight() - 2;
    return new NinePatch(new TextureRegion(t, 1, 1, width, height), 3, 3, 3, 3);
}

This loads the texture, creates a sub-region that trims off the 1-pixel meta-data border, and then just guesses that the nine-patch border elements are 3 pixels wide/tall. (Computing that correctly by mucking about in the texture data seems possible, but not worth the effort -- just put the texture in an atlas in that case.)

Grus answered 12/3, 2013 at 21:14 Comment(1)
NinePatch javadoc got improved to make this clearer: github.com/libgdx/libgdx/commit/…Grus

© 2022 - 2024 — McMap. All rights reserved.