openfl / openfl

The Open Flash Library for creative expression on the web, desktop, mobile and consoles.
http://www.openfl.org
MIT License
1.91k stars 434 forks source link

Setting a TextField's text crashes android app #650

Closed jeichelbaum closed 8 years ago

jeichelbaum commented 9 years ago

After compiling and installing this app on my Galaxy S3 it just crashes whenever I try to open the app. This started happening after I updated openfl today

class Main extends Sprite {

public function new() 
{
    super();

    var txt:TextField = new TextField();
    txt.type = TextFieldType.INPUT; // wont bring up keyboard on android if you touch the textfield
    txt.text = "fail";  // crashes app completely
    addChild(txt);
}

}

larsiusprime commented 9 years ago

Couple of sanity checks:

1) Is txt == null? (it shouldn't be, but if it is that's eye-opening) 2) Can you put a try/catch around the text assignment and get anything useful? 3) Does setting the text to null or the empty string give you different behavior? 4) If you don't add the text to the stage but keep the rest unchanged, do you get different behavior?

jeichelbaum commented 9 years ago

1) txt is not null 2) it still crashes even with try/catch 3) the app doesn't crash if I set txt.text = null; 4) that in fact changes something: only doing one of both actions, adding or setting the text, does not crash it but if I perform both (no matter what order), it crashes

larsiusprime commented 9 years ago

Okay, so I bet this is an issue with text rendering. Just setting the text to some non-null value should not be an issue (what could possibly go wrong???) -- but by setting the text to something, AND adding it to stage, the engine later tries to render it, and then perhaps a recent change knocked something loose.

Let me see where you can make some further changes (perhaps inserting some traces in the TextField.hx class to see if you can get some output before the crash to see where it goes bad)

Unfortunately, I don't have an Android device so someone else will have to test this for me :( But I'm happy to help diagnose the issue.

larsiusprime commented 9 years ago

Diagnostics:

Open openfl/_internal/renderer/opengl/GLTextField.hx

Add these traces (you can get trace output, right?):

public static function render (textField:TextField, renderSession:RenderSession) {

    trace("render text a");
    if (!textField.__renderable || textField.__worldAlpha <= 0) return;

    trace("render text b");
    TextFieldGraphics.render (textField);

    text("render text c");
    GraphicsRenderer.render (textField, renderSession);

    text("render text d");  
}

And see if some or all of those traces happen. That will narrow down where the error is since we don't get clean crash information telling us where things break.

  1. If some of these trigger, but not all, then we dive into that function.
  2. If none of these trigger, something bad happens before we even get here.
  3. If all of these trigger, it's probably safe and we look into TextField.hx instead and check that.
jeichelbaum commented 9 years ago

narrowed it down till renderText()

it seems to crash, when trying to perform this: fontGlyphs.set (size, font.renderGlyphs (font.getGlyphs (), size));

hope that helps?!

larsiusprime commented 9 years ago

okay, cool, if you've narrowed it down to that line, can you verify if font is null before it tries to call that?

jeichelbaum commented 9 years ago

neither font nor fontGlyphs is null

larsiusprime commented 9 years ago

okay, so our problem is going to be in either renderGlyphs or font.getGlyphs.

Quickest thing is to do something like this

var temp = font.getGlyphs() 
trace("temp == " + temp);
fontGlyphs.set(size,font.renderGlyphs(temp,size));

And see if that's null. If so we dive into getGlyphs, if not, renderGlyphs

jeichelbaum commented 9 years ago

temp is not null, but an array of integers

larsiusprime commented 9 years ago

Okay, was afraid of that, guess we get to dive into renderGlyphs and figure out what thorny thing is going wrong.

larsiusprime commented 9 years ago

