I'm getting confused by the way of drawImage and scaledToHeight (or any kind of scaling) is working. Could any of you help me to understand what's going on here?
So I have the fallowing code:
auto cellX = this->parentWidget()->width() / 100;
auto cellY = this->parentWidget()->height() / 100;
QImage icon(dir.absoluteFilePath(m_viewModel.icon));
QImage scaled = icon.scaledToHeight(cellY * 40, Qt::SmoothTransformation);
painter.drawImage(cellX * 20, cellY * 20, scaled);
Now if I understand it correctly this should work as the following:
QImage QImage::scaledToHeight(int height, Qt::TransformationMode mode
= Qt::FastTransformation) const
Returns a scaled copy of the image. The returned image is scaled to
the given height using the specified transformation mode.
This function automatically calculates the width of the image so that
the ratio of the image is preserved.
If the given height is 0 or negative, a null image is returned.
and also
void QPainter::drawImage(int x, int y, const QImage &image, int sx =
0, int sy = 0, int sw = -1, int sh = -1, Qt::ImageConversionFlags
flags = Qt::AutoColor)
This is an overloaded function.
Draws an image at (x, y) by copying a part of image into the paint
device.
(x, y) specifies the top-left point in the paint device that is to be
drawn onto. (sx, sy) specifies the top-left point in image that is to
be drawn. The default is (0, 0).
(sw, sh) specifies the size of the image that is to be drawn. The
default, (0, 0) (and negative) means all the way to the bottom-right
of the image.
So in other words, scaledToHeight is going to return a new image scaled according to that specific height and drawImage is going to draw that specific image started from point X, Y which I'm going to mention down to the end of the image (because it's -1 and -1 default)
QUESTION:
As you could see already my scaled image is strictly dependent on the position of drawImage, why is that? How can I scale and draw my image properly? Or in other words WHY if I will position my image at 0 0 or not will affect how my image looks like?
UPDATE:
I have my Widget Class which looks something like this:
class AC_SpeedLevelController : public QWidget {
Q_OBJECT
protected:
void paintEvent(QPaintEvent *event) override;
private:
AC_ButtonViewModel m_viewModel{};
public:
explicit AC_SpeedLevelController(QWidget *parent);
void setupStyle(const AC_ButtonViewModel &model) override;
};
My paintEvent is going to look like:
void AC_SpeedLevelController::paintEvent(QPaintEvent *event) {
QWidget::paintEvent(event);
QPainter painter(this);
QDir dir(qApp->applicationDirPath());
dir.cd("icons");
auto cellX = this->parentWidget()->width() / 100;
auto cellY = this->parentWidget()->height() / 100;
QImage icon(dir.absoluteFilePath(m_viewModel.icon));
QImage scaled = icon.scaledToHeight(cellY * 20, Qt::SmoothTransformation);
painter.drawImage(0, 0, scaled);
}
Related
I would like to make an application which includes rotation widget inside a circle. I started this with the AnalogClock example in qt. I wouldd like to rotate a quarter pie (quarter circle) instead of gauge. My problem is I cannot locate the pie in the center of my circle.
Below picture, I barely locate the pi in the center of circle but I would like to make the pi bigger than below picture.
enter image description here
I realized that all paintings locate inside of rectangle like below.
enter image description here
When I change the drawPie function parameters rectangle's location change and rotates based on on edge point. I can make the ractange bigger but this time drawPie center changes too like the last visual.
enter image description here
You can check the code below. It is modifed from the AnalogClock example. I would like to draw a quarter pie and make it the rotate endlessly. Any help will be appreciated. If you have a better opinion, I would like hear that too.
AnalogClock::AnalogClock(QWidget *parent) :
QWidget(parent),
ui(new Ui::AnalogClock)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, QOverload<>::of(&AnalogClock::update));
timer->start(1000);
setWindowTitle(tr("Analog Clock"));
resize(200, 200);
}
void AnalogClock::paintEvent(QPaintEvent *)
{
QColor minuteColor(0, 127, 127, 191);
int side = qMin(width(), height());
QTime time = QTime::currentTime();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(width() / 2, height() / 2);
painter.scale(side / 200.0, side / 200.0);
painter.setPen(Qt::NoPen);
qreal radius=10;
qreal startAngle=0;
qreal span=60;
for (int i = 0; i < 12; ++i) {
painter.drawLine(88, 0, 96, 0);
painter.rotate(30.0);
}
painter.setBrush(minuteColor);
painter.save();
painter.rotate(6.0 * time.second() );
//painter.drawConvexPolygon(minuteHand, 3);
QRect rect( -radius, -radius, radius*10, radius*10);
painter.drawPie( rect, startAngle*16, span*16 );
// painter.fillRect(rect,QBrush(Qt::green));
painter.restore();
painter.setPen(minuteColor);
for (int j = 0; j < 60; ++j) {
painter.drawLine(92, 0, 96, 0);
painter.rotate(6.0);
}
}
The drawPie() function takes a rectangle that defines the hypothetical full circle in which the pie (slice) will be painted. In this example, we want that circle to be the same as the full clock circle - same size, same center.
Now the code has normalized the coordinate system for us, so that the clock center point is at (0, 0), and the radius is 100. So we need a rectangle that has its center point in (0,0) and sides that are 2*radius long. That is:
QRect rect(-100, -100, 200, 200);
Changing the rect in the code sample to that fixes the basic issue.
Reported to Qt as a bug: https://bugreports.qt.io/browse/QTBUG-93475
I am re-drawing a QPixmap multiple times in different locations, with differnt rotations by transforming the QPainter. In certain situations the QPixmap is not drawing correctly. The GIF below shows my initial discovery of this issue with a QPixmap containing a green cylinder, notice how the rendering behaves as expected to the left of the GIF, but there is a boundary beyond which the rendering is incorrect. The QPixmap content appears to stick in place, and the pixels at the edge appear to smear out accross the rest of the pixmap. In the GIF there is a magenta background to the QPixmap, this because the targetRect used by QPainter::drawPixmap() is also beuing used to seperately fill a rectangle underneath the pixmap, this was because I wanted to check that the target rect was being computed correctly.
Minimum reproducable example:
To keep things simple I am simply filling the QPixmap with magenta pixels, with a 1 pixel wide transparent edge so that the smearing causes the pixmaps to dissapear completely. It doesn't show the image "sticking" in place but it clearly shows the boundary as beyond it the pixmaps seem to dissapear.
I have been experimenting with this myself and I believe this to be entirely caused by the rotating of the QPainter.
The angle of rotation seems to have an effect, if all of the pixmaps are rotated to the same angle then the boundary changes from a fuzzy diagonal line (where fuzzy means the boundary for dissapearing is different for each pixmap) to a sharp 90 degree corner (where sharp means that the boundary for dissapearing is the same for all pixmaps).
The range of different angles also seems to play a part, if the randomly generated angles are in a small 10 degree range, then the boundary is just a slightly fuzzier right angle, with a bevelled corner. There seems to be a progression from sharp right angle to fuzzy diagonal line as the number of different rotations is applied.
Code
QtTestBed/pro:
QT += widgets
CONFIG += c++17
CONFIG -= app_bundle
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
MainWindow.cpp \
main.cpp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
MainWindow.h
main.cpp:
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QPainter>
#include <random>
class MainWindow : public QWidget {
Q_OBJECT
public:
MainWindow();
void wheelEvent(QWheelEvent* event) override;
void mouseReleaseEvent(QMouseEvent* /*event*/) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void resizeEvent(QResizeEvent* /*event*/) override;
void paintEvent(QPaintEvent* event) override;
private:
struct PixmapLocation {
QPointF location_;
qreal rotation_;
qreal radius_;
};
QPixmap pixmap_;
std::vector<PixmapLocation> drawLocations_;
qreal panX_ = 0.0;
qreal panY_ = 0.0;
qreal scale_ = 1.0;
bool dragging_ = false;
qreal dragX_ = 0.0;
qreal dragY_ = 0.0;
QPointF transformWindowToSimCoords(const QPointF& local) const;
QPointF transformSimToWindowCoords(const QPointF& sim) const;
static qreal randomNumber(qreal min, qreal max);
};
#endif // MAINWINDOW_H
MainWindow.cpp:
#include "MainWindow.h"
MainWindow::MainWindow()
: pixmap_(30, 50)
{
setAutoFillBackground(true);
constexpr int count = 10000;
constexpr qreal area = 10000.0;
constexpr qreal size = 44.0;
for (int i = 0; i < count; ++i) {
// qreal rotation = 0.0; // No rotation fixes the issue
// qreal rotation = 360.0; // No rotation fixes the issue
// qreal rotation = 180.0; // Mirroring also fixes the issue
// qreal rotation = 90.0; // The boundary is now a corner, and has a sharp edge (i.e. all images dissapear at the same point)
// qreal rotation = 0.1; // The boundary is now a corner, and has a sharp edge (i.e. all images dissapear at the same point)
// qreal rotation = randomNumber(0.0, 10.0); // The boundary is still a corner, with a bevel, with a fuzzy edge (i.e. not all images dissapear at the same point)
qreal rotation = randomNumber(0.0, 360.0); // The boundary appears to be a diagonal line with a fuzzy edge (i.e. not all images dissapear at the same point)
drawLocations_.push_back(PixmapLocation{ QPointF(randomNumber(-area, area), randomNumber(-area, area)), rotation, size });
}
// Make edges transparent (the middle will be drawn over)
pixmap_.fill(QColor::fromRgba(0x000000FF));
/*
* Fill with magenta almost up to the edge
*
* The transparent edge is required to see the effect, the misdrawn pixmaps
* appear to be a smear of the edge closest to the boundary between proper
* rendering and misrendering. If the pixmap is a solid block of colour then
* the effect is masked by the fact that the smeared edge looks the same as
* the correctly drawn pixmap.
*/
QPainter p(&pixmap_);
p.setPen(Qt::NoPen);
constexpr int inset = 1;
p.fillRect(pixmap_.rect().adjusted(inset, inset, -inset, -inset), Qt::magenta);
update();
}
void MainWindow::wheelEvent(QWheelEvent* event)
{
double d = 1.0 + (0.001 * double(event->angleDelta().y()));
scale_ *= d;
update();
}
void MainWindow::mouseReleaseEvent(QMouseEvent*)
{
dragging_ = false;
}
void MainWindow::mousePressEvent(QMouseEvent* event)
{
dragging_ = true;
dragX_ = event->pos().x();
dragY_ = event->pos().y();
}
void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
if (dragging_) {
panX_ += ((event->pos().x() - dragX_) / scale_);
panY_ += ((event->pos().y() - dragY_) / scale_);
dragX_ = event->pos().x();
dragY_ = event->pos().y();
update();
}
}
void MainWindow::resizeEvent(QResizeEvent*)
{
update();
}
void MainWindow::paintEvent(QPaintEvent* event)
{
QPainter paint(this);
paint.setClipRegion(event->region());
paint.translate(width() / 2, height() / 2);
paint.scale(scale_, scale_);
paint.translate(panX_, panY_);
for (const PixmapLocation& entity : drawLocations_) {
paint.save();
QPointF centre = entity.location_;
const qreal scale = (entity.radius_ * 2) / std::max(pixmap_.width(), pixmap_.height());
QRectF targetRect(QPointF(0, 0), pixmap_.size() * scale);
targetRect.translate(centre - QPointF(targetRect.width() / 2, targetRect.height() / 2));
// Rotate our pixmap
paint.translate(centre);
paint.rotate(entity.rotation_);
paint.translate(-centre);
// paint.setClipping(false); // This doesn't fix it so it isn't clipping
paint.drawPixmap(targetRect, pixmap_, QRectF(pixmap_.rect()));
// paint.setClipping(true); // This doesn't fix it so it isn't clipping
paint.restore();
}
}
QPointF MainWindow::transformWindowToSimCoords(const QPointF& local) const
{
qreal x = local.x();
qreal y = local.y();
// Sim is centred on screen
x -= (width() / 2);
y -= (height() / 2);
// Sim is scaled
x /= scale_;
y /= scale_;
// Sim is transformed
x -= panX_;
y -= panY_;
return { x, y };
}
QPointF MainWindow::transformSimToWindowCoords(const QPointF& sim) const
{
qreal x = sim.x();
qreal y = sim.y();
// Sim is transformed
x += panX_;
y += panY_;
// Sim is scaled
x *= scale_;
y *= scale_;
// Sim is centred on screen
x += (width() / 2);
y += (height() / 2);
return { x, y };
}
qreal MainWindow::randomNumber(qreal min, qreal max)
{
static std::mt19937 entropy = std::mt19937();
std::uniform_real_distribution<qreal> distribution{ min, max };
// distribution.param(typename decltype(distribution)::param_type(min, max));
return distribution(entropy);
}
My research into the issue
Top left above shows all pixmaps drawing correctly, at random angles
Top right above shows the same instance as top left, panned so that the pixmaps are over the fuzzy boundary, with the bottom rightmost pixmaps not being drawn (or to be more accurate, are being drawn as entirely transparent pixels, due to the transparent edge being smeared over the entire image)
Bottom left shows all pixmaps being rotated by 0.1 degree, this leads to a sharp boundary, which when the square is panned to overlap it, clips the square to a rectangle.
Bottom right shows a small range of random angles, between 0.0 and 10.0, this leads to a slightly fuzzier, but still vertical edge, this looks similar to bottom left, but as well as the sharp clipped edge, there is also a slight gradient effect as some of the pixmaps closer to the edge have also not be rendered correctly.
I have tried turning clipping off in the QPainter when drawing the pixmaps, this has had no effect.
I have tried seperately saving a copy of the QPainters transform and setting it back afterwards, this had no effect.
I have tried upgrading to Qt 6.0.3 (which claimed to have solved a number of graphical bugs), issue still present.
The absolute coordinates don't matter, I can offset all of the locations by QPointF(-10000, 10000), pan over to them and the dissapearing point is in the same relative position in the window.
To see the bug in action, scroll out, then click and drag in the window to move the pixmaps to the lower right of the screen, depending on how far out you have zoomed, a number of the pixmaps will no longer be drawn.
Update
I have also discovered that making the original QPixmap larger makes the issue worse, i.e. the boundary becomes apparent at more zoomed in levels, plus further rendering abberations occur. Note that they are still being scaled down to the same size as before, there are just more source pixels.
I changed pixmap_(30, 50) to pixmap_(300, 500)
The image above shows that when panning to move the pixmaps towards the bottom right, they dissapear sooner than before (i.e. while zoomed further in and more towards the top left), the curved arrow indicates the movement of the pixmaps drawn in an arc beyond the dissapearance boundary, they seem to be moving faster than the correct pixmaps are drawn as they are moved.
EDIT: Closer inspection shows that the apparent circular motion is not real, the order in which pixmaps are appearing and dissapearing just made it look that way. With the below update, you can see that there are concentric rings where the pixmaps that have dissapeard re-appear (very briefly) in the correct place, but the re-appeaance is only for a thin window that seems to be narrower than the size of the pixmap, so the content that is drawn appears again to be stuck in place, but the part shown is clipped.
Update
To see the pixmaps "stick" at the boundary, you can adjust the contents of MainWindow::MainWindow() to
MainWindow::MainWindow()
: pixmap_(500, 500)
{
setAutoFillBackground(true);
constexpr int count = 10000;
constexpr qreal area = 10000.0;
constexpr qreal size = 44.0;
for (int i = 0; i < count; ++i) {
qreal rotation = randomNumber(0.0, 360.0);
drawLocations_.push_back(PixmapLocation{ QPointF(randomNumber(-area, area), randomNumber(-area, area)), rotation, size });
}
// Make edges transparent (the middle will be drawn over)
pixmap_.fill(QColor::fromRgba(0x000000FF));
/*
* Fill with magenta almost up to the edge
*
* The transparent edge is required to see the effect, the misdrawn pixmaps
* appear to be a smear of the edge closest to the boundary between proper
* rendering and misrendering. If the pixmap is a solid block of colour then
* the effect is masked by the fact that the smeared edge looks the same as
* the correctly drawn pixmap.
*/
QPainter p(&pixmap_);
p.setPen(Qt::NoPen);
constexpr int smallInset = 1;
const int bigInset = std::min(pixmap_.width(), pixmap_.height()) / 5;
p.fillRect(pixmap_.rect().adjusted(smallInset, smallInset, -smallInset, -smallInset), Qt::magenta);
p.fillRect(pixmap_.rect().adjusted(bigInset, bigInset, -bigInset, -bigInset), Qt::green);
update();
}
Which results in see right hand edge for squares that appear to have been clipped. When moving them around, the square seems to get stuck in place and instead of the edge at the boundary dissapearing, the edge furthest from the boundary dissapears first.
This issue is very interesting. As far as I could test, your code looks good, I feel like this is a Qt bug, and I think you need to report it to Qt: https://bugreports.qt.io/. You should post a single piece of code to illustrate the issue, your second one from your "Update" edit is good: it makes it easy to reproduce the issu. Maybe you should also post a small video to illustrate how things are getting wrong when you zoom in/out or move the area with the mouse.
I tried some alternatives to hopefully find a workaround, but I found none:
Tried to use a QImage rather than a QPixmap, same issue
Tried to load the pixmap from a frozen png/qrc file, same issue
Tried to use QTransform to play with scale/translation/rotation, same issue
Tried Linux and Windows 10: same issue observed
Note that:
If you don't rotate (comment paint.rotate(entity.rotation_);), the issue is not visible
If your pixmap is a simple mono-colored square (simply fill your pixmap with a single color using pixmap_.fill(QColor::fromRgba(0x12345600));), the issue is not visible anymore. That's the most surprising, looks like a pixel from the image is being reused as background and messes things up but if all the image pixels are the same it does not lead to any display issue.
Workaround proposed by the Qt team
"The issue can easily be worked around by enabling the SmoothPixmapTransform render hint on the painter"
I'm trying to draw a 3-pixel large point with QPainter. But the following code instead draws a horizontal line with width of 3 pixels.
#include <QPainter>
#include <QImage>
int main()
{
const int w=1000, h=1000;
QImage img(w, h, QImage::Format_RGBX8888);
{
QPainter p(&img);
p.fillRect(0,0,w,h,Qt::black);
p.scale(w,h);
p.setPen(QPen(Qt::red, 3./w, Qt::SolidLine, Qt::RoundCap));
p.drawPoint(QPointF(0.1,0.1));
}
img.save("test.png");
}
Here's the top left corner of the resulting image:
I am expecting to get a point which is red circle, or at least a square — but instead I get this line segment. If I comment out p.scale(w,h) and draw the point with width 3 (instead of 3./w) at position (100,100), then I get a small mostly symmetric 3-pixel in height and width point.
What's going on? Why do I get a line segment instead of point as expected? And how to fix it, without resorting to drawing an ellipse or to avoiding QPainter::scale?
I'm using Qt 5.10.0 on Linux x86 with g++ 5.5.0. The same happens on Qt 5.5.1.
It appears that QPaintEngineEx::drawPoints renders points as line segments of length 1/63.. See the following code from qtbase/src/gui/painting/qpaintengineex.cpp in the Qt sources:
void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
{
QPen pen = state()->pen;
if (pen.capStyle() == Qt::FlatCap)
pen.setCapStyle(Qt::SquareCap);
if (pen.brush().isOpaque()) {
while (pointCount > 0) {
int count = qMin(pointCount, 16);
qreal pts[64];
int oset = -1;
for (int i=0; i<count; ++i) {
pts[++oset] = points[i].x();
pts[++oset] = points[i].y();
pts[++oset] = points[i].x() + 1/63.;
pts[++oset] = points[i].y();
}
QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
stroke(path, pen);
pointCount -= 16;
points += 16;
}
} else {
for (int i=0; i<pointCount; ++i) {
qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + qreal(1/63.), points[i].y() };
QVectorPath path(pts, 2, 0);
stroke(path, pen);
}
}
}
Notice the pts[++oset] = points[i].x() + 1/63.; line in the opaque brush branch. This is the second vertex of the path — shifted with respect to the desired position of the point.
This explains why the line extends to the right of the position requested and why it depends on the scale. So, it seems the code in the OP isn't wrong for an ideal QPainter implementation, but just has come across a Qt bug (be it in the implementation of the method or in its documentation).
So the conclusion: one has to work around this problem by either using different scale, or drawing ellipses, or drawing line segments with much smaller lengths than what QPainter::drawPoints does.
I've reported this as QTBUG-70409.
Though I have not been able to pin point why exactly the problem occurs, I have got relatively close to the solution. The problem lies with scaling. I did a lots of trial and error with different scaling and width of point with the below code.
const int w=500, h=500;
const int scale = 100;
float xPos = 250;
float yPos = 250;
float widthF = 5;
QImage img(w, h, QImage::Format_RGBX8888);
{
QPainter p(&img);
p.setRenderHints(QPainter::Antialiasing);
p.fillRect(0,0,w,h,Qt::black);
p.scale(scale, scale);
p.setPen(QPen(Qt::red, widthF/(scale), Qt::SolidLine, Qt::RoundCap));
p.drawPoint(QPointF(xPos/scale, yPos/scale));
}
img.save("test.png");
The above code produces the image
My observations are
1) Due to high scaling, the point (which is just 3 pixel wide) is not able to scale proportionally at lower width (width of point), if you set width as something like 30 the round shape is visible.
2) If you want to keep the width of point low then you have to decrease the scaling.
Sadly I can not explain why at high scaling it is not expanding proportionally.
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);
I've got a class: mySquare which inherits from QGraphicsRectItem
added only my constructor, painter and an animation:
ANIMATION:
void mySquare::animation(mySquare *k)
{
QTimeLine *timeLine = new QTimeLine();
timeLine->setLoopCount(1);
QGraphicsItemAnimation *animation = new QGraphicsItemAnimation();
animation->setItem(k);
animation->setTimeLine(timeLine);
int value = 30;
animation->setTranslationAt(0.3, value, value);
timeLine->start();
// (*)
// x += 30;
// y += 30;
}
PAINTER:
void Klocek::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *widget)
{
bokKwadratu = (min(widget->width(), widget->height()))/5;
setRect(x * 30, y * 30, 30 - 3, 30 - 3);
QRectF rect = boundingRect();
painter->setBrush(brush);
painter->setPen(pen);
QFont font;
font.setPixelSize(bokKwadratu/3);
painter->setFont(font);
painter->drawRect(rect);
painter->drawText(rect,Qt::AlignCenter, QString::number(wartosc));
}
CONSTRUCTOR:
mySquare::mySquare(qreal x, qreal y) : QGraphicsRectItem(x * 10, y * 10, 10, 10)
{
setAcceptHoverEvents(true);
this->x = x;
this->y = y;
pen.setColor(Qt::red);
pen.setWidth(2);
brush.setColor(Qt::blue);
brush.setStyle(Qt::SolidPattern);
}
after performing animation (translation) I need to change the object coordinates so they are compatible which the situation on the screen. In other words after the translation (30, 30) I want the coordinates of the rectangle to be change (x += 30, y += 30)
my problem is that when i try to do this ( (*) fragment in the code) the triangle is put far away from its position (just as if the translation was performed twice)
My question is how to translate it and change the coordinates without such complications.
To begin with, I think you're misunderstanding the use of the function setTranslationAt in the QGraphicsItem Animation.
An animation has a normalised value over time, so can start at 0.0 and end at 1.0 (or the reverse). Therefore, by calling
animation->setTranslationAt(0.3, value, value);
You've stated that at the point when the normalised value reaches 0.3, you want the x and y position to be set to 'value'. That's fine, but you also need to set other values for the animation to occur (especially at the val of 1.0!). If you use a for loop, you can iterate through values from 0.0 to 1.0 and set where you want the position of your item to be. Take a look at the example code in the Qt help files for QGraphicsItemAnimation. The QGraphicsItemAnimation uses interpolation to work out the position of your object between the known points that you've given it. If you're interested: -
http://en.wikipedia.org/wiki/Linear_interpolation
Secondly, the item's rect is the definition of the item in its local coordinate space. So if you wanted a rect with its axis in the centre, you'd define it with x,y,w,h of (-w/2, -h/2, w, h). As these are local coordinates, they then get mapped into world coordinates in the GraphicsScene, which is where you set its actual position in the world.
Once you've setup your QGraphicsItemRect's local coordinates and world position, you can then simply paint it with drawRect and should not be setting positions in the paint function.