-import "instance"
+import "OpenGLDisplayDriver"
#include <stdio.h>
#include <math.h>
#define OFFSET(s, m) ((uint)(uintptr) (&((s *) 0)->m))
-import "fontManager"
import "textureManager"
////
-static struct DMDrawVertex
+static struct DMDrawVertexFlat
{
- // TODO FIXME: switch to short
float vertex[2];
float texcoord0[2];
uint32 color;
} __attribute__((aligned(16)));
+static struct DMDrawVertex
+{
+ short vertex[2];
+ short texcoord0[2];
+ uint32 color;
+} __attribute__((aligned(16)));
+
struct DMDrawBuffer
{
GLuint vbo;
define DM_PROGRAM_NORMAL = 0;
define DM_PROGRAM_ALPHABLEND = 1;
+define DM_PROGRAM_ALPHABLEND_INTENSITY = 2;
#ifdef _DEBUG
static inline void OpenGLErrorCheck( const char *file, int line )
#define ERRORCHECK()
#endif
-define rotationNormFactor = 32767.0f;
+#define DM_IMAGE_ROTATION_NORMFACTOR (32767.0f)
+#define DM_VERTEX_VERTEX_NORMFACTOR (4.0f)
+#define DM_VERTEX_TEXCOORD_NORMFACTOR (32767.0)
static GLuint dmCreateShader( GLenum type, const char *shadersource, const char *optionstring )
{
"void main()\n"
"{\n"
" \n"
-" varTexcoord0 = inTexcoord0;\n"
+" varTexcoord0 = inTexcoord0 * (1.0/" CC_STRINGIFY(DM_VERTEX_TEXCOORD_NORMFACTOR) ");\n"
" varColor = inColor;\n"
" gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n"
" return;\n"
"void main()\n"
"{\n"
" \n"
-" varTexcoord0 = inTexcoord0;\n"
+" varTexcoord0 = inTexcoord0 * (1.0/" CC_STRINGIFY(DM_VERTEX_TEXCOORD_NORMFACTOR) ");\n"
" varColor = inColor;\n"
" gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n"
" return;\n"
"}\n"
;
+const char *dmVertexShaderAlphaIntensity =
+"#version 130\n"
+"uniform mat4 uniMatrix;\n"
+"in vec2 inVertex;\n"
+"in vec2 inTexcoord0;\n"
+"in vec4 inColor;\n"
+"out vec2 varTexcoord0;\n"
+"out vec4 varColor;\n"
+"void main()\n"
+"{\n"
+" \n"
+" varTexcoord0 = inTexcoord0 * (1.0/" CC_STRINGIFY(DM_VERTEX_TEXCOORD_NORMFACTOR) ");\n"
+" varColor = inColor;\n"
+" gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n"
+" return;\n"
+"}\n"
+;
+
+
+const char *dmFragmentShaderAlphaIntensity =
+"#version 130\n"
+"uniform sampler2D texBase;\n"
+"in vec2 varTexcoord0;\n"
+"in vec4 varColor;\n"
+"out vec4 gl_FragColor;\n"
+"void main()\n"
+"{\n"
+" vec2 tex;\n"
+" tex = texture2D( texBase, varTexcoord0 ).rg;\n"
+" gl_FragColor = vec4( varColor.rgb * tex.g, varColor.a * tex.r );\n"
+" return;\n"
+"}\n"
+;
+
static void matrixOrtho( float *m, float left, float right, float bottom, float top, float nearval, float farval )
{
float x = 2.0f / ( right - left );
int drawBarrierIndex;
uint32 orderBarrierMask;
- // Font manager
- FontManager fm;
-
// Counter to track program uniforms and such
int64 updateCount;
GLuint prevProgram;
- static void flushRenderDrawBuffer( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount )
- {
- if( !program || flags.prehistoricOpenGL )
- {
- glEnable( GL_TEXTURE_2D );
- glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
- glColor3f( 1.0, 1.0, 1.0 );
-
- glEnableClientState( GL_VERTEX_ARRAY );
- glEnableClientState( GL_TEXTURE_COORD_ARRAY );
- glEnableClientState( GL_COLOR_ARRAY );
-
- glVertexPointer( 2, GL_FLOAT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) );
- glTexCoordPointer( 2, GL_FLOAT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) );
- glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) );
-
- glDrawArrays( GL_TRIANGLES, 0, vertexCount );
-
- glDisableClientState( GL_VERTEX_ARRAY );
- glDisableClientState( GL_TEXTURE_COORD_ARRAY );
- glDisableClientState( GL_COLOR_ARRAY );
- glDisable( GL_TEXTURE_2D );
- }
- else
- {
- glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
- if( program->vertexloc != -1 )
- {
- glEnableVertexAttribArray( program->vertexloc );
- glVertexAttribPointer( program->vertexloc, 2, GL_FLOAT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) );
- }
- if( program->texcoord0loc != -1 )
- {
- glEnableVertexAttribArray( program->texcoord0loc );
- glVertexAttribPointer( program->texcoord0loc, 2, GL_FLOAT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) );
- }
- if( program->colorloc != -1 )
- {
- glEnableVertexAttribArray( program->colorloc );
- glVertexAttribPointer( program->colorloc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) );
- }
-
- glDrawArrays( GL_TRIANGLES, 0, vertexCount );
-
- if( program->vertexloc != -1 )
- glDisableVertexAttribArray( program->vertexloc );
- if( program->texcoord0loc != -1 )
- glDisableVertexAttribArray( program->texcoord0loc );
- if( program->colorloc != -1 )
- glDisableVertexAttribArray( program->colorloc );
- }
-
- #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER
- glFlush();
- #endif
- }
-
- static DMProgram *dmFlushUseProgram( int programIndex )
+ static DMProgram *flushUseProgram( int programIndex )
{
DMProgram *program;
return program;
}
-public:
- bool init( FontManager fontManager, DrawManagerFlags flags )
- {
- int drawBufferIndex, programIndex;
- DMDrawBuffer *drawBuffer;
-
- imageBufferCount = 0;
- imageBufferSize = 4096;
- imageBuffer = new DMImageBuffer[imageBufferSize];
- imageBufferTmp = new DMImageBuffer[imageBufferSize];
-
- for( drawBufferIndex = 0 ; drawBufferIndex < DM_CONTEXT_DRAW_BUFFER_COUNT ; drawBufferIndex++ )
- {
- drawBuffer = &this.drawBuffer[drawBufferIndex];
- drawBuffer->glType = GL_FLOAT;
- drawBuffer->vertexCount = 0;
- drawBuffer->vertexAlloc = DM_CONTEXT_DRAW_BUFFER_VERTEX_ALLOC;
- glGenBuffers( 1, &drawBuffer->vbo );
- glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
- glBufferData( GL_ARRAY_BUFFER, drawBuffer->vertexAlloc * sizeof(DMDrawVertex), 0, GL_DYNAMIC_DRAW );
- drawBuffer->vertexBuffer = new DMDrawVertex[drawBuffer->vertexAlloc];
- }
-
- fm = fontManager;
- this.flags = flags;
-
- if( !flags.prehistoricOpenGL )
- {
- DMProgram *program;
- for( programIndex = 0 ; programIndex < DM_PROGRAM_COUNT ; programIndex++ )
- {
- program = &shaderprograms[ programIndex ];
- program->flags = 0;
- program->lastUpdateCount = -1;
- }
- program = &shaderprograms[ DM_PROGRAM_NORMAL ];
- if( !( dmCreateProgram( program, dmVertexShaderNormal, dmFragmentShaderNormal, 0 ) ) )
- return false;
- program = &shaderprograms[ DM_PROGRAM_ALPHABLEND ];
- if( !( dmCreateProgram( program, dmVertexShaderAlpha, dmFragmentShaderAlpha, 0 ) ) )
- return false;
- // glUseProgram( 0 );
- }
-
- updateCount = 0;
-
- return true;
- }
-
-
- void end( )
- {
- int i;
-
- for( i = 0 ; i < DM_CONTEXT_DRAW_BUFFER_COUNT ; i++ )
- {
- DMDrawBuffer *db = &drawBuffer[i];
- glDeleteBuffers( 1, &db->vbo );
- delete db->vertexBuffer;
- }
-
- // TODO: Destroy the shaders!
- delete imageBuffer;
- delete imageBufferTmp;
- }
-
- void ready( int viewportwidth, int viewportheight )
- {
- glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&prevProgram);
- // while(glGetError());
-
- // ERRORCHECK();
-
- // Save OpenGL state
- // FIXME: no glPushAttrib() in core profile
-//#ifndef SHADERS
- glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS );
- glPushAttrib( GL_ALL_ATTRIB_BITS );
-//#endif
-
- // Prepare rendering pass
- matrixOrtho( matrix, 0.0, (float)viewportwidth, (float)viewportheight, 0.0, -1.0f, 1.0 );
- drawBarrierIndex = 0;
- orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
- orderBarrierMask = 0;
-
- updateCount++;
- }
-
- void drawImage( DMImage *image, int offsetx, int offsety, int sizex, int sizey, uint32 color )
+ static void flushRenderDrawBufferArchaic( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount )
{
- DMImageBuffer *imageBuffer;
-
- if( image->flags.empty || ( sizex <= 0 ) || ( sizey <= 0 ) )
- return;
+ glEnable( GL_TEXTURE_2D );
+ glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+ glColor3f( 1.0, 1.0, 1.0 );
- if( imageBufferCount >= imageBufferSize )
- {
- imageBufferSize <<= 1;
- this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
- imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
- }
-
- imageBuffer = &this.imageBuffer[ imageBufferCount ];
- imageBuffer->image = image;
- imageBuffer->offsetx = (short)offsetx;
- imageBuffer->offsety = (short)offsety;
- imageBuffer->sizex = (short)sizex;
- imageBuffer->sizey = (short)sizey;
- #if DM_ENABLE_IMAGE_ROTATION
- imageBuffer->angsin = 0;
- imageBuffer->angcos = (short)rotationNormFactor;
- #endif
- imageBuffer->color = color;
- imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
-
- #if DM_RENDER_IMAGE_DEBUG
- printf( " Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
- #endif
+ glEnableClientState( GL_VERTEX_ARRAY );
+ glEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ glEnableClientState( GL_COLOR_ARRAY );
- imageBufferCount++;
- }
-
- void drawImageFloat( DMImage *image, float offsetx, float offsety, float sizex, float sizey, float angsin, float angcos, uint32 color )
- {
- DMImageBuffer *imageBuffer;
-
- if( image->flags.empty || sizex <= 0 || sizey <= 0 )
- return;
+ glVertexPointer( 2, GL_FLOAT, sizeof(DMDrawVertexFlat), (void *)OFFSET(DMDrawVertexFlat,vertex) );
+ glTexCoordPointer( 2, GL_FLOAT, sizeof(DMDrawVertexFlat), (void *)OFFSET(DMDrawVertexFlat,texcoord0) );
+ glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(DMDrawVertexFlat), (void *)OFFSET(DMDrawVertexFlat,color) );
- if( imageBufferCount >= imageBufferSize )
- {
- imageBufferSize <<= 1;
- this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
- imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
- }
+ glDrawArrays( GL_TRIANGLES, 0, vertexCount );
- imageBuffer = &this.imageBuffer[ imageBufferCount ];
- imageBuffer->image = image;
- imageBuffer->offsetx = (short)offsetx;
- imageBuffer->offsety = (short)offsety;
- imageBuffer->sizex = (short)sizex;
- imageBuffer->sizey = (short)sizey;
- #if DM_ENABLE_IMAGE_ROTATION
- imageBuffer->angsin = (short)roundf( angsin * rotationNormFactor );
- imageBuffer->angcos = (short)roundf( angcos * rotationNormFactor );
- #endif
- imageBuffer->color = color;
- imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
+ glDisableClientState( GL_VERTEX_ARRAY );
+ glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+ glDisableClientState( GL_COLOR_ARRAY );
+ glDisable( GL_TEXTURE_2D );
- #if DM_RENDER_IMAGE_DEBUG
- printf( " Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
+ #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER
+ glFlush();
#endif
-
- imageBufferCount++;
}
-
- void flushImages( )
+ void flushDrawImagesArchaic( )
{
bool flushflag, stateBlend;
int index, vertexCount, programIndex;
DMImage *image, *bindimage;
Texture texture, bindTexture;
DMDrawBuffer *drawBuffer;
- DMDrawVertex *vboVertex;
+ DMDrawVertexFlat *vboVertex;
DMProgram *program;
ERRORCHECK();
this.orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
if(imageBufferCount)
{
- /* Sort by image type and texture, minimize state changes */
+ // Sort by image type and texture, minimize state changes
dmSortImages( this.imageBuffer, imageBufferTmp, imageBufferCount, (uint32)( (intptr_t)this.imageBuffer >> 4 ) );
- /* Fill a drawBuffer, write vertex and texcoords */
+ // Fill a drawBuffer, write vertex and texcoords
drawBuffer = &this.drawBuffer[drawBufferIndex];
drawBufferIndex = ( drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
{
glUnmapBuffer( GL_ARRAY_BUFFER );
// Flush font manager texture updates
- fm.flushUpdate( );
+ flush( );
// Render buffered images
- flushRenderDrawBuffer( drawBuffer, program, vertexCount );
+ flushRenderDrawBufferArchaic( drawBuffer, program, vertexCount );
drawBuffer = &this.drawBuffer[drawBufferIndex];
drawBufferIndex = ( drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
if( programIndex != image->programIndex )
{
programIndex = image->programIndex;
- program = dmFlushUseProgram( programIndex );
+ program = flushUseProgram( programIndex );
}
if( texture != bindTexture )
{
#endif
#if DM_ENABLE_IMAGE_ROTATION
- angsin = (float)imageBuffer->angsin * (1.0f/rotationNormFactor);
- angcos = (float)imageBuffer->angcos * (1.0f/rotationNormFactor);
+ angsin = (float)imageBuffer->angsin * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
+ angcos = (float)imageBuffer->angcos * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
sizex = (float)imageBuffer->sizex;
sizey = (float)imageBuffer->sizey;
vx0 = (float)( imageBuffer->offsetx );
}
glUnmapBuffer( GL_ARRAY_BUFFER );
+
// Flush font manager texture updates
- fm.flushUpdate( );
+ flush();
+
// Render buffered images
- flushRenderDrawBuffer( drawBuffer, program, vertexCount );
+ flushRenderDrawBufferArchaic( drawBuffer, program, vertexCount );
imageBufferCount = 0;
ERRORCHECK();
//#endif
}
+ void flushRenderDrawBuffer( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount )
+ {
+ glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+ if( program->vertexloc != -1 )
+ {
+ glEnableVertexAttribArray( program->vertexloc );
+ glVertexAttribPointer( program->vertexloc, 2, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) );
+ }
+ if( program->texcoord0loc != -1 )
+ {
+ glEnableVertexAttribArray( program->texcoord0loc );
+ glVertexAttribPointer( program->texcoord0loc, 2, GL_UNSIGNED_SHORT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) );
+ }
+ if( program->colorloc != -1 )
+ {
+ glEnableVertexAttribArray( program->colorloc );
+ glVertexAttribPointer( program->colorloc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) );
+ }
+
+ glDrawArrays( GL_TRIANGLES, 0, vertexCount );
+
+ if( program->vertexloc != -1 )
+ glDisableVertexAttribArray( program->vertexloc );
+ if( program->texcoord0loc != -1 )
+ glDisableVertexAttribArray( program->texcoord0loc );
+ if( program->colorloc != -1 )
+ glDisableVertexAttribArray( program->colorloc );
+
+ #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER
+ glFlush();
+ #endif
+ }
+
+ void flushDrawImages( )
+ {
+ int index, stateblend, vertexcount, flushflag, programIndex;
+ float vx0, vx1, vx2, vx3, vy0, vy1, vy2, vy3;
+ #if DM_ENABLE_IMAGE_ROTATION
+ float angsin, angcos, sizex, sizey;
+ #endif
+ float tx0, tx1, ty0, ty1;
+ DMImageBuffer *imageBuffer;
+ DMImage *image, *bindimage;
+ Texture texture, bindtexture;
+ DMDrawBuffer *drawBuffer;
+ DMDrawVertex *vbovertex;
+ DMProgram *program;
+
+ ERRORCHECK();
+
+ this.drawBarrierIndex = 0;
+ this.orderBarrierMask = this.drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
+ if( imageBufferCount )
+ {
+ /* Sort by image type and texture, minimize state changes */
+ dmSortImages( this.imageBuffer, this.imageBufferTmp, imageBufferCount, (uint32)( (uintptr)this.imageBuffer >> 4 ) );
+
+ /* Fill a drawBuffer, write vertex and texcoords */
+ drawBuffer = &this.drawBuffer[this.drawBufferIndex];
+ this.drawBufferIndex = ( this.drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
+ glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+ vbovertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
+ vertexcount = 0;
+
+ glActiveTexture( GL_TEXTURE0 );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ glDisable( GL_BLEND );
+
+ #if DM_RENDER_IMAGE_DEBUG
+ printf( " Flush %d images\n", (int)imageBufferCount );
+ #endif
+
+ bindimage = 0;
+ bindtexture = 0;
+ stateblend = 0;
+ programIndex = -1;
+ program = 0;
+ imageBuffer = this.imageBuffer;
+ for( index = 0 ; index < imageBufferCount ; index++, imageBuffer++ )
+ {
+ image = imageBuffer->image;
+ texture = image->texture;
+
+ flushflag = 0;
+ if( image != bindimage )
+ {
+ if( stateblend != ( image->flags.blending ) )
+ flushflag = 1;
+ if( texture != bindtexture )
+ flushflag = 1;
+ }
+ if( vertexcount >= ( drawBuffer->vertexAlloc - 6 ) )
+ flushflag = 1;
+
+ if( flushflag )
+ {
+ if( vertexcount )
+ {
+ glUnmapBuffer( GL_ARRAY_BUFFER );
+ // Flush font manager texture updates
+ flush();
+
+ // Render buffered images
+ flushRenderDrawBuffer( drawBuffer, program, vertexcount );
+ drawBuffer = &this.drawBuffer[this.drawBufferIndex];
+ this.drawBufferIndex = ( this.drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT;
+ glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+ vbovertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
+ vertexcount = 0;
+ }
+
+ if( stateblend != ( image->flags.blending ) )
+ {
+ stateblend = image->flags.blending;
+ ( stateblend ? glEnable : glDisable )( GL_BLEND );
+ #if DM_RENDER_IMAGE_DEBUG
+ printf( " Switch blending %d\n", ( stateblend != 0 ) );
+ #endif
+ }
+ if( programIndex != image->programIndex )
+ {
+ programIndex = image->programIndex;
+ program = flushUseProgram( programIndex );
+ }
+ if( texture != bindtexture )
+ {
+ bindtexture = texture;
+ glBindTexture( GL_TEXTURE_2D, bindtexture.glTex );
+ #if DM_RENDER_IMAGE_DEBUG
+ printf( " Switch to texture 0x%x\n", (int)texture.ordermask );
+ #endif
+ }
+ bindimage = image;
+ }
+
+ #if DM_RENDER_IMAGE_DEBUG
+ printf( " Render image at %d %d, order 0x%x, texture %p\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex, texture );
+ #endif
+
+ #if DM_ENABLE_IMAGE_ROTATION
+ /* FIXME TODO: Don't go through float, compute texcoord integers directly */
+ angsin = (float)imageBuffer->angsin * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
+ angcos = (float)imageBuffer->angcos * (1.0f/DM_IMAGE_ROTATION_NORMFACTOR);
+ sizex = (float)imageBuffer->sizex;
+ sizey = (float)imageBuffer->sizey;
+ vx0 = (float)( imageBuffer->offsetx );
+ vy0 = (float)( imageBuffer->offsety );
+ vx1 = vx0 + ( angcos * sizex );
+ vy1 = vy0 + ( angsin * sizex );
+ vx2 = vx0 - ( angsin * sizey );
+ vy2 = vy0 + ( angcos * sizey );
+ vx3 = vx0 + ( angcos * sizex ) - ( angsin * sizey );
+ vy3 = vy0 + ( angsin * sizex ) + ( angcos * sizey );
+ #else
+ /* FIXME TODO: Don't go through float, compute texcoord integers directly */
+ vx0 = (float)( imageBuffer->offsetx );
+ vy0 = (float)( imageBuffer->offsety );
+ vx3 = vx0 + (float)( imageBuffer->sizex );
+ vy3 = vy0 + (float)( imageBuffer->sizey );
+ vx1 = vx3;
+ vy1 = vy0;
+ vx2 = vx0;
+ vy2 = vy3;
+ #endif
+
+ /* FIXME TODO: Don't go through float, compute texcoord integers directly */
+ tx0 = (float)( image->srcx ) * texture.widthinv;
+ ty0 = (float)( image->srcy ) * texture.heightinv;
+ tx1 = (float)( image->srcx + image->sizex ) * texture.widthinv;
+ ty1 = (float)( image->srcy + image->sizey ) * texture.heightinv;
+
+ /* Write data to VBO */
+ /* TODO: write vertex/texcoord all at once with SSE */
+ vbovertex[0].vertex[0] = (short)( vx3 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[0].vertex[1] = (short)( vy3 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[0].texcoord0[0] = (short)( tx1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[0].texcoord0[1] = (short)( ty1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[0].color = imageBuffer->color;
+ vbovertex[1].vertex[0] = (short)( vx1 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[1].vertex[1] = (short)( vy1 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[1].texcoord0[0] = (short)( tx1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[1].texcoord0[1] = (short)( ty0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[1].color = imageBuffer->color;
+ vbovertex[2].vertex[0] = (short)( vx2 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[2].vertex[1] = (short)( vy2 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[2].texcoord0[0] = (short)( tx0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[2].texcoord0[1] = (short)( ty1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[2].color = imageBuffer->color;
+ vbovertex[3].vertex[0] = (short)( vx0 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[3].vertex[1] = (short)( vy0 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[3].texcoord0[0] = (short)( tx0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[3].texcoord0[1] = (short)( ty0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[3].color = imageBuffer->color;
+ vbovertex[4].vertex[0] = (short)( vx2 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[4].vertex[1] = (short)( vy2 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[4].texcoord0[0] = (short)( tx0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[4].texcoord0[1] = (short)( ty1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[4].color = imageBuffer->color;
+ vbovertex[5].vertex[0] = (short)( vx1 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[5].vertex[1] = (short)( vy1 * DM_VERTEX_VERTEX_NORMFACTOR );
+ vbovertex[5].texcoord0[0] = (short)( tx1 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[5].texcoord0[1] = (short)( ty0 * DM_VERTEX_TEXCOORD_NORMFACTOR );
+ vbovertex[5].color = imageBuffer->color;
+
+ vbovertex += 6;
+ vertexcount += 6;
+ }
+
+ glUnmapBuffer( GL_ARRAY_BUFFER );
+ // Flush font manager texture updates
+ flush();
+ // Render buffered images
+ flushRenderDrawBuffer( drawBuffer, program, vertexcount );
+ imageBufferCount = 0;
+
+ ERRORCHECK();
+
+ #if 1
+ glBindBuffer( GL_ARRAY_BUFFER, 0 );
+ glabCurArrayBuffer = 0;
+
+ glUseProgram( prevProgram );
+ #endif
+ }
+
+ // Restore OpenGL state
+ // FIXME: no glPushAttrib() in core profile
+//#ifndef SHADERS
+ glPopAttrib();
+ glPopClientAttrib();
+//#endif
+ }
+
+public:
+
+ virtual void flush();
+
+ bool init( DrawManagerFlags flags )
+ {
+ int drawBufferIndex, programIndex;
+ DMDrawBuffer *drawBuffer;
+ uint vertexSize;
+
+ imageBufferCount = 0;
+ imageBufferSize = 4096;
+ imageBuffer = new DMImageBuffer[imageBufferSize];
+ imageBufferTmp = new DMImageBuffer[imageBufferSize];
+
+ this.flags = flags;
+
+ if( flags.prehistoricOpenGL )
+ vertexSize = sizeof(DMDrawVertexFlat);
+ else
+ {
+ DMProgram *program;
+ for( programIndex = 0 ; programIndex < DM_PROGRAM_COUNT ; programIndex++ )
+ {
+ program = &shaderprograms[ programIndex ];
+ program->flags = 0;
+ program->lastUpdateCount = -1;
+ }
+ program = &shaderprograms[ DM_PROGRAM_NORMAL ];
+ if( !( dmCreateProgram( program, dmVertexShaderNormal, dmFragmentShaderNormal, 0 ) ) )
+ return false;
+ program = &shaderprograms[ DM_PROGRAM_ALPHABLEND ];
+ if( !( dmCreateProgram( program, dmVertexShaderAlpha, dmFragmentShaderAlpha, 0 ) ) )
+ return false;
+ program = &shaderprograms[ DM_PROGRAM_ALPHABLEND_INTENSITY ];
+ if( !( dmCreateProgram( program, dmVertexShaderAlphaIntensity, dmFragmentShaderAlphaIntensity, 0 ) ) )
+ return false;
+ // glUseProgram( 0 );
+ vertexSize = sizeof(DMDrawVertex);
+ }
+
+ for( drawBufferIndex = 0 ; drawBufferIndex < DM_CONTEXT_DRAW_BUFFER_COUNT ; drawBufferIndex++ )
+ {
+ drawBuffer = &this.drawBuffer[drawBufferIndex];
+ drawBuffer->glType = GL_FLOAT;
+ drawBuffer->vertexCount = 0;
+ drawBuffer->vertexAlloc = DM_CONTEXT_DRAW_BUFFER_VERTEX_ALLOC;
+ glGenBuffers( 1, &drawBuffer->vbo );
+ glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo );
+ glBufferData( GL_ARRAY_BUFFER, drawBuffer->vertexAlloc * vertexSize, 0, GL_DYNAMIC_DRAW );
+ drawBuffer->vertexBuffer = new byte[drawBuffer->vertexAlloc * vertexSize];
+ }
+
+ updateCount = 0;
+
+ return true;
+ }
+
+
+ void end( )
+ {
+ int i;
+
+ for( i = 0 ; i < DM_CONTEXT_DRAW_BUFFER_COUNT ; i++ )
+ {
+ DMDrawBuffer *db = &drawBuffer[i];
+ glDeleteBuffers( 1, &db->vbo );
+ delete db->vertexBuffer;
+ }
+
+ // TODO: Destroy the shaders!
+ delete imageBuffer;
+ delete imageBufferTmp;
+ }
+
+ void ready( int viewportwidth, int viewportheight )
+ {
+ int mindex;
+ float norminv;
+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&prevProgram);
+ // while(glGetError());
+
+ // ERRORCHECK();
+
+ // Save OpenGL state
+ // FIXME: no glPushAttrib() in core profile
+//#ifndef SHADERS
+ glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS );
+ glPushAttrib( GL_ALL_ATTRIB_BITS );
+//#endif
+
+ // Prepare rendering pass
+ matrixOrtho( matrix, 0.0, (float)viewportwidth, (float)viewportheight, 0.0, -1.0f, 1.0 );
+ norminv = 1.0f / DM_VERTEX_VERTEX_NORMFACTOR;
+ for( mindex = 0 ; mindex < 12 ; mindex += 4 )
+ {
+ matrix[mindex+0] *= norminv;
+ matrix[mindex+1] *= norminv;
+ matrix[mindex+2] *= norminv;
+ }
+ drawBarrierIndex = 0;
+ orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT;
+ orderBarrierMask = 0;
+
+ updateCount++;
+ }
+
+ void drawImage( DMImage *image, int offsetx, int offsety, int sizex, int sizey, uint32 color )
+ {
+ DMImageBuffer *imageBuffer;
+
+ if( image->flags.empty || ( sizex <= 0 ) || ( sizey <= 0 ) )
+ return;
+
+ if( imageBufferCount >= imageBufferSize )
+ {
+ imageBufferSize <<= 1;
+ this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
+ imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
+ }
+
+ imageBuffer = &this.imageBuffer[ imageBufferCount ];
+ imageBuffer->image = image;
+ imageBuffer->offsetx = (short)offsetx;
+ imageBuffer->offsety = (short)offsety;
+ imageBuffer->sizex = (short)sizex;
+ imageBuffer->sizey = (short)sizey;
+ #if DM_ENABLE_IMAGE_ROTATION
+ imageBuffer->angsin = 0;
+ imageBuffer->angcos = (short)DM_IMAGE_ROTATION_NORMFACTOR;
+ #endif
+ imageBuffer->color = color;
+ imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
+
+ #if DM_RENDER_IMAGE_DEBUG
+ printf( " Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
+ #endif
+
+ imageBufferCount++;
+ }
+
+ void drawImageFloat( DMImage *image, float offsetx, float offsety, float sizex, float sizey, float angsin, float angcos, uint32 color )
+ {
+ DMImageBuffer *imageBuffer;
+
+ if( image->flags.empty || sizex <= 0 || sizey <= 0 )
+ return;
+
+ if( imageBufferCount >= imageBufferSize )
+ {
+ imageBufferSize <<= 1;
+ this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize];
+ imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize];
+ }
+
+ imageBuffer = &this.imageBuffer[ imageBufferCount ];
+ imageBuffer->image = image;
+ imageBuffer->offsetx = (short)offsetx;
+ imageBuffer->offsety = (short)offsety;
+ imageBuffer->sizex = (short)sizex;
+ imageBuffer->sizey = (short)sizey;
+ #if DM_ENABLE_IMAGE_ROTATION
+ imageBuffer->angsin = (short)roundf( angsin * DM_IMAGE_ROTATION_NORMFACTOR );
+ imageBuffer->angcos = (short)roundf( angcos * DM_IMAGE_ROTATION_NORMFACTOR );
+ #endif
+ imageBuffer->color = color;
+ imageBuffer->orderindex = image->orderMask | this.orderBarrierMask;
+
+ #if DM_RENDER_IMAGE_DEBUG
+ printf( " Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex );
+ #endif
+
+ imageBufferCount++;
+ }
+
+ void flushImages( )
+ {
+ if( flags.prehistoricOpenGL )
+ flushDrawImagesArchaic( );
+ else
+ flushDrawImages( );
+ }
+
void drawBarrier( )
{
drawBarrierIndex++;
import "File"
import "atlasBuilder"
+import "imgDistMap"
#include <math.h>
public:
FontManager fm;
- virtual bool init();
+ virtual bool init(int channelCount);
// Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
virtual int createTexture( int width, int height );
#define FM_ENABLE_HINTING (1)
#define FM_SUBPIXEL_ROUNDING_RANGE (16)
-#if FM_SUPPORT_GLYPH_ROTATION
- #define FM_TEXTURE_PADDING (1)
-#else
- #define FM_TEXTURE_PADDING (0)
-#endif
-#define FM_DEBUG_WHITERECT (0)
#define FM_HASH_TABLE_SIZE (4096)
return 1;
}
- static void copyGlyphBitmap( byte *dst, int glyphwidth, int glyphheight, int dststride, int glyphindex )
- {
- int x, y;
- byte *dstrow, *src;
- FT_GlyphSlot glyphslot;
-
- glyphslot = face->glyph;
- src = glyphslot->bitmap.buffer;
- for( y = 0 ; y < glyphslot->bitmap.rows ; y++ )
- {
- dstrow = &dst[ y * dststride ];
- for( x = 0 ; x < glyphslot->bitmap.width ; x++ )
- dstrow[ x ] = src[ x ];
- src += glyphslot->bitmap.width;
- }
- return;
- }
-
static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
{
FT_Vector ftKerning;
FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
return ftKerning.x;
}
+
+ static inline byte *getGlyphBitmap( int glyphindex )
+ {
+ FT_GlyphSlot glyphslot;
+ glyphslot = face->glyph;
+ return glyphslot->bitmap.buffer;
+ }
};
#define FM_DEF_CODEPOINT_BITS (32)
int glyphalloc;
int glyphcount;
int hashtable[FM_HASH_TABLE_SIZE];
+ int glyphPaddingWidth;
+
+ void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth );
+ void *processImageContext;
+
+ /*
+ public void setFontImageProcessing(
+ void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
+ void *opaquecontext )
+ {
+ this.processImage = processImage;
+ this.processImageContext = opaquecontext;
+ }
+ */
+
+ float outlineRadius;
+ float outlineAlphaFactor;
+ float outlineIntensityFactor;
+
+ static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth )
+ {
+ int x, y;
+ byte *src, *dst, *dstrow;
+ float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
+ float *distancemap, *dmap;
+
+ distancemap = new float[width * height];
+
+ src = &image[0];
+ imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
+
+ alphafactor = font.outlineAlphaFactor; //2.0f;
+ intensityfactor = font.outlineIntensityFactor; // 0.2f;
+ range = (float)font.outlineRadius;
+ rangeinv = 1.0f / range;
+
+ dmap = distancemap;
+ dst = &image[0];
+ for( y = 0 ; y < height ; y++ )
+ {
+ dstrow = dst;
+ for( x = 0 ; x < width ; x++ )
+ {
+ rangebase = ( range - dmap[ x ] ) * rangeinv;
+ alpha = alphafactor * rangebase;
+ intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase );
+ /* Alpha channel */
+ dstrow[0] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
+ /* Intensity channel */
+ dstrow[1] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
+ dstrow += bytesperpixel;
+ }
+ dst += bytesperline;
+ dmap += width;
+ }
+ delete distancemap;
+ }
+
+ public void setOutline(float intensityFactor, float alphaFactor, float radius) // TODO: Figure out radius?
+ {
+ outlineIntensityFactor = intensityFactor;
+ outlineAlphaFactor = alphaFactor;
+ outlineRadius = radius;
+ processImage = outlineProcessGlyphImage;
+ processImageContext = this;
+ }
~FMFont()
{
static float getVertAlign( FMTextAlignment align, int size )
{
+ float sizef = size;
if( align.vertAlignment == top )
- return ascender * size;
+ return ascender * sizef;
else if( align.vertAlignment == middle )
- return middleAlign * size;
+ return middleAlign * sizef;
else if( align.vertAlignment == bottom )
- return descender * (float)size;
+ return descender * sizef;
return 0.0f;
}
static FT_Library ftLibrary2;
+static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
+{
+ int x, y;
+ for( y = 0 ; y < glyphheight ; y++ )
+ {
+ byte *dstrow = &dst[ y * dststride ];
+ for( x = 0 ; x < glyphwidth ; x++ )
+ dstrow[ x ] = src[ x ];
+ src += glyphwidth;
+ }
+}
+
+static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
+{
+ int x, y;
+ for( y = 0 ; y < glyphheight ; y++ )
+ {
+ byte *dstrow = &dst[ y * dststride ];
+ for( x = 0 ; x < glyphwidth ; x++ )
+ dstrow[ x << 1 ] = src[ x ];
+ src += glyphwidth;
+ }
+}
+
+static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
+{
+ int x, y;
+ for( y = 0 ; y < glyphheight ; y++ )
+ {
+ byte *dstrow = &dst[ y * dststride ];
+ for( x = 0 ; x < glyphwidth ; x++ )
+ dstrow[ x << 2 ] = src[ x ];
+ src += glyphwidth;
+ }
+}
+
public class FontManager
{
FontManagerRenderer renderer;
int width, height;
float widthinv, heightinv;
+ int bytesperpixel;
+ int bytesperline;
+ int channelindex;
AtlasBuilder atlas { };
byte *texdata;
FMState states[FM_MAX_STATES];
int nstates;
- #if FM_DEBUG_WHITERECT
- static void addWhiteRect( int w, int h )
- {
- int gx, gy;
- if( atlas.addRect( w, h, &gx, &gy ) )
- {
- // Rasterize
- byte * dst = &texdata[ gx + ( gy * width ) ];
- int x, y;
- for( y = 0 ; y < h ; y++)
- {
- for( x = 0 ; x < w ; x++ )
- dst[x] = 0xff;
- dst += width;
- }
-
- dirtyrect[0] = Min( dirtyrect[0], gx );
- dirtyrect[1] = Min( dirtyrect[1], gy );
- dirtyrect[2] = Max( dirtyrect[2], gx + w );
- dirtyrect[3] = Max( dirtyrect[3], gy + h );
- }
- }
- #endif
+ void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
+
static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale )
{
- int i, glyphindex, advance, x0, y0, x1, y1, glyphwidth, glyphheight, gx, gy;
- #if FM_TEXTURE_PADDING >= 1
- int x, y;
- #endif
+ int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
+ int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
uint64 glyphdef;
FMGlyph *glyph;
uint32 hashindex;
int padding, added;
byte *bdst;
- byte *dst;
+ byte *dst, *src;
glyph = 0;
if( size < 0.2 )
return 0;
- padding = blurradius + FM_TEXTURE_PADDING;
+ padding = blurradius + font.glyphPaddingWidth;
/* Find code point and size. */
glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale );
i = font.glyphs[i].listnext;
}
- /* Could not find glyph, create it. */
+ /* Could not find glyph, create it */
if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
{
glyphindex = -1;
#endif
y0 = -(int)ceilf( font.limitmaxy * (float)size );
y1 = -(int)floorf( font.limitminy * (float)size );
- glyphheight = y1 - y0;
- i = ( glyphheight - size ) / 3;
+ i = ( (y1 - y0) - size ) / 3;
y0 += i;
y1 -= i;
}
return 0;
}
}
- glyphwidth = ( x1 - x0 ) + ( padding << 1 );
- glyphheight = ( y1 - y0 ) + ( padding << 1 );
+ glyphwidth = ( x1 - x0 );
+ glyphheight = ( y1 - y0 );
+ glyphareawidth = glyphwidth + (padding << 1);
+ glyphareaheight = glyphheight + (padding << 1);
// Find free spot for the rect in the atlas
- added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
+ added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
if( !( added ) && ( onAtlasFull ) )
{
/* Atlas is full, let the user to resize the atlas (or not), and try again. */
onAtlasFull();
- added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
+ added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
}
if( !( added ) )
return 0;
glyph->glyphindex = glyphindex;
glyph->x0 = (short)gx;
glyph->y0 = (short)gy;
- glyph->x1 = (short)( glyph->x0 + glyphwidth );
- glyph->y1 = (short)( glyph->y0 + glyphheight );
+ glyph->x1 = (short)( glyph->x0 + glyphareawidth );
+ glyph->y1 = (short)( glyph->y0 + glyphareaheight );
glyph->advance = (short)advance;
glyph->offsetx = (short)( x0 - padding );
glyph->offsety = (short)( y0 - padding );
glyph->listnext = 0;
glyph->imageIndex = -1;
if( renderer.registerImage )
- glyph->imageIndex = renderer.registerImage( gx, gy, glyphwidth, glyphheight );
+ glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight );
- /* Add char to hash table */
+ // Add char to hash table
glyph->listnext = font.hashtable[hashindex];
font.hashtable[hashindex] = font.glyphcount - 1;
+ // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
+ dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
+ for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
+ memset( dst, 0, glyphareawidth * bytesperpixel );
+
/* Rasterize */
- dst = &texdata[ ( glyph->x0 + padding ) + ( ( glyph->y0 + padding ) * this.width ) ];
+ dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
- buildCursorGlyph( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width );
+ {
+ src = new byte[ glyphwidth * glyphheight ];
+ buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
+ copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
+ delete src;
+ }
else
- font.ftFont.copyGlyphBitmap( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width, glyphindex );
+ {
+ src = font.ftFont.getGlyphBitmap(glyphindex);
+ copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
+ }
/* Blur */
if( blurradius > 0 )
{
- bdst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
+#if 1
+ printf( "Do NOT use the blur! This will be removed in favor of external image processing\n" );
+#endif
+ bdst = &texdata[ glyph->x0 * bytesperpixel + ( glyph->y0 * bytesperline ) ];
if( blurscale == 1 )
- blur(bdst, glyphwidth, glyphheight, this.width, blurradius );
+ blur(bdst, glyphareawidth, glyphareaheight, this.width, blurradius );
else
- blurScale(bdst, glyphwidth, glyphheight, this.width, blurradius, blurscale );
+ blurScale(bdst, glyphareawidth, glyphareaheight, this.width, blurradius, blurscale );
}
- #if FM_TEXTURE_PADDING >= 1
- dst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
- for( y = 0 ; y < glyphheight ; y++ )
- {
- dst[ y * this.width] = 0;
- dst[ ( glyphwidth - 1 ) + ( y * this.width ) ] = 0;
- }
- for( x = 0 ; x < glyphwidth ; x++ )
+ // User custom font image processing
+ if(font.processImage)
{
- dst[ x ] = 0;
- dst[ x + ( ( glyphheight - 1 ) * this.width ) ] = 0;
+ dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
+ font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth );
}
- #endif
dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
virtual void (*onAtlasFull)();
// Create and destroy font manager
- bool create( int width, int height, FontManagerRenderer renderer)
+ bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
{
bool result = false;
+ if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
+ return 0;
+ if( ( width <= 0 ) || ( height <= 0 ) )
+ return 0;
+
this.renderer = renderer;
incref renderer;
- renderer.init();
+ renderer.init(channelCount);
// Initialize implementation library
if(FMFreeTypeFont::init() )
{
this.width = width;
this.height = height;
+ bytesperpixel = channelCount;
+ bytesperline = width * bytesperpixel;
+ this.channelindex = channelIndex;
if(renderer.createTexture( width, height ))
{
if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
// Create texture for the cache.
widthinv = 1.0f / width;
heightinv = 1.0f / height;
- texdata = new byte[width * height];
+ texdata = new byte[width * height * bytesperpixel];
if(texdata)
{
- memset( texdata, 0, width * height );
+ memset( texdata, 0, height * bytesperline );
dirtyrect[0] = this.width;
dirtyrect[1] = this.height;
dirtyrect[2] = 0;
dirtyrect[3] = 0;
- // Add white rect at 0,0 for debug drawing.
- #if FM_DEBUG_WHITERECT
- fmAddWhiteRect(2, 2 );
- #endif
+ if( bytesperpixel == 1 )
+ copyGlyphBitmap = copyGlyphBitmap1;
+ else if( bytesperpixel == 2 )
+ copyGlyphBitmap = copyGlyphBitmap2;
+ else
+ copyGlyphBitmap = copyGlyphBitmap4;
pushState();
clearState();
states[ nstates - 1 ].blurscale = (uint16)scale;
}
+ // Set image manipuation callback
+ void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
+ {
+
+ }
// State handling
void pushState( )
}
// Add font from file
- FMFont addFont(const String path )
+ FMFont addFont(const String path, int glyphPaddingWidth )
{
FMFont font = null;
File f = FileOpen(path, read);
if(data)
{
f.Read(data, 1, dataSize);
- font = addFontData(data, dataSize );
+ font = addFontData(data, dataSize, glyphPaddingWidth);
if(!font)
delete data;
}
}
// Add font from data ; do not free( data ), the font manager will do that when removing the font
- FMFont addFontData( byte *data, int dataSize )
+ FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
{
FMFont font
{
glyphs = new FMGlyph[FM_INIT_GLYPHS];
glyphalloc = FM_INIT_GLYPHS;
+ glyphPaddingWidth = glyphPaddingWidth;
};
if(font)
{
}
}
- void fmGetFontLimits( int *retlimitminy, int *retlimitmaxy )
+ void getFontLimits( int *retlimitminy, int *retlimitmaxy )
{
FMFont font;
FMState *state;
int i;
// Copy old texture data over.
- if( !( data = new byte[width * height] ) )
+ if( !( data = new byte[width * bytesperline] ) )
return false;
for( i = 0 ; i < this.height ; i++ )
{
- byte * dst = &data[ i * width ];
- byte * src = &this.texdata[ i * this.width ];
- memcpy( dst, src, this.width );
+ byte * dst = &data[ (i * width) * bytesperpixel ];
+ byte * src = &this.texdata[ i * this.bytesperline ];
+ memcpy( dst, src, bytesperline);
if( width > this.width )
- memset( dst+this.width, 0, width - this.width );
+ memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
}
if( height > this.height )
- memset( &data[ this.height * width ], 0, ( height - this.height ) * width );
+ memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
delete this.texdata;
texdata = data;
this.width = width;
this.height = height;
+ this.bytesperline = this.width * bytesperpixel;
widthinv = 1.0f / this.width;
heightinv = 1.0f / this.height;
atlas.reset( width, height );
// Clear texture data.
- texdata = renew texdata byte[width * height];
+ texdata = renew texdata byte[width * height * bytesperpixel];
if(!texdata) return 0;
memset( this.texdata, 0, width * height );
this.width = width;
this.height = height;
+ this.bytesperline = width * bytesperpixel;
this.widthinv = 1.0f / this.width;
this.heightinv = 1.0f / this.height;
- // Add white rect at 0,0 for debug drawing.
- #if FM_DEBUG_WHITERECT
- fmAddWhiteRect(2, 2 );
- #endif
-
return true;
}
return false;