I try to transform some text rendering code from WinForm to Skia.
Winform: graphics.MeasureString(text, font)
Skia: var rect = new SKRect(pos.X, pos.Y, 0, 0)
paint.MeasureText(text, ref rect)
If you compare the results you will realize that Skia actually calculates the size and position of the rendered text very precise. In contrast Winforms returns a much rougher approximation of size and position including an generous margin. (Please ignore the vertical offset.)
In order to replace WinForms with Skia I need to convert Skia to Winform results.
Has anybody an idea? Or am I on my own here?
Related
I am trying to write String at given position in drawing and it works fine. I have implemented Slider in my app. And as the user changes the slider value I want the font and thickness of String to increase and decrease. Here is the code I tried
Slider slider= new new Slider();
slider.addDataChangedListener(new DataChangedListener() {
#Override
public void dataChanged(int type, int index) {
this.setBrushGirth(index * 2);
}
});
int offset = girth * 2;
Font f= Font.createSystemFont(Font.FACE_PROPORTIONAL, offset , offset);
g.setFont(f);
g.drawString("Hello World" , currentLine.startX + imageDrawnFromX, currentLine.startY + imageDrawnFromY);
Here girth is nothing but the value I get from slider movement.
So, How do I set the stroke width for the text so that it increases or decreases according to value obtained from Slider. I can set the Font color of text but not this.
Any suggestions would be helpful. Thanks
We don't support that, stroke is only used to draw shapes and currently there is no API to convert text to a shape. I doubt if there ever will be.
The reason for this is simple, in order to do this we'd need to take over the drawing/kerning of text completely. This is something done by Swing/JavaFX which makes their text look different from the way the OS renders the same text. That's not something we want as it makes the app look less native.
I want to stretch the text in a label to fill the width of the label using VB.NET code. This is in a WPF application.
I have tried numerous methods including changing only the font size like this:
scale = 0.25
lbl.FontSize = l.Width * scale
The problem with this method is I need the height to remain the same for all labels and the 0.25 was a guess that gives approximately the correct fill for a string of 4 characters
I tried a recursive method where I change the font size in small increments and compare the string width to the label width until it fits.
Transformations was also considered and it would looks like a good bet:
w = lbl.Width
scale = lbl.content.length/100
lbl.LayoutTransform = New System.Windows.Media.ScaleTransform(w * scale, 1)
This solves my problem of hight but I still need to guess the factor of transformation until it sort of works. Also the stretched text lose it quality and becomes distorted as expected.
Is there any way to reduce the distortion effect on text when its transformed. Perhaps some fonts that display better than other on after transformations?
I’m running into problems when rendering text on my document. Specifically, the text renders too low. I tried filling a rectangle behind the text to see what happens, and I discovered that they appear to render slightly offset:
Here’s the code I used to render the box and text:
_doc.FillRectangle(Colors.LightGray, 36, 72, 37.344, 9);
_doc.DrawString("Lorem", new Font("Arial", 12), Colors.Black,
new Rect(36, 72, 37.344, 9));
I know that the height of the rectangle (9) doesn’t appear to match the height of the font (12), which I thought might have been the problem at first. However, I then did a MeasureString on the font itself and discovered that its height was actually 9 rather than 12 (I used the immediate window for this, which is why it's a pic and not a text block):
Any ideas as to what could be causing it and how to avoid it?
Thanks!
-Ari
There are couple of posts that discuss the WPF text rendering inconsistencies.
One of the other posts: WPF Text rendering problem, stated that SnapToDevicePixels could ruin text rendering if text has been resized to display across pixels. The suggested answer was to keep,
SnapToDevicePixels = True on borders/backgrounds but turn it off for text elements.
As for the current method your are using. Please take a look at one of my earliers posts: Increase bar chart values with button clicks : I have used DrawString() to add a letter within a rectangle. All drawing is done in a Panel.
code:
...
panel1.Paint += new PaintEventHandler(panel1_Paint);
using (Graphics g = this.panel1.CreateGraphics())
{
Brush brush = new SolidBrush(Color.Green);
g.FillRectangle(brush, px, py, 20, 20);
Pen pen = new Pen(new SolidBrush(Color.White));
g.DrawRectangle(pen, px, py, 20, 20);
//add each total5Click into chart block
g.DrawString((total5Times).ToString(), new Font("Arial", 7),
new SolidBrush(Color.AntiqueWhite),
px + 1, py+8, StringFormat.GenericDefault);
pen.Dispose();}
...
I would suggest using the method DrawString Method (String, Font, Brush, RectangleF, StringFormat) and supplying the String Format. After reviewing ComponentOne it appears they are putting together several methods so I may be an issue with the StringFormat default set for the method. I am kind of assuming they are calling the main DrawString method and passing in default params if one was not supplied.
Also be sure to check the section for
Use LineAlignment to specify the vertical alignment of the string.
in the link below
Link to Method
Well, after further research and experimentation there's definitely a bug in the ComponentOne library. Specifically, the overload I happened to have used here returned the wrong hight. If you specific an available width explicitly, you get the correct height. Specifically, this code generates the correct data:
var resultHeight = _doc.MeasureString(text, pdfFont, double.MaxValue).Height;
var resultWidth = _doc.MeasureString(text, pdfFont).Width;
return new Tuple<double,double>(resultHeight, resultWidth);
Note the addition of the third parameter for the height only -- double.MaxValue. The width is correctly calculated in both cases, but the height is only correctly calculated if you provide that double parameter. I chose double.MaxValue in this case simply because I don't know how wide the string is going to turn out to be so I don't want to risk being given a multi-line height.
I'm trying to do simple drawing in a subclass of a decorator, similar to what they're doing here...
How can I draw a border with squared corners in wpf?
...except with a single-pixel border thickness instead of the two they're using there. However, no matter what I do, WPF decides it needs to do its 'smoothing' (e.g. instead of rendering a single-pixel line, it renders a two-pixel line with each 'half' about 50% of the opacity.) In other words, it's trying to anti-alias the drawing. I do not want anti-aliased drawing. I want to say if I draw a line from 0,0 to 10,0 that I get a single-pixel-wide line that's exactly 10 pixels long without smoothing.
Now I know WPF does that, but I thought that's specifically why they introduced SnapsToDevicePixels and UseLayoutRounding, both of which I've set to 'True' in the XAML. I'm also making sure that the numbers I'm using are actual integers and not fractional numbers, but still I'm not getting the nice, crisp, one-pixel-wide lines I'm hoping for.
Help!!!
Mark
Aaaaah.... got it! WPF considers a line from 0,0 to 10,0 to literally be on that logical line, not the row of pixels as it is in GDI. To better explain, think of the coordinates in WPF being representative of the lines drawn on a piece of graph paper whereas the pixels are the squares those lines make up (assuming 96 DPI that is. You'd need to adjust accordingly if they are different.)
So... to get the drawing to refer to the pixel locations, we need to shift the drawing from the lines themselves to be the center of the pixels (squares on graph paper) so we shift all drawing by 0.5, 0.5 (again, assuming a DPI of 96)
So if it is a 96 DPI setting, simply adding this in the OnRender method worked like a charm...
drawingContext.PushTransform(new TranslateTransform(.5, .5));
Hope this helps others!
M
Have a look at this article: Draw lines exactly on physical device pixels
UPD
Some valuable quotes from the link:
The reason why the lines appear blurry, is that our points are center
points of the lines not edges. With a pen width of 1 the edges are
drawn excactly between two pixels.
A first approach is to round each point to an integer value (snap to a
logical pixel) an give it an offset of half the pen width. This
ensures, that the edges of the line align with logical pixels.
Fortunately the developers of the milcore (MIL stands for media
integration layer, that's WPFs rendering engine) give us a way to
guide the rendering engine to align a logical coordinate excatly on a
physical device pixels. To achieve this, we need to create a
GuidelineSet
protected override void OnRender(DrawingContext drawingContext)
{
Pen pen = new Pen(Brushes.Black, 1);
Rect rect = new Rect(20,20, 50, 60);
double halfPenWidth = pen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(rect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(rect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(null, pen, rect);
drawingContext.Pop();
}
I'm trying to implement a function that takes a System.Drawing.Bitmap object and renders it on a WPF Canvas. The bitmap has to be cropped and joined a few times before rendering.
Environment: WPF application running on .NET 3.5 SP1
Input: System.Drawing.Bitmap object, of size 800x600 and pixel format RGB24
Goal: to display an image which is composed of two stripes of the input bitmap (on one line). The stripes are two bitmap halves - (0,0,800,300) and (0,300,800,600). Later on I want to be able to scale the image up or down.
I've already implemented a solution with GDI and Graphics.DrawImage (that renders into a Bitmap object), but I want to improve performance (this function could be called 30 times per second).
Is there a faster way to implement this with WPF, assuming I want to render the image on a WPF window?
The best solution I found so far is using WriteableBitmap, something like this:
void Init()
{
m_writeableBitmap = new WriteableBitmap(DesiredWidth, DesiredHeight, DesiredDpi, DesiredDpi, PixelFormats.Pbgra32, null);
{
void CopyPixels(System.Drawing.Bitmap frame, Rectangle source, Point destBegin)
{
var bmpData = frame.LockBits(source, ImageLockMode.ReadOnly, frame.PixelFormat);
m_writeableBitmap.Lock();
var dest = new Int32Rect(destBegin.X, destBegin.Y, bmpData.Width, bmpData.Height);
m_writeableBitmap.WritePixels(dest, bmpData.Scan0, bmpData.Stride * bmpData.Height, bmpData.Stride);
m_writeableBitmap.Unlock();
frame.UnlockBits(bmpData);
}
CopyPixels would be called twice for the use case I described in my question (two stripes).