measureText() bugged?

SilverDash

Veteran
Veteran
Joined
Oct 11, 2015
Messages
426
Reaction score
173
First Language
Dutch
Primarily Uses
RMMV


   Silv.EvText.MeasureBmp.fontSize = this.fontSize;    Silv.EvText.MeasureBmp.outlineWidth = this.outlineWidth;    Silv.EvText.MeasureBmp.fontItalic = this.italic;        this.bitmap = new Bitmap(Silv.EvText.MeasureBmp.measureTextWidth(text) + this.outlineWidth * 2, this.fontSize + this.outlineWidth * 2);
Code:
this.bitmap.fontSize = this.fontSize; this.bitmap.outlineWidth = this.outlineWidth;this.bitmap.fontItalic  = this.italic;this.bitmap.drawText(this.text, 0, 0, this.bitmap.width, this.bitmap.height, 'center');
Still the width is not enough? Measuring texts bugs for me when setting a very thick outlineWidth like 16+.

I tested it with static values just to make sure!

Silv.EvText.MeasureBmp.outlineWidth = 16; alert(Silv.EvText.MeasureBmp.measureTextWidth(text)); // 108 Silv.EvText.MeasureBmp.outlineWidth = 1; alert(Silv.EvText.MeasureBmp.measureTextWidth(text)); // 108 THEY ARE EQUAL!What? They are equal???
So Bitmap.measureText does NOT INCLUDE the outline-width... That's.... fricking.... fantastic... And then I found another bug related to text-alignment... Gotta be kidding me. RPG Maker is lately really really really pissing me of big time.

Why would you NOT include the outline width in the text-measurement... The only reason this does not go wrong with the default outlineWidth is because RPG Maker adds some sort of extra width & height around the text after measuring it because I always end up with too much space without changing the default outlineWidth. Looks like another dirty RM-quick-fix from the RM-devs.

Note that just add + this.outlineWidth * 2 is not good enough because there are lots of other factors that determine the final width (italic, fonttype, etc).

Silv.EvText.MeasureBmp.fontSize = this.fontSize + this.outlineWidth; does also not work.

An outlineWidth value of 16 equals to 13 pixels on the left side and 12 pixels on the right side (non-italic for my text with default font). So calculating it isn't easily doable... You gotta know all of the internal workings...
 
Last edited by a moderator:

Iavra

Veteran
Veteran
Joined
Apr 9, 2015
Messages
1,797
Reaction score
863
First Language
German
Primarily Uses
Please post the whole code.
 

SilverDash

