OpenGL ES 2.0 Convert int[] to GLubyte[] - opengl-es

The following works as a texture...
GLubyte bytePix[4 * 3] ={
255, 0, 0, //red
0, 255, 0, //green
0, 0, 255, //blue
255, 255, 0 //yellow
};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pixelWidth, pixelHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pbytePix);
Problem is I am passing in my BMP as an int[] so I would need something more like this...
int bytePix[4 * 3] ={
255, 0, 0, //red
0, 255, 0, //green
0, 0, 255, //blue
255, 255, 0 //yellow
};
But this doesn't show the same result.
My question is how do I convert the latter into a GLubtye[] or some other recognizable format.

On your platform, sizeof(int) clearly isn't equal to sizeof(GLubyte). I guess the immediate question is — why are you using int? It's likely just to be a huge waste of space if you're storing only values in the range 0–255.
You can't just use GL_INT or GL_UNSIGNED_INT in place of GL_UNSIGNED_BYTE, even if they are the same size as your int as you're using only a byte's range within each integer.
That aside, you'll notice that glTexImage2D doesn't have a stride parameter unlike glVertexAttribPointer and most of the other functions that exist primarily to provide data. So even though you have your values within bytes and those bytes are a predictable space apart, OpenGL can't pull them apart and repack them for you.
So the easiest option is to do it yourself:
void glTexImage2DWithStride(..., GLsizei stride, ...)
{
// the following is written to assume GL_RGB; adapt as necessary
GLubyte *byteBuffer = (GLubyte *)malloc(width * height * 3);
for(int c = 0; c < width * height * 3; c++)
byteBuffer[c] = originalBuffer[c];
glTexImage2D(..., byteBuffer, ...);
free(byteBuffer);
}
Failing that, supposing your int is four times as large as a byte, you could upload the original as an RGBA texture that's four times as large as its real size, then shrink it down in a shader, combining the .r or .as (as per your endianness) into the correct output channels.

Since ubyte and int are different in size, I guess you have to create a new ubyte array and convert explicitly element by element with a for loop, before passing it to OpenGL.

Related

How to draw TrueType fonts into an array of pixels in WinAPI?

There are some WinAPI functions that draw TrueType fonts into a windows GDI context.
I want to have this TrueType text written, but into my own 2-dimensional array of pixels (the one I just blit onto a window with just this code :)
int blit_mode = STRETCH_DELETESCANS;
void BlitFrame()
{
BITMAPINFO bmi = { {sizeof(BITMAPINFOHEADER), frame_size_x, -frame_size_y,1,32,BI_RGB,0,0,0,0,0}, {0,0,0,0} };
SetStretchBltMode(hdc, blit_mode);
int result = StretchDIBits(hdc,
0, 0, client_x, client_y,
0, 0, frame_size_x, frame_size_y,
frame_bitmap,
&bmi,
DIB_RGB_COLORS,
SRCCOPY);
}
This code is very messy in WinAPI and I couldn't find out (at least to this moment) how to do that.
How can I do that?
tnx Barmak Shemirani
i get your code and produced something like that
void BlitFrame2()
{
BITMAPINFO bmi = { {sizeof(BITMAPINFOHEADER), frame_size_x, -frame_size_y,1,32,BI_RGB,0,0,0,0,0}, {0,0,0,0} };
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateBitmap(frame_size_x, frame_size_y, 1, 32, frame_bitmap);
HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
SetBkMode(memdc, TRANSPARENT);
SetTextColor(memdc, 0xffffff);
TextOut(memdc, 0, 0, "123", 3);
SelectObject(memdc, oldbmp);
GetDIBits(memdc, hbitmap, 0, frame_size_y, frame_bitmap, &bmi, 0);
DeleteObject(hbitmap);
DeleteDC(memdc);
// ReleaseDC(0, hdc);
SetStretchBltMode(hdc, blit_mode);
int result = StretchDIBits(hdc,
0, 0, client_x, client_y,
0, 0, frame_size_x, frame_size_y,
frame_bitmap,
&bmi,
DIB_RGB_COLORS,
SRCCOPY);
}
its adds stable text to my blitted frames, tnx
hovever i would get yet some question as i dont understand it
1) could maybe someone tell me a bit more how it works and where memory transfers are? do i have simple byte acces to this pixel table that is updated with drawed text? (for example to postprocess it)
2) it works but it gets slower quite noticably, for example when my oryginal frame was 2 ms (draw some bitmap sprite then blit) when using this tame grows up to 8 ms
3) can i move some of those calls outside of frame loop?
PS
when thinking on this i assume it works like that
1) it copies my pixel table in memdc at some point (where?)
2) it draws to this memdc those fonts
3) GetDIBits updates my oryginal pixel table with changed pixels (im not sure as to this hovever, but almost sure)
4) i blit it just like before
if so instead of one blit i get three (co it should be 3 times slower,
measurements show its more like 4 times, but maybe its an measurment error (2 and 8 may be 2.7 and 8.1 for example)
if this is three it would be ok, hovever i think i not always would need to get those table pixels reupdated from memdc, is there a way of
blitting it stright from memdc? (then it would be only two times slower instead of 3, still it is sad those fount routines cant just render stright into my own ram table - then it would be no slower at all) (isnt it really possible?)

Why would TextOut() be using a different coordinate system than AlphaBlend()?

I'm trying to write a text overlay function that generates a semitransparent background with text on it in the top right hand corner of the viewport. I wrote a test MFC application project with mostly default settings (I don't remember exactly, but AFAIK, none of the settings should cause the problems I'm seeing).
Here is the code:
void DrawSemitransparentRect(CDC& destDC, CRect rect, float percentGrayBackground, COLORREF overlayColour, float overlayPercentOpaque)
{
rect.NormalizeRect();
CDC temp_dc; // Temp dc for semitransparent text background
temp_dc.CreateCompatibleDC(&destDC);
CBitmap layer; // Layer for semitransparent text background
layer.CreateCompatibleBitmap(&destDC, 1, 1);
CBitmap* pOldBitmap = temp_dc.SelectObject(&layer);
BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 0, 0 };
auto DrawSemitransparentRectHelper = [&](COLORREF colour, float transparency)
{
temp_dc.SetPixel(0, 0, colour);
blendFunction.SourceConstantAlpha = BYTE(transparency * 255 / 100);
// Draw semitransparent background
VERIFY(destDC.AlphaBlend(rect.left, rect.top, rect.Width(), rect.Height()
, &temp_dc, 0, 0, 1, 1, blendFunction));
};
// Lighten up the area to make more opaque without changing overlay colour.
DrawSemitransparentRectHelper(RGB(255, 255, 255), percentGrayBackground);
// Draw overlay colour
DrawSemitransparentRectHelper(overlayColour, overlayPercentOpaque);
temp_dc.SelectObject(pOldBitmap);
}
void DrawOverlayText(CDC & dc, CFont &windowFont, CRect const& windowRectDP, CString const& overlayText, CRect* pBoundingRectDP)
{
static bool debug = true;
int savedDC = dc.SaveDC();
::SetMapMode(dc.GetSafeHdc(), MM_TWIPS);
// Reset the window and viewport origins to (0, 0).
CPoint windowOrg, viewportOrg;
::SetWindowOrgEx(dc.GetSafeHdc(), 0, 0, &windowOrg);
::SetViewportOrgEx(dc.GetSafeHdc(), 0, 0, &viewportOrg);
LOGFONT logFont;// = { 12 * 10, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 255, _T("Times New Roman") };
windowFont.GetLogFont(&logFont);
logFont.lfHeight = 12 * 10; // 12 point font? Why isn't this *20? TWIPS are 20ths of a point.
// Font for the overlay text
CFont font;
font.CreatePointFontIndirect(&logFont, &dc);
CFont* pOldFont = dc.SelectObject(&font);
// window rect in Logical Points
CRect windowRect(windowRectDP);
dc.DPtoLP(windowRect);
// Get text extent in Logical Points
CRect textRect;
dc.DrawText(overlayText, textRect, DT_CALCRECT);
// inflation rectangle to add pixels around text
CRect inflate(8, 0, 8, 4);
dc.DPtoLP(&inflate);
// Create the bounding rect on the right hand of the view, making it a few pixels wider.
CRect boundingRect(textRect);
if (!debug)
{
boundingRect.InflateRect(inflate);
}
boundingRect.NormalizeRect();
boundingRect += CPoint(windowRect.Width() - boundingRect.Width(), 0);
CRect boundingRectDP(boundingRect);
if (pBoundingRectDP || !debug)
{
// Get the bounding rect in device coordinates
dc.LPtoDP(boundingRectDP);
*pBoundingRectDP = boundingRectDP;
}
if (!debug)
{
// round the bottom corners of the text box by clipping it
CRgn clip;
boundingRectDP.NormalizeRect();
clip.CreateRoundRectRgn(
boundingRectDP.left + 1 // +1 needed to make rounding coner match more closely to bottom right coner
, boundingRectDP.top - boundingRectDP.Height() // Getting rid of top rounded corners
, boundingRectDP.right
, boundingRectDP.bottom + 1
, 16, 16 // rounding corner may have to be more dynamic for different DPI screens
);
::SelectClipRgn(dc.GetSafeHdc(), (HRGN)clip.GetSafeHandle());
clip.DeleteObject();
}
// Calculatte centre position of text
CPoint centrePos(
boundingRect.left + (boundingRect.Width() - textRect.Width()) / 2 + 1
, boundingRect.top + (boundingRect.Height() - textRect.Height()) / 2 + 1);
if (debug)
{
// in debug mode, output text and then put semitransparent bounding rect over it.
dc.SetBkMode(debug ? OPAQUE : TRANSPARENT);
dc.SetBkColor(RGB(255, 0, 0));
dc.SetTextColor(RGB(0, 0, 0));
dc.TextOut(centrePos.x, centrePos.y, overlayText);
DrawSemitransparentRect(dc, boundingRect, 60, RGB(0, .25 * 255, .75 * 255), 40);
}
else
{
// 2 pixel offset in Logical Points
CPoint textShadowOffset(2, 2);
dc.DPtoLP(&textShadowOffset);
// in !debug mode, output semitransparent bounding rect and then put text over it.
DrawSemitransparentRect(dc, boundingRect, 60, RGB(0, .25 * 255, .75 * 255), 40);
dc.SetBkMode(debug ? OPAQUE : TRANSPARENT);
dc.SetTextColor(RGB(0, 0, 0));
dc.TextOut(centrePos.x, centrePos.y, overlayText);
dc.SetTextColor(RGB(255, 255, 255));
dc.TextOut(centrePos.x - textShadowOffset.x, centrePos.y - textShadowOffset.y, overlayText);
}
// Restore DC's state
dc.SelectObject(pOldFont);
dc.RestoreDC(savedDC);
}
// OnPaint() function for CView derived class.
void COverlayOnCViewView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CString m_overlayText = _T("abcdefg ABCDEFG");
CFont windowFont;
LOGFONT logFont = { -12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, _T("Segoe UI") };
windowFont.CreatePointFontIndirect(&logFont, &dc);
CRect windowRect;
GetClientRect(windowRect);
DrawOverlayText(dc, windowFont, windowRect, m_overlayText, nullptr);
}
Now, this works perfectly well in the default project, where I get the following:
But when I put it into another preexisting project, I get this:
You can see that the text is actually positioned above the translucent rectangle.
If I move the rectangle down the height of the text box, by changing
boundingRect += CPoint(windowRect.Width() - boundingRect.Width(), 0);
to
boundingRect += CPoint(windowRect.Width() - boundingRect.Width(), textRect.Height());
I get:
It's like the text function is specifying the bottom left corner rather than the top left corner for placement.
I wrote the free functions so that it should work with any DC, even if that DC has had its coordinate system manipulated, but perhaps I've forgotten to reset something?
The default project is using MFC 14.0.24212.0, but the project I tried to import this code into is using MFC 12.0.21005.1. Could that be an issue? I'm not sure how to change the default project to use the earlier version of MFC to test that.
Edit
Note that in the default project, I could have put the code into the OnDraw() function like this:
void COverlayOnCViewView::OnDraw(CDC* pDC)
{
COverlayOnCViewDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
CString m_overlayText = _T("abcdefg ABCDEFG");
CFont windowFont;
LOGFONT logFont = { -12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, _T("Segoe UI") };
windowFont.CreatePointFontIndirect(&logFont, pDC);
CRect windowRect;
GetClientRect(windowRect);
DrawOverlayText(*pDC, windowFont, windowRect, m_overlayText, nullptr);
}
The only reason why I didn't was because the application I'm putting this into doesn't have one and I wanted to mimic that project as closely as possible. If you create a default application to test this, remember either to put the ON_WM_PAINT() macro in the MESSAGE MAP or use the OnDraw() function shown instead. They both seem to have the same results in the default project.

OpenGL Immediate Mode textures not working

I am attempting to build a simple project using immediate mode textures.
Unfortunately, when I render, the GL color shows up rather than the texture. I've searched around for solutions, but found no meaningful difference between online examples and my code.
I've reduced it to a minimal failing example, which I have provided here. If my understanding is correct, this should produce a textured quad, with corners of black, red, green, and blue. Unfortunately, it appears purple, as if it's ignoring the texture completely. What am I doing wrong?
#include <glut.h>
GLuint tex;
void displayFunc() {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_TRIANGLE_STRIP);
glColor3f(0.5, 0, 1);
glTexCoord2f(0.0, 0.0);
glVertex2f(-1.0, -1.0);
glTexCoord2f(1.0, 0.0);
glVertex2f(1.0, -1.0);
glTexCoord2f(0.0, 1.0);
glVertex2f(-1.0, 1.0);
glTexCoord2f(1.0, 1.0);
glVertex2f(1.0, 1.0);
glEnd();
glutSwapBuffers();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowPosition(0, 0);
glutInitWindowSize(640, 480);
glutCreateWindow("Test");
glutDisplayFunc(displayFunc);
GLubyte textureData[] = { 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0 };
GLsizei width = 2;
GLsizei height = 2;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB8, GL_UNSIGNED_BYTE, (GLvoid*)textureData);
glutMainLoop();
}
The output:
Also possibly worth mentioning:
I am building this project on a Mac (running El Capitan 10.11.1)
Graphics card: NVIDIA GeForce GT 650M 1024 MB
You're passing an invalid argument to glTexImage2D(). GL_RGB8 is not one of the supported values for the 7th (format) argument. The correct value is GL_RGB.
Sized formats, like GL_RGB8, are used for the internalFormat argument. In that case, the value defines both the number of components and the size of each component used for the internal storage of the texture.
The format and type parameters define the data you pass in. For these, the format only defined the number of components, while the type defines the type and size of each component.
Whenever you have problems with your OpenGL code, make sure that you call glGetError() to check for errors. In this case, you would see a GL_INVALID_ENUM error caused by your glTexImage2D() call.

Generating and updating 8-bit gray-scale texture in OpenGL ES 2.0

For an OpenGL texture cache I need to initialize a large (≥ 2048x2048) texture and then frequently update little sections of it.
The following (pseudo-)code works:
// Setup texture
int[] buffer = new int[2048*2048 / 4]; // Generate dummy buffer with 1 byte per pixel
int id = glGenTexture();
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 2048, 2048, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
// Perform update
glBindTexture(GL_TEXTURE_2D, id);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, data);
But I find the totally unnecessary creation of a 4MB int-buffer a bit undesirable to say the least. So, I tried the following instead:
// Setup texture
int id = glGenTexture();
glBindTexture(GL_TEXTURE_2D, id);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 0, 0, 2048, 2048, 0);
This gave me a GL_INVALID_OPERATION error, which I believe is caused by the fact that the frame-buffer does not contain an alpha value, so rather than just setting that to 1, the call fails.
Next attempt:
// Setup texture
int id = glGenTexture();
glBindTexture(GL_TEXTURE_2D, id);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0, 2048, 2048, 0);
This works, but now my glTexSubImage2D call fails with GL_INVALID_OPERATION because it specifies GL_ALPHA instead of GL_LUMINANCE. So, I changed that as well to get:
// Perform update
glBindTexture(GL_TEXTURE_2D, id);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
And I changed my shader to read the value from the r rather than the a component.
This works on some devices, but on the iPhone 3GS, I still get the GL_INVALID_OPERATION error in the glTexSubImage2D call. Why? And how can I fix this? Is there some way, for example, to change the internal texture format? Or can I create some other framebuffer that does have an alpha component that I can use as the source for glCopyTexImage2D?
data can be NULL in your glTexImage2D() call if you just want to allocate an empty texture:
data may be a null pointer. In this case, texture memory is allocated to accommodate a texture of width and height. You can then download subtextures to initialize this texture memory. The image is undefined if the user tries to apply an uninitialized portion of the texture image to a primitive.

Optimizing an OpenGL ES operation?

I'm using the following code to draw characters on screen (each UTF8 character is a texture):
int row = 0;
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glVertexPointer(2, GL_FLOAT, 0, vertices);
for (StdStr* line in _lines) {
const char* str = [line cString];
for (int i = 0; i < strlen(str); i++) {
((XX::OGL::GLESContext*)context)->viewport(C_WIDTH*i,
C_HEIGHT*row,
C_WIDTH,
C_HEIGHT);
glColor4f(1.0, 1.0, 1.0, 1.0);
glBindTexture(GL_TEXTURE_2D, _textures[0] + *(str + i));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
row++;
}
When there are a lot of characters, the code takes longer to run. In this case, almost 99% of the time is spent in the glDrawArrays routine. Is it possible to minimise the amount of calls to glDrawArrays? The OpenGL ES version is 1.1.
Actually I think that you should try to limit the amount of calls to viewport, glBindTexture and glDrawArrays.
Technically, you should pack all your characters in a single texture, so that you can bind it once.
Then, you could compute the vertices and texcoords in a loop like you do actually, but doing the viewport maths yourself, and accumulating results in a CPU array. Once your array constituted, you should submit a draw call once, providing this array.
You can probably find inpiration here:
http://www.angelcode.com/products/bmfont/
http://sourceforge.net/projects/oglbmfont/

Resources