Oh real quick -- what is size? (Making sure it's not 0 or -1 or something weird)

jeichelbaum commented 9 years ago

wish I could just copy paste but it wont let me do that

output

larsiusprime commented 9 years ago

Ah I meant the size of the font you were passing in to fontGlyphs.set, not the size of the array

jeichelbaum commented 9 years ago

Oh haha Im sorry. It's 12

larsiusprime commented 9 years ago

Okay so into renderglyphs() we go.

So not only is your array of glyphs not null, "Glyph" is just an abstract int, so none of the values can possibly be null either. Also your trace shows that in fact, non of them are null. And your fontSize is not weird.

So here's our next test.

    public function renderGlyphs (glyphs:Array<Glyph>, fontSize:Int):Map<Glyph, Image> {
        trace("a");
        #if (cpp || neko || nodejs)

        var uniqueGlyphs = new Map<Int, Bool> ();

        for (glyph in glyphs) {

            uniqueGlyphs.set (glyph, true);

        }

        var glyphList = [];

        for (key in uniqueGlyphs.keys ()) {

            glyphList.push (key);

        }

        trace("before lime_font_set_size("+src+","+fontSize+")");
        lime_font_set_size (src, fontSize);
        trace("after lime_font_set_size");

        var bytes = new ByteArray ();
        bytes.endian = "littleEndian";

        if (lime_font_render_glyphs (src, glyphList, bytes)) {

            trace("inside big if block");
            bytes.position = 0;

            var count = bytes.readUnsignedInt ();

            var bufferWidth = 128;
            var bufferHeight = 128;
            var offsetX = 0;
            var offsetY = 0;
            var maxRows = 0;

            var width, height;
            var i = 0;

            trace("before while loop");
            while (i < count) {

                bytes.position += 4;
                width = bytes.readUnsignedInt ();
                height = bytes.readUnsignedInt ();
                bytes.position += (4 * 2) + width * height;

                if (offsetX + width > bufferWidth) {

                    offsetY += maxRows + 1;
                    offsetX = 0;
                    maxRows = 0;

                }

                if (offsetY + height > bufferHeight) {

                    if (bufferWidth < bufferHeight) {

                        bufferWidth *= 2;

                    } else {

                        bufferHeight *= 2;

                    }

                    offsetX = 0;
                    offsetY = 0;
                    maxRows = 0;

                    // TODO: make this better

                    bytes.position = 4;
                    i = 0;
                    continue;

                }

                offsetX += width + 1;

                if (height > maxRows) {

                    maxRows = height;

                }

                i++;

            }

            trace("after while loop");
            var map = new Map<Int, Image> ();
            var buffer = new ImageBuffer (null, bufferWidth, bufferHeight, 1);
            var data = new ByteArray (bufferWidth * bufferHeight);

            bytes.position = 4;
            offsetX = 0;
            offsetY = 0;
            maxRows = 0;

            var index, x, y, image;

            trace("before for loop");
            for (i in 0...count) {

                index = bytes.readUnsignedInt ();
                width = bytes.readUnsignedInt ();
                height = bytes.readUnsignedInt ();
                x = bytes.readUnsignedInt ();
                y = bytes.readUnsignedInt ();

                if (offsetX + width > bufferWidth) {

                    offsetY += maxRows + 1;
                    offsetX = 0;
                    maxRows = 0;

                }

                for (i in 0...height) {

                    data.position = ((i + offsetY) * bufferWidth) + offsetX;
                    //bytes.readBytes (data, 0, width);

                    for (x in 0...width) {

                        var byte = bytes.readUnsignedByte ();
                        data.writeByte (byte);

                    }

                }

                image = new Image (buffer, offsetX, offsetY, width, height);
                image.x = x;
                image.y = y;

                map.set (index, image);

                offsetX += width + 1;

                if (height > maxRows) {

                    maxRows = height;

                }

            }

            trace("after for loop");

            #if js
            buffer.data = data.byteView;
            #else
            buffer.data = new UInt8Array (data);
            #end

            return map;

        }

                trace("z");
        #end

                trace("fall through null");
        return null;

    }

If you want to be super thorough, put some traces in the for and while loops that print out the iterator value, but even the above should narrow it down pretty good.

Pinpickle commented 9 years ago

I don't know what it's worth, but when I was experiencing the same bug in #613, adding the -debug flag stopped crashing, but resulted in the text rendering very strangely.

weird

The HaxePunk debug text is supposed to be white (on the edges of the screen), but it's black with white specks.

Dyhr commented 8 years ago

I think I had the same issue. It turned out that there weren't any fonts available on my android device. Adding one obviously solved it.

KnightMearh commented 8 years ago

Just adding notes/ bumping this issue:

Specifically this crash occurs with Lime 2.9.1 / Openfl 3.6.1 (Haxe 3.2.1, though still happens with Haxe 3.3.0). Lime 2.9.0 / Openfl 3.6.0 display the text properly on Android.

As mentioned above, it only crashes when you both set text and addChild. If you do only one it doesn't crash. I tried setting up fonts to see if I could get around the issue, but no luck.

KnightMearh commented 8 years ago

This problem seems fixed with lime 3.0.0 and openfl 4.0.0 (with haxe 3.3.02).

man2 commented 8 years ago

I can't upgrade to that version, because my game relies so much on drawTiles. Is there any work around for this?

update : fixed by using lime and openfl from git