draw_text crashes the client

● ARCHIVED · READ-ONLY
Started by Napoleon 6 posts View original ↗
  1. Bitmap.draw_text seems to crash the client when there is a lot of text.

    I pasted this code in a new project:

    class Scene_Map < Scene_Base #-------------------------------------------------------------------------- # * Start Processing #-------------------------------------------------------------------------- def start super SceneManager.clear $game_player.straighten $game_map.refresh $game_message.visible = false create_spriteset create_all_windows @menu_calling = false @str = 'Curabitur justo nisi, venenatis a hendrerit vel, tristique in eros. Maecenas cursus enim adipiscing lectus sodales, a interdum risus hendrerit. Proin laoreet lacus dui, eu sagittis erat pharetra sed. Nam sit amet elit venenatis, congue orci ultrices, placerat ipsum. Quisque laoreet facilisis mi, et tincidunt nulla rhoncus ac. Mauris fringilla non tortor et rhoncus. Nulla pharetra risus id placerat malesuada. Pellentesque et varius ante, nec viverra magna. Phasellus lacinia cursus leo, a lacinia sem adipiscing feugiat. Nam at sem nisl. Integer tempus blandit tortor, a tempus mauris varius ut. Maecenas eu elit vitae eros suscipit dignissim. Etiam consequat at tellus et placerat.' @sprite = Sprite.new @sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height) @sprite.bitmap.font.size = 22 @sprite.bitmap.draw_text(Rect.new(32, 64, Graphics.width - 64, Graphics.height - 96), @str) endendI tried increasing the size of the bitmap to roughly 6800x4800 to ensure that the paragraph fits within the rect (also increased the Rect size) but it still crashes the client. Why?

    Also, draw_text does not support the newline constant \n ? Do I have to make the text an array and then draw it line by line? In other words, am I forced to extend the Sprite class with a Sprite.draw_wrapped_text(Rect, text) that does not crash and that accepts newline constants?
  2. That script still crashes it. But I guess I'll have to perform smaller draws anyway.

    But if you would ever for any reason increase the max resolution to full HD (like that resolution breaker script) then this text draw is still gonna be giving you some annoying crashes that you need to fix manually I guess. You still need to split it into smaller chunks.

    I wonder why they implemented it like that. draw_text causes 0.0 fps drain even if I perform it 20x or more per frame on one single processor in C#/XNA. Why is it so sensitive for large data and why does it perform crap? It's not drawn by the gpu? I guess that the performance drain is also linked to the crashes somehow.

    I did a performance test on text_size() (1000 calls with if-statement and no delay whatsoever) and that method performs super good. So for the wrapping I can just check the size of every word.

    Anyway, my solution (combined with Mithran's script of course):

    Spoiler
    Code:
    #===============================================================================# Bitmap#===============================================================================class Bitmap    # Accepts the multiline character: \n  # Accepts no Window_Escape characters except \n. Just escape it yourself before calling this method.  def draw_text_wrapped(rect, str, align=0) #horizontal align: 0 = left, 1 = center, 2 = right align    words = str.split(/[\s]|\\n/) # \s for white space, [] = any single character, | = logical or, \\N = the \n escape to match    return if words.length == 0        space_width = text_size(' ').width    line = ''    lines = [];    line_size = 0    y = 0    begin      chunk_size = space_width + text_size(words[0]).width      if line_size + chunk_size < rect.width || line == '' # 2nd check is required in case a single word is longer than the total allowed width. In that case the word is simply drawn.        line += ' ' + words.shift        line_size += chunk_size      else        # Draw line        line[0] = '' # remove the single leading space added by this code        line_height = text_size(line).height        draw_text(rect.x, rect.y + y, rect.width, line_height, line, align)        y += line_height        line = ''        line_size = 0      end    end while words.length > 0        # Draw the only/last line, if any    if line.length > 0      line[0] = '' # remove the single leading space added by this code      draw_text(rect.x, rect.y + y, rect.width, text_size(line).height, line, align)    end  end # draw_text_wrappedend # class Bitmap
  3. Hrm, I wasn't even aware of this bug. My Text Cache script deactivates if you draw text to a rect smaller than can fit it, and also smaller than what will activate the 'wrap glitch' to support using the original squeeze methods (you can't "get" the text size of a squeezed character, without looking at specific pixels anyway, and squeezing it manually using stretch blt or some similar looks really bad). Basically, it is being completely bypassed, which is why it doesn't help at all with your original test. If you draw to a rect bigger than 640 pixel in width, the text squeezing contingency is ignored (because a default draw of this size would cause the text to wrap on itself, which is even worse) in favor of drawing character by character. Tested with the above string * 1000, no crash if you use my script and the squeeze contingency is disabled (because it draws character by character).


    I'd like to add a contingency to combat this bug (without having to resort to a line formatter, that is a separate issue) and while still supporting squeeze (terrible as it is), but I can't even find a universal cutoff point for either string size or total draw attempt width correlating to when the crash occurs, as it changes non-linearly depending on the font size and also changes depending on characters in the string supplied (independent of both size, and text width). You mentioned draw_text crashed with the resolution breaker script, but the string supplied above only crashed for me on the last two sentences (a text width of over 5700 pixels, far above even full HD draw width), anything less than that would draw fine (several tests with widths of ~3k pixels all worked fine with various font sizes). The string 'x' * 1000 (draw width of 9000 for 22 point font) also passed into the base draw_text method without crashing. There might also be another factor at work here if the results aren't the same from machine to machine.
  4. Yes I didn't have any word-wrap enabled so I drew a long string at once. Then it crashed. So I thought it was the script for increasing the max resolution.

    The bug would not have occurred if I would have used the/a word-wrap script from the start because it slices the text. And draw_text() doesn't seem to work with \n (sadly) so parsing in a lot of text with linebreak constants shouldn't be a problem either because it is not even supported. But I still found it rather bizarre that RPG Maker crashes when drawing long strings.

    I could guess perhaps why draw_text performs so bad. Probably because we draw onto a bitmap once and then the game internally draws it every frame to the buffer. Normally you draw the GUI every frame but RPG Maker probably already does that under the hood so calling draw_text every frame would make no sense at all in most cases. Or something like that :p .

    But anyway, now I understand and I can proceed with my scripts.
  5. Yes, draw_text is not a screen draw, we are altering pixels in a bitmap, where the pixels to be altered are dynamically selected based on font, size, styles, shadow, etc, they are changed by the grouping of letters (evidenced by the bugs outlined in the header of my Text Cache script), to give a reasonable approximation of what the font actually is.  The fonts start looking really terrible really fast below 18 pt, even if you remove the or use one solid color on a solid background, because they contain semi-transparent and shaded pixels (they are drawn to looked anti-aliased, with no way to turn it off).  The bitmap drawn to then needs a container class (sprite, window, etc), and THIS collection of container classes is considered by the Graphics module when painting the screen every frame (including transparencies, layering, and blending).  The main overhead there is from them needing to be considered, period which happens whether they even have a bitmap or not, then their respective bitmap sizes and their contents, so draw_text has almost no residual overhead on this side of things.

    Anyway, my Text Cache already draws things character by character, and as such, will prevent this crash (which only happens passing huge strings into the raw draw_text method) if you draw things to appropriate sized areas.  It just didn't for your above example because it uses the base method for attempting to squeeze text, and the text in your original example was super squeezed (5500+ width text width to < 640 draw width).  I still want a contingency for this issue (I can't say it wouldnt happen in any real-world circumstances, because you might actually want to have a horizontally scrolling paragraph for some reason), so I'll probably force cached character draw above certain string lengths.