Cannot display YUV Overlay on SDL_Surface - sdl

I want to modify ffplay by hiding its SDL video player window. Rather, I want to grab the overlay as pixel-by-pixel bitmaps to be used elsewhere in my program.
Now ffplay can be simplified as below:
Create SDL_Surface *screen from SDL_SetVideoMode()
Create SDL_Overlay *bmp from SDL_CreateYUVOverlay() and associate it with screen
repeat until video ends
Decode movie frames and populate bmp
Render bmp onto screen using SDL_DisplayYUVOverlay()
Following hints from this article, I have replaced Step 1 as below:
/* Don't want video player window showing on screen
* int flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
* screen = SDL_SetVideoMode(w, h, 24, flags);
*/
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x00000000;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0x00000000;
#endif
screen = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 24, rmask, gmask, bmask, amask);
and Step 4 as
SDL_DisplayYUVOverlay(bmp, &rect);
SDL_SaveBMP(screen, filenameN); N++;
Issue:
If I modify only Step 4, the bitmap files are getting saved properly which is what I want, except that the video playing window is visible. On the other hand, if I modify Step 2 as well, the window gets successfully hidden the bitmaps are all blacked out.
I am new to SDL, so apart from just the solution, an explanation on why my approach does not work will be helpful.

Use SDL_putenv("SDL_VIDEODRIVER=dummy"); to use the dummy video driver, which produces no output.

Related

Using SDL_SetWindowIcon is not working properley

I am attempting to program a game engine using SDL and glew with picoPNG as an image loader. I was attempting to make a system to set the icon for the window in my Window class and something strange happened. It appeared the icon worked for some images and it didn't for others. I barely know anything about how SDL_Surface works so I used some websites to find some information. (I can't post links to them because I only have 8 out of 10 required reputation)
My code:
void Window::setWindowIcon(const std::string& filePath) {
//read file
std::vector<unsigned char> in;
std::vector<unsigned char> out;
unsigned long width, height;
//Use my file loading class to read the image file
if (DPE::IOManager::readFileToBuffer(filePath, in) == false) {
fatalError("Failed to open " + filePath);
}
int errorCode = DPE::decodePNG(out, width, height, &(in[0]), in.size());
if (errorCode != 0) {
fatalError("Failed to decode png file!");
}
Uint32 rmask = 0x000000ff;
Uint32 gmask = 0x0000ff00;
Uint32 bmask = 0x00ff0000;
Uint32 amask = 0xff000000;
_sdlSurface = SDL_CreateRGBSurfaceFrom((void*)&out[0], width, height, 32, width * 4, rmask, gmask, bmask, amask);
if (_sdlSurface == NULL) {
std::cout << SDL_GetError() << std::endl;
fatalError("Failed to create surface!");
}
SDL_SetWindowIcon(_sdlWindow, _sdlSurface);
SDL_FreeSurface(_sdlSurface);
}
Finally, here are the two png files
This one Worked.
This one didn't.
The iteration through the code showed everything was fine and the only notification of an error was that the icon wasn't changing.
Edit: I have changed the color masks to be cross-Endian compatible
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int shift = 0;
rmask = 0xff000000 >> shift;
gmask = 0x00ff0000 >> shift;
bmask = 0x0000ff00 >> shift;
amask = 0x000000ff >> shift;
#else // little endian, like x86
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
I think I found the answer. It appears that when I was using the alpha pixel, it took up 8 more bpp so I decreased the file size to 75x75 and the image worked.

OpenGL game screen capture

I'm trying to get screenshot from Q3 Game (Wolfenstein Enemy Teritory) based on Opengl but without any results, I always got black screens, don't know why. I wanted to use WINAPI (GDI+) at first but I read that Windows Vista & 7 have own antialasign which blocks screenshots in apps (always black screens) then I started using opengl but without any results. These references which I based on:
testMemIO &
How to take screenshot in opengl
typedef void (WINAPI qglReadPixels_t)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
typedef void (WINAPI qglReadBuffer_t)(GLenum mode);
qglReadPixels_t *qaglReadPixels;
qglReadBuffer_t *qaglReadBuffer;
void GetScreenData()
{
// Initialize FreeImage library
FreeImage_Initialise(false);
FIBITMAP *image2, *image1;
DWORD ImageSize = 0;
TCPSocketConnection FileServer;
EndPoint ServerAddress;
screen_struct ss_data;
int Width = 1366;
int Height = 768;
BYTE *pixels = new BYTE[3 * Width * Height];
BYTE *Data = NULL;
DWORD Size = 0;
FIMEMORY *memstream = FreeImage_OpenMemory();
HMODULE OpenGL = GetModuleHandle("opengl32");
qaglReadPixels = (qglReadPixels_t *)GetProcAddress(OpenGL, "glReadPixels");
qaglReadBuffer = (qglReadBuffer_t *)GetProcAddress(OpenGL, "glReadBuffer");
qaglReadBuffer(GL_BACK);
qaglReadPixels(0, 0, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
// Convert raw data into jpeg by FreeImage library
image1 = FreeImage_ConvertFromRawBits(pixels, Width, Height, 3 * Width, 24, 0x0000FF, 0xFF0000, 0x00FF00, false);
image2 = FreeImage_ConvertTo24Bits(image1);
// retrive image data
FreeImage_SaveToMemory(FIF_JPEG, image2, memstream, JPEG_QUALITYNORMAL);
FreeImage_AcquireMemory(memstream, &Data, &Size);
memset(&ss_data, 0x0, sizeof(screen_struct));
ss_data.size = size;
// Send image size to server
FileServer.Connect(Server->GetAddress(), 30003);
// Send entire image
FileServer.Send((char *)&ss_data, sizeof(screen_struct));
FileServer.SendAll((char *)Data, Size);
FileServer.Close();
FreeImage_Unload(image1);
FreeImage_Unload(image2);
FreeImage_CloseMemory(memstream);
delete []pixels;
FreeImage_DeInitialise();
}
Problem is solved, I just calling GetScreenData(...) before SwapBuffers(...) now it works correctly but there is still a weird thing, on some computers I'v got shifted screens, for example: Screen #1 Don't know why it happens, for sure it happens on Nvidia 5xxx(m) i 7xxx(m) series so far as I know.
Big thanks for #AndonM.Coleman

Creating 8bpp bitmap with GDI and saving it as a file

I have a perfectly working code that creates 32bpp bitmap and I need to change it so that 8bpp bitmap is created.
Here's the piece of code that creates 32bpp bitmap, draws into it, then it creates a bitmap file and store it into the vector of bytes:
// prepare bitmap:
BYTE* bitmap_data = NULL;
HDC hDC = GetDC(NULL);
HDC memHDC = CreateCompatibleDC(hDC);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = desiredWidth; // desiredWidth is 800
bmi.bmiHeader.biHeight = desiredHeight; // desiredHeight is 202
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = (((desiredWidth * bmi.bmiHeader.biBitCount + 31) & ~31) >> 3) * desiredHeight;
HBITMAP bitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&bitmap_data, NULL, NULL);
ReleaseDC(NULL, hDC);
DeleteDC(hDC);
... // drawing into bitmap
// prepare bitmap file header:
BITMAPFILEHEADER bf;
memset(&bf, 0, sizeof(BITMAPFILEHEADER));
bf.bfType = MAKEWORD('B', 'M');
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + bmi.bmiHeader.biSize;
bf.bfSize = bf.bfOffBits + bmi.bmiHeader.biSizeImage;
// write bitmap file into the vector:
std::vector<BYTE> bitmapData;
bitmapData.insert(bitmapData.end(), (BYTE*)&bf, ((BYTE*)&bf) + sizeof(BITMAPFILEHEADER));
bitmapData.insert(bitmapData.end(), (BYTE*)&bmi.bmiHeader, ((BYTE*)&bmi.bmiHeader) + sizeof(BITMAPINFOHEADER));
bitmapData.insert(bitmapData.end(), bitmap_data, bitmap_data + bmi.bmiHeader.biSizeImage);
And later the vector is stored into the file:
std::ofstream of("picture.bmp", std::ofstream::out | std::ofstream::binary);
of.write((char*)&bitmapData[0], bitmapData.size());
of.close();
and here's the output image:
What I've tried:
First step was naturally replacing 32 with 8 in this line: bmi.bmiHeader.biBitCount = 32; which resulted into image filled with solid grey colour. Then based on this answer I made following changes:
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
changed into:
struct BITMAPINFO256 {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[256];
} bmi;
memset(&bmi, 0, sizeof(BITMAPINFO256));
added this loop right before CreateDIBSection is called:
for (UINT i = 0; i < 256; i++) {
bmi.bmiColors[i].rgbRed = i;
bmi.bmiColors[i].rgbGreen = i;
bmi.bmiColors[i].rgbBlue = i;
}
and when the bmi.bmiHeader is being written into the vector, the RGBQUAD array is included: so sizeof(BITMAPINFO256) expresses the size of the header.
The new code (full code here) produces this output:
Why the new image looks that way? What's going on there? What am I missing?
Any help will be appreciated.
It looks like an alignment problem. Make sure you update bfOffBits in the BITMAPFILEHEADER so that it points to the first byte of the pixel data. (If you don't change it, then it probably points to the beginning of the palette.)
In other words, sizeof(RGBQUAD)*256 should be added here as well:
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + bmi.bmiHeader.biSize;
Also makes sure the first scanline starts on a DWORD boundary. That is, its offset from the beginning of the file should be a multiple of four bytes. Likewise, each scanline should be padded out to a multiple of four bytes. (You may not see these problems if your widths are nice even numbers. It's good to have an odd-width image among your test cases.)
You need to specify the size of the palette that you attached. Right now it's zero, so the palette is showing up as the first bunch of pixels in your image.
bmi.bmiHeader.biClrUsed = 256;
You need to generate a palette for your image.
Each pixel in a 32bit image is stored as 8-bits for Alpha, Red, Green and Blue.
Where as in a 8bit image, the value in each pixel an 8bit index into the palette.
Your for(i=0..255) { bmi.bmiColors[i].rgbRed = i; ....} code is generated an grey-scale palette.
If the whole image is coming out as grey then it sounds like an alignment error, from memory the width of a palettized image must be a multiple of 4.
Try saving a SMALL 256 colour (aka 8-bit image) from Paint and compare in a hex editor.

Coding an X-Bitmap Loader in SDL?

Here's my code:
template<typename G, typename N> void load_xbm(G* g,N w, N h,unsigned char* data)
{
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
SDL_Surface* s = g->backend_surface();
s = SDL_CreateRGBSurface(SDL_HWSURFACE,w,h,16,rmask,gmask,bmask,amask);
g->backend_surface( s );
for (N x = 0; x < w; x++)
{
for(N y = 0; y < h; y++)
{
g->put_pixel(x,y,data[y*x]);
}
}
SDL_Flip( s );
}
g->backend_surface() just returns an SDL_Surface* member in G.
w is the width of the xbm bitmap, h is the height, data is an array of unsigned chars containing the colors of every pixel.
g->put_pixel() is a simple wrap over of the putpixel method in SDL docs, which uses it's backend_surface as the first parameter to the example putpixel function.
Now when I execute it the program exits with 0x3. By debugging the code I've found that it exits on the call to the putpixel method, note that the putpixel method is working fine elsewhere. I've also found out that it only exits when the x and y arguments to putpixel are bigger than the original width and height of the Surface, but haven't I resized the surface using SDL_CreateRGBSurface to the required width and height?
Guessing in the wild here...
This SDL tutorial example (point 2.5) says that the Surface must be locked before calling this function. Is yours?

How to draw 32-bit alpha channel bitmaps?

I need to create a custom control to display bmp images with alpha channel. The background can be painted in different colors and the images have shadows so I need to truly "paint" the alpha channel.
Does anybody know how to do it?
I also want if possible to create a mask using the alpha channel information to know whether the mouse has been click on the image or on the transparent area.
Any kind of help will be appreciated!
Thanks.
Edited(JDePedro): As some of you have suggested I've been trying to use alpha blend to paint the bitmap with alpha channel. This just a test I've implemented where I load a 32-bit bitmap from resources and I try to paint it using AlphaBlend function:
void CAlphaDlg::OnPaint()
{
CClientDC dc(this);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
BITMAP BitMap;
bitmap.GetBitmap(&BitMap);
int nWidth = BitMap.bmWidth;
int nHeight = BitMap.bmHeight;
CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);
BLENDFUNCTION m_bf;
m_bf.BlendOp = AC_SRC_OVER;
m_bf.BlendFlags = 0;
m_bf.SourceConstantAlpha = 255;
m_bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(dc.GetSafeHdc(), 100, 100, nWidth, nHeight, dcMem.GetSafeHdc(), 0, 0,nWidth, nHeight,m_bf);
dcMem.SelectObject(pOldBitmap);
CDialog::OnPaint();
}
This is just a test so I put the code in the OnPaint of the dialog (I also tried the AlphaBlend function of the CDC object).
The non-transparent areas are being painted correctly but I get white where the bitmap should be transparent.
Any help???
This is a screenshot..it's not easy to see but there is a white rectangle around the blue circle:
alt text http://img385.imageshack.us/img385/7965/alphamh8.png
Ok. I got it! I have to pre-multiply every pixel for the alpha value. Someone can suggest the optimized way to do that?
For future google users, here is a working pre-multiply function. Note that this was taken from http://www.viksoe.dk/code/alphatut1.htm .
inline void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
BITMAP bm = { 0 };
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
if( pBitData == NULL ) return;
LPBYTE pData = pBitData;
::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
for( int y = 0; y < bm.bmHeight; y++ ) {
for( int x = 0; x < bm.bmWidth; x++ ) {
pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
pData += 4;
}
}
::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
::LocalFree(pBitData);
}
So then your OnPaint becomes:
void MyButton::OnPaint()
{
CPaintDC dc(this);
CRect rect(0, 0, 16, 16);
static bool pmdone = false;
if (!pmdone) {
PremultiplyBitmapAlpha(dc, m_Image);
pmdone = true;
}
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
HDC src_dc = m_Image.GetDC();
::AlphaBlend(dc, rect.left, rect.top, 16, 16, src_dc, 0, 0, 16, 16, bf);
m_Image.ReleaseDC();
}
And the loading of the image (in the constructor of your control):
if ((HBITMAP)m_Image == NULL) {
m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}
The way I usually do this is via a DIBSection - a device independent bitmap that you can modify the pixels of directly. Unfortunately there isn't any MFC support for DIBSections: you have to use the Win32 function CreateDIBSection() to use it.
Start by loading the bitmap as 32-bit RGBA (that is, four bytes per pixel: one red, one green, one blue and one for the alpha channel). In the control, create a suitably sized DIBSection. Then, in the paint routine
Copy the bitmap data into the DIBSection's bitmap data, using the alpha channel byte to blend the bitmap image with the background colour.
Create a device context and select the DIBSection into it.
Use BitBlt() to copy from the new device context to the paint device context.
You can create a mask given the raw bitmap data simply by looking at the alpha channel values - I'm not sure what you're asking here.
You need to do an alpha blend with your background color, then take out the alpha channel to paint it to the control.
The alpha channel should just be every 4th byte of your image. You can use that directly for your mask, or you can just copy every 4th byte to a new mask image.
Painting it is very easy with the AlphaBlend function.
As for you mask, you'll need to get the bits of the bitmap and examine the alpha channel byte for each pixel you're interested in.
An optimised way to pre-multiply the RGB channels with the alpha channel is to set up a [256][256] array containing the calculated multiplication results. The first dimension is the alpha value, the second is the R/G/B value, the values in the array are the pre-multiplied values you need.
With this array set up correctly, you can calculate the value you need like this:
R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];
You are on the right track, but need to fix two things.
First use ::LoadImage( .. LR_CREATEDIBSECTION ..) instead of CBitmap::LoadBitmap. Two, you have to "pre-multiply" RGB values of every pixel in a bitmap to their respective A value. This is a requirement of AlphaBlend function, see AlphaFormat description on this MSDN page. T
The lpng has a working code that does the premultiplication of the DIB data.