Draw stroke characters with outlines in openGL - opengl

I am using openGL to draw fonts. My fonts need to be rotatable and I cannot use glut, so I created a static class that has vertex lists for every needed character. I then render these using GL_LINE_STRIP.
Recently I've been asked to allow my characters to have outlines, sort of like they would if you went into word, created a bold character and then under Font, gave it an outline.
Not sure how to put a picture of that in stack overflow.
I was wondering if there was a way to do that given my current method of drawing characters using lines. Is it possible to have a line border, or is that inherently nonsense?

Two passes:
const float OUTLINE_WIDTH = 3.0f;
const float INNER_WIDTH = 1.0f;
glLineWidth( OUTLINE_WIDTH );
glColor3ub( outline.r, outline.g, outline.b );
DrawText();
glLineWidth( INNER_WIDTH );
glColor3ub( inner.r, inner.g, inner.b );
DrawText();

Related

Direct2D CreateTextLayout() - How to get caret coordinates

I am rendering Text using Direct2D starting with a text Layout
HRESULT hr = m_spWriteFactory->CreateTextLayout(
m_wsText.c_str( ),
m_wsText.length( ),
m_spWriteTextFormat.Get( ),
m_rect.right - m_rect.left - m_spacing.right - m_spacing.left,
m_rect.bottom - m_rect.top - m_spacing.top - m_spacing.bottom,
&m_spTextLayout
);
and then rendering it to a bitmap which I later use with Direct3D
m_sp2DDeviceContext->DrawTextLayout(
D2D1::Point2F( m_spacing.left, m_spacing.top ),
m_spTextLayout.Get( ),
m_spTextBrush.Get( )
);
I would like to draw a simple thin flashing line as a caret. I know how to draw a line and how to make it appear / disappear.
Question: How do I get the starting point and the end point coordinates for my caret line?
Simplification: If it is much easier to assume that the text consists of one line only, then that's ok. But of course a more general solution is appreciated.
Use IDWriteTextLayout's hit-testing functions to determine these:
HitTestTextPosition for mapping a text position index (relative to the first character) to a rectangle.
HitTestTextRange for getting a whole range of rectangles such as for selection.
HitTestPoint for mapping a mouse coordinate to a text position index.
For carets, this below works for all horizontal reading directions and proportional/monospace fonts:
...
DWRITE_HIT_TEST_METRICS hitTestMetrics;
float caretX, caretY;
bool isTrailingHit = false; // Use the leading character edge for simplicity here.
// Map text position index to caret coordinate and hit-test rectangle.
textLayout->HitTestTextPosition(
textPosition,
isTrailingHit,
OUT &caretX,
OUT &caretY,
OUT &hitTestMetrics
);
// Respect user settings.
DWORD caretWidth = 1;
SystemParametersInfo(SPI_GETCARETWIDTH, 0, OUT &caretWidth, 0);
DWORD halfCaretWidth = caretWidth / 2u;
// Draw a thin rectangle.
D2D1::RectF caretRect = {
layoutOriginX + caretX - halfCaretWidth,
layoutOriginY + hitTestMetrics.top,
layoutOriginX + caretX + (caretWidth - halfCaretWidth),
layoutOriginY + hitTestMetrics.top + hitTestMetrics.height
};
solidColorBrush->SetColor(D2D1::ColorF::AliceBlue);
d2dRenderTarget->FillRectangle(&caretRect, solidColorBrush);
Notes:
The above code as-is doesn't account for vertical reading directions such as in Japanese newspapers. You would need to draw a wide flat caret instead of the tall thin one here when the DWRITE_READING_DIRECTION was either top-to-bottom or bottom-to-top.
IDWriteTextLayout::GetMetrics only gives the overall bounding box to you, not the caret position.
IDWriteTextLayout::HitTestPoint's isInside flag is true if it is over the text itself, not just the layout bounds.
You can get the layout's bounding rectangle via IDWriteTextLayout::GetMetrics.
DWRITE_TEXT_METRICS textMetrics;
textLayout->GetMetrics(&textMetrics);
Your rectangle is
D2D1::RectF( textMetrics.left,
textMetrics.top,
textMetrics.left + textMetrics.width,
textMetrics.top + textMetrics.height );
You can then draw the caret along the right boundary line.

2D Sprite animation techniques with OpenGL

I'm currently trying to setup a 2D sprite animation with OpenGL 4.
For example, I've designed a ball smoothly rotating with Gimp. There are about 32 frames ( 8 frames on 4 rows).
I aim to create a sprite atlas within a 2D texture and store my sprite data in buffers (VBO). My sprite rectangle would be always the same ( i.e. rect(0,0,32,32) ) but my texture coordinates will change each time the frame index is incremented.
I wonder how to modify the coordinates.
As the sprite tiles are stored on several rows if appears to be difficult to manage it in the shader.
Modify the sprite texture coordinate within the buffer using glBufferSubData() ?
I spent a lot of time with OpenGL 1.x....and I get back to OpenGL few months ago and I realized many things changed though. I will try several options though, but your suggestions and experience are welcome.
As the sprite tiles are stored on several rows if appears to be
difficult to manage it in the shader.
Not really, all your sprites are the same size, so you get a perfect uniform grid, and going from some 1D index to 2D is just a matter of division and modulo. Not really hard.
However, why do you even store the single frames in an mxn grid? Now you could store them just in one row. However, in modern GL, we have array textures. These are basically a set of independent 2D layers, all of the same size. You just access them by a 3D coordinate, with the third coordinate being the layer from o to n-1. This is ideally suited for your use case, and will eliminate any issues of texture filtering/bleeding at the borders, and it also will work well with mipmapping (if you need that). When array textures were introduced, the minumim number of layers an implementation is required to support was 64 (it is much higher nowadays), so 32 frames will be a piece of cake even for old GPUs.
You could do this a million ways but I'm going to propose a naive solution:
Create a VBO with 32(frame squares)*2(triangles per frame square)*3(triangle vertices)*5(x,y,z, u,v per vertex) = 960 floats of space. Fill it in with the vertices of all your sprites in a 2 triangler-per frame fashion.
Now according to the docs of glDrawArrays, you can specify where you start and how long you render for. Using this you can specify the following:
int indicesPerFrame = 960/32;
int indexToStart = indicesPerFrame*currentBallFrame;
glDrawArrays( GL_TRIANGLES, indexToStart, indicesPerFrame);
No need to modify the VBO. Now from my point of view, this is overkill to just render 32 frames 1 frame at a time. There are better solutions to this problem but this is the simplest for learning OpenGL4.
In OpenGL 2.1, I'm using your 2nd option:
void setActiveRegion(int regionIndex)
{
UVs.clear();
int numberOfRegions = (int) textureSize / spriteWidth;
float uv_x = (regionIndex % numberOfRegions)/numberOfRegions;
float uv_y = (regionIndex / numberOfRegions)/numberOfRegions;
glm::vec2 uv_up_left = glm::vec2( uv_x , uv_y );
glm::vec2 uv_up_right = glm::vec2( uv_x+1.0f/numberOfRegions, uv_y );
glm::vec2 uv_down_right = glm::vec2( uv_x+1.0f/numberOfRegions, (uv_y + 1.0f/numberOfRegions) );
glm::vec2 uv_down_left = glm::vec2( uv_x , (uv_y + 1.0f/numberOfRegions) );
UVs.push_back(uv_up_left );
UVs.push_back(uv_down_left );
UVs.push_back(uv_up_right );
UVs.push_back(uv_down_right);
UVs.push_back(uv_up_right);
UVs.push_back(uv_down_left);
glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, UVs.size() * sizeof(glm::vec2), &UVs[0]);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Source: http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-11-2d-text/
He implemented it to render 2D Text but it's the same concept!
I hope have helped!

Inverted text when rendering text in Cairo

I am writing a program using Cairo where I need to change the coordinates from screen to Cartesian using the following code:
cairo_translate( cr, x, height );
cairo_scale( cr, 1.0, -1.0 ); // FLIP the Y axis
But when I try to render text (cairo_show_text), the text is inverted also and, hence, unreadable.
The same happens when using a scaled font (cairo_scaled_font_t):
cairo_font_options_t *font_options;
cairo_matrix_t ctm, font_matrix;
cairo_scaled_font_t *scaled_font;
font_options = cairo_font_options_create();
cairo_get_matrix( cr, &ctm );
cairo_get_font_matrix( cr, &font_matrix );
font_matrix.xx = font_matrix.yy = 20.0; // font size
// font_face initialized elsewhere and is valid
scaled_font = cairo_scaled_font_create( font_face, &font_matrix, &ctm, font_options );
cairo_set_scaled_font( cr, scaled_font );
cairo_move_to( cr, 1, 50 );
cairo_show_text( cr, "some text" );
...
I think the matrix needs some adjustments, but I don't know how. Any help is appreciated. Thanks in advance.
EDIT:
The text is upside down. For example, 'L' looks like a Gamma, and 'W' looks like an 'M'.
Theres two ways to do this that I know:
cairo_matrix_t ms, mt, m;
cairo_matrix_init_scale(&ms, 1.0f, -1.0f);
cairo_matrix_init_translate(&mt, 0.0f, -m_height);
cairo_matrix_multiply(&m, &mt, &ms);
cairo_set_matrix(cr, &m);
or:
cairo_scale(cr, 1.0f, -1.0f);
cairo_translate(cr, 0.0f, -m_height);
Both solved the problem for me. Scale the cr by the -1 on y, but that means the text is off the surface, so translate by the -height of the surface and it should sit right.
Since you flip the coordinate system upside down, text is also shown upside down. The solution to this problem is to manually flip back text, by setting a font matrix with a negative entry for the vertical scale. You can do this, by modifying your code for setting the font size like this:
double font_size = 20.0;
font_matrix.xx = font_size;
font_matrix.yy = -font_size; // negative size to vertically flip text
Well, you are flipping the Y-axis, thus the text gets flipped, too. Try this:
cairo_save(cr);
cairo_reset_clip(cr);
cairo_show_text(cr, "Some Text");
cairo_restore(cr);
The above replaces the cairo_show_text() call in your example.

OpenGL Rotate something that has been popped off matrix

I have a question about applying transformations to textures/vertices (I'm an OpenGL learner). I've got a situation where I'm using SharpGL's DrawText function. You can specify the X and Y coordinate but I want to rotate the text so that it's oriented vertically. The function doesn't take an angle though. The way that SharpGL writes text is that it gets an array of bitmaps that it writes to the screen.
No matter what I try, my text always comes out horizontal.
The gl.DrawText function does the following (I dug into the source code).
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.PushMatrix();
gl.LoadIdentity();
int[] viewport = new int[4];
gl.GetInteger(OpenGL.GL_VIEWPORT, viewport);
gl.Ortho(0, width, 0, height, -1, 1);
// Create the appropriate modelview matrix.
gl.MatrixMode(OpenGL.GL_MODELVIEW);
gl.PushMatrix();
gl.LoadIdentity();
gl.Color(r, g, b);
gl.RasterPos(x, y);
gl.PushAttrib(OpenGL.GL_LIST_BIT | OpenGL.GL_CURRENT_BIT |
OpenGL.GL_ENABLE_BIT | OpenGL.GL_TRANSFORM_BIT);
gl.Color(r, g, b);
gl.Disable(OpenGL.GL_LIGHTING);
gl.Disable(OpenGL.GL_TEXTURE_2D);
gl.Disable(OpenGL.GL_DEPTH_TEST);
gl.RasterPos(x, y);
// Set the list base.
gl.ListBase(fontBitmapEntry.ListBase);
// Create an array of lists for the glyphs.
var lists = text.Select(c => (byte) c).ToArray();
// Call the lists for the string.
gl.CallLists(lists.Length, lists);
gl.Flush();
Is there a way that I can make transformation calls apply to any drawing that has already been made and had the associated matrices popped off?
If SharpGL is using glBitmap() behind the scenes you'll have to render the text to a texture (glReadPixels()/PBO/FBO) then render a (rotated) quad with that texture.

Using point sprites with direct x. what steps need to be taken?

This is still an outstanding issue.
I am trying to get a point sprites system workign render a sun in my world. I noticed another user asking a similar question (with the same code, presumably from my class :) ) but they were not able to complete this. My current code for this is as follows:
float fPointSize = 10.0f,fPointScaleB = 100.0f;
IDirect3DDevice9 *m_Device = LudoRenderer::GetInstance()->GetDevice();
m_Device->SetRenderState(D3DRS_POINTSPRITEENABLE,true);
m_Device->SetRenderState(D3DRS_POINTSCALEENABLE,true);
m_Device->SetRenderState(D3DRS_POINTSIZE,
*((DWORD*)&fPointSize));
m_Device->SetRenderState(D3DRS_POINTSCALE_B,
*((DWORD*)&fPointScaleB));
m_Device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
m_Device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);
m_Device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ONE);
std::wstring hardcoded = L"..\\Data\\sun.png";
m_SunTexture = LudoTextureManager::GetInstance()->GetTextureData(hardcoded.c_str()).m_Texture;
m_Device->SetTexture(0,m_SunTexture);
m_Device->DrawPrimitive(D3DPT_POINTLIST,0,12);
I do not see my sun on the screen, and it seems to be doing the alpha blend on the rest of my world, rather than on any sun I'm trying to load. Could this be because of which devices I'm using? Any help would be greatly appreciated :)
You don't actually seem to have a draw call in there. Have you missed posting some code, or is this perhaps your problem?
If you're missing the draw call, I would suggest the DrawPrimitiveUP() call is likely the one you want. You'll also need to set the stream format to match your vetex structure ( setFVF() ). Something along these lines:
#define D3DFVF_SUNVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
struct SUNVERTEX
{
float x,y,z;
DWORD colour;
};
D3DVECTOR pos = { 0.f, 0.f, 0.f }; // Set this to the position you want
SUNVERTEX sunVert = { pos.x, pos.y, pos.z, D3DCOLOR_RGBA( 255, 255, 0, 255 ) };
IDirect3DDevice9& device = LudoRenderer::GetInstance()->GetDevice();
device->SetFVF( D3DFVF_SUNVERTEX );
device->DrawPrimitiveUP( D3DPT_POINTLIST, 1, sunVert, sizeof(SUNVERTEX) );
If you have time I'd strongly recommend reading the "Programming Guide" from the DirectX SDK documentation (you should have it installed). It's not much of a tutorial, but it does cover the basics of DirectX architecture.