I'm new to game programming. Here I have some sprites, say Mario sprites in a spritesheet. It just 32 x 32 pixel for each sprite. One sprite contain one movement, full body of Mario. Unfortunately, I have to work at least with 800 x 640 display. As you might guess, Mario become look so small in display.
So far, I'm just scale spritesheet in GIMP2 so that Mario doesn't look like ant in screen.
Is there any way to handle it? Maybe Allegro has something I don't know.
I already search it in documentation.
It sounds like you want a way to scale an image within allegro. There are two was to achieve this:
use al_draw_tinted_scaled_rotated_bitmap_region. Pass your scaling factor (e.g. 2.0) as the xscale and yscale arguments.
void al_draw_tinted_scaled_rotated_bitmap_region(ALLEGRO_BITMAP *bitmap,
0, 0, 32, 32, // draw the first 32x32 sprite in the sheet
al_map_rgb(255, 255, 255), // don't tint the sprite
16, 16, // the origin is 16, 16, half the 32x32 size
200, 200, // draw at the point 200, 200 on the display
2.0, 2.0, // scale by 2 in the x and y directions
0, 0); // don't apply any angle or flags
Use a transform to scale your image.
ALLEGRO_TRANSFORM trans;
al_identity_transform(&trans);
al_scale_transform(&trans, 2, 2); // scale by a factor of 2
al_use_transform(&trans);
// draw here
Note that in any case (including your original solution of scaling the spritesheet), scaling an image up will cause it to appear more pixellated.
Related
I am trying to draw an arrow with OpenCV 3.2:
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
int main()
{
Mat image(480, 640, CV_8UC3, Scalar(255, 255, 255)); //White background
Point from(320, 240); //Middle
Point to(639, 240); //Right border
arrowedLine(image, from, to, Vec3b(0, 0, 0), 1, LINE_AA, 0, 0.1);
imshow("Arrow", image);
waitKey(0);
return 0;
}
An arrow is drawn, but at the tip some pixels are missing:
To be more precise, two columns of pixels are not colored correctly (zoomed):
If I disable antialiasing, i.e., if I use
arrowedLine(image, from, to, Vec3b(0, 0, 0), 1, LINE_8, 0, 0.1);
instead (note the LINE_8 instead of LINE_AA), the pixels are there, albeit without antialiasing:
I am aware that antialiasing might rely on neighboring pixels, but it seems strange that pixels are not drawn at all at the borders instead of being drawn without antialiasing. Is there a workaround for this issue?
Increasing the X coordinate, e.g. to 640 or 641) makes the problem worse, i.e., more of the arrow head pixels disappear, while the tip still lacks nearly two complete pixel columns.
Extending and cropping the image would solve the neighboring pixels issue, but in my original use case, where the problem appeared, I cannot enlarge my image, i.e., its size must remain constant.
After a quick review, I've found that OpenCV draws AA lines using a Gaussian filter, which contracts the final image.
As I've suggested in comments, you can implement your own function for the AA mode (you can call the original one if AA is disabled) extending the points manually (see code below to have an idea).
Other option may be to increase the line width when using AA.
You may also simulate the AA effect of OpenCV but on the final image (may be slower but helpful if you have many arrows). I'm not an OpenCV expert so I'll write a general scheme:
// Filter radius, the higher the stronger
const int kRadius = 3;
// Image is extended to fit pixels that are not going to be blurred
Mat blurred(480 + kRadius * 2, 640 + kRadius * 2, CV_8UC3, Scalar(255, 255, 255));
// Points moved a according to filter radius (need testing, but the idea is that)
Point from(320, 240 + kRadius);
Point to(639 + kRadius * 2, 240 + kRadius);
// Extended non-AA arrow
arrowedLine(blurred, ..., LINE_8, ...);
// Simulate AA
GaussianBlur(blurred, blurred, Size(kRadius, kRadius), ...);
// Crop image (be careful, it doesn't copy data)
Mat image = blurred(Rect(kRadius, kRadius, 640, 480));
Another option may be to draw the arrow in an image twice as large and the scale it down with a good smoothing filter.
Obviously, last two options will work only if you don't have any previous data on the image. If so, then use a transparent image for temporal drawing and overlay it at the end.
I am fairly new to Irrlicht, but I am not new to C++. For the last couple of weeks I did alot of Googling, reading Irrlicht API documentations, etc. For some reason I can't seems to be able to create a 3D plane mesh.
Here is what I got so far.
irr::scene::ISceneNode* ground = sceneManager->addMeshSceneNode(plane);
ground->setPosition(irr::core::vector3df(0, 0, 10));
irr::scene::ICameraSceneNode* cam = sceneManager->addCameraSceneNode();
cam->setTarget(ground->getPosition());
sceneManager->addMeshSceneNode(plane);
I also try creating a 3D cube mesh using this method
irr::scene::IMesh* plane = geomentryCreator->createPlaneMesh(irr::core::dimension2d<irr::f32>(100, 100), irr::core::dimension2d<irr::u32>(100, 100));
irr::scene::ISceneNode* cube = sceneManager->addCubeSceneNode(20);
cube->render();
For some reason the screen remain black with nothing rendered. Nothing seems to work. Any suggestions?
Your problem is that the camera and the plane both have the same Y coordinate. You never specified any position for the camera, so it is at the point (0, 0, 0), so its Y coordinate is 0. You also specified the coordinate of the plane to be (0, 0, 10), so its Y coordinate is also 0. Since the Y coordinate is up in Irrlicht, this means that you are looking at the plane from the slice like in this drawing:
This is why you don't see anything. To see something, you have to place the camera higher up. The point (0, 50, 0) will work.
Also, if you don't have any lights in the scene, the plane will be black just like the background, since it's by default sensitive to lighting. To change this, you need to make the plane insensitive to lighting with the following code:
plane->setMaterialFlag(irr::video::EMF_LIGHTING, false);
If the plane's color is black, which it is by default, you will have a black plane on a black background, so you won't see anything. So I suggest you make the background white instead by using this as the beginScene method in the main loop:
driver->beginScene(true, true, irr::video::SColor(255, 255, 255, 255));
Normally with this code, you should be able to see the following screenshot:
irr::IrrlichtDevice *device = irr::createDevice(irr::video::EDT_OPENGL);
irr::video::IVideoDriver *driver = device->getVideoDriver();
irr::scene::ISceneManager *sceneManager = device->getSceneManager();
const irr::scene::IGeometryCreator *geomentryCreator = sceneManager->getGeometryCreator();
irr::scene::IMesh* plane = geomentryCreator->createPlaneMesh(irr::core::dimension2d<irr::f32>(100, 100), irr::core::dimension2d<irr::u32>(100, 100));
irr::scene::ISceneNode* cube = sceneManager->addCubeSceneNode(20);
cube->render();
irr::scene::ISceneNode* ground = sceneManager->addMeshSceneNode(plane);
ground->setPosition(irr::core::vector3df(0, 0, 10));
plane->setMaterialFlag(irr::video::EMF_LIGHTING, false); //This is important
irr::scene::ICameraSceneNode* cam = sceneManager->addCameraSceneNode();
cam->setPosition(irr::core::vector3df(0, 50, 0)); //This is also important
cam->setTarget(ground->getPosition());
sceneManager->addMeshSceneNode(plane);
while(device->run()){
driver->beginScene(true, true, irr::video::SColor(255, 255, 255, 255)); //Important for the background to be white
sceneManager->drawAll();
driver->endScene();
}
I am currently reading image from a decoder and paint each frame of it in a widget.
This is what I am doing now:
paintEvent(...){
...
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setRenderHint(QPainter::HighQualityAntialiasing, false);
QPixmap pmap = QPixmap::fromImage(glImage).scaledToWidth(width());
painter.drawPixmap(0, (height() - pmap.height()) / 2, pmap);
...
}
However, I found it to be computationally expensive...
Is there any solution to this without using the OpenGL in Qt?
You could try to use QPainter::drawImage instead of doing manual conversion between image representation (QImage -> QPixmap). Refering to documentation it should still provide way to scale the image -"Note: The image is scaled to fit the rectangle, if both the image and rectangle size disagree.".
First of all, there is no need to scale your pixmap before painting. You can pass the desired width and height as an argument to painter.drawPixmap. This will scale the image while painting which is (probably) faster.
QPixmap pmap = QPixmap::fromImage(glImage);
int w = width();
// "scaledToWidth"
int h = w * pmap.height() / (double)pmap.width();
painter.drawPixmap(0, (height() - h) / 2, w, h, pmap);
Then, you could try to draw the image directly. Depending on which operating system you are using, this might be slower or faster.
On Windows, for example, QPixmap is internally represented by a QImage anyway. And therefore, QPixmap::fromImage will basically create a (possible unnecessary) copy of that image.
int w = width();
int h = w * glImage.height() / (double)glImage.width();
painter.drawImage(0, (height() - h) / 2, w, h, glImage);
If you draw the image directly, alpha blending can become quite expensive. So if possible, use a pixel format without alpha channel or with premultiplied alpha. (In the premultiplied format the red, green, and blue channels are multiplied by the alpha component divided by 255.) (See also: QImage::Format_ARGB32_Premultiplied is your friend).
Bonus fact: That's basically what QPixmap::fromImage on Windows does. If you pass a QImage with alpha channel to that function, the internal QImage will be stored with premultiplied alpha to optimize render performance. See source code.
I want to draw a rectangle in an image using open cv c++?I read a function called CV::rectangle,can anyone explain how this function works?Or is there any other method which can be used to draw rectangle?
You are right, you can use cv::rectangle.
You should be able to draw something using this code
cv::rectangle( img, cv::Point2f( 10, 10 ), cv::Point2f(100, 100), cv::Scalar( 255, 0, 0 ) );
This will draw a red rectangle starting with top left at (10, 10) and bottom right at (100,100).
This also assumes that img has 3 channels of usigned int type, if the type is different, then you need to change the values in the Scalar.
How can I draw a shape like a tear? I need to draw without using more than one shape (an ellipse and a polygon) because QPen will draw for each shape. I need to join shapes to create a new one, or tell QT to join the border across both shapes, something like this:
If the shape you want to draw can be represented as a layering of other shapes, as with the image you've linked to, it's pretty easy to do:
First we need to build a QPainterPath to represent the outer edge of the shape. We build it by layering up simpler shapes; in the case of your example we need a circle and a square. Note the use of QPainterPath::setFillRule(Qt::WindingFill): this will later affect the way that the path is painted (try removing it to see the difference!).
QPainterPath OuterPath;
OuterPath.setFillRule(Qt::WindingFill);
OuterPath.addEllipse(QPointF(60, 60), 50, 50);
OuterPath.addRect(60, 10, 50, 50);
With the example you've given we'll also need to remove a circular area from the centre of our filled shape. Let's represent that inner 'border' as a QPainterPath and then use QPainterPath::subtracted() to subtract InnerPath from OuterPath and produce our final shape:
QPainterPath InnerPath;
InnerPath.addEllipse(QPointF(60, 60), 20, 20);
QPainterPath FillPath = OuterPath.subtracted(InnerPath);
Once we've built the shape paths, we need to use them to fill/outline the shape. Let's first create a QPainter and set it to use antialiasing:
QPainter Painter(this);
Painter.setRenderHint(QPainter::Antialiasing);
We then need to fill the shape that we've built:
Painter.fillPath(FillPath, Qt::blue);
Finally, let's paint the outlines. Note that, because we have separate paths for the inner and outer borders, we are able to stroke each border with different line thicknesses. Note also the use of QPainterPath::simplified(): this converts the set of layered shapes into one QPainterPath which has no intersections:
Painter.strokePath(OuterPath.simplified(), QPen(Qt::black, 1));
Painter.strokePath(InnerPath, QPen(Qt::black, 3));
If we put all of that together, it looks like this:
void Shape::paintEvent(QPaintEvent *)
{
QPainterPath OuterPath;
OuterPath.setFillRule(Qt::WindingFill);
OuterPath.addEllipse(QPointF(60, 60), 50, 50);
OuterPath.addRect(60, 10, 50, 50);
QPainterPath InnerPath;
InnerPath.addEllipse(QPointF(60, 60), 20, 20);
QPainterPath FillPath = OuterPath.subtracted(InnerPath);
QPainter Painter(this);
Painter.setRenderHint(QPainter::Antialiasing);
Painter.fillPath(FillPath, Qt::blue);
Painter.strokePath(OuterPath.simplified(), QPen(Qt::black, 1));
Painter.strokePath(InnerPath, QPen(Qt::black, 3));
}
This is actually quite difficult to do without a good math background. If you knew the formula to create that shape, you could just put it into your QGraphicsItem::paint() function. But there are some alternatives:
Make the image in a vector editing program like Inkscape (free), save it as a .svg file, and then load it into a QGraphicsSvgItem. (This is what I would do.)
Have a look at QPainterPath::cubicTo(), which allows you to make a Bezier curve