SDL_Surface Transparency Issues - c++

I have been working with sdl ttf, Now, I'm creating a text renderer function for multiline text rendering.
I am creating a surface for each line, blitting in in a "global" surface, and then converting that "global" surface to a texture.
Actually My problem is the following: If I let the global surface transparent, the blitting does not works (It shows an empty surface), If I set that SDL_FillRect, and then do the SDL_SetColorKey, does not works.
I tried to do that with a per-pixel method, it works, but it's not the result expected.
What I need actually is: Blit surfaces (Text rendered surfaces with TTF_RenderText_Blended), blit them inside a SDL_Surface with a transparent background, that's all.
SDL_Surface * surfed_texture = SDL_CreateRGBSurface(SDL_SWSURFACE,surface_width,surface_height,32,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
Uint32 tormv = SDL_MapRGB(surfed_texture->format,255,0,255);
int possiblecolors[] = { 255,0,255,255,255,255 };
int in_pc = 0;
if(color.r == possiblecolors[0] && color.g == possiblecolors[1] && color.b == possiblecolors[2]){
in_pc=3;
}
/*SDL_FillRect(surfed_texture,NULL,SDL_MapRGB(surfed_texture->format,possiblecolors[in_pc],possiblecolors[in_pc+1],possiblecolors[in_pc+2]));
SDL_SetColorKey(surfed_texture, SDL_SRCCOLORKEY, SDL_MapRGB(surfed_texture->format,possiblecolors[in_pc],possiblecolors[in_pc+1],possiblecolors[in_pc+2]));
*/
SDL_Surface * temporal = NULL;
for(int i=0;i<(int)buff_split.size();i++){
const char* c_text = buff_split.at(i).c_str();
temporal = TTF_RenderText_Blended(font,c_text,color);
int w_fo;
int h_fo;
TTF_SizeText(font,c_text,&w_fo,&h_fo);
SDL_Rect rct;
rct.y=i*lineSkip;
if(txt_align==ALIGN_LEFT){
rct.x=0;
}else if(txt_align==ALIGN_CENTER){
rct.x = surface_width/2 - w_fo/2;
}else if(txt_align==ALIGN_RIGHT){
rct.x = surface_width - w_fo;
}
rct.w=0;
rct.h=0;
// Blit surface
SDL_BlitSurface(temporal,NULL,surfed_texture,&rct);
SDL_FreeSurface(temporal);
}
Blitting into "surfed_texture" with transparent background = Does not shows the blitted surface.
Per-Pixel method: Does not removes all the background.
And the SDL_SetColorKey is not working! (I already tried SDL_DisplayFormatAlpha and stills not working).
Some help?

The solution is the following:
Use the SDL_SetAlpha NOT in the "global" surface, apply a SDL_SetAlpha to each "temporal" surface with no flags, and with a value of 0:
SDL_SetAlpha(temporal,NULL,0);
Now it's working fine!

Related

SDL2 load certain texture make SDL_RenderFillRect color weird

I made a program that has two different states, one is for menu display-"Menu State", and the other state is for drawing some stuff-"Draw State".
But I came across a weird thing, if i load certain png for texture and copy them to renderer to display , then leave "Menu State" to enter "Draw State". The texture will somehow make the rectangle color not display properly (for example make green go dark).
In my code, switching to a new state(invoke MenuState::onExit()) will erase the texture map(map of texture smart pointer indexing with std::string)
So the texutre loaded doesn't even exist in the "Drawing State".
I couldn't figure out what went wrong. Here is some of my codes
void TextureManager::DrawPixel(int x, int y, int width, int height, SDL_Renderer *pRenderer)
{
SDL_Rect rect;
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
SDL_SetRenderDrawColor(pRenderer, 0, 255, 0, 255);//same color value
SDL_RenderFillRect(pRenderer, &rect);
}
static bool TextureManagerLoadFile(std::string filename, std::string id)
{
return TextureManager::Instance().Load(filename, id, Game::Instance().GetRenderer());
}
bool TextureManager::Load(std::string filename, std::string id, SDL_Renderer *pRenderer)
{
if(m_textureMap.count(id) != 0)
{
return false;
}
SDL_Surface *pTempSurface = IMG_Load(filename.c_str());
SDL_Texture *pTexutre = SDL_CreateTextureFromSurface(pRenderer, pTempSurface);
SDL_FreeSurface(pTempSurface);
if(pTexutre != 0)
{
m_textureMap[id] = std::make_unique<textureData>(pTexutre, 0, 0);
SDL_QueryTexture(pTexutre, NULL, NULL, &m_textureMap[id]->width, &m_textureMap[id]->height);
return true;
}
return false;
}
void TextureManager::ClearFromTextureMap(std::string textureID)
{
m_textureMap.erase(textureID);
}
bool MenuState::onEnter()
{
if(!TextureManagerLoadFile("assets/Main menu/BTN PLAY.png", "play_button"))
{
return false;
}
if(!TextureManagerLoadFile("assets/Main menu/BTN Exit.png", "exit_button"))
//replace different png file here will also affect the outcome
{
return false;
}
if(!TextureManagerLoadFile("assets/Main menu/BTN SETTINGS.png", "setting_button"))
{
return false;
}
int client_w,client_h;
SDL_GetWindowSize(Game::Instance().GetClientWindow(),&client_w, &client_h);
int playBtn_w = TextureManager::Instance().GetTextureWidth("play_button");
int playBtn_h = TextureManager::Instance().GetTuextureHeight("play_button");
int center_x = (client_w - playBtn_w) / 2;
int center_y = (client_h - playBtn_h) / 2;
ParamsLoader pPlayParams(center_x, center_y, playBtn_w, playBtn_h, "play_button");
int settingBtn_w = TextureManager::Instance().GetTextureWidth("setting_button");
int settingBtn_h = TextureManager::Instance().GetTuextureHeight("setting_button");
ParamsLoader pSettingParams(center_x , center_y + (playBtn_h + settingBtn_h) / 2, settingBtn_w, settingBtn_h, "setting_button");
int exitBtn_w = TextureManager::Instance().GetTextureWidth("exit_button");
int exitBtn_h = TextureManager::Instance().GetTuextureHeight("exit_button");
ParamsLoader pExitParams(10, 10, exitBtn_w, exitBtn_h, "exit_button");
m_gameObjects.push_back(std::make_shared<MenuUIObject>(&pPlayParams, s_menuToPlay));
m_gameObjects.push_back(std::make_shared<MenuUIObject>(&pSettingParams, s_menuToPlay));
m_gameObjects.push_back(std::make_shared<MenuUIObject>(&pExitParams, s_menuExit));
//change order of the 3 line code above
//or replace different png for exit button, will make the rectangle color different
std::cout << "Entering Menu State" << std::endl;
return true;
}
bool MenuState::onExit()
{
for(auto i : m_gameObjects)
{
i->Clean();
}
m_gameObjects.clear();
TextureManager::Instance().ClearFromTextureMap("play_button");
TextureManager::Instance().ClearFromTextureMap("exit_button");
TextureManager::Instance().ClearFromTextureMap("setting_button");
std::cout << "Exiting Menu State" << std::endl;
return true;
}
void Game::Render()
{
SDL_SetRenderDrawColor(m_pRenderer, 255, 255, 255, 255);
SDL_RenderClear(m_pRenderer);
m_pGameStateMachine->Render();
SDL_RenderPresent(m_pRenderer);
}
Menu State Figure
Correct Color
Wrong Color
edit :Also, I found out that this weird phenomenon only happens when the renderer was created with 'SDL_RENDERER_ACCELERATED' flag and -1 or 0 driver index, i.e SDL_CreateRenderer(m_pWindow, 1, SDL_RENDERER_ACCELERATED); or SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_SOFTWARE);works fine!
I have been plagued by this very same issue. The link provided by ekodes is how I resolved it, as order of operations had no effect for me.
I was able to pull the d3d9Device via SDL_RenderGetD3D9Device(), then SetTextureStageState as described in ekodes d3d blending link.
I was having the same issue. I got a vibrant green color when trying to render a light gray.
The combination of the parameters that are fixing the issue for you pertain to the driver to be used. -1 selects the first driver that meets the criteria, int this case it needs to be accelerated.
Using SDL_GetRendererInfo I was able to see this happens when using the "direct3d" driver.
I found this question talking about blending in direct3d.
I figured it out eventually. In addition to Alpha Blending there is a Color Blending. So DirectX merges color of the last texture with the last primitive.
The question does provide a fix for this in DirectX, however I'm not sure how to apply that it in regards to SDL. I also have not been able to find a solution for this problem in SDL.
I was drawing Green text with SDL_ttf, which uses a texture. Then drawing a gray rectangle for another component elsewhere on the screen.
What's strange is it doesn't seem to happen all the time. However, mine seems to predominantly happen with SDL_ttf. At first I thought it may be a byproduct of TTF_RenderText_Blended however, it happens with the other ones as well. It also does not appear to be affected by the blend mode of the Texture generated by those functions
So in my case, I was able to change the order of the operations to get the correct color.
Alternatively, using the OpenGL driver appeared to fix this as well. Similar to what you mentioned. (This was driver index 1 for me)
I'm not sure this classifies as an "Answer" but hopefully it helps someone out or points them in the right direction.

