How can i scale sprites in SDL?
SDL doesn't provide scaling functionality directly, but there's an additional library called SDL_gfx which provides rotation and zooming capabilities. There's also another library called Sprig which provides similar features.
You can do scaling if you are getting sprites from a texture with SDL_RenderCopy() but i cannot guarantee you antialiasing.
With function SDL_RenderCopy() you pass 4 params:
a pointer to a renderer (where you are going to renderize).
a pointer to a texture (where you are going to get the sprite).
pointer to source rect(the area and position where you get the sprite on the texture).
and pointer to dest rect(the area and position on the renderer you are going to draw).
You should only modify your dest rect like for example, if you are going to render an image 300 x 300 and you want it scaled, your dest rect should be like 150 x 150 or 72 x 72 or whatever value you wanted to scale.
You haven't provided any code, so I'm going to assume you are using textures and an SDL_Renderer:
When using SDL_RenderCopy() the texture will be stretched to fit the destination SDL_Rect, so if you make the destination SDL_Rect larger or smaller you can perform a simple scaling of the texture.
https://wiki.libsdl.org/SDL_RenderCopy
Solution from Ibrahim CS works.
Let me expand on top of this solution and providing the code.
Another thing to note is to calculate new position (x,y) with top-left is origin to render scaled texture.
I do it like this
// calculate new x and y
int new_x = (x + texture->w/2) - (texture->w/2 * new_scale);
int new_y = (y + texture->h/2) - (texture->h/2 * new_scale);
// form new destination rect
SDL_Rect dest_rect = { new_x, new_y, texture->w * scale, texture->h * scale };
// render
SDL_RenderCopy(renderer, texture, NULL, &dest_rect);
assume that texture is SDL_Texture, and renderer is SDL_Renderer, and you render fully from input texture to destination.
If you use SFML instead then you get a very similar set of cross-platform capabilities but the graphics are hardware accelerated and features such as scaling and rotation come for free, both in terms of needing no additional dependencies and in terms of taking no noticeable CPU time to operate.
Related
I am using SDL2 to create a context for OpenGL. I use SDL_image to load the images, and I bind them to OpenGL textures. But because the coordinate system isn't the same the textures are flipped.
I found two ways to correct this:
Modify the texture after loading it
Advantage: Only done once per texture
Disadvantage: Done using the CPU which slows down the loading of each texture
Apply a rotation of 180° on the Y and Z axis when rendering
Advantage: Using super fast functions
Disadvantage: Needs to be done multiple times per frame
Is there another way to flip back the textures after they have been loaded with SDL_Image? And if not, which method is usually used?
There are a bunch of options. Some that come to mind:
Edit original assets
You can flip the image files upside down with an image processing tool, and use the flipped images as your assets. They will look upside down when viewed in an image viewer, but will then turn out correct when used as textures.
This is the ideal solution if you're in full control of the images. It obviously won't work if you get images from external sources at runtime.
Flip during image load
Some image loading libraries allow you to flip the image during loading. From the documentation of SOIL_image I could find, I did not see this option there. But you might be able to find an alternate library that supports it. And of course you can do this if you write your own image loading.
This is a good solution. The overhead is minimal sice you do the flipping while you're touching the data anyway. One common approach is that you read the data row by row, and store in the texture in the opposite order, using glTexSubImage2D().
Flip between loading and first use
You can create a flipped copy of the texture after you already loaded it. The typical way to do this would be by drawing a screen sized quad while sampling the original texture and rendering to an FBO that has the resulting flipped texture as a rendering target. Or, more elegant, use glBlitFramebuffer().
This is not very appealing because it involves copying the memory. While it should be quite efficient if you let the GPU create the copy, extra copying is always undesirable. Even if it happens only once for each texture, it can increase your startup/loading time.
Apply transformation to texture coordinates
You can apply a transformation to the texture coordinates in either the vertex or fragment shader. You're talking about rotations in your question, but the transformation you need is in fact trivial. You basically just map the y of the texture coordinate to 1.0 - y, and leave the x unchanged.
This adds a small price to shader execution. But the operation is very simple and fast compared to the texture sampling operation it goes along with. In reality, the added overhead is probably insignificant. While I don't think it's very pretty, it's a perfectly fine solution.
Invert the texture coordinates
This is similar to the previous option, but instead of inverting the texture coordinates in the shader, you already specify them inverted in the vertex attribute data.
This is often trivial to do. For example, it is very common to texture quads by using texture coordinates of (0, 0), (1, 0), (0, 1), (1, 1) for the 4 corners. Instead, you simply replace 0 with 1 and 1 with 0 in the second components of the texture coordinates.
Or say you load a model containing texture coordinates from a file. You simply replace each y in the texture coordinates by 1.0f - y during reading, and before storing away the texture coordinates for later rendering.
IMHO, this is often the best solution. It's very simple to do, and has basically no performance penalty.
I would disagree with most of the previous answer's point, except for flipping the image either on load, or before first use.
The reason being that if you are following data driven software development practices, you should never allow code to dictate the nature of data. The software should be designed to support the data accurately. Anything else is not fit for purpose.
Modifying texture coordinates is hackery, despite it's ease of use. What happens if you decide at some later stage, to use a different image library which doesn't flip the image? Now your image will be inverted again during rendering.
Instead, deal with the problem at the source, and flip the image during load or before first use (I advocate on load, as it can be integrated into the code that loads the image via SDL_Image, and therefore is more easily maintained).
To flip an image, I'll post some simple pseudo code which will illustrate how to do it:
function flip_image( char* bytes, int width, int height, int bytes_per_pixel):
char buffer[bytes_per_pixel*width]
for ( i = 0 -> height/2 ) loop
offset = bytes + bytes_per_pixel*width * i
copy row (offset -> offset + bytes_per_pixel*width) -> buffer
offset2 bytes + bytes_per_pixel * height * width;
copy row (offset2 -> offset2 + bytes_per_pixel*width) -> (offset -> offset + bytes_per_pixel*width)
copy row(buffer -> buffer + width * bytes_per_pixel ) -> offset
end loop
Here is a visual illustration of one iteration of this code loop:
Copy current row N to buffer
Copy row (rows - N) to row N
Copy buffer to row (rows - N)
Increment N and repeat until N == rows/2
However, this will only work on images which have an even number of rows, which is fine as opengl doesn't like texture with non-power of two dimensions.
It should also be noted that if the image loaded does not have power of two width, SDL_Image pads it. Therefore, the "width" passed to the function should be the pitch of the image, not it's width.
Im programming a Prototyp of an Tilebased Game to learn SDL.My map is just a 257x257 array of Tiles, each Tile is 1x1 to 60x60 pixels (different zoomings).The SDL Window has a resolution of 1024x768. So i can display 18x13 to 1024*768 Tiles.
Till now i tried 2 approches.
1st: Render from Tiles
//for (at worst) 1024*768 Tiles
SDL_Rect Tile;
SDL_SetRenderDrawColor(gRenderer, /*some color*/ , 255);
Tile = { Tile_size * x, Tile_size * y, Tile_size, Tile_size };
SDL_RenderFillRect(gRenderer, &(Tile));
con: it is way to time consuming and the game starts lagging if i try to move the map.
2nd create an texture before the Game starts
with: SDL_CreateRGBSurface, SDL_FillRect, SDL_CreateTextureFromSurface
con: the Texture would be (257x257)(Tiles)x(60x60)(pixel/Tile)x(32)(bit/pixel) ~ 951 MB. and with multiple Textures for different Zoom steps its way to huge to handle.
I'd appreciate any tips to improve the performance.
The first example just draws a single filled rectangle... That can't be slow, I'd have to see more to give a better answer.
In general you'll want to render only the tiles which are visible on the screen not the tiles on the map itself. With 60x60 tiles you can get away with just using SDL 2d drawing functions then. When you add different zoom levels I'm afraid you won't be able to just use 1x1 pixel tiles and use the same approach - you would try to call every pixel via a function call!
So once you add different zoom levels you'll have to figure out how to get that on screen - and what that's supposed to mean to the player anyway :)
I'm currently writing a simple program using SDL2 where you can drag some shapes (square, circle, triangle, etc) into a canvas and rotate them and move them around. Each shape is represented visually by a SDL texture that is created from a PNG file (using the IMG_LoadTexture function from the SDL_image library).
The thing is that I would like to know whether a certain pixel from the texture is transparent, so that when someone clicks on the image I could determine if I have to do some action (because the click is on the non transparent area) or not.
Because this is some school assignment I'm facing some restrictions, that is, only use SDL2 libraries and I can't have some map where I can look up if the pixel in question is transparent because the images are dinamically selected. Furthermore I thought about using a SDL surface for this task creating them from the original images but due to the fact that the shapes are being rotated through the texture that wouldn't work.
You can accomplish this by using Render Targets.
SDL_SetRenderTarget(renderer, target);
... render your textures rotated, flipped, translated using SDL_RenderCopyEx
SDL_RenderReadPixels(renderer, rect, format, pixels, pitch);
With the last step you read the pixels from the render target using SDL_RenderReadPixels and then you have to figure out if the alpha channel of the desired pixel is zero (transparent) or not. You can read just the one pixel you want from the render target, or the whole texture, which option you take depends on the number of hit tests you have to perform, how often the texture is rotated/moved around, etc.
You need to create your texture using the SDL_TEXTUREACCESS_STREAMING flag and lock your texture before being able to manipulate pixel data. To tell if a certain pixel is transparent in a texture make sure that you call
SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND);
this allows the texture to recognize an alpha channel.
Try something like this:
SDL_Texture *t;
int main()
{
// initialize SDL, window, renderer, texture
int pitch, w, h;
void *pixels;
SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND);
SDL_QueryTexture(t, NULL, &aw, &h);
SDL_LockTexture(t, NULL, &pixels, &pitch);
Uint32 *upixels = (Uint32*) pixels;
// you will need to know the color of the pixel even if it's transparent
Uint32 transparent = SDL_MapRGBA(SDL_GetWindowSurface(window)->format, r, g, b, 0x00);
// manipulate pixels
for (int i = 0; i < w * h; i++)
{
if (upixels[i] == transparent)
// do stuff
}
// replace the old pixels with the new ones
memcpy(pixels, upixels, (pitch / 4) * h);
SDL_UnlockTexture(t);
return 0;
}
If you have any questions please feel free to ask. Although I am no expert on this topic.
For further reading and tutorials, check out http://lazyfoo.net/tutorials/SDL/index.php. Tutorial 40 deals with pixel manipulation specifically.
I apologize if there are any errors in method names (I wrote this off the top of my head).
Hope this helped.
I have a color image along with its depth map which both are captured by Kinect. I want to project it to another location (to see how it looks like in another viewpoint). Since I don't have the intrinsic parameters (camera param.) of the Kinect; How can I implement it?
P.S: I'm writing my codes in C++.
With the depth frame and the color frame you should have enough data to achieve something similar to what you want to do.
In the color frame, you have the color of each pixels.
In the depth frame, you have the distance of each pixels.
(Keep in mind that there is a small gap between the data in the depth frame and the color frame due to the position of each sensor. Have a look at the mapping helper methods : MapDepthFrameToColorFrame)
If you take all the data in both the depth and the color frame, at the same time, you could draw each pixel as a point in a 3-dimensional world. Let's say you have a resolution of 640x480. You'll have a scene drawn in a rectangle of 640 (x = width), 480 (y = height), ~3000 (z = depth). Then you can change the point of view !
The only problem is that you won't have the right scale for the Z axis. If you want a better result, you should also use the SkeletonFrame. Thanks to that you'll have the actual X, Y, and Z values (in meters). Once again you can use the helper method (MapDepthToSkeletonPoint) to get the corresponding skeleton point for each depth point !
If you look at this post, you'll find a video that shows the result, some piece of code in C#, and a project sample that you can reuse.
I am tasked with making a sun/moon object flow across the screen throughout a time-span (as it would in a regular day). One of the options available to me is to use a "billboard", which is a quad that is always facing the camera.
I have yet to use many direct x libraries or techniques. This is my first graphics project. How does this make sense? And how can you use this to move a sun object across a screen?
Thanks :) This will be run on windows machines only and I only have the option for direct x (9).
I have gotten this half working. I have a sun image displaying, but it sits at the front of my screen overtop of 80% of my screen, no matter which way my camera is pointing. I'm looking down towards the ground? Still a huge sun there. Why is this? Here is the code I used to create it...
void Sun::DrawSun()
{
std::wstring hardcoded = L"..\\Data\\sun.png";
m_SunTexture = MyTextureManager::GetInstance()->GetTextureData(hardcoded.c_str()).m_Texture;
LPD3DXSPRITE sprite = NULL;
if (SUCCEEDED(D3DXCreateSprite(MyRenderer::GetInstance()->GetDevice(), &sprite)))
{
//created!
}
sprite->Begin(D3DXSPRITE_ALPHABLEND);
D3DXVECTOR3 pos;
pos.x = 40.0f;
pos.y = 20.0f;
pos.z = 20.0f;
HRESULT someHr;
someHr = sprite->Draw(m_SunTexture, NULL, NULL, &pos, 0xFFFFFFFF);
sprite->End();
}
Obviously, my position vector is hardcoded. Is this what I need to be changing? I have noticed in the documentation the possibility of D3DXSPRITE_BILLBOARD rather than D3DXSPRITE_ALPHABLEND, will this work? Is it possible to use both?
As per the tutorial mentioned in an earlier post, D3DXSPRITE is a 2d object, and probably will not work for displaying within the 3d world? What is a smarter alternative?
The easiest way to do a screen aligned quad is by using Point Sprites with texture rewrite.
I never did that with DirectX but in OpenGL, enabling point sprites is a matter of 2 API calls that look like this.
glEnable(GL_POINT_SPRITE);
glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, true);
With this mode enabled, You draw a single vertex and instead of a point, a screen aligned quad is renderd. The coordinate replace thing means that the rendered quad is processed with texture coordinates. This means that you can place any texture on the quad. Usually you'll want something with an alpha channel to blend into the background seamlessly.
There should be an equivalently easy way to do it in D3D. In addition, if you write a shader, it may allow you do do some additional stuff like discard some of the pixels of the texture.
This tutorial might help
Also, google.
--Edit
To transform a quad to any other shape, use the alpha channel of the texture. If the alpha is 0, the pixel is not visible. You can't add an alpha channel to a JPEG but you can do it to a PNG. Here's an example:
sun with alpha channel http://www.shiny.co.il/shooshx/sun.png
If you'll open this image in photoshop you'll see that the background is invisible.
Using alpha blending might cause some problems if you have other things going on in the scene so if that happens, you can write a simple fragment shader which discards the pixels with alpha==0.
Okay im not a direct x expert so i am going to assume a few things.
First i am assuming you have some sort of DrawQuad() function that takes the 4 corners and a texture inside your rendering class.
First we want to get the current viewport matrix
D3DMATRIX mat;
hr = m_Renderer->GetRenderDevice()->GetTransform(D3DTS_VIEW,&mat);
Lets just set some sort of arbitrary size
float size = 20.0f;
Now we need to calculate two vectors the up unit vector and the right unit vector.
D3DXVECTOR3 rightVect;
D3DXVECTOR3 viewMatrixA(mat._11,mat._21,mat._31);
D3DXVECTOR3 viewMatrixB(mat._12,mat._22,mat._32);
D3DXVec3Normalize(&rightVect, &viewMatrixA);
rightVect = rightVect * size * 0.5f;
D3DXVECTOR3 upVect;
D3DXVec3Normalize(&upVect, &viewMatrixB);
upVect = upVect * size * 0.5f;
Now we need to define a Location for our object, i am just going with the origin.
D3DXVECTOR3 loc(0.0f, 0.0f, 0.0f);
Lets load the sun texture:
m_SunTexture = <insert magic texture load>
Now lets figure out the 4 corners.
D3DXVECTOR3 upperLeft = loc - rightVect;
D3DXVECTOR3 upperRight = loc + upVect;
D3DXVECTOR3 lowerRight = loc-upVect;
D3DXVECTOR3 lowerLeft= loc + rightVect;
Lets draw our quad. I am assuming this function exists otherwise you'll need to do
some vertex drawing.
m_Renderer->DrawQuad(
upperLeft.x, upperLeft.y, upperLeft.z,
upperRight.x, upperRight.y, upperRight.z,
lowerRight.x, lowerRight.y, lowerRight.z,
lowerLeft.x, lowerLeft.y, lowerLeft.z, m_SunTexture);
Enjoy :)!
Yes, a billboard would typically be used for this. It's pretty straightforward to calculate the coordinates of the corners of the billboard and the texture parameters. The texture itself could be rendered using some other technique (in the application in system ram for instance).
It it simple enough to take the right and up vectors of the camera and add/sub those (scaled appropriately) from the centre point of the world-coordinates you want to render the object in.