Search code examples
mobilebitmaptextfieldhaxeopenfl

Haxe, OpenFL: draw TextField on Bitmap in a loop


So, this app I am making for mobile phones with Haxe, and OpenFL. I have a very long text which I load from a text file, and put it into a very tall TextField. However I want to convert this into a Bitmap, due to performance issues.

Again, however, a very tall Bitmap drawn from a text field just shows blank (maybe too much data?), so I decided to split the bitmap data into "pages" bitmaps, which the user can swipe on screen.

When I add the first "page" to display, it does. But rest of the "pages" just show as a blank image. Here's my code:

images = new Array();

var contentHeight:Float = 560;

field = new TextField();
var fieldFont = Assets.getFont("fonts/Kreon-Regular.ttf");
var format:TextFormat = new TextFormat(fieldFont.fontName, 26 /*currentZoom*/, 0xffffff);// 0x4F4F4F);

format.align = TextFormatAlign.LEFT;
field.defaultTextFormat = format;

var fieldWidth:Float = 410;

field.embedFonts = true;
field.text = fullText;
field.selectable = false;
field.wordWrap = true;
field.border = false;
field.autoSize = TextFieldAutoSize.LEFT;
field.width = fieldWidth;
//field.x = 0;
//addChild(field);

//loop through lines, if line within reach, increase clip height, else make new bd
var clipY:Float = 0;
var clipHeight:Float = 0;
trace(field.numLines);
var h_:Float = field.getLineMetrics(0).height;
var bd:BitmapData;
var mainBd:BitmapData = new BitmapData(Std.int(field.width), Std.int(field.height), true, 0x00000000);
mainBd.draw(field);

for (i in 0... field.numLines)
{
    try {
        h_ = field.getLineMetrics(i).height+0.2;
    } catch (e:Dynamic) {}

    if (clipHeight < contentHeight + h_)//line can be accomodated
    {
        clipHeight += h_;
    }
    else { //can't be accomodated, do clipping
        bd = new BitmapData(Std.int(field.width), Std.int(clipHeight + 5), true, 0x00000000);
        trace("clip:  clipY:" + clipY + " height:" + clipHeight);

        bd.copyPixels(mainBd, new Rectangle(0, clipY, field.width, clipHeight), new Point(0, clipY)); 
        //bd.draw(field, new Matrix(), new ColorTransform(), BlendMode.NORMAL, new Rectangle(0, clipY, field.width, clipHeight), true);

        images.push(new Bitmap(bd, PixelSnapping.AUTO, true));
        clipY += clipHeight;
        clipHeight = 0;
    }
}
addChild(images[1]);

Solution

  • You only add one of your images (addChild(images[1]);) to view.

    Also, I'd recommend, you to :

    1. Find the exact problem with displaying it as a single TextField, start with the exact amount of text needed for it to break.
    2. Check if there are some weird Unicode characters in your text, they may just plain break the renderer layouting for example(which should be counted as a bug in renderer, but still the way to fix it is to remove them).
    3. If you can't fix that problem, use multiple textfields instead of bitmaps, since with a large text you have a high chance to exceed memory limits(one page of your text currently takes around 1Mb of memory, which is A LOT.
    4. The way you do this currently wouldn't work in any case since you do the same renderer normally does. If you want to render it to partial bitmaps(and there is a real problem with rendering big texts), you need to divide the text in parts and render each part individually. (Basically the same as using multiple textfields with cacheAsBitmap).

    NOTE: Bitmaps shouldn't actually be any faster than TextFields, unless you use a VERY fancy font or a lot of filters. In any case, cacheAsBitmap property should do what you want automagically, without writing all this code. But I'm 99% sure thats not the case and you don't need that.