Veteran
Veteran
Joined
Oct 11, 2015
Messages
426
Reaction score
173
First Language
Dutch
Primarily Uses
RMMV
//=============================================================================// SilvEventText.js// Version: 1.01//=============================================================================/*: * @plugindesc v1.01 Attaches texts on the map to events. <SilvEventText> * @author Silver * * @param -- Default Values -- * * @param Default Font Size * @desc Default font size * @default 24 * * @param Default Font Color * @desc Default font color * @default #FFFFFF * * @param Default Outline Color * @desc Default text outline color. Red, Green, Blue, Alpha * @default rgba(0, 0, 0, 0.5) * * @param Default Outline Width * @desc Default width of the text-outline * @default 4 * * @param Default Text Alignment * @desc Allowed values: topleft, topcenter, topright, center * @default topcenter * * @param Default Opacity * @desc Text Opacity 0-255 * @default 255 * * @param Default Italic * @desc By default, make all the text italic? * @default false * * @param Default Offset X * @desc Offset x. A negative value means more to the left. * @default 0 * * @param Default Offset Y * @desc Offset y. A negative value means more to the top. * @default 0 * * @param Default Extra Bitmap Width * @desc The default extra bitmap-width. * @default 0 * * @param Default Extra Bitmap Height * @desc The default extra bitmap-height. * @default 0 * * @help *-------------------------------------- * Notes *-------------------------------------- * - This plugin only works for sprites that are max 48 pixels in height. But you can use the offset-tags to make it work for any size * - Use multiple comments to fit in all of your tags. But only 1 tag per line and don't forget the </evtext> at the end. * - This plugin does not have 'jittering-text' * - Events can have multiple event-texts. * - Switching the event-page will clear the EventText(s) from the old page. * *-------------------------------------- * Event Comment (NOT the event-notetag): *-------------------------------------- * The comment must start with: <evtext> * Nothing is case-sensitive here * * Supported tags: * Text: value // << this tag must be the first tag! * FontSize: value * FontColor: value // Supported format examples: #FFFFFF, FFFFFF, rgba(0, 0, 0, 0.5) * OutlineColor: value // Supported format examples: #FFFFFF, FFFFFF, rgba(0, 0, 0, 0.5) * OutlineWidth: value * Offset_x: value * Offset_y: value * Opacity: value // 0-255 * Align: value // Supported values: topleft, topcenter, topright, center * Alignment: value // same as align-tag * Italic: value // true/false * ExtraBmpWidth: value // handy for thick outlineWidth's (and because RPG Maker MV fails) * ExtraBmp_W: value // Same as the above * ExtraBmpHeight: value // handy for thick outlineWidth's (and because RPG Maker MV fails) * ExtraBmp_H: value // Same as the above * </evtext> // without this tag the text will not be drawn. So don't forget it. * * * Example comment: * <evText> * Text:Well Hello There! * FontSize: 30 * FontColor: FF0000 * Italic: true * </evText> * *-------------------------------------- * Dev notes (ONLY for other developers): *-------------------------------------- * - The Game Events maintain a reference to the EventTexts using this variable: this.silvEvTexts[] * - To change an event's fontsize for example (after it's been created): * this.silvEvTexts[0].fontSize = 40; * this.silvEvTexts[0].render(); * *-------------------------------------- * Version History: *-------------------------------------- * v.1.01 (25 December 2015) * - Added 2 new parameters and 2 new commands. * - Applied my new coding standards. * - Fixed a crash. * - EventTexts are now deleted when the events themselves are erased. * * v.1.00 (24 December 2015) * - First Release * */// Importedvar Imported = Imported || {};Imported.SILV_EventText = 1.01;// #Parametersvar Silv = Silv || {};Silv.EvText = Silv.EvText || {};Silv.Parameters = $plugins.filter(function(p) { return p.description.contains('<SilvEventText>'); })[0].parameters;// Default ValuesSilv.EvText.DefaultFontSize = parseInt(Silv.Parameters['Default Font Size']);Silv.EvText.DefaultFontColor = Silv.Parameters['Default Font Color'];Silv.EvText.DefaultOutlineColor = Silv.Parameters['Default Outline Color'];Silv.EvText.DefaultOutlineWidth = parseInt(Silv.Parameters['Default Outline Width']);Silv.EvText.DefaultTextAlign = Silv.Parameters['Default Text Alignment'];Silv.EvText.DefaultOpacity = parseInt(Silv.Parameters['Default Opacity']);Silv.EvText.DefaultItalic = parseInt(Silv.Parameters['Default Italic']);Silv.EvText.DefaultOffset_X = parseFloat(Silv.Parameters['Default Offset X']);Silv.EvText.DefaultOffset_Y = parseFloat(Silv.Parameters['Default Offset Y']);Silv.EvText.DefaultExtraBmpWidth = parseInt(Silv.Parameters['Default Extra Bitmap Width']);Silv.EvText.DefaultExtraBmpHeight = parseInt(Silv.Parameters['Default Extra Bitmap Height']);// AliasSilv.Alias = Silv.Alias || {};if (!Silv.AddAlias){ Silv.AddAlias = function(alias, original_method) { if (Silv.Alias[alias]) { throw new Error('Alias already exists: ' + alias); } Silv.Alias[alias] = original_method; };}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Utilities//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Silv.EvText.GetTextAfter = function(str, character){ return str.substr(str.indexOf(character) + 1);};Silv.EvText.MeasureBmp = new Bitmap(1, 1);//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Scene Map//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Silv.AddAlias('evText_Scene_Map_initialize', Scene_Map.prototype.initialize);Scene_Map.prototype.initialize = function(){ Silv.Alias.evText_Scene_Map_initialize.apply(this, arguments); this.evSpritesToAdd = [];};Silv.AddAlias('evText_Scene_Map_createSpriteset', Scene_Map.prototype.createSpriteset);Scene_Map.prototype.createSpriteset = function(){ Silv.Alias.evText_Scene_Map_createSpriteset.apply(this, arguments); this.evSpritesToAdd.forEach(function(sprite) { this._spriteset.addChild(sprite); }, this);};//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Event Text//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function EventText() { this.initialize.apply(this, arguments); }EventText.prototype = Object.create(Sprite_Base.prototype);EventText.prototype.constructor = EventText;EventText.prototype.initialize = function(gameEvent, text){ Sprite_Base.prototype.initialize.call(this); this.gameEvent = gameEvent; this.setDefaults(); this.setText(text); if (SceneManager._scene._spriteset) { SceneManager._scene._spriteset.addChild(this); // SceneManager._scene can be null when the map was just loaded } else { SceneManager._scene.evSpritesToAdd.push(this); // if it's null, just push it to a temporary list instead }};EventText.prototype.setDefaults = function(){ this._fontSize = Silv.EvText.DefaultFontSize; this.fontColor = Silv.EvText.DefaultFontColor; this.outlineColor = Silv.EvText.DefaultOutlineColor; this._outlineWidth = Silv.EvText.DefaultOutlineWidth; this.textOpacity = Silv.EvText.DefaultOpacity; this.italic = Silv.EvText.DefaultItalic; this.extraOffset_x = Silv.EvText.DefaultOffset_X; this.extraOffset_y = Silv.EvText.DefaultOffset_Y; this._extraBmpWidth = Silv.EvText.DefaultExtraBmpWidth; this._extraBmpHeight = Silv.EvText.DefaultExtraBmpHeight;};EventText.prototype.extraBmpWidth = function(value){ this._extraBmpWidth = value; this.setSizePropertyAfter();};EventText.prototype.extraBmpHeight = function(value){ this._extraBmpHeight = value; this.setSizePropertyAfter();};EventText.prototype.fontSize = function(value){ this._fontSize = value; this.setSizePropertyAfter();};EventText.prototype.outlineWidth = function(value){ this._outlineWidth = value; this.setSizePropertyAfter();};// Creates a new bitmap to fit the size of the new properties, recalculates the alignment and of course draws the contents onto the new bitmapEventText.prototype.setSizePropertyAfter = function(){ this.createBitmap(); this.setAlignment(this.alignment); if (!this.requiresRender) { this.render(); }};EventText.prototype.createBitmap = function(text){ Silv.EvText.MeasureBmp.fontSize = this._fontSize; Silv.EvText.MeasureBmp.outlineWidth = this._outlineWidth; Silv.EvText.MeasureBmp.fontItalic = this.italic; this.bitmap = new Bitmap(Silv.EvText.MeasureBmp.measureTextWidth(text) + this._extraBmpWidth, this._fontSize + this._extraBmpHeight);};EventText.prototype.setText = function(text){ this.createBitmap(); this.text = text; this.setAlignment(Silv.EvText.DefaultTextAlign); this.requiresRender = true;};// alignment: topLeft, topCenter, topRight, centerEventText.prototype.setAlignment = function(alignment){ if (!this.text) { throw new Error('setAlignment() requires the text to be set first.'); } this.alignment = alignment; switch(alignment.toLowerCase()) { case 'topleft': this.alignOffset_x = -$gameMap.tileWidth() / 2; this.alignOffset_y = -$gameMap.tileHeight() - this.bitmap.height; break; case 'topcenter': this.alignOffset_x = -this.bitmap.width / 2; this.alignOffset_y = -$gameMap.tileHeight() - this.bitmap.height; break; case 'topright': this.alignOffset_x = +$gameMap.tileWidth() / 3 - this.bitmap.width; this.alignOffset_y = -$gameMap.tileHeight() - this.bitmap.height; break; case 'center': this.alignOffset_x = -this.bitmap.width / 2; this.alignOffset_y = -$gameMap.tileHeight() / 2 - this.bitmap.height / 2; break; default: throw new Error('Unknown alignment: ' + alignment); }};EventText.prototype.update = function(){ Sprite_Base.prototype.update.call(this); this.setLocation(this.gameEvent.screenX(), this.gameEvent.screenY());};// x and y are expected to be the center-coords of a tile.EventText.prototype.setLocation = function(x, y){ this.x = x + this.alignOffset_x + this.extraOffset_x; this.y = y + this.alignOffset_y + this.extraOffset_y;};EventText.prototype.render = function(){ this.bitmap.clear(); // (Re)-Initialize bitmap settings this.bitmap.fontSize = this._fontSize; this.bitmap.textColor = this.fontColor; this.bitmap.outlineColor = this.outlineColor; this.bitmap.outlineWidth = this._outlineWidth; this.bitmap.paintOpacity = this.textOpacity; this.bitmap.fontItalic = this.italic; this.bitmap.drawText(this.text, 0, 0, this.bitmap.width, this.bitmap.height, 'center'); this.requiresRender = false;};//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Game Event//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Silv.AddAlias('evText_Game_Event_setupPage', Game_Event.prototype.setupPage);Game_Event.prototype.setupPage = function(){ Silv.Alias.evText_Game_Event_setupPage.apply(this, arguments); if (!this._erased) { this.deleteEventTexts(); this.checkEventText(); }};Silv.AddAlias('evText_Game_Event_erase', Game_Event.prototype.erase);Game_Event.prototype.erase = function(){ Silv.Alias.evText_Game_Event_erase.apply(this, arguments); this.deleteEventTexts();};Game_Event.prototype.deleteEventTexts = function(){ if (this.silvEvTexts) { if (SceneManager._scene._spriteset) { this.silvEvTexts.forEach(function(evText) { SceneManager._scene._spriteset.removeChild(evText); }); } else { this.silvEvTexts.forEach(function(evText) { var arrayIdx = SceneManager._scene.evSpritesToAdd.indexOf(evText); if (arrayIdx > -1) { SceneManager._scene.evSpritesToAdd.splice(arrayIdx, 1); } // delete from array }); } } this.silvEvTexts = [];};Game_Event.prototype.checkEventText = function(){ var page = this.page(); if (!page) { return; } var newEventText = null; var evTextCmdFound = false; page.list.forEach(function(cmd) { if (!evTextCmdFound && (cmd.code === 108)) // 108 = first comment-line, 408 = subsequent comment-lines { if (cmd.parameters[0].trim().toLowerCase() === '<evtext>') { evTextCmdFound = true; } } else if (evTextCmdFound && ((cmd.code === 108) || cmd.code === 408)) { var option = (cmd.parameters[0].split(':')[0]).trim().toLowerCase(); var value = Silv.EvText.GetTextAfter(cmd.parameters[0], ':'); switch(option) { case 'text': newEventText = new EventText(this, value); this.silvEvTexts.push(newEventText); break; case 'fontsize': newEventText.fontSize(parseInt(value)); break; case 'fontcolor': var fontColor = value.trim(); if ((fontColor[0] !== '#') && (fontColor[0] !== 'r')) { fontColor = '#' + fontColor; } newEventText.fontColor = fontColor; break; case 'outlinecolor': var outlineColor = value.trim(); if ((outlineColor[0] !== '#') && (outlineColor[0] !== 'r')) { outlineColor = '#' + outlineColor; } newEventText.outlineColor = outlineColor; break; case 'outlinewidth': newEventText.outlineWidth(parseInt(value)); break; case 'opacity': newEventText.textOpacity = parseInt(value); break; case 'offset_x': newEventText.extraOffset_x = parseFloat(value); break; case 'offset_y': newEventText.extraOffset_y = parseFloat(value); break; case 'align': case 'alignment': newEventText.setAlignment(value); break; case 'italic': newEventText.italic = (value.trim().toLowerCase() === 'true'); break; case 'extrabmp_w': case 'extrabmpwidth': newEventText.extraBmpWidth(parseInt(value)); break; case 'extrabmp_h': case 'extrabmpheight': newEventText.extraBmpHeight(parseInt(value)); break; case '</evtext>': newEventText.render(); evTextCmdFound = false; // This line is not required but nice to have in case of 'code-expansion' later return; default: throw new Error('Empty line or unknown command for line: ' + cmd.parameters[0]); } } }, this);};
Measurement is performed here: EventText.prototype.createBitmap = function(text)

