I have a Windows application which uses OpenGL in a fairly simple way to render textured images (QUADS) into multiple windows in its client-area.
The application behaves fine on Windows 7, but on Windows 8 it sometimes flickers and shows the background in response to user actions, like a mouse click on a window. To be exact: sometimes when some window is mouse-clicked (not on every mouse click), all windows briefly flicker.
I was looking into possible reasons for this behavior. I tried catching the WM_ERASEBKGND message, but that didn't seem to be the cause. I tried working with different GPU cards (and corresponding drivers), and it happens on all (Nvidia Quadro & ATI Radeon HD). In addition, I made some of the image-windows use GDI for drawing, and indeed - when other windows flicker, these images don't (as well as the menu, button area & status bar).
UPDATE: Since the windows are C# User Controls, I've set the AllPaintingInWmPaint & UserPaint Styles to "true". DoubleBuffer is "false", because setting it "true" causes the flicker to be a lot more frequent.
Bottom line: all windows drawn with OpenGL flicker once in a while, and only them, and only on Win8 (not on Win7).
I would very much appreciate tips for the cause of this. My code follows. I initialize the contexts like this:
void InitWindow(HWND *hWnd, HDC *hDC, HGLRC *hRC)
{
*hDC = GetDC(*hWnd);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
// pfd.cRedBits = 8;
// pfd.cGreenBits = 8;
// pfd.cBlueBits = 8;
pfd.cAlphaBits = 0;
pfd.cDepthBits = 0;
pfd.iLayerType = PFD_MAIN_PLANE;
int format = ChoosePixelFormat(*hDC, &pfd);
SetPixelFormat(*hDC, format, &pfd);
*hRC = wglCreateContext(*hDC);
wglMakeCurrent(*hDC, *hRC);
}
And draw to the screen like this:
int Render(HWND hWnd, Raster* raster)
{
Size RasterSize = raster->GetSize();
HDC hDC = NULL;
HGLRC hRC = NULL;
InitWindow(&hWnd, &hDC, &hRC);
GLenum raster_pixel_format = GL_BGRA_EXT;
GLint internal_format = GL_RGBA;
GLuint texture;
glGenTextures(1, &texture);
glBindTexture (GL_TEXTURE_2D, texture);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
RasterSize.width, RasterSize.height,
0, raster_pixel_format, GL_UNSIGNED_BYTE,
raster->GetData());
glBindTexture(GL_TEXTURE_2D, texture);
RECT wndRect;
::GetClientRect(hWnd, &wndRect);
GLsizei wndWidth = wndRect.right;
GLsizei wndHeight = wndRect.bottom;
glEnable(GL_TEXTURE_2D);
// this is usually stated in window coordinates,
// but since we know the raster gets its size from
// the window - we can use raster coordinates
glViewport(0, 0, RasterSize.width, RasterSize.height);
glBegin( GL_QUADS );
glTexCoord2d(0.0,0.0); glVertex2d(-1.0,+1.0);
glTexCoord2d(1.0,0.0); glVertex2d(+1.0,+1.0);
glTexCoord2d(1.0,1.0); glVertex2d(+1.0,-1.0);
glTexCoord2d(0.0,1.0); glVertex2d(-1.0,-1.0);
glEnd();
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &texture);
SwapBuffers(hDC);
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
return SUCCESS;
}
Issue was due to using a machine with multiple video cards.
Disabling any one card solved it.
Related
I'm trying to save a texture in tiff file from an other thread. But the only result i get is a white picture, I think it come from the glcontext ( because it's not possible to have one glcontext for several thread). That's why i've tried to create two glcontext and share the display context. But still i don't have the gl texture. I can't get the texture from the second opengl context
. I'm tring to do that because at the end the texture will be a video stream from a camera .
Here is my context creation :
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
8, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
GLuint PixelFormat;
// create the pixel pixel format descriptor
PixelFormat = ChoosePixelFormat(dc, &pfd);
// set the pixel format descriptor
SetPixelFormat(dc, PixelFormat, &pfd);
gl = wglCreateContext(dc);
gl2 = wglCreateContext(dc);
wglShareLists(gl, gl2);
wglMakeCurrent(dc, gl);
GLenum g= glewInit();
wglewInit();
loadImage();
rec = new Recorder(dc,gl2);
rec->Start_StopRecord(text, true);
Here is the code to save to tiff file :
Recorder::Recorder(HDC &hdc, HGLRC &_gl)
{
isStarted = false;
dc = hdc;
gl = _gl;
}
Recorder::~Recorder()
{
if (isStarted) {
isStarted = false;
recordThread.join();
CloseTifFile();
delete mp_fileTifIn;
}
}
void Recorder::Start_StopRecord(GLuint Texture, bool launched){
if (launched) {
if (isStarted) {
wglMakeCurrent(dc, gl);
isStarted = false;
recordThread.join();
CloseTifFile();
pixels.release();
}
else {
isStarted = true;
//wglMakeCurrent(NULL, NULL);
//RecordShot(&Texture);
recordThread = std::thread(&Recorder::RecordShot, this,&Texture);
}
}
}
void Recorder::RecordShot(GLuint* texture){
wglMakeCurrent(dc, gl);
OpenTifFile(*texture);
pixels = std::unique_ptr<int>(new int[width*height]);
//while (isStarted) {
WriteTif8Bits(*texture);
WriteDirectory();
//Sleep(16);
//}
pixels.release();
}
void Recorder::OpenTifFile(GLuint &Texture){
char* filename="../test3.tiff";
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
mp_fileTifIn = TIFFOpen(filename,"w");
}
void Recorder::CloseTifFile(){
TIFFClose(mp_fileTifIn);
}
/*
* Open Sub data for a Tiff file (allow multiple picture in one tif file)
*/
void Recorder::WriteDirectory(){
TIFFWriteDirectory(mp_fileTifIn);
}
void Recorder::WriteTif8Bits(GLuint &Texture){
//Setup Tiff Configuration
TIFFSetField(mp_fileTifIn,TIFFTAG_IMAGEWIDTH,width);
TIFFSetField(mp_fileTifIn,TIFFTAG_IMAGELENGTH,height);
TIFFSetField(mp_fileTifIn,TIFFTAG_SAMPLESPERPIXEL,4);
TIFFSetField(mp_fileTifIn,TIFFTAG_BITSPERSAMPLE,8);
TIFFSetField(mp_fileTifIn,TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(mp_fileTifIn,width));
TIFFSetField(mp_fileTifIn,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
TIFFSetField(mp_fileTifIn,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
TIFFSetField(mp_fileTifIn, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
TIFFSetField(mp_fileTifIn, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
glBindTexture(GL_TEXTURE_2D,Texture);
assert(glGetError() == GL_NO_ERROR);
glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,pixels.get());
assert(glGetError() == GL_NO_ERROR);
//Image Reversal
Reverse(pixels.get(), height, width);
//Write one picture
/*for (int row = 0; row < height; row++) {
TIFFWriteScanline(mp_fileTifIn, pixels.get(), row, 0);
lineChange(pixels.get(), width);
}*/
TIFFWriteEncodedStrip(mp_fileTifIn, 0, pixels.get(), height*width * sizeof(int));
}
void Recorder::lineChange(int* pointer, int width) {
pointer -= width;
}
void Recorder::Reverse(int* pointer, int height, int width) {
pointer += (height - 1) * width;
}
And here is the loadImages function
int loadImage() {
wglMakeCurrent(dc, gl);
cv::Mat image;
image = cv::imread(std::string("C:/Users/Public/Pictures/Sample Pictures/Desert.jpg"), CV_LOAD_IMAGE_COLOR);
if (!image.data)
return -1;
cvNamedWindow("try", cv::WINDOW_AUTOSIZE);
cv::imshow("try", image);
cv::flip(image, image, 0);
glGenTextures(1, &text);
GLenum g=glGetError();
glBindTexture(GL_TEXTURE_2D, text);
assert(glGetError() == GL_NO_ERROR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, image.ptr());
return 0;
}
Here is a test project where i'm loading a picture with opencv and i try to save it in an other thread : https://mega.nz/#!SBMUnJRI!dLC_l9hmCkhIDDUaygHuq4Kw2SKIuxRE7m19md74p0k
To run the project you need Opencv and glew, libtiff is already packaged inside
If you think somethink is missing for this post, i invite you to comment it before downgrade as i'm following my subject
I finally solved my problem by doing all opengl action in one thread ( i retrieve the image and display it in one thread, and i save it in another, which doesn't need an openglcontext)
An other thing that were confusing me is a bad configuration of
glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,this->rec[0].pixels.get());
But my textures are GL_TEXTURE_RECTANGLE_NV, that's why i had only a white picture sometimes.
I tried to make gdiplus and opengl works together in one offscreen hdc. Current code is something like this (no Internet access)
create a gdiplus bitmap A
create a gdiplus graphics from A as B
get B's HDC
choose a proper pixelformat for HDC
create a gdiplus graphics from HDC as C
create an opengl context from HDC as D
drawing with C and D
release HDC to B
draw A to other graphics
C is needed because I found that after I release the HDC to B, it is not possible for opengl to change the bitmap.
To check if things work, I added a bitmap E by reading gl pixels before (9)
There are still a problem in current program
After releasing the hdc, the bitmap A losses its alpha information.
The bitmap E works fine with correct alpha, but the GL_BGRA cannot read out alpha so I have to read GL_RGBA data and do a per pixel convertion into gdiplus color format.
Should I just use E or is there any other attempt?
Example of releasehdc alpha lossing:
Bitmap bitmap(100,100);
Graphics graphics(&bitmap);
HDC hdc=graphics.GetHDC();
Graphics graphics2(hdc);
graphics2.Clear(Color(128,255,0,0));
graphics2.Flush();
graphics.ReleaseHDC(hdc);
//draw bitmap to other graphics
Note: I just figure out that gdiplus didn't actually use the alpha channel in the HDC, only the rgb channels are shared, so I wonder how it works with a BGRA target bitmap
I think the best way to use OpenGL and GDI+ together is to generate a texture in GDI+, then load that into OpenGL.
The gist of this is:
void MakeTexture(GLuint& texId)
{
Bitmap offscreen(512, 512, PixelFormat32bppARGB);
Graphics gr(&offscreen);
gr.Clear(Color(128, 255, 0, 0));
Gdiplus::SolidBrush brush(Color(255, 0, 0, 255));
Pen pen(Color(128, 255, 0, 0), 16.f);
Font font(L"Arial", 48.f);
Rect r(25, 25, 100, 100);
gr.DrawRectangle(&pen, r);
gr.DrawString(TEXT("TEST STRING"), -1, &font, PointF(50, 50), &brush);
vector<DWORD> argb;
GetBitsLockBits(offscreen, argb, 1);
genTexture(texId, offscreen.GetWidth(), offscreen.GetHeight(), argb);
}
void GetBitsLockBits(Bitmap& bmp, vector<DWORD>& argb, bool invert = 0)
{
BitmapData bmpData;
RectF rectf;
Unit unit;
bmp.GetBounds(&rectf, &unit);
Rect rect(rectf.X, rectf.Y, rectf.Width, rectf.Height);
printf("Got rect %d %d %d %d\n", rect.X, rect.Y, rect.Width, rect.Height);
bmp.LockBits(&rect, ImageLockModeRead, PixelFormat32bppARGB, &bmpData);
printf("BMP has w=%d h=%d stride=%d\n", bmpData.Width, bmpData.Height, bmpData.Stride);
argb.resize(bmpData.Width * bmpData.Height);
if (invert)
for (int i = 0; i < bmpData.Height; i++)
memcpy(&argb[i * bmpData.Width], (GLuint*)bmpData.Scan0 + (bmpData.Height - 1 - i) * bmpData.Width, bmpData.Width * 4);
else if (bmpData.Stride == bmpData.Width * 4)
memcpy(&argb[0], bmpData.Scan0, bmpData.Width * bmpData.Height * 4); // If the bmp is padded then
// this won't read the image correctly (it will read it with pad bits between)
else
for (int i = 0; i < bmpData.Height; i++)
memcpy(&argb[i * bmpData.Width], (GLuint*)bmpData.Scan0 + i * bmpData.Width, bmpData.Width * 4);
bmp.UnlockBits(&bmpData);
}
void genTexture(GLuint& texId, int w, int h, const vector<DWORD>& argb)
{
glGenTextures(1, &texId); CHECK_GL;
glBindTexture(GL_TEXTURE_2D, texId); CHECK_GL;
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); CHECK_GL;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); CHECK_GL;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); CHECK_GL;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &argb[0]); CHECK_GL;
}
Trying to have OpenGL and GDI+ cooperate by drawing into the same window directly will probably get you very flickery results.
I want to load a HBITMAP from a resource file and use it as an OpenGL texture. The code I use:
HBITMAP hBmp = (HBITMAP) LoadImage(hInstance,
MAKEINTRESOURCE(id), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
BITMAP BM;
GetObject(hBmp, sizeof(BM), &BM);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
GLvoid* bits = BM.bmBits;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, BM.bmWidth,
BM.bmHeight, 0, GL_BGRA_EXT,
GL_UNSIGNED_BYTE,
bits);
But I allways get an error from Visual Studio that I dont have access to the pointer bits. The error is at the last line of code:
bits);
I actually can use the NULL pointer instead of bits without getting an error. I can output bits to using a messagebox. Does anybody have an idea what is wrong with my code?
From the GetObject documentation:
If hgdiobj is a handle to a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling the GetDIBits or GetBitmapBits function.
In context, "other means" is anything other than CreateDIBSection. You're not using CreateDIBSection, you're using LoadImage. Which category the LR_CREATEDIBSECTION flag puts you into is unclear, but the workaround is clear: Use GetDIBits.
Working solution for GLUT on Windows XP
(tcc or lcc compiler)
GLuint LoadTexture(GLuint tex, const char * filename)
{
HBITMAP hBitmap;
BITMAP bm;
HINSTANCE hInstance = GetModuleHandle(NULL);
//standard bmp 24 bit
//supported resolutions 64x64, 128x128, 256x256, 512x512
//type "char" has a 1 byte size, other types take more byte and will not work
unsigned char * data;
unsigned char R, G, B;
//LoadImage() loads the bmp picture as an interlaced image
hBitmap = LoadImage(NULL, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
GetObject(hBitmap, sizeof(BITMAP), &bm);
//get the address of the start of the image data in memory
data = bm.bmBits;
//swap R, G, B values for correct color display
int index, i;
for (i = 0; i < bm.bmWidth * bm.bmHeight ; i++)
{
index = i*3;
B = data[index]; G = data[index+1]; R = data[index+2];
data[index] = R; data[index+1] = G; data[index+2] = B;
}
//print image parameters
printf ("bmType %u\n",bm.bmType);
printf ("bmWidth %u\n",bm.bmWidth);
printf ("bmHeight %u\n",bm.bmHeight);
printf ("bmWidthBytes %u\n",bm.bmWidthBytes);
printf ("bmPlanes %u\n",bm.bmPlanes);
printf ("bmBitsPixel %u\n",bm.bmBitsPixel);
printf ("bmBits %p\n",bm.bmBits);
printf ("hInstance %p\n",hInstance);
//create texture from loaded bmp image
glGenTextures( 1, &tex);
glBindTexture( GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, 4, bm.bmWidth, bm.bmHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bm.bmBits);
printf ("--- texture %u created ---\n", tex);
//texture filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
}
void init(void)
{
//enable texturing
glEnable(GL_TEXTURE_2D);
//load texture from bmp 24 bit image file
//bmp exported from mspaint for windows xp
LoadTexture(1, "image1.bmp");
LoadTexture(2, "image2.bmp");
LoadTexture(3, "image3.bmp");
LoadTexture(4, "image4.bmp");
LoadTexture(5, "image5.bmp");
LoadTexture(6, "image6.bmp");
. . . . . . . . . . . . . . . . . . . .
Compiling.
no cyrilic names, "" for names with spases
tcc C:\tcc\src\box\box.c -o C:\tcc\src\box\box.exe -LC:\tcc\lib -luser32 -lgdi32 -lopengl32 -lglu32 -lglut32 -Wl,-subsystem=console
(without console -Wl,-subsystem=windows)
I'm using ffmpeg 1.2 to take video from ip camera.I make it draw on the screen, so I wonder if there is some event mechanism to to know if it is time to call av_read_frame?
If I read frame not so frequent as the camera gives frames i get segmentation fault = on some malloc functions inside ffmpeg routines(video_get_buffer)
I also get segmentation fault just when drawing on screen.
In Render function call every 0 miliseconds
void BasicGLPane::DrawNextFrame()
{
int f=1;
while(av_read_frame(pFormatCtx, &packet)>=0)
{
// Is this a packet from the video stream?
if(packet.stream_index==videoStream)
{
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &FrameFinished,
&packet);
// Did we get a video frame?
if(FrameFinished)
{
f++;
this->fram->Clear();
// if (pFrame->pict_type == AV_PICTURE_TYPE_I) wxMessageBox("I cadr");
if (pFrame->pict_type != AV_PICTURE_TYPE_I)
printMVMatrix(f, pFrame, pCodecCtx);
pFrameRGB->linesize[0]= pCodecCtx->width*3; // in case of rgb4 one plane
sws_scale(swsContext, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
//glGenTextures(1, &VideoTexture);
if ((*current_Vtex)==VideoTexture) current_Vtex = &VideoTexture2;else current_Vtex = &VideoTexture;
glBindTexture(GL_TEXTURE_2D, (*current_Vtex));
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pCodecCtx->width, pCodecCtx->height, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pCodecCtx->width, pCodecCtx->height, 0, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
//glDeleteTextures(1, &VideoTexture);
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR)
{
cerr << "OpenGL error: " << err << endl;
}
// av_free(buffer);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
if (f>1) break;
}
//av_free(pFrameRGB);
}
The picture I get on the screen is strange (green quads and red lines are motion vectors of those quads)
http://i.stack.imgur.com/9HJ9t.png
If you hang on the video device, you should read frames as soon as camera has them; you should either query the camera hardware for supported FPS, or get this information from the codec. If no information is available, you have to guess.
It is suspicious that you get a crash when you don't read the frame in time; the worst which should happen in this case would be a lost frame.
Depending on the camera colorspace you may also need to convert it before showing it on a screen. I don't see you doing that.
I am looking for a way to draw text on EGLTexture using Skia library in C/C++. I am planning to run a program that does it on Android platform during bootup before SurfaceFlinger comes up.
Please don't point me to Android Java examples as that is not what I am looking for. I am troubleshooting UI issue at frame buffer level. I am looking for a way to do this in C/C++ using Android native libs (Skia etc).
I have a sample program that can render an image using SkBitmap onto EGLTexture. I was able to display it on monitor. I followed same example and came up with a strategy like this. But it doesn't work though.
0) Clear the screen with green color
1) Create SkBitmap of size 640x480.
2) Create EGLTexture backed by the pixel buffer returned by SkBitmap.lockPixels()
3) Draw text on SkBitmap using SkCanvas. Upload the bitmap into the above texture.
4) Then draw the texture on current surface
I used boot animation program (of android) as my starting point for this.
When I ran this program, all I am seeing is green color. I checked for errors of EGL calls. They all seem to succeed. Thanks for your help
Posted this question on google groups. Brian from Google has some pointers here:
https://groups.google.com/d/topic/skia-discuss/aC5f6HB4gSU/discussion
Below is code that implements the above.
#define EXPECT_NO_GL_ERROR(stmt) \
do { \
stmt; \
const EGLint error_code = eglGetError(); \
if (EGL_SUCCESS != error_code){ \
LOGD("GLTest: GL error code %d at %s:%d", error_code, __FILE__, __LINE__); \
__android_log_assert("GLTest", "GLtest", "GlTest"); \
}\
} while(0)
struct Texture
{
GLint w;
GLint h;
GLuint id;
};
bool GLTest::frametest()
{
Texture texFrame;
// Paint screen with green color
glShadeModel (GL_FLAT);
glDisable (GL_DITHER);
glDisable (GL_SCISSOR_TEST);
glClearColor(0, 1, 0, 1);
glClear (GL_COLOR_BUFFER_BIT);
eglSwapBuffers(mDisplay, mSurface);
SkGraphics::Init();
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 640, 480);
bitmap.allocPixels();
if (NO_ERROR != initTexture(&texFrame, bitmap))
{
LOGD("GLTest: Unable to create a texture that is backed by SkBitmap");
return false;
}
SkCanvas canvas(bitmap);
SkPaint textAttribs;
textAttribs.setColor(0xFFFFFFFF);
textAttribs.setTextSize(SkIntToScalar(24));
const nsecs_t startTime = systemTime();
int frame_count = 0;
do
{
nsecs_t now = systemTime();
double time = now - startTime;
canvas.drawColor(0xFF0000FF);
canvas.drawText("Hello world", strlen("Hello world"), 200, 400,
textAttribs);
initTexture(&texFrame, bitmap); // Upload bitmap into canvas
glEnable (GL_BLEND);
EXPECT_NO_GL_ERROR(glBindTexture(GL_TEXTURE_2D, texFrame.id));
EXPECT_NO_GL_ERROR(glDrawTexiOES(0, 0, 0, texFrame.w, texFrame.h));
EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
if (res == EGL_FALSE)
break;
frame_count++;
if (0 == (frame_count % 150))
LOGD("GLTest: Completed %d frames", frame_count);
// 12fps: don't animate too fast to preserve CPU
const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
if (sleepTime > 0)
usleep(sleepTime);
} while (!exitPending());
return false;
}
status_t GLTest::initTexture(Texture* texture, SkBitmap &bitmap)
{
bitmap.lockPixels();
const int w = bitmap.width();
const int h = bitmap.height();
const void* p = bitmap.getPixels();
GLint crop[4] =
{ 0, h, w, -h };
texture->w = w;
texture->h = h;
EXPECT_NO_GL_ERROR(glGenTextures(1, &(texture->id)));
EXPECT_NO_GL_ERROR(glBindTexture(GL_TEXTURE_2D, texture->id));
switch (bitmap.getConfig())
{
case SkBitmap::kA8_Config:
EXPECT_NO_GL_ERROR(
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p));
break;
case SkBitmap::kARGB_4444_Config:
EXPECT_NO_GL_ERROR(
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, p));
break;
case SkBitmap::kARGB_8888_Config:
EXPECT_NO_GL_ERROR(
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, p));
break;
case SkBitmap::kRGB_565_Config:
EXPECT_NO_GL_ERROR(
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p));
break;
default:
break;
}
EXPECT_NO_GL_ERROR(
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop));
EXPECT_NO_GL_ERROR(
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
EXPECT_NO_GL_ERROR(
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
EXPECT_NO_GL_ERROR(
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
EXPECT_NO_GL_ERROR(
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
return NO_ERROR;
}
I found why my code didn't work. When creating textures, the width and height must be power of 2. For example, if the width is 1920, then texture should be created with a width of 2048 (as 2048 is next 2 power).
Changed initTexture to below. Now I am able to draw text to SkBitmap and then upload the bitmap to texture and draw the texture.
Below is new initTexture that uploads given bitmap to a texture.
bool initTexture(Texture* texture, const SkBitmap &bitmap)
{
bool result = true;
SkAutoLockPixels alp(bitmap);
const int w = bitmap.width();
const int h = bitmap.height();
const void* p = bitmap.getPixels();
int tw = 1 << (31 - __builtin_clz(w));
int th = 1 << (31 - __builtin_clz(h));
if (tw < w)
tw <<= 1;
if (th < h)
th <<= 1;
if (NULL == texture)
return false;
if (texture->id != 0)
{
glBindTexture(GL_TEXTURE_2D, texture->id);
switch (bitmap.getConfig())
{
case SkBitmap::kA8_Config:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, p);
break;
case SkBitmap::kARGB_4444_Config:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, p);
break;
case SkBitmap::kARGB_8888_Config:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
break;
case SkBitmap::kRGB_565_Config:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
break;
default:
break;
}
return true;
}
GLint crop[4] = { 0, h, w, -h };
texture->w = w;
texture->h = h;
glEnable (GL_TEXTURE_2D);
glGenTextures(1, &(texture->id));
glBindTexture(GL_TEXTURE_2D, texture->id);
switch (bitmap.getConfig())
{
case SkBitmap::kA8_Config:
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p);
break;
case SkBitmap::kARGB_4444_Config:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, p);
break;
case SkBitmap::kARGB_8888_Config:
if (tw != w || th != h)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA,
GL_UNSIGNED_BYTE, p);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, p);
}
break;
case SkBitmap::kRGB_565_Config:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
break;
default:
break;
}
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
return result;
}