My goal is to draw text in a single layout with certain ranges different sizes and opacities.
The ID2D1RenderTarget::DrawTextLayout method seems to be the way to go.
The documentation for the defaultForegroundBrush parameter:
The brush used to paint any text in textLayout that does not already
have a brush associated with it as a drawing effect (specified by the
IDWriteTextLayout::SetDrawingEffect method).
According to the Remarks section of the IDWriteTextLayout::SetDrawingEffect method,
An ID2D1Brush, such as a color or gradient brush, can be set as a
drawing effect if you are using the ID2D1RenderTarget::DrawTextLayout
to draw text and that brush will be used to draw the specified range
of text.
This drawing effect is associated with the specified range and will be
passed back to the application by way of the callback when the range
is drawn at drawing time.
It sounds like ID2D1RenderTarget::DrawTextLayout will definitely use any brush set by IDWriteTextLayout::SetDrawingEffect. This unmanaged C++ answer seems to corroborate this idea.
However, in practice, DrawTextLayout ignores any SolidColorBrush I set using SetDrawingEffect. I get styles and sizes in the appropriate ranges, but everything is painted using the default brush.
I worked around this by implementing a custom text renderer (gist) which is dead simple and drew exactly what I expected from ID2D1RenderTarget::DrawTextLayout as per the documentation. I would have been satisfied but the performance of a TextRendererBase and DrawGlyphRun are more than 25% slower than ID2D1RenderTarget::DrawTextLayout.
What might be causing this issue? Can I use color as the documentation suggests and still use ID2D1RenderTarget::DrawTextLayout?
instead of:
layout.SetDrawingEffect(myBrush, new TextRange(1, 5));
call it like this:
layout.SetDrawingEffect(myBrush.NativePointer, new TextRange(1, 5));
Related
I'm trying to make something similar to paint. I'm trying to figure out how make different brush styles. Like in Paint 3D you get a certain line fills when using the pen tool vs using the paint brush tool.
I have no idea where to even start. I've spent a good portion of the day looking through documentations, and watching YouTube videos. I'm more lost than when I started. The closest thing I came across was line caps, but that's definitely not what I'm looking for.
!!See the UPDATE below!!
Hans' link should point you in the right direction, namely toward TextureBrushes.
To help you further here a few points to observe:
TextureBrush is a brush, not a pen. So you can't follow a path, like the mouse movements to draw along that curve. Instead, you need to find an area to fill with the brush.
This also implies that you need to decide how and when to trigger the drawing; basic options are by time and/or by distance. Usually, the user can set parameters for these often called 'flow' and 'distance'..
Instead of filling a simple shape and drawing many of those, you can keep adding the shapes to a GraphicsPath and fill that path.
To create a TextureBrush you need a pattern file that has transparency. You can either make some or download them from the web where loads of them are around, many for free.
Most are in the Photoshop Brush format 'abr'; if they are not too recent (<=CS5) you can use abrMate to convert them to png files.
You can load a set of brushes to an ImageList, set up for large enough size (max 256x256) and 32bpp to allow alpha.
Most patterns are black with alpha, so if you want color you need to create a colored version of the current brush image (maybe using a ColorMatrix).
You may also want to change its transparency (best also with the ColorMatrix).
And you will want to change the size to the current brush size.
Update
After doing a few tests I have to retract the original assumption that a TextureBrush is a suitable tool for drawing with textured tips.
It is OK for filling areas, but for drawing free-hand style it will not work properly. There are several reasons..:
one is that the TextureBrush will always tile the pattern in some way, flipped or not and this will always look like you are revealing one big underlying pattern instead of piling paint with several strokes.
Another is that finding the area to fill is rather problematic.
Also, tips may or may not be square but unless you fill with a rectangle there will be gaps.
See here for an example of what you don't want at work.
The solution is really simple and much of the above still applies:
What you do is pretty much regular drawing but in the end, you do a DrawImage with the prepared 'brush' pattern.
Regular drawing involves:
A List<List<Point>> curves that hold all the finished mouse paths
A List<Point> curentCurve for the current path
In the Paint event you draw all the curves and, if it has any points, also the current path.
For drawing with a pattern, it is necessary to also know when to draw which pattern version.
If we make sure not to leak them we can cache the brush patterns..:
Bitmap brushPattern = null;
List<Tuple<Bitmap,List<Point>>> curves = new List<Tuple<Bitmap,List<Point>>>();
Tuple<Bitmap, List<Point>> curCurve = null;
This is a simple/simplistic caching method. For better efficiency you could use a Dictionary<string, Bitmap> with a naming scheme that produces a string from the pattern index, size, color, alpha and maybe a rotation angle; this way each pattern would be stored only once.
Here is an example at work:
A few notes:
In the MouseDown we create a new current curve:
curCurve = new Tuple<Bitmap, List<Point>>(brushPattern, new List<Point>());
curCurve.Item2.Add(e.Location);
In the MouseUp I add the current curve to the curves list:
curves.Add(new Tuple<Bitmap, List<Point>>(curCurve.Item1, curCurve.Item2.ToList()));
Since we want to clear the current curve, we need to copy its points list; this is achieved by the ToList() call!
In the MouseMove we simply add a new point to it:
if (e.Button == MouseButtons.Left)
{
curCurve.Item2.Add(e.Location);
panel1.Invalidate();
}
The Paint goes over all curves including the current one:
for (int c = 0; c < curves.Count; c++)
{
e.Graphics.TranslateTransform(-curves[c].Item1.Width / 2, -curves[c].Item1.Height / 2);
foreach (var p in curves[c].Item2)
e.Graphics.DrawImage(curves[c].Item1, p);
e.Graphics.ResetTransform();
}
if (curCurve != null && curCurve.Item2.Count > 0)
{
e.Graphics.TranslateTransform(-curCurve.Item1.Width / 2, -curCurve.Item1.Height / 2);
foreach (var p in curCurve.Item2)
e.Graphics.DrawImage(curCurve.Item1, p);
e.Graphics.ResetTransform();
}
It makes sure the patterns are drawn centered.
The ListView is set to SmallIcons and its SmallImageList points to a smaller copy of the original ImageList.
It is important to make the Panel Doublebuffered! to avoid flicker!
Update: Instead of a Panel, which is a Container control and not really meant to draw onto you can use a Picturebox or a Label (with Autosize=false); both have the DoubleBuffered property turned on out of the box and support drawing better than Panels do.
Btw: The above quick and dirty example has only 200 (uncommented) lines. Adding brush rotation, preview, a stepping distance, a save button and implementing the brushes cache takes it to 300 lines.
How do I set the background color of a piece of text in a PDF document using iTextSharp without taking a form field?
The answer in this post uses a FormField, which according to me is an overkill and too long-winded a way to do something really simple.
Is there a simple way of coloring the background of a piece of text?
You can use the method SetBackground that is available in the Chunk class. There are two variations of this method: one that takes default padding and one that allows you to change the padding.
If you use the onGenericTag() method on a Chunk, you can draw a custom background (and do much more). For instance: you'd use onGenericTag() if you want to draw a rectangle with rounded corners. See my answer to your duplicate question Draw a rectangle at the *current position* and then get its position coordinates
After some trying, I have come to the conclusion that there are 3 ways to do this other than using the FormField (which is the fourth way and how to do that is already linked in the question):
1) Judging from this answer to another similar question, it appears as though there is no concept of a background color for text in the PDF specification. Therefore, one has to draw a rectangle at an absolute position before drawing the text (at that position).
This is like drawing on a Win32 DeviceContext.
2) You can draw a table and set the background color of the cell in which you want a background color.
3) You can write a chunk. The Chunk class has a method named SetBackground(). This doesn't look very nice because it doesn't let you control the padding around the text and between the borders of the box. You can control how far above the baseline the bottom of the text will appear by calling the chunk.SetTextRise(float f) method but that's about it. Still, it's a fast and easy way to get things done if you don't want too much beautification.
I am using the GraphicsPath.AddString() function, but it draws the text with a little space around the text. Any idea how to draw the string without that padding, only the paths of the text?
My code is like this:
GraphicsPath gp = new GraphicsPath();
gp.AddString(text, font.FontFamily, (int)font.Style, font.Size,
boundsRectangle, format);
g.DrawPath(pen, gp);
What is happening is the under the hood it is probably using Graphics.MeasureString(), from the documentation :
GDI+ adds a small amount (1/6 em) to each end of every string displayed. This 1/6 em allows >for glyphs with overhanging ends (such as italic 'f'), and also gives GDI+ a small amount >of leeway to help with grid fitting expansion.
The default action of DrawString will work against you in displaying adjacent runs:
Firstly the default StringFormat adds an extra 1/6 em at each end of each output;
Secondly, when grid fitted widths are less than designed, the string is allowed to contract
by up to an em.
To avoid these problems:
Always pass MeasureString and DrawString a StringFormat based on the typographic >StringFormat (GenericTypographic).
Set the Graphics TextRenderingHint to TextRenderingHintAntiAlias. This rendering method >uses anti-aliasing and sub-pixel glyph positioning to avoid the need for grid-fitting, and >is thus inherently resolution independent.
So it looks like you should be able to fix this using the correct StringFormat.
I have polygons of various shapes and sizes. They have a solid fill and currently a solid border.
I would like to give the polygons a gradient on their edge to soften them.
So far I've tried using a Pen with a LinearGradientBrush and whilst the effect it produces is very interesting it's most definitely not what I want ;)
I've looked through the System.Drawing.Drawing2D namespace but there didn't seem to be any other classes that would be applicable for this purpose.
I've had a search around and the articles that I can find are mostly about creating borders for rectangles, which are mush easier, or are irrelevant.
So to summarize, does anyone have a way of drawing a gradient border in on a polygon using GDI+?
Perhaps a screen shot of what your previous attempt produced and a mock up of what you would like would help?
Though I suspect the issue you're running into is that the direction and offset of the gradient is consistent throughout the entire shape and does not change with the orientation of the lines of the polygon.
Have you taken a look instead at the PathGradientBrush? (Examples) If you can't achieve the effect using it with a Pen for the stroke of the shape, perhaps you could do it with two "fills" with the first (the border) being slightly larger than the second (the interior).
I think I have done exactly what you're asking for, but in my case I have used it for creating soft shadows on text.
I do the following:
Draw the text (in your case:
polygon) to a Bitmap
Apply a
softening filter on the alpha
channel only
Iterate step 2 as
many times needed to get the desired
gradient width
Finally draw the
result onto the resulting
bitmap/screen
I have seen many other samples out there that draw smooth text on glass. But I can't use them. I need every single label that gets added at runtime to be smooth. I can't just "draw" text onto the screen.
Is this at all possible, and are there and sources around?
Thank you
Take a long at this article http://msdn.microsoft.com/en-us/magazine/cc163435.aspx#S6
It's a bit long but it answers alot of your question and alore more in regards to glass.
but the relevant part for you directly is
One particular gotcha is that
rendering a GDI item in black uses the
bit pattern 0x00000000-which also
happens to be a completely transparent
black if you are using an alpha
channel. This means that if you draw
with a black GDI brush or pen you'll
get a transparent color, not a black
one. The biggest problem this presents
is when you try to use the default
text color in a control of a text
label that sits on the glass area.
Since the default text color is
usually black, the DWM will consider
this to be transparent and the text
will be written in the glass
incorrectly. An example can be seen in
Figure 10. The first line is written
with GDI+, the second is a text label
control using the default color. As
you can see, it's nearly illegible
because it's actually incorrectly
rendered text that shows up as gray,
not black.
Happily, there are a number of ways
around this problem. Using owner-draw
controls is one. Rendering to a bitmap
that has an alpha channel is another.
Fortunately, the easiest way to get
text on controls is to let the .NET
Framework 2.0 use GDI+ for you. This
is easily accomplished by setting the
UseCompatibleTextRendering property on
your controls. By default, this
property is set to false so that
controls written for previous versions
of the .NET Framework will render the
same. But if you set it to true, your
text will come out looking correct.
You can set the property globally with
the
Application.SetUseCompatibleTextRenderingDefault
method.
He also provides example code you can place in your Main()
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
Application.Run(new GlassForm());
}
But I recommend reading the article, It'll clear up alot of what's going on with Aero/Glass
Cheers,
Phyx