I actually hope I made a mistake and that this is not yet another RM problem (actually 2).
 
Last edited by a moderator:

Iavra

Veteran
Veteran
Joined
Apr 9, 2015
Messages
1,797
Reaction score
863
First Language
German
Primarily Uses
A small demo i just made, seems to be working fine:

var test = function(text) { var padding = 5, fontSize = 20; var dummy = new Bitmap(); dummy.fontSize = fontSize; var width = dummy.measureTextWidth(text); var bmp = new Bitmap(width + padding * 2, fontSize + padding * 2); bmp.fontSize = fontSize; bmp.fillAll('#000'); bmp.drawText(text, padding, padding, width, fontSize); SceneManager._scene.addChild(new Sprite(bmp));};/edit: One moment, i forgot the outline.//edit: Indeed, the measureTextWidth function doesn't include the outline:

Code:
Bitmap.prototype.measureTextWidth = function(text) {    var context = this._context;    context.save();    context.font = this._makeFontNameText();    var width = context.measureText(text).width;    context.restore();    return width;};
You could create your own function and add th part from Bitmap._drawTextOutline, though.
 
Last edited by a moderator:

SilverDash

Veteran
Veteran
Joined
Oct 11, 2015
Messages
426
Reaction score
173
First Language
Dutch
Primarily Uses
RMMV
Thanks for checking.

