Recent Blogposts of Programming | Page 2

Removing Unused Glyphs of a Font

Dec. 7, 2015, 11:38 a.m. Programming Simple Tools

As you might have know, CJK fonts are huge in size. If the characters to be used are known, the file size of the font file can be greatly reduced by removing the unused glyphs.

I'll probably encounter this issue when I use HaxeFlixel to display CJK characters in my next project. Then I Googled for a solution. Unfortunately, there is no such thing searchable in Google.

That's why I've written the script below with the use of FontForge:

#!/usr/bin/python2
import sys
import fontforge

if len(sys.argv) == 4:
    font = fontforge.open(sys.argv[1])

    f = open(sys.argv[2], "r")

    for i in f.read().decode("UTF-8"):
        font.selection[ord(i)] = True
    f.close()

    font.selection.invert()

    for i in font.selection.byGlyphs:
        font.removeGlyph(i)

    font.generate(sys.argv[3])
else:
    print "WARNING: Check the license of the source font\nbefore distributing the output font generated by this script.\nI'm not responsible for any legal issue caused by\ninappropriate use of this script!\n"
    print "Usage: {} [source font] [file with glyphs NOT to be removed] [output]".format(sys.argv[0])
    print "Example: {} /path/to/ukai.ttc chineseTranslation.txt ukaiStripped.ttf".format(sys.argv[0])

Mind you, ensure to check the license of the source font. It may not be legal to use this script under certain condition with certain source fonts. I'm not responsible for any legal issue cause by fonts generated with this script.

I've also posted this code to stackoverflow. Hopefully the internet would find it useful!


Java String Split Gotcha

Oct. 6, 2015, 2:12 p.m. Programming

I was fixing a networking-related bug in Koloniigo. I did something like this:

String[] fields = data.split(";"); //data contains something like "a;b;;"
foo(fields[3]);

Then the program gave IndexOutOfBoundsException

I wonder what's wrong. After a few hours of debugging, I found this thingie in the documentation of split()

Trailing empty strings are therefore not included in the resulting array.

Oh well, wtf? A few hours wasted.

You may think that I'm an idiot that I didn't notice such an obvious mistake. Actually, the real code is way more complicated and there was very many possible causes of the issue. That's why it took me so long to figure out that it's the issue of the split(). :(


Gotcha of Rendering libgdx NinePatch

Sept. 15, 2015, 1:22 p.m. Gamedev Programming libgdx

Today I ran into a gotcha of libgdx. I was trying to render a NinePatch packed with texture packer by constructing a NinePatch using this:

new NinePatch(assetManaget.get("pack.atlas", TextureAtlas.class).findRegion("path/to/ninepatch"));

Then I tried to render the object using its draw() method. Didn't work. Instead, the NinePatch image was just stretched.

Then I read the documentation about the constructor of new NinePatch(TextureRegion)

Construct a degenerate "nine" patch with only a center component.

What? This is obviously not what I was expecting.

Turned out that the proper way to do this is:

assetManaget.get("pack.atlas", TextureAtlas.class).createPatch("path/to/ninepatch");

See also: TextureAtlas.createPatch(java.lang.String)


libgdx pinch to zoom

Aug. 15, 2015, 12:40 p.m. Programming libgdx

I was looking for the code for pinch to zoom on libgdx that zooms with the origin at the center of two fingers. Then I found this. Unfortunately, it seems that the solution there is far from being elegant. Then I figure out a new solution using the camera matrix myself. Here is the code:

this.gestureDetector = new GestureDetector(new GestureAdapter(){
        private Vector2 oldInitialFirstPointer=null, oldInitialSecondPointer=null;
        private float oldScale;
        @Override
        public boolean pan(float x, float y, float deltaX, float deltaY) {
            game.getCamera().update();
            game.getCamera().position.add(
                game.getCamera().unproject(new Vector3(0, 0, 0))
                .add(game.getCamera().unproject(new Vector3(deltaX, deltaY, 0)).scl(-1f))
            );
            return true;
        }
        @Override
        public boolean pinch (Vector2 initialFirstPointer, Vector2 initialSecondPointer, Vector2 firstPointer, Vector2 secondPointer){
            if(!(initialFirstPointer.equals(oldInitialFirstPointer)&&initialSecondPointer.equals(oldInitialSecondPointer))){
                oldInitialFirstPointer = initialFirstPointer.cpy();
                oldInitialSecondPointer = initialSecondPointer.cpy();
                oldScale = game.getCamera().zoom;
            }
            Vector3 center = new Vector3(
                    (firstPointer.x+initialSecondPointer.x)/2,
                    (firstPointer.y+initialSecondPointer.y)/2,
                    0
            );
            zoomCamera(center, oldScale*initialFirstPointer.dst(initialSecondPointer)/firstPointer.dst(secondPointer));
            return true;
        }
        private void zoomCamera(Vector3 origin, float scale){
            game.getCamera().update();
            Vector3 oldUnprojection = game.getCamera().unproject(origin.cpy()).cpy();
            game.getCamera().zoom = scale; //Larger value of zoom = small images, border view
            game.getCamera().zoom = Math.min(2.0f, Math.max(game.getCamera().zoom, 0.5f));
            game.getCamera().update();
            Vector3 newUnprojection = game.getCamera().unproject(origin.cpy()).cpy();
            game.getCamera().position.add(oldUnprojection.cpy().add(newUnprojection.cpy().scl(-1f)));
        }
});

The method Camera.unproject() is used to convert the coordinate of the points you touch(let's say it's coordinate A) to the coordinate that you use for displaying objects(let's say it's coordinate B). For example, if you draw a object on a SpriteBatch, and the batch has set to use a camera, then the coordinate used there is probably in coordinate B. However, another coordinate system, coordinate A is used for touch input. That's why we need to do an unprojection for the conversion between two coordinate systems. Remember to update the camera before using the unproject method. Otherwise, it may do that unprojection based on an outdated transformation matrix of the camera, which make the calculation inaccurate