Every value I set for .scale() lower than 0.01 will be handled as I set 0.01, rendering a text that is not lower than when I set it to 0.01.
FreetypeFontLoader.FreeTypeFontLoaderParameter sizeParams = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
sizeParams.fontFileName = "MyFont.ttf";
sizeParams.fontParameters.size = (int)Math.ceil(2*MINIMUM_VIEWPORT_SIZE_PIXEL/9f/2f/2f);
sizeParams.fontParameters.color = new Color(Color.Red);
sizeParams.fontParameters.borderColor = new Color(Color.Green);
sizeParams.fontParameters.borderWidth = 2;
sizeParams.fontParameters.minFilter = Texture.TextureFilter.Linear;
sizeParams.fontParameters.magFilter = Texture.TextureFilter.Linear;
assetManager.load("MyFont.ttf", BitmapFont.class, sizeParams);
assetManager.finishLoading();
BitmapFont fontFreeType = assetManager.get("myFont.ttf", BitmapFont.class);
Label.LabelStyle miniLabelStyle = new Label.LabelStyle();
miniLabelStyle.font = fontFreeType;
miniLabelStyle.font.getData().scale(0.005f);
Label labelDebug = new Label("my sample text", game.miniLabelStyle);
I tried this, without any change (either setting true or false):
miniLabelStyle.font.setUseIntegerPositions(false);
I tried this, but the text results so grainy:
labelDebug.setFontScale(0.5f);
How to get a lower scale than 0.01?
This is just a guess, since I don't use FreetypeFontGenerator, but maybe what you're missing is mip mapping, which is necessary for drawing images smaller than their original size without them getting blurry and chunky.
You'll want to make these two settings to enable it, I think:
sizeParams.fontParameters.minFilter = Texture.TextureFilter.MipMapLinearLinear;
sizeParams.fontParameters.genMipMaps = true;
You might also need to add padding around the glyphs so characters don't absorb some of their neighbors when shrunk down. (Settings padTop, padLeft, padBottom, padRight.)
However, there's a reason mip mapping defaults to being disabled. Text looks clearest if it's generated at exactly the size it will be on screen and is rendered with nearest filtering. This isn't universally true, since you might be projecting your text in 3D space or scaling it up and down in real-time. But if it's static text for a GUI, it's probably best to calculate exactly what size it would need to be for it to be one-texture-pixel to one-screen-pixel.
Related
I am trying to implement a chart with a ColumnRenderableSeries3D series, but with a small amount of data points (25x25) the columns are nearly invisible. At higher numbers of data points (100x100) with a wider range of values this problem becomes even worse and a Moiré pattern appears. What can be done to significantly increase the column's diameter, so they are easily seen and so the Moiré pattern disappears?
If it is relevant this is being rendered on a VM with a VMware ESXi 6.5 SVGA adapter on Windows Server 2016, over a RemoteDesktop connection. Surprisingly even though 3D support isnt enabled for the VM, SciChart.Examples.Demo.exe says DirectX hardware acceleration is enabled. The version of SciChart is 5.1.0.11405 and SharpDX is 4.0.1.
SciChart3DSurface SciChartSurface3d = new SciChart3DSurface();
XyzDataSeries3D<Double, Double, DateTime> MyXyzDataSeries = new XyzDataSeries3D<Double, Double, DateTime>();
SciChartSurface3d.XAxis = new NumericAxis3D();
SciChartSurface3d.YAxis = new NumericAxis3D();
SciChartSurface3d.ZAxis = new DateTimeAxis3D();
SciChartSurface3d.Camera = new Camera3D() { ZoomToFitOnAttach = true };
SciChartSurface3d.WorldDimensions = new Vector3(200, 100, 200);
SciChartSurface3d.RenderableSeries.Add(new ColumnRenderableSeries3D() { DataSeries= MyXyzDataSeries, ColumnShape = typeof(CubePointMarker3D), DataPointWidthX = 1.0, Opacity = 1.0 });
SciChartSurface3d.BorderThickness = new Thickness(0);`
SomeMethodToLoadTheDataSeries();
25x25
100x100
Edit
Changing DataPointWidthX to DataPointWidth doesn't help. With a width of 1.0:
There are two modes of the column width definition:
First and the default is called MaxNonOverlapping. In this mode, the maximum possible width is calculated where any column has enough space not to overlap others.
Second is called FixedSize. In this mode, the width of a column is defined by a value from the ColumnRenderableSeries3D.CoulmnFixedSize property.
Definition of the mode is performed over the ColumnRenderableSeries3D.ColumnSpacingMode property. Below is the example how to setup fixed size column chart:
var renderableSeries3D = new ColumnRenderableSeries3D();
renderableSeries3D.ColumnSpacingMode = ColumnSpacingMode.FixedSize;
renderableSeries3D.CoulmnFixedSize = 25;
Note, a value of the CoulmnFixedSize property represents coordinates space. Thus it is related to the SciChart3DSurface.WorldDimensions. You can find more information about the coordinates space here.
So this one is a little hard to explain. I have a custom Text class that automatically resizes and sets the width of the text when you change its value. I then take that Text and draw it on a Bitmap to scale it up to make the text look pixelated.
I have a property called maxWidth that allows you to restrict the width of the text if you want it to maintain a certain width. By default the maxWidth is the width of the text's parent so that it doesn't get cut off or expand the parent's boundaries unexpectedly.
So unfortunately when I draw the text it sometimes gets cut off on the right side. Now I've checked all the values and the width and textWidth are showing up as within their maxWidth values, but when I take a look myself through screenshots I see the text is actually about 3 pixels wider than it should be.
Here's an image to better explain what I mean:
I turned on borders so you can easily see what I mean. The word "and" on the first line gets drawn outside its border. Here is the line of code that handles resizing text when you change its bounds.
override protected function checkResize(value:String):void {
var bufferWidth:uint = Math.floor(Number(defaultTextFormat.size) / bufferDivisor) + bufferMin;
var maxWidth:Number = this.maxWidth;
x = y = 0;
if (parent is Stage) {
var stageParent:Stage = Stage(parent);
super.width = stageParent.stageWidth;
super.height = stageParent.stageHeight;
if (maxWidth == 0) maxWidth = stageParent.stageWidth;
}
else {
super.width = parent.width;
super.height = parent.height;
if (maxWidth == 0) maxWidth = parent.width;
}
maxWidth = maxWidth / scale;
text = value;
if (textWidth + bufferWidth <= maxWidth) super.width = textWidth + bufferWidth;
else super.width = maxWidth;
super.height = textHeight + 4;
if (textSnapshot) updateSnapshot();
if (alignRelation) Align.alignTo(textSprite, alignRelation, alignDirection, alignXOffset, alignYOffest);
}
And for this text specifically the width value states it's 512, which is correct since that's the maxWidth. However if you notice the top line in the text, it goes beyond the 512 width border, it actually goes all the way to 515 even though it says its width is 512. Even more bizarre is the textWidth states it's 510.4 even though the first line goes well beyond that amount. I just want to know if I'm doing anything wrong or if there's a way to get a true textWidth value.
This seems to be related to embedding fonts, at least it was when I had the same problem. A workaround is to set the right margin of the text field, like so
var tf:TextFormat = new TextFormat();
tf.rightMargin = 10; // or whatever fixes your problem, e.g. relate it to font size
textField.setTextFormat(tf);
In the following example, there is a function called generateTexture().
Is it possible to draw text (numbers) into the pixel array? Or is it possible to draw text (numbers) on top of that shader?
Our goal is to draw a circle with a number inside of it.
https://forge.autodesk.com/blog/using-dynamic-texture-inside-custom-shaders
UPDATE:
We noticed that each circle can't use a unique generateTexture(). The generateTexture() result is used by every single one of them. The only thing that can be customized per object is the color, plus what texture is used.
We could create a workaround for this, which is to generate every texture from 0 to 99, and to then have each object choose the correct texture based on the number we want to display. We don't know if this will be efficient enough to work properly though. Otherwise, it might have to be 0 to 9+ or something in that direction. Any guides on our updated question would be really appreciated. Thanks.
I am able to successfully display text with the following code, simply replace generateTexture() by generateCanvasTexture() in the sample and you should get the result below:
const generateCanvasTexture = () => {
const canvas = document.createElement("canvas")
const ctx = canvas.getContext('2d')
ctx.font = '20pt Arial'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(new Date().toLocaleString(),
canvas.width / 2, canvas.height / 2)
const canvasTexture = new THREE.Texture(canvas)
canvasTexture.needsUpdate = true
canvasTexture.flipX = false
canvasTexture.flipY = false
return canvasTexture
}
It is possible but you would need to implement it yourself. Shaders are a pretty low level feature so there is no way to directly draw a number or a text, but you can convert a given character into its representation as a 2d pixel array.
I've read a lot of StackOverflow answers and other pages talking about how to do letter spacing in Canvas. One of the more useful ones was Letter spacing in canvas element
As that other question said, 'I've got this canvas element that I'm drawing text to. I want to set the letter spacing similar to the CSS letter-spacing attribute. By that I mean increasing the amount of pixels between letters when a string is drawn.' Note that letter spacing is sometimes, and incorrectly, referred to as kerning.
I notice that the general approach seems to be to output the string on a letter by letter basis, using measureText(letter) to get the letter's width and then adding additional spacing. The problem with this is it doesn't take into account letter kerning pairs and the like. See the above link for an example of this and related comments.
Seems to me that the way to do it, for a line spacing of 'spacing', would be to do something like:
Start at position (X, Y).
Measure wAll, the width of the entire string using measureText()
Remove the first character from the string
Print the first character at position (X, Y) using fillText()
Measure wShorter, the width of the resulting shorter string using measureText().
Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
Increment X by wChar + spacing
wAll = wShorter
Repeat from step 3
Would this not take into account kerning? Am I missing something? Does measureText() add a load of padding that varies depending on the outermost character, or something, and if it does, would not fillText() use the same system to output the character, negating that issue? Someone in the link above mentioned 'pixel-aligned font hinting' but I don't see how that applies here. Can anyone advise either generally or specifically if this will work or if there are problems with it?
EDIT: This is not a duplicate of the other question - which it links to and refers to. The question is NOT about how to do 'letter spacing in canvas', per the proposed duplicate; this is proposing a possible solution (which as far as I know was not suggested by anyone else) to that and other questions, and asking if anyone can see or knows of any issues with that proposed solution - i.e. it's asking about the proposed solution and its points, including details of measureText(), fillText() and 'pixel-aligned font hinting'.
Well, I've written the code, based on the pseudocode above, and done a few comparisons by screenshotting and eyeballing it for differences (zoomed, using straight lines from eg clip boxes to compare X position and width for each character). Looks exactly the same for me, with spacing set at 0.
Here's the HTML:
<canvas id="Test1" width="800px" height="200px"><p>Your browser does not support canvas.</p></canvas>
Here's the code:
this.fillTextWithSpacing = function(context, text, x, y, spacing)
{
//Start at position (X, Y).
//Measure wAll, the width of the entire string using measureText()
wAll = context.measureText(text).width;
do
{
//Remove the first character from the string
char = text.substr(0, 1);
text = text.substr(1);
//Print the first character at position (X, Y) using fillText()
context.fillText(char, x, y);
//Measure wShorter, the width of the resulting shorter string using measureText().
if (text == "")
wShorter = 0;
else
wShorter = context.measureText(text).width;
//Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
wChar = wAll - wShorter;
//Increment X by wChar + spacing
x += wChar + spacing;
//wAll = wShorter
wAll = wShorter;
//Repeat from step 3
} while (text != "");
}
Code for demo/eyeball test:
element1 = document.getElementById("Test1");
textContext1 = element1.getContext('2d');
textContext1.font = "72px Verdana, sans-serif";
textContext1.textAlign = "left";
textContext1.textBaseline = "top";
textContext1.fillStyle = "#000000";
text = "Welcome to go WAVE";
this.fillTextWithSpacing(textContext1, text, 0, 0, 0);
textContext1.fillText(text, 0, 100);
Ideally I'd throw multiple random strings at it and do a pixel by pixel comparison. I'm also not sure how good Verdana's default kerning is, though I understand it's better than Arial - suggestions on other fonts to try gratefully accepted.
So... so far it looks good. In fact it looks perfect.
Still hoping that someone will point out any flaws in the process.
In the meantime I will put this here for others to see if they are looking for a solution on this.
My answer got deleted.
So, I'm using chrome and here is my complete code.
second_image = $('#block_id').first();
canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';
ctx = canvas.getContext('2d');
canvas.crossOrigin = "Anonymous";
canvasDraw = function(text, font_size, font_style, fill_or_stroke){
canvas.width = second_image.width();
canvas.height = second_image.height();
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(second_image.get(0), 0, 0, canvas.width, canvas.height);
//refill text
ctx.font = font_size +'px '+ font_style + ',Symbola';
$test = ctx.font;
ctx.textAlign = "center";
if(fill_or_stroke){
ctx.fillStyle = "#d2b76d";
ctx.strokeStyle = "#9d8a5e";
ctx.strokeText(text,canvas.width*$left,canvas.height*$top);
ctx.fillText(text,canvas.width*$left,canvas.height*$top);
}
else{
ctx.strokeStyle = "#888888";
ctx.strokeText(text,canvas.width*$left,canvas.height*$top);
}
};
And you don't need to use this function this.fillTextWithSpacing. I didn't use and it worked like a charm)
I'm learning ActionScript/Flash. I love to play with text, and have done a lot of that kind of thing with the superb Java2D API.
One of the things I like to know is "where, exactly, are you drawing that glyph?" The TextField class provides the methods getBounds and getCharBoundaries, but these methods return rectangles that extend far beyond the actual bounds of the whole text object or the individual character, respectively.
var b:Sprite = new Sprite();
b.graphics.lineStyle(1,0xFF0000);
var r:Rectangle = text.getCharBoundaries(4);
r.offset(text.x, text.y);
b.graphics.drawRect(r.x,r.y,r.width,r.height);
addChild(b);
b = new Sprite();
b.graphics.lineStyle(1,0x00FF00);
r = text.getBounds(this);
b.graphics.drawRect(r.x,r.y,r.width,r.height);
addChild(b);
Is there any way to get more precise information about the actual visual bounds of text glyphs in ActionScript?
Richard is on the right track, but BitmapData.getColorBounds() is much faster and accurate... I've used it a couple of times, and optimized for your specific needs its not as slow as one might think.
Cory's suggestion of using flash.text.engine is probably the "correct" way to go, but I warn you that flash.text.engine is VERY (very!) hard to use compared to TextField.
Not reasonably possible in Flash 9 -- Richard's answer is a clever work-around, though probably completely unsuitable for production code (as he mentions) :)
If you have access to Flash 10, check out the new text engine classes, particularly TextLine.
I'm afraid all the methods that are available on TextField are supposed to do what you have already found them to do. Unless performance is key in your application (i.e. unless you intend to do this very often) maybe one option would be to draw the text field to a BitmapData, and find the topmost, leftmost, et c colored pixels within the bounding box retrieved by getCharBoundaries()?
var i : int;
var rect : Rectangle;
var top_left : Point;
var btm_right : Point;
var bmp : BitmapData = new BitmapData(tf.width, tf.height, false, 0xffffff);
bmp.draw(tf);
rect = tf.getCharBoundaries(4);
top_left = new Point(Infinity, Infinity);
btm_right = new Point(-Infinity, -Infinity);
for (i=rect.x; i<rect.right; i++) {
var j : int;
for (j=rect.y; j<rect.bottom; j++) {
var px : uint = bmp.getPixel(i, j);
// Check if pixel is black, i.e. belongs to glyph, and if so, whether it
// extends the previous bounds
if (px == 0) {
top_left.x = Math.min(top_left.x, i);
top_left.y = Math.min(top_left.y, j);
btm_right.x = Math.max(btm_right.x, i);
btm_right.y = Math.max(btm_right.y, j);
}
}
}
var actualRect : Rectangle = new Rectangle(top_left.x, top_left.y);
actualRect.width = btm_right.x - top_left.x;
actualRect.height = btm_right.y - top_left.y;
This code should loop through all the pixels that were deemed part of the glyph rectangle by getCharBoundaries(). If a pixel is not black, it gets discarded. If black, the code checks whether the pixels extends further up, down, right or left than any pixel that has previuosly been checked in the loop.
Obviously, this is not optimal code, with nested loops and unnecessary point objects. Hopefully though, the code is readable enough, and you are able to make out the parts that can most easily be optimized.
You might also want to introduce some threshold value instead of ignoring any pixel that is not pitch black.