Bitmap._drawTextOutline calls Bitmap.stroke() and that one is not in the pixi.js. It comes from the build-in Javascript Canvas. And when I look closely at how measureText() works, it seems not to be accurate and instead always adds a tiny bit of extra size (to compensate?) adding to the problem of a wrongly measured size.

Javascript does not have anything (afaik) build-in in any way to measure the exact size of a text-stroke. I would have to lookup the internal working of the strokeText() function of Javascript in order to really make the measureText() work properly. And that is not gonna be easy I bet due to the amount of factors that determine the final size of a stroked-text.

Screw this. I just add manual offsets. Dirty as hell but it works and not like RM itself does it better. At least I know it's not me.
 
Last edited by a moderator:

Iavra

Veteran
Veteran
Joined
Apr 9, 2015
Messages
1,797
Reaction score
863
First Language
German
Primarily Uses
The measureTextWidth function i quoted above delegates to CanvasRenderingContext2D.measureText, which is oart of the JavaScript Web API.
 

Latest Threads

Latest Posts

Latest Profile Posts

Looking back at some sketches, and game design documents on my PC dated summer of 2015. I started development with the release of MZ, but in 2015, I felt a strong desire to make a game out of the blue. I remember feeling sad for no apparent reason, and all these ideas rushed into my head. Now that I think about it, since that day, everything has become easier to do on my PC . . . it’s very creepy.
Everything's going to be alright! We're all in this together. <3
Aaaaannd published my game's tech demo. :D

Feel free to download and play it. And give feedback!
Hey everyone, we know that the edit bar is missing. We're working on it. You can talk about it in the announcement here: https://forums.rpgmakerweb.com/index.php?threads/forum-errors-missing-edit-bar-etc.132715/
So, explain why we can no longer use BBC code or smilies in our posts? This sparks much sadness...

Forum statistics

Threads
107,794
Messages
1,032,257
Members
139,941
Latest member
Finley
Top