SDL_FillRect Not Drawing?

I'm making a game in C++ with SDL, and I want to render particles with SDL_FillRect().
I've played with the code for hours, but no matter what I do, the particles are not drawing.
This is the code in my Render function (I made sure that I was in fact calling the function):
void Particle::Render()
{
SDL_Rect rect;
rect.x = x;
rect.y = y;
//rect.w = Particle::Particle_Size;
//rect.h = Particle::Particle_Size;
rect.w = 8;
rect.h = 8;
surface = SDL_CreateRGBSurface(SDL_SWSURFACE,8,8,32,0,0,0,0);
if(SDL_FillRect(surface,&rect,SDL_MapRGB(surface->format,0,0,0)) != 0) printf("Error");
//SDL_RenderCopy(renderer,texture,NULL,&rect);
}
The console isn't printing "Error", so the SDL_FillRect() is successful. However, no rects are being drawn to the screen.
I tried creating a texture with SDL_CreateTextureFromSurface() with that surface passed in, and then used SDL_RenderCopy, which is commented out in the above function, but it worked before I commented it out. I want to use SDL_FillRect so I could have colored textures though.
Am I missing anything?
I think you could use this function to do what you are looking for:
SDL_RenderFillRect()
https://wiki.libsdl.org/SDL_RenderFillRect
You would have to set the renderer color before with:
SDL_SetRenderDrawColor();
I think you could also update the window surface to get what you have to work.
That would use
SDL_UpdateWindowSurface().
https://wiki.libsdl.org/SDL_UpdateWindowSurface?highlight=%28%5CbCategoryVideo%5Cb%29%7C%28CategoryEnum%29%7C%28CategoryStruct%29
Hope it helps!
The SDL_CreateRGBSurface() function creates an off-screen surface. If you want to draw to the screen, you will have to draw to the surface returned by SDL_GetWindowSurface().
That is, if you are using SDL 2.0.

cocos2d and glReadPixels don't work?

i have a problem with cocos2d and glReadPixels because don't work correctly.
I found in web a code for pixel perfect collision and i modified for my app, but with the animation or more fast animation don't work.
This is the code:
-(BOOL) isCollisionBetweenSpriteA:(CCSprite*)spr1 spriteB:(CCSprite*)spr2 pixelPerfect:(BOOL)pp
{
BOOL isCollision = NO;
CGRect intersection = CGRectIntersection([spr1 boundingBox], [spr2 boundingBox]);
// Look for simple bounding box collision
if (!CGRectIsEmpty(intersection))
{
// If we're not checking for pixel perfect collisions, return true
if (!pp) {return YES;}
// Get intersection info
unsigned int x = intersection.origin.x;
unsigned int y = intersection.origin.y;
unsigned int w = intersection.size.width;
unsigned int h = intersection.size.height;
unsigned int numPixels = w * h;
//NSLog(#"\nintersection = (%u,%u,%u,%u), area = %u",x,y,w,h,numPixels);
// Draw into the RenderTexture
[_rt beginWithClear:0 g:0 b:0 a:0];
// Render both sprites: first one in RED and second one in GREEN
glColorMask(1, 0, 0, 1);
[spr1 visit];
glColorMask(0, 1, 0, 1);
[spr2 visit];
glColorMask(1, 1, 1, 1);
// Get color values of intersection area
ccColor4B *buffer = malloc( sizeof(ccColor4B) * numPixels );
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
/******* All this is for testing purposes *********/
// Draw the intersection rectangle in BLUE (testing purposes)
/**************************************************/
[_rt end];
// Read buffer
unsigned int step = 1;
for(unsigned int q=0; q<1; q+=step)
{
ccColor4B color = buffer[q];
if (color.r > 0 && color.g > 0)
{
isCollision = YES;
break;
}
}
// Free buffer memory
free(buffer);
}
return isCollision;
}
where is the problem?I tried but nothing.
Thank you very much.
regards.
If you are using iOS6, have a look at this post for a solution:
CAEAGLLayer *eaglLayer = (CAEAGLLayer *) self.layer;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
nil];
The explanation is that iOS6 fixes some bugs in iOS Open GL implementation, so that the GL buffer is (correctly) cleared each time it is presented to the screen. Here what Apple writes about this:
Important: You must call glReadPixels before calling EAGLContext/-presentRenderbuffer: to get defined results unless you're using a retained back buffer.
The correct solution would be calling glReadPixels before the render buffer is presented to the screen. After that, it invalidated.
The solution above is just a workaround to make the image sort of "sticky".
Be aware that it can impact your app rendering performance. The point is that if you are using cocos2d, you cannot easily call glReadPixels before the the render buffer is presented.
Hope it helps.

Dynamic 2D Ogre3D Texutre

I'm trying to create a 2D background for my ogre scene that renders the camera frames for the QCAR SDK. This is on an iPad with iOS 6.
At the moment I'm retrieving the pixel data like so in renderFrameQCAR:
const QCAR::Image *image = camFrame.getImage(1);
if(image) {
pixels = (unsigned char *)image->getPixels();
}
This returns pixels in the RGB888 format, then passing it to my ogre scene in the renderOgre() functions:
if(isUpdated)
scene.setCameraFrame(pixels);
scene.m_pRoot->renderOneFrame();
The setCameraFrame(pixels) function consists of:
void CarScene::setCameraFrame(const unsigned char *pixels)
{
HardwarePixelBufferSharedPtr pBuffer = m_pBackgroundTexture->getBuffer();
pBuffer->lock(HardwareBuffer::HBL_DISCARD);
const PixelBox& pBox = pBuffer->getCurrentLock();
PixelBox *tmp = new PixelBox(screenWidth, screenHeight, 0, PF_R8G8B8, &pixels);
pBuffer->blit(pBuffer, *tmp, pBox);
pBuffer->unlock();
delete tmp;
}
In this function I'm attempting to create a new PixelBox, copy the pixels into it and the copy that over the the pixelBuffer.
When I first create my Ogre3D scene, I set up the m_pBackgroundTexture & background rect2d like so:
void CarScene::createBackground()
{
m_pBackgroundTexture = TextureManager::getSingleton().createManual("DynamicTexture", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, m_pViewport->getActualWidth(), m_pViewport->getActualHeight(), 0, PF_R8G8B8, TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
m_pBackgroundMaterial = MaterialManager::getSingleton().create("Background", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
m_pBackgroundMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("DynamicTexture");
m_pBackgroundMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
m_pBackgroundMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
m_pBackgroundMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false);
m_pBackgroundMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
m_pBackgroundRect = new Rectangle2D(true);
m_pBackgroundRect->setCorners(-1.0, 1.0, 1.0, -1.0);
m_pBackgroundRect->setMaterial("Background");
m_pBackgroundRect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND);
AxisAlignedBox aabInf;
aabInf.setInfinite();
m_pBackgroundRect->setBoundingBox(aabInf);
SceneNode* node = m_pSceneManager->getRootSceneNode()->createChildSceneNode();
node->attachObject(m_pBackgroundRect);
}
After this all I get is a white background with no texture, and I have no idea why this is not displaying the output! My goal for this is just to have the camera rendering in the background so I can project my 3d model onto it.
Thanks,
Harry.

Want to place several images with transparent colour on the same background

I am desparately searching for place several graphics having a transparent background on the same background with GDI+. I did not have any programming experience with Windows or graphic programming (like games) before, so it is more difficult to find a solution. GDI+ has a transparent colour. GDI+ only uses the transparency information to bitmap this colour properly on another image. Once bitmaped, however, the first image is no more transparent. If you put the same image several times on the same background, you will see that only the first placement was transparent.
My problem is placing several transparent objects on a background at once. You can see the code below that works for one ship (nNrOfShips = 1;). If you write a larger value for this variable, no ship will be placed.
How should I modify the code? I also tried to use Ship arrays, but no ship appears on the screen. You may create your own example by using a background with (slightly) changing colour and just place an image transparently. I hope that that example would help me.
Here the code example...
HDC hdcScreen = GetLockedScreen();
m_hdcShip = CreateCompatibleDC(hdcScreen);
ReleaseLockedScreen();
// Draw the ship image on restored background
Graphics grBkg(m_hdcNewBackground);
grBkg.SetSmoothingMode(SmoothingModeHighQuality);
// Restore new background
BitBlt(m_hdcNewBackground, 0, 0,
GetWtsMetrics(wtsm_ScreenSizeX), GetWtsMetrics(wtsm_ScreenSizeY),
m_hdcSavedBackground, 0, 0, SRCCOPY); // 20100125 SAE
BYTE nNrOfShips = 1; // DATA->GetNrOfShips();
for (BYTE nShipId = 0; nShipId < nNrOfShips; nShipId++)
{
Ship ship = DATA->GetShipList()[nShipId];
ShipModel shipModel = DATA->FindShipModel(ship.nShipModelId); // 20100202 SAE
WORD nCurResId = DATA->FindCurShipResourceId(ship); // 20100131 SAE
WORD nIndex = nCurResId - shipModel.nFirstResId; // 20100131 SAE
assert(nIndex >= 0);
ShipResource shipRes = (*shipModel.pvectResource)[nIndex]; // 20100202 SAE
// 20100126 SAE
// Always take the first (upper left) coordinates of the ship rectangle:
QuadrantVector &wpQuadrants =
*DATA->GetWallpapers()[DATA->SelectWallpaper()].pvectQuadrant;
do
{ // 20100115 SAE: Determine first the coordinates of the ship
ship.vectRectangle = DATA->RandomRectangleCoordinates(
shipModel.nHeight, shipModel.nWidth);
} while (!DATA->AreCoordinatesValid(ship.vectRectangle, wpQuadrants) &&
!DATA->AreShipsTooClose(ship, DATA->GetShipList(), DATA->GetDistance()));
grBkg.TranslateTransform(ship.vectRectangle[0].fX,
ship.vectRectangle[0].fY);
grBkg.RotateTransform(0); // 20100201 SAE
grBkg.DrawImage(shipRes.pimgPicture,
-shipModel.nWidth/2, -shipModel.nHeight/2);
// Determine bounding rectangle of ship after drawing on transformed page
// 20100125 SAE
Rect rcSrc(-shipModel.nWidth/2, -shipModel.nHeight/2,
shipModel.nWidth, shipModel.nHeight);
TransformRect(&grBkg, &m_rcCurShip, &rcSrc,
CoordinateSpacePage, CoordinateSpaceWorld);
} // for
DeleteDC(m_hdcShip);
m_hdcShip = 0;
Use the Bitmap.MakeTransparent() method on the images when you load them. You'll need to select the color that's the background color for those images. Storing the images in the PNG format with the transparency selected in the graphics editor would be another way.