Why drawString method does not seem to always start at the given coordinates? - drawing

In my code I cannot draw a String at precise coordinates. Its upper left corner does not start at the given coordinates but somewhere else. However if I draw a rectangle from the same given coordinates it is well placed. How on earth can this behaviour be possible ?
Here is my code I call in the beforeShow() method :
Image photoBase = fetchResourceFile().getImage("Voiture_4_3.jpg");
Image watermark = fetchResourceFile().getImage("Watermark.png");
f.setLayout(new LayeredLayout());
final Label drawing = new Label();
f.addComponent(drawing);
// Image mutable dans laquelle on va dessiner (fond blancpar défaut)
Image mutableImage = Image.createImage(photoBase.getWidth(), photoBase.getHeight());
// Paint all the stuff
paintAll(mutableImage.getGraphics(), photoBase, watermark, photoBase.getWidth(), photoBase.getHeight());
drawing.getUnselectedStyle().setBgImage(mutableImage);
drawing.getUnselectedStyle().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
// Save the graphics
// Save the image with the ImageIO class
long time = new Date().getTime();
OutputStream os;
try {
os = Storage.getInstance().createOutputStream("screenshot_" + Long.toString(time) + ".png");
ImageIO.getImageIO().save(mutableImage, os, ImageIO.FORMAT_PNG, 1.0f);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And the paintAll method
public void paintAll(Graphics g, Image background, Image watermark, int width, int height) {
// Full quality
float saveQuality = 1.0f;
// Create image as buffer
Image imageBuffer = Image.createImage(width, height, 0xffffff);
// Create graphics out of image object
Graphics imageGraphics = imageBuffer.getGraphics();
// Do your drawing operations on the graphics from the image
imageGraphics.drawImage(background, 0, 0);
imageGraphics.drawImage(watermark, 0, 0);
imageGraphics.setColor(0xFF0000);
// Upper left corner
imageGraphics.fillRect(0, 0, 10, 10);
// Lower right corner
imageGraphics.setColor(0x00FF00);
imageGraphics.fillRect(width - 10, height - 10, 10, 10);
imageGraphics.setColor(0xFF0000);
Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(220, Font.STYLE_BOLD);
imageGraphics.setFont(f);
// Draw a string right below the M from Mercedes on the car windscreen (measured in Gimp)
int w = 0, h = 0;
imageGraphics.drawString("HelloWorld", w, h);
// Coin haut droit de la string
imageGraphics.setColor(0x0000FF);
imageGraphics.fillRect(w, h, 20, 20);
// Draw the complete image on your Graphics object g (the screen I guess)
g.drawImage(imageBuffer, 0, 0);
}
Result for w = 0, h = 0 (no apparent offset) :
Result for w = 841 , h = 610 (offset appears on both axis : there is an offset between the blue point near Mercedes M on the windscreen and the Hello World String)
EDIT1:
I also read this SO question for Android where it is advised to convert the dpi into pixel. Does it also applies in Codename One ? If so how can I do that ? I tried
Display.getInstance().convertToPixel(measureInMillimeterFromGimp)
without success (I used mm because the javadoc tells that dpi is roughly 1 mm)
Any help would be appreciated,
Cheers

Both g and imageGraphics are the same graphics created twice which might have some implications (not really sure)...
You also set the mutable image to the background of a style before you finished drawing it. I don't know if this will be the reason for the oddities you are seeing but I would suspect that code.

Inspired from Gabriel Hass' answer I finally made it work using another intermediate Image to only write the String at (0 ; 0) and then drawing this image on the the imageBuffer Image now at the right coordinates. It works but to my mind drawString(Image, Coordinates) should directly draw at the given coordinates, shouldn't it #Shai ?
Here is the method paintAll I used to solve my problem (beforeShow code hasn't changed) :
// Full quality
float saveQuality = 1.0f;
String mess = "HelloWorld";
// Create image as buffer
Image imageBuffer = Image.createImage(width, height, 0xffffff);
// Create graphics out of image object
Graphics imageGraphics = imageBuffer.getGraphics();
// Do your drawing operations on the graphics from the image
imageGraphics.drawImage(background, 0, 0);
imageGraphics.drawImage(watermark, 0, 0);
imageGraphics.setColor(0xFF0000);
// Upper left corner
imageGraphics.fillRect(0, 0, 10, 10);
// Lower right corner
imageGraphics.setColor(0x00FF00);
imageGraphics.fillRect(width - 10, height - 10, 10, 10);
// Create an intermediate image just with the message string (will be moved to the right coordinates later)
Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(150, Font.STYLE_BOLD);
// Get the message dimensions
int messWidth = f.stringWidth(mess);
int messHeight = f.getHeight();
Image messageImageBuffer = Image.createImage(messWidth, messHeight, 0xffffff);
Graphics messageImageGraphics = messageImageBuffer.getGraphics();
messageImageGraphics.setColor(0xFF0000);
messageImageGraphics.setFont(f);
// Write the string at (0; 0)
messageImageGraphics.drawString(mess, 0, 0);
// Move the string to its final location right below the M from Mercedes on the car windscreen (measured in Gimp)
int w = 841, h = 610;
imageGraphics.drawImage(messageImageBuffer, w, h);
// This "point" is expected to be on the lower left corner of the M letter from Mercedes and on the upper left corner of the message string
imageGraphics.setColor(0x0000FF);
imageGraphics.fillRect(w, h, 20, 20);
// Draw the complete image on your Graphics object g
g.drawImage(imageBuffer, 0, 0);

Related

Problem with drawing a clock with Direct2D

I am trying to draw a clock with Direct2D. The program correctly gets the current time, however the error is when Direct2D draws the clock, since it seems that the clock is out of date with the current time. To get the current time, use the GetLocalTime() function and then map the values to transform them to an angle, so that they can be drawn. How could I fix it?
...
renderTarget->BeginDraw();
renderTarget->Clear(ColorF(ColorF::Black));
GetLocalTime(&sysTime);
wstring text = L"Hour: " + to_wstring(sysTime.wHour)
+ L"\nMinute: " + to_wstring(sysTime.wMinute)
+ L"\nSecond: " + to_wstring(sysTime.wSecond);
brush->SetColor(ColorF(ColorF::White));
renderTarget->DrawTextW(text.c_str(), text.length(), textFormat, textRect, brush);
D2D1_POINT_2F centerPoint = Point2F(320, 240);
FLOAT hourAngle = map(sysTime.wHour%12, 0, 12, 0, 360);
FLOAT minuteAngle = map(sysTime.wMinute, 0, 60, 0, 360);
FLOAT secondAngle = map(sysTime.wSecond, 0, 60, 0, 360);
brush->SetColor(ColorF(ColorF::DeepPink));
renderTarget->DrawEllipse(D2D1::Ellipse(centerPoint, 150, 150), brush, 5);
renderTarget->SetTransform(Matrix3x2F::Rotation(secondAngle, centerPoint));
brush->SetColor(ColorF(ColorF::Blue));
renderTarget->DrawLine(centerPoint,Point2F(centerPoint.x,centerPoint.y + 150*0.9), brush, 10,lineStrokeStyle);
renderTarget->SetTransform(Matrix3x2F::Rotation(minuteAngle, centerPoint));
brush->SetColor(ColorF(ColorF::White));
renderTarget->DrawLine(centerPoint,Point2F(centerPoint.x,centerPoint.y + 150*0.7), brush, 10, lineStrokeStyle);
renderTarget->SetTransform(Matrix3x2F::Rotation(hourAngle, centerPoint));
brush->SetColor(ColorF(ColorF::GreenYellow));
renderTarget->DrawLine(centerPoint, Point2F(centerPoint.x,centerPoint.y + 150*0.5), brush, 10,lineStrokeStyle);
renderTarget->SetTransform(Matrix3x2F::Identity());
HRESULT hrErr = renderTarget->EndDraw();
if (hrErr != S_OK) {
MessageBox(hWnd, L"Direct2D Error", L"Direct2D Error", MB_OK | MB_ICONERROR);
SafeRelease(&brush);
SafeRelease(&renderTarget);
SafeRelease(&factory);
}
...
Yes, you can use GetLocalTime to get the current local date and time. The problem may be in mapping the time to an angle.
But these can be solved, because there are very detailed examples in MSDN.
Instead of calculating the coordinates for the lines, we can calculate the angle and then apply a rotation transform. The following code shows a function that draws one clock hand. The fAngle parameter gives the angle of the hand, in degrees.
void Scene::DrawClockHand(float fHandLength, float fAngle, float fStrokeWidth)
{
m_pRenderTarget->SetTransform(
D2D1::Matrix3x2F::Rotation(fAngle, m_ellipse.point)
);
// endPoint defines one end of the hand.
D2D_POINT_2F endPoint = D2D1::Point2F(
m_ellipse.point.x,
m_ellipse.point.y - (m_ellipse.radiusY * fHandLength)
);
// Draw a line from the center of the ellipse to endPoint.
m_pRenderTarget->DrawLine(
m_ellipse.point, endPoint, m_pStroke, fStrokeWidth);
}
This code draws a vertical line, starting from the center of the clock face and ending at the point endPoint. The line is rotated around the center of the ellipse by applying a rotation transform. The center point for the rotation is the center of ellipse that forms the clock face.
void Scene::RenderScene()
{
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::SkyBlue));
m_pRenderTarget->FillEllipse(m_ellipse, m_pFill);
m_pRenderTarget->DrawEllipse(m_ellipse, m_pStroke);
// Draw hands
SYSTEMTIME time;
GetLocalTime(&time);
// 60 minutes = 30 degrees, 1 minute = 0.5 degree
const float fHourAngle = (360.0f / 12) * (time.wHour) + (time.wMinute * 0.5f);
const float fMinuteAngle =(360.0f / 60) * (time.wMinute);
DrawClockHand(0.6f, fHourAngle, 6);
DrawClockHand(0.85f, fMinuteAngle, 4);
// Restore the identity transformation.
m_pRenderTarget->SetTransform( D2D1::Matrix3x2F::Identity() );
}
Refer: Drawing Clock Hands
Debug:
The whole code sample:
Direct2D Clock Sample

Gradient color text

What I actually try to achieve:
I'd like to draw text with a gradient vertical color. I found this solution, but it doesn't quite fit for me, as it has black square around the gradient font in my case - don't know how to get rid of it, so I started simple (the irrelevant part) question to understand better the physics of blending and frame buffer in opengl and libgdx
What I was trying to understand, irrelevant to my goal:
I have a texture with a white square on it, I draw it on top of red background. I am trying to draw a green square on top of the white one, the green square partially covers the white one, and partially on top of the red background (see picture below).
My intention is: the white area, that is behind of the green square should be painted in green color, but all red background should not be affected and stayed unchanged (red as it is).
How can I do this?
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class Game extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
private int height;
private int width;
private ShapeRenderer shapeRenderer;
#Override
public void create() {
batch = new SpriteBatch();
img = new Texture("white.png");
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
shapeRenderer = new ShapeRenderer();
shapeRenderer.setAutoShapeType(true);
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, width / 7, height / 4);
batch.end();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_ONE, GL20.GL_SRC_COLOR);
shapeRenderer.begin();
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(Color.GREEN);
shapeRenderer.rect(width / 2 - 100, height / 4 - 50, 200, 200);
shapeRenderer.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
}
#Override
public void dispose() {
batch.dispose();
img.dispose();
}
}
Ideally, the green square should not be transparent anyhow, just should block white where it hides the white area.
The output I'm getting:
Update:
I mark #Xoppa 's answer as correct, as it solves my original question with the following result:
You could indeed use some kind of mask to blend it using a square. For that you can first render the text to the stencil buffer using a custom shader that discards fragments with an alpha value below a certain threshold. After that you can render the square using the stencil function to only affect the fragments "touched" by the text. Note that this does involve multiple render calls though and therefore adds complexity to your calling code as well.
However, you say that you actually just want to render text using gradient. For that you don't need such complex approach and can simply apply the gradient within the same render call.
When you draw text, you actually render many little squares, for each character in the text one square. Each of this square has a textureregion applied that contains the character on a transparent background. If you open the font image (e.g. this is the default), then you'll see this source image.
Just like you can apply a gradient to a normal square, you can also apply a gradient to each of those individual squares that make up the text. There are multiple ways to do that. Which best suits depends on the use-case. For example if you need a horizontal gradient or have multiline text, then you need some additional steps. Since you didn't specify this, I'm going to assume that you want to apply a vertical gradient on a single line of text:
public class MyGdxGame extends ApplicationAdapter {
public static class GradientFont extends BitmapFont {
public static void applyGradient(float[] vertices, int vertexCount, float color1, float color2, float color3, float color4) {
for (int index = 0; index < vertexCount; index += 20) {
vertices[index + SpriteBatch.C1] = color1;
vertices[index + SpriteBatch.C2] = color2;
vertices[index + SpriteBatch.C3] = color3;
vertices[index + SpriteBatch.C4] = color4;
}
}
public GlyphLayout drawGradient(Batch batch, CharSequence str, float x, float y, Color topColor, Color bottomColor) {
BitmapFontCache cache = getCache();
float tc = topColor.toFloatBits();
float bc = bottomColor.toFloatBits();
cache.clear();
GlyphLayout layout = cache.addText(str, x, y);
for (int page = 0; page < cache.getFont().getRegions().size; page++) {
applyGradient(cache.getVertices(page), cache.getVertexCount(page), bc, tc, tc, bc);
}
cache.draw(batch);
return layout;
}
}
SpriteBatch batch;
GradientFont font;
float topColor;
float bottomColor;
#Override
public void create () {
batch = new SpriteBatch();
font = new GradientFont();
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
font.drawGradient(batch, "Hello world", 0, 100, Color.GREEN, Color.BLUE);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
font.dispose();
}
}
Btw, to get better answers you should include the actual problem you are trying to solve, instead of focusing on what you think is the solution. See also: https://stackoverflow.com/help/asking.
You can fake blending by doing some math here's what I came up with:
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
public class CalculatedMask extends Game {
private SpriteBatch batch; // The SpriteBatch to draw the white image
private ShapeRenderer renderer; // The ShapeRenderer to draw the green rectangle
private Texture img; // The texture of the image
private Rectangle imgBounds; // The bounds of the image
private Rectangle squareBounds; // The bounds of the square
private float width; // The width of the screen
private float height; // The height of the screen
private float squareX; // The x position of the green square
private float squareY; // The y position of the green square
private float squareWidth; // The width of the green square
private float squareHeight; // The height of the green square
#Override
public void create() {
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
batch = new SpriteBatch();
renderer = new ShapeRenderer();
renderer.setAutoShapeType(true);
img = new Texture("pixel.png"); // A 1x1 white pixel png
imgBounds = new Rectangle(); // The white image bounds
imgBounds.setPosition(width / 7f, height / 4f); // Position the white image bounds
imgBounds.setSize(400f, 300f); // Scale the white image bounds
calculateRectangle();
}
private void calculateRectangle() {
// Here we define the green rectangle's original position and size
squareBounds = new Rectangle();
squareX = width / 2f - 300f;
squareY = height / 4f - 50f;
squareWidth = 200f;
squareHeight = 200f;
// Adjust green square x position
squareBounds.x = MathUtils.clamp(squareX, imgBounds.x, imgBounds.x + imgBounds.width);
// Adjust green square y position
squareBounds.y = MathUtils.clamp(squareY, imgBounds.y, imgBounds.y + imgBounds.height);
// Adjust green square width
if (squareX < imgBounds.x) {
squareBounds.width = Math.max(squareWidth + squareX - imgBounds.x, 0f);
} else if (squareX + squareWidth > imgBounds.x + imgBounds.width) {
squareBounds.width = Math.max(imgBounds.width - squareX + imgBounds.x, 0f);
} else {
squareBounds.width = squareWidth;
}
// Adjust green square height
if (squareY < imgBounds.y) {
squareBounds.height = Math.max(squareHeight + squareY - imgBounds.y, 0f);
} else if (squareY + squareHeight > imgBounds.y + imgBounds.height) {
squareBounds.height = Math.max(imgBounds.height - squareY + imgBounds.y, 0f);
} else {
squareBounds.height = squareHeight;
}
}
#Override
public void render() {
// Clear previous frame
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Draw the white image
batch.begin();
batch.draw(img, imgBounds.x, imgBounds.y, imgBounds.width, imgBounds.height);
batch.end();
// Draw the green rectangle without affecting background
renderer.begin();
renderer.setColor(Color.GREEN);
// Debug so we can see the real green rectangle
renderer.set(ShapeRenderer.ShapeType.Line);
renderer.rect(squareX, squareY, squareWidth, squareHeight);
// Draw the modified green rectangle
renderer.set(ShapeRenderer.ShapeType.Filled);
renderer.rect(squareBounds.x, squareBounds.y, squareBounds.width, squareBounds.height);
renderer.end();
}
}
And the results are:
And with:
squareX = width / 2f + 100f;
squareY = height / 4f + 150f;

Why a String drawn on a Graphics object change its position depending on the used skin?

If I draw a String onto a Graphics (from a mutable image) in a specific position why does the String position moves (on the Y Axis) depending on the simulator skin that is used ?
public void paints(Graphics g, Image background, Image watermark, int width, int height) {
g.drawImage(background, 0, 0);
g.drawImage(watermark, 0, 0);
g.setColor(0xFF0000);
// Upper left corner
g.fillRect(0, 0, 10, 10);
// Lower right corner
g.setColor(0x00FF00);
g.fillRect(width - 10, height - 10, 10, 10);
g.setColor(0xFF0000);
Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(220, Font.STYLE_BOLD);
g.setFont(f);
// Draw a string right below the M from Mercedes on the car windscreen (measured in Gimp)
g.drawString("HelloWorld",
(int) (848 ),
(int) (610)
);
}
This is the way I save a screenshot programatically with CodenameOne :
Image screenshot = Image.createImage(photoBase.getWidth(), photoBase.getHeight());
f.revalidate();
f.setVisible(true);
drawing.paintComponent(screenshot.getGraphics(), true);
String imageFile = FileSystemStorage.getInstance().getAppHomePath() + "screenshot.png";
try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(imageFile)) {
ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1);
} catch(IOException err) {
err.printStackTrace();
}
And here is the result with the iPhone 6 skin :
And with the Xoom skin :
Thanks a lot to anyone that could give me hints on how to solve this problem and start the String always at the position nevermind the skin (and device) used !
Regards,
Is the "drawing" component on the screen?
If so it will be sized differently based on the specific device you are running on as each device has different densities/resolutions. So elements from the screen will appear in different positions which is what we want normally.

Is there a way to save a Graphics object in Codename One without without taking a screenshot?

The question is in the title. For example how can I save g in a file in the following snippet ?
public void paints(Graphics g, Image background, Image watermark, int width, int height) {
g.drawImage(background, 0, 0);
g.drawImage(watermark, 0, 0);
g.setColor(0xFF0000);
// Upper left corner
g.fillRect(0, 0, 10, 10);
// Lower right corner
g.setColor(0x00FF00);
g.fillRect(width - 10, height - 10, 10, 10);
g.setColor(0xFF0000);
Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(220, Font.STYLE_BOLD);
g.setFont(f);
// Draw a string right below the M from Mercedes on the car windscreen (measured in Gimp)
g.drawString("HelloWorld",
(int) (848 ),
(int) (610)
);
// NOW how can I save g in a file ?
}
The reaseon why I don't want to take a screenshot is because I want to keep the full resolution of g (eg : 2000 x 1500).
I would be so grateful to anyone that can tell me how to do that with Codename one. If not possible then it is already good to know it!
Cheers,
What you could do is to create an Image as buffer, get the graphics object from the image an do all your drawings operations on it. Then draw the whole image to the display and save it as a file:
int height = 2000;
int width = 1500;
float saveQuality = 0.7f;
// Create image as buffer
Image imageBuffer = Image.createImage(width, height, 0xffffff);
// Create graphics out of image object
Graphics imageGraphics = imageBuffer.getGraphics();
// Do your drawing operations on the graphics from the image
imageGraphics.drawWhaterver(...);
// Draw the complete image on your Graphics object g (the screen I guess)
g.drawImage(imageBuffer, w, h);
// Save the image with the ImageIO class
OutputStream os = Storage.getInstance().createOutputStream("storagefilename.png");
ImageIO.getImageIO().save(imageBuffer, os, ImageIO.FORMAT_PNG, saveQuality);
Note, that I have not tested it, but it should work like that.
Graphics is just a proxy to a surface, it has no knowledge or access to the underlying surface to which it is drawing and the reason for that is quite simple. It can draw to a hardware accelerated "surface" where there is physically no underlying image.
This is the case both on iOS and Android where the "screen" is natively drawn and has no buffer.

Drawing points of handwritten stroke using DrawEllipse (GDI+)

I'm working on an application that draws handwritten strokes. Strokes are internally stored as vectors of points and they can be transformed into std::vector<Gdiplus::Point>. Points are so close to each other, that simple drawing of each point should result into an image of continual stroke.
I'm using Graphics.DrawEllipse (GDI+) method to draw these points. Here's the code:
// prepare bitmap:
Bitmap *bitmap = new Gdiplus::Bitmap(w, h, PixelFormat32bppRGB);
Graphics graphics(bitmap);
// draw the white background:
SolidBrush myBrush(Color::White);
graphics.FillRectangle(&myBrush, 0, 0, w, h);
Pen blackPen(Color::Black);
blackPen.SetWidth(1.4f);
// draw stroke:
std::vector<Gdiplus::Point> stroke = getStroke();
for (UINT i = 0; i < stroke.size(); ++i)
{
// draw point:
graphics.DrawEllipse(&blackPen, stroke[i].X, stroke[i].Y, 2, 2);
}
At the end I just save this bitmap as a PNG image and sometimes the following problem occurs:
When I saw this "hole" in my stroke, I decided to draw my points again, but this time, by using ellipse with width and height set to 1 by using redPen with width set to 0.1f. So right after the code above I added the following code:
Pen redPen(Color::Red);
redPen.SetWidth(0.1f);
for (UINT i = 0; i < stroke.size(); ++i)
{
// draw point:
graphics.DrawEllipse(&redPen, stroke[i].X, stroke[i].Y, 1, 1);
}
And the new stoke I've got looked like this:
When I use Graphics.DrawRectangle instead of DrawEllipse while drawing this new red stroke, it never happens that this stroke (drawn by drawing rectangles) would have different width or holes in it:
I can't think of any possible reason, why drawing circles would result into this weird behaviour. How come that stroke is always continual and never deformed in any way when I use Graphics.DrawRectangle?
Could anyone explain, what's going on here? Am I missing something?
By the way I'm using Windows XP (e.g. in case it's a known bug). Any help will be appreciated.
I've made the wrong assumption that if I use Graphics.DrawEllipse to draw a circle with radius equal to 2px with pen of width about 2px, it will result in a filled circle with diameter about 4-5 px being drawn.
But I've found out that I actually can't rely on the width of the pen while drawing a circle this way. This method is meant only for drawing of border of this shape, thus for drawing filled ellipse it's much better to use Graphics.FillEllipse.
Another quite important fact to consider is that both of mentioned functions take as parameters coordinates that specify "upper-left corner of the rectangle that specifies the boundaries of the ellipse", so I should subtract half of the radius from both coordinates to make sure the original coordinates specify the middle of this circle.
Here's the new code:
// draw the white background:
SolidBrush whiteBrush(Color::White);
graphics.FillRectangle(&whiteBrush, 0, 0, w, h);
// draw stroke:
Pen blackBrush(Color::Black);
std::vector<Gdiplus::Point> stroke = getStroke();
for (UINT i = 0; i < stroke.size(); ++i)
graphics.FillEllipse(&blackBrush, stroke[i].X - 2, stroke[i].Y - 2, 4, 4);
// draw original points:
Pen redBrush(Color::Red);
std::vector<Gdiplus::Point> origStroke = getOriginalStroke();
for (UINT i = 0; i < origStroke.size(); ++i)
graphics.FillRectangle(&redBrush, origStroke[i].X, origStroke[i].Y, 1, 1);
which yields following result:
So in case someone will face the same problem as I did, the solution is: