Closed makrohard closed 7 years ago
Thank you! I will look this on Monday on my computer when I get home.
Just a heads up, the forceResize was my last minute addition because I was having trouble drawing in the canvas at 60fps so I decided to cache the text and store in an array of png images, so when I need that text in the next frame, I just grabbed from memory. The result ended up in this code: ericoporto/fgmkJsEngine/src/textbuffer.js
Hey @makrohard your code is essentially disabling wrap function when forceResize is true. This breaks my game.
You need to pass the canvas to pngfont.ctx, for example by calling setup:
png_font.setup(
document.getElementById(
"target").getContext("2d"),
"unifont.png"
);
But at any time, before calling any drawImage, you have to set the canvas size you are going to draw to. If the canvas size is zero, it will raise the InvalidStateError.
But how is this happening?
var size = 3;
png_font.drawText('X',[0, 0],'#000', size, null, null, true);
this code will use the canvas, wrap (it's only an 'X'!), so it will draw and resize for X. The character has 16px width, size is 3, so 3*16= 48px. I always sum 2*size to the final size because you may be using a shadow and I am too lazy to check for this (it's usually cheaper to not check). So we have a 54 x 54 pixel canvas now!
png_font.ctx.clearRect(0, 0, size * 16, size * 16); // clear
size = 7;
We clear the canvas, and set size to 7. No problem here...
png_font.drawText('X',[0, 0],'#000', size, null, null, true);
Now we want to draw X, with size 7, which is 7x16 = 112px . But we can't, the canvas isn't that big, so the expected behavior should be not draw X, and return X. But I made a mistake, I don't check for empty wrapped2DArray. So I will try to forceResize anyway.
Now my canvas width and height is 0 pixels. Note that png_font.drawText returns "X"
as expected, since it wasn't able to write X in the canvas.
But if I try to draw anything on the canvas now, I will get the invalid state error.
I tested your lines going here and opening the console with f12 on Google Chrome.
I wasn't able to trigger the InvalidState error since when wrapped2DArray length is 0, no drawImage operations happen - it skips the for
line that effectively draws everything.
Above is the code, note that no exception was raised.
Hi Erico,
first of all, thanks a lof for your quick and in-depth reply. I was obviously mislead about the expected behavior of forceResize. In my use case, I need to draw the text in any case, if necessary resizing the canvas (smaller or bigger). The resulting canvas size is handled by the browser. What your script does, is not to draw in case the text does not fit to the canvas but return the text instead which then can be handled from the outside. That does make much sense for your game-engine. Resizing smaller does work like expected in any case.
What I did - as you have seen, is to calculate the canvas size from rows / cols of the text string which may be given in the form drawText('First Line\nSecond Line', ... - to provide it in the wrap variable before calling wrapText. This does indeed prevent text-wrapping, because right or lower bounds of canvas will, by definition, not be reached. For that it's not suitable as default value, as intended initially by me.
edit: You were right on the exception: It only occurs when drawText is called again, with canvas size set to 0.
Anyway, I do need a slightly different behavior as intended by you. So I'll go with my 'q&d-patch' for now. It might be better though, to resize the canvas manually as you proposed or to provide the wrap attribute on calling drawText, using my function at the outside to calculate the desired values first. That way your code is usable for my case too, without any changes.
By the way: You have achieved to make unifont, which covers nearly the whole Unicode-BMP with 65k characters, usable with a very small size. That makes it the best tool I have been able to find in the web for that task. I have been thinking about writing a small tool that coverts the unifont.hex format to a image, but that file is 3.8 M uncompressed and still 900 K gzipped. To use png, which is a loss-less compressed raster format as datasource results in 840K and is for that genious. Thank you for providing that tool MIT-licensed, hopefully it'll help many others, too!
So I added a possibility to pass 'nowrap'
or false
to the wrap parameter. I think it gives your expected behavior.
var size = 3;
png_font.drawText('X',[0, 0],'#000', size, null, false /*no wrap*/, true);
png_font.ctx.clearRect(0, 0, size * 16, size * 16); // clear
size = 7;
png_font.drawText('X',[0, 0],'#000', size, null, false /*no wrap*/, true);
I used your code but just now I noticed I did this through ctrl+c and ctrl+v when I should have done through Github to account for collaboration stats.
Thanks for your comment, it made me happy! If you wish to reduce size you can just delete the unused characters from the image. If you don't wish to resize the image because you need some sparse characters, png compression will still give you smaller filesize.
That's pretty much exactly what I needed! More easy to use for me now and more 'intuitive' for others imho. Definitely worth a star. You developed png_font with your game-engine in mind, but you created a tool, that can make any web-app show text, using ALL BMP chars without depending on local fonts or serving heavy web-fonts (given that the unifont 16*16 + GPL is acceptable, anyway currently no higher-res opensource fonts with a comparable coverage are available). That's why I do like it :)
Thanks :) ! I also renamed forceResize
to tightenCanvas
which should make the intent of the parameter clearer. Will close this issue now.
The following code shall draw an X with size 3, clear the canvas and redraw with size 7, using the undocumented forceResize parameter, leaving shadow and wrap undefined:
However, the following error shows up when attempting to draw the second, bigger text:
The error does not appear if the wrap attribute is specified with correct values.
Looking at the code, I found that in function drawText at line 278 the default values for the wrap array are set like this:
wrap = [this.ctx.canvas.width-pos[0],this.ctx.canvas.height-pos[1],0];
Which refers to the old canvas size, the new size is calculated in line 286 as a return value of wrapText. To quickfix that, I added a helper function that calculates the new canvas size from the rows and columns of the specified text and returns an wrap-array that is used to set the wrap default value in case of forceResize.
Replacement for line 278:
Helper function:
I forked your project with the modified file, because that solution works for me. It needs to be reviewed though, if the helper function is calculating the values as expected. There may be good reasons to use a different solution, too. In that case, at least the problem should be clear now.