2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/render/renderers/scene-graph-text-renderer.h>
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/public-api/actors/text-actor.h>
24 #include <dali/internal/common/text-parameters.h>
25 #include <dali/internal/common/text-vertex-2d.h>
26 #include <dali/internal/event/text/font-impl.h>
27 #include <dali/internal/render/gl-resources/context.h>
28 #include <dali/internal/render/gl-resources/texture.h>
29 #include <dali/internal/render/gl-resources/texture-cache.h>
30 #include <dali/internal/render/gl-resources/texture-units.h>
31 #include <dali/internal/render/common/culling-algorithms.h>
32 #include <dali/internal/render/common/performance-monitor.h>
33 #include <dali/internal/render/common/vertex.h>
34 #include <dali/internal/render/renderers/scene-graph-renderer-debug.h>
35 #include <dali/internal/render/shaders/program.h>
36 #include <dali/internal/render/shaders/scene-graph-shader.h>
37 #include <dali/internal/update/controllers/scene-controller.h>
39 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gTextFilter = Debug::Filter::New(Debug::Concise, false, "LOG_SCENE_GRAPH_TEXT_RENDERER");
55 TextRenderer* TextRenderer::New( NodeDataProvider& dataprovider )
57 return new TextRenderer( dataprovider );
60 TextRenderer::~TextRenderer()
64 mTextureCache->RemoveObserver(mTextureId, this);
72 void TextRenderer::TextureDiscarded( ResourceId textureId )
74 DALI_ASSERT_DEBUG( mTextureId == textureId || mTextureId == 0 );
80 void TextRenderer::AllocateTextParameters()
82 if( !mTextParameters )
84 mTextParameters = new TextParameters;
88 void TextRenderer::SetTextureId( ResourceId textureId )
90 DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::SetTextureId(%d)\n", textureId );
94 mTextureCache->RemoveObserver(mTextureId, this);
97 mTextureId = textureId;
102 mTextureCache->AddObserver(textureId, this);
106 void TextRenderer::UpdateIndexBuffer( std::size_t size )
112 DALI_ASSERT_DEBUG( (size % 4 == 0) && "Invalid vertex length");
114 // @todo need to create a simple gpu-buffer-manager class which allow us
115 // to use a single indice buffer for all text-renderers and image renderers (minus 9 patch).
117 // number of indices(points) = number of (vertices / 4 ) = number of quads * 6
118 // to display the quad as two triangles (each triangle has 3 points).
119 // equivalent to 1.5 * number verts, which can be done with bit shifting.
120 std::size_t numberIndices = size + (size >> 1);
122 std::size_t numberQuads = size >> 2; // quads = verts / 4
124 GLushort* indices = new GLushort[ numberIndices ];
127 for( std::size_t i = 0; i < numberQuads; ++i )
136 * Draw 2 triangles with clock wise winding: 0->1->2 and 0->2->3
138 std::size_t vertIndex = i << 2; // vert index = i * 4
140 indices[ n++ ] = 0 + vertIndex;
141 indices[ n++ ] = 1 + vertIndex;
142 indices[ n++ ] = 2 + vertIndex;
144 indices[ n++ ] = 0 + vertIndex;
145 indices[ n++ ] = 2 + vertIndex;
146 indices[ n++ ] = 3 + vertIndex;
149 mIndexBuffer->UpdateDataBuffer( numberIndices * sizeof(GLushort),indices );
154 void TextRenderer::SetVertexData( TextVertexBuffer* vertexData )
156 DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::SetVertexData(this: %p, vertexData: %p)\n", this, vertexData );
160 DALI_ASSERT_DEBUG( 0 && "No vertex data"); // vertex data structure is required even for empty strings
163 if( vertexData->mVertices.size() > 0 )
165 SetTextureId(vertexData->mTextureId);
167 if ( !mVertexBuffer )
169 mVertexBuffer = new GpuBuffer( *mContext, GpuBuffer::ARRAY_BUFFER, GpuBuffer::DYNAMIC_DRAW );
174 mIndexBuffer = new GpuBuffer( *mContext, GpuBuffer::ELEMENT_ARRAY_BUFFER, GpuBuffer::STATIC_DRAW );
177 mVertexBuffer->UpdateDataBuffer( vertexData->mVertices.size() * sizeof(TextVertex2D), &vertexData->mVertices[0] );
179 UpdateIndexBuffer( vertexData->mVertices.size() ); // Used in DoRender()
181 // Get inverted text size, as this is faster for the shader to operate on,
182 // and shader won't throw any errors performing a multiplication rather than a divide by zero
183 // on a bad size value.
184 mGeometryExtent = vertexData->mGeometryExtent;
188 // no text to display, delete the GPU buffers, this will stop anything rendering.
189 mVertexBuffer.Reset();
190 mIndexBuffer.Reset();
192 // vertex data no longer required.
196 void TextRenderer::SetFontSize( float pixelSize )
198 mPixelSize = pixelSize;
201 void TextRenderer::SetGradient( const Vector4& color, const Vector2& startPoint, const Vector2& endPoint )
203 AllocateTextParameters();
204 mTextParameters->SetGradient( color, startPoint, endPoint );
207 void TextRenderer::SetTextColor( const Vector4& color )
209 if( NULL == mTextColor )
211 mTextColor = new Vector4( color );
219 void TextRenderer::SetOutline( const bool enable, const Vector4& color, const Vector2& params )
221 AllocateTextParameters();
223 mTextParameters->SetOutline( enable, color, params );
226 void TextRenderer::SetGlow( const bool enable, const Vector4& color, const float params )
228 AllocateTextParameters();
230 mTextParameters->SetGlow( enable, color, params );
233 void TextRenderer::SetDropShadow( const bool enable, const Vector4& color, const Vector2& offset, const float size )
235 AllocateTextParameters();
237 mTextParameters->SetShadow( enable, color, offset, size );
240 void TextRenderer::SetSmoothEdge( float params )
245 void TextRenderer::GlContextDestroyed()
249 mVertexBuffer->GlContextDestroyed();
253 mIndexBuffer->GlContextDestroyed();
257 void TextRenderer::GlCleanup()
259 mVertexBuffer.Reset();
260 mIndexBuffer.Reset();
263 bool TextRenderer::RequiresDepthTest() const
268 bool TextRenderer::CheckResources()
270 if ( ! ( mVertexBuffer && mIndexBuffer ) )
272 // This character has no geometry, must be a white space
276 if( !mVertexBuffer->BufferIsValid() )
281 if( mTexture == NULL )
283 mTexture = mTextureCache->GetTexture( mTextureId );
285 if( mTexture == NULL )
287 // texture atlas hasn't been created yet
291 if( mTexture->GetTextureId() == 0 )
299 void TextRenderer::ResolveGeometryTypes( BufferIndex bufferIndex, GeometryType& outType, ShaderSubTypes& outSubType )
301 outType = GEOMETRY_TYPE_TEXT;
303 // If we have a color gradient, then we cannot use the default shader.
305 outSubType = SHADER_DEFAULT;
306 if( mTextParameters )
308 if( mTextParameters->IsOutlineEnabled() )
310 if( mTextParameters->IsGlowEnabled() )
312 outSubType = SHADER_GRADIENT_OUTLINE_GLOW;
316 outSubType = SHADER_GRADIENT_OUTLINE;
319 else if( mTextParameters->IsGlowEnabled() )
321 outSubType = SHADER_GRADIENT_GLOW;
323 else if( mTextParameters->IsDropShadowEnabled() )
325 outSubType = SHADER_GRADIENT_SHADOW;
329 outSubType = SHADER_GRADIENT;
334 bool TextRenderer::IsOutsideClipSpace( const Matrix& modelMatrix, const Matrix& modelViewProjectionMatrix )
336 mContext->IncrementRendererCount();
338 Rect<float> boundingBox(mGeometryExtent.width*-0.5f, mGeometryExtent.height*-0.5f, mGeometryExtent.width, mGeometryExtent.height);
339 DEBUG_BOUNDING_BOX( *mContext, boundingBox, modelViewProjectionMatrix );
341 if(Is2dBoxOutsideClipSpace( modelMatrix, modelViewProjectionMatrix, boundingBox ) )
343 mContext->IncrementCulledCount();
349 void TextRenderer::DoRender( BufferIndex bufferIndex, Program& program, const Matrix& modelViewMatrix, const Matrix& viewMatrix )
351 DALI_ASSERT_DEBUG( NULL != mTexture && "TextRenderer::DoRender. mTexture == NULL." );
352 if( NULL == mTexture )
354 // Nothing to render.
358 DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::DoRender(this: %p) textureId:%d\n", this, mTextureId );
360 mTextureCache->BindTexture( mTexture, mTextureId, GL_TEXTURE_2D, TEXTURE_UNIT_TEXT );
361 if( mTexture->GetTextureId() == 0 )
363 return; // early out if we haven't got a GL texture yet (e.g. due to context loss)
366 // Set sampler uniform
367 const GLint samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER );
368 if( Program::UNIFORM_UNKNOWN != samplerLoc )
371 program.SetUniform1i( samplerLoc, TEXTURE_UNIT_TEXT );
374 mTexture->ApplySampler( TEXTURE_UNIT_TEXT, mSamplerBitfield );
376 const float SMOOTHING_ADJUSTMENT( 12.0f );
377 const float SMOOTHING_ADJUSTMENT_PIXEL_SIZE( 32.0f );
379 float smoothWidth = SMOOTHING_ADJUSTMENT / mPixelSize;
380 float smoothing = mSmoothing;
382 const GLint smoothingLoc = program.GetUniformLocation( Program::UNIFORM_SMOOTHING );
383 if( Program::UNIFORM_UNKNOWN != smoothingLoc )
385 smoothWidth = std::min( std::min(mSmoothing, 1.0f - mSmoothing), smoothWidth );
387 if( mPixelSize < SMOOTHING_ADJUSTMENT_PIXEL_SIZE )
389 smoothing *= Lerp( mPixelSize / SMOOTHING_ADJUSTMENT_PIXEL_SIZE, 0.5f, 1.0f );
392 program.SetUniform2f( smoothingLoc, std::max(0.0f, smoothing - smoothWidth), std::min(1.0f, smoothing + smoothWidth) );
395 if( mTextParameters )
397 if( mTextParameters->IsOutlineEnabled() )
399 const GLint outlineLoc = program.GetUniformLocation( Program::UNIFORM_OUTLINE );
400 const GLint outlineColorLoc = program.GetUniformLocation( Program::UNIFORM_OUTLINE_COLOR );
402 if( Program::UNIFORM_UNKNOWN != outlineLoc && Program::UNIFORM_UNKNOWN != outlineColorLoc )
404 const Vector2& outline = mTextParameters->GetOutlineThickness();
405 const Vector4& outlineColor = mTextParameters->GetOutlineColor();
406 float outlineWidth = outline[1] + smoothWidth;
407 float outlineStart = outline[0];
408 float outlineEnd = std::min( 1.0f, outlineStart + outlineWidth );
410 program.SetUniform2f(outlineLoc, outlineStart, outlineEnd);
411 program.SetUniform4f(outlineColorLoc, outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
415 if( mTextParameters->IsGlowEnabled() )
417 const GLint glowLoc = program.GetUniformLocation( Program::UNIFORM_GLOW );
418 const GLint glowColorLoc = program.GetUniformLocation( Program::UNIFORM_GLOW_COLOR );
420 if( Program::UNIFORM_UNKNOWN != glowLoc && Program::UNIFORM_UNKNOWN != glowColorLoc )
422 // if mGlow is > mSmoothing we get an inverted glyph, so clamp the value
423 program.SetUniform1f(glowLoc, std::min(mTextParameters->GetGlowIntensity(), mSmoothing));
424 const Vector4& glowColor = mTextParameters->GetGlowColor();
425 program.SetUniform4f(glowColorLoc, glowColor.r, glowColor.g, glowColor.b, glowColor.a);
429 if( mTextParameters->IsDropShadowEnabled() )
431 const GLint shadowLoc = program.GetUniformLocation( Program::UNIFORM_SHADOW );
432 const GLint shadowColorLoc = program.GetUniformLocation( Program::UNIFORM_SHADOW_COLOR );
433 const GLint shadowSmoothingLoc = program.GetUniformLocation( Program::UNIFORM_SHADOW_SMOOTHING );
435 if( Program::UNIFORM_UNKNOWN != shadowLoc && Program::UNIFORM_UNKNOWN != shadowColorLoc && Program::UNIFORM_UNKNOWN != shadowSmoothingLoc )
437 // convert shadow offset from tile to atlas coordinates
438 const Vector2& offset( mTextParameters->GetDropShadowOffset() / mTexture->GetWidth());
439 float shadowSmoothing = std::max(0.0f, smoothing - mTextParameters->GetDropShadowSize() );
440 program.SetUniform2f(shadowLoc, offset.x, offset.y);
441 const Vector4& dropShadowColor = mTextParameters->GetDropShadowColor();
442 program.SetUniform4f(shadowColorLoc, dropShadowColor.r, dropShadowColor.g, dropShadowColor.b, dropShadowColor.a);
443 program.SetUniform2f( shadowSmoothingLoc, std::max(0.0f, shadowSmoothing - smoothWidth), std::min(1.0f, shadowSmoothing + smoothWidth) );
448 // Set the text color uniform
449 const GLint textColorLoc = program.GetUniformLocation( Program::UNIFORM_TEXT_COLOR );
450 if( Program::UNIFORM_UNKNOWN != textColorLoc )
452 Vector4 textColor( (NULL != mTextColor) ? *mTextColor : TextStyle::DEFAULT_TEXT_COLOR );
454 program.SetUniform4f(textColorLoc, textColor.r, textColor.g, textColor.b, textColor.a);
457 if( mTextParameters )
459 // All shaders except default shader require the uGradientLine.zw uniform to be set
460 // at the very least. (setting it to vec2(0.0, 0.0) will disable gradient)
461 Vector2 projection( Vector2::ZERO );
462 Vector2 startPoint( TextStyle::DEFAULT_GRADIENT_START_POINT );
463 startPoint = mTextParameters->GetGradientStartPoint();
464 projection = mTextParameters->GetGradientEndPoint() - startPoint;
465 if( mTextParameters->IsGradientEnabled() ) // same as: mGradientEndPoint != mGradientStartPoint
467 projection /= projection.LengthSquared();
469 // For valid gradients Gradient Color and Text Size information must be set.
470 const GLint gradientColorLoc = program.GetUniformLocation( Program::UNIFORM_GRADIENT_COLOR );
471 const GLint textSizeLoc = program.GetUniformLocation( Program::UNIFORM_INVERSE_TEXT_SIZE );
473 if( Program::UNIFORM_UNKNOWN != gradientColorLoc && Program::UNIFORM_UNKNOWN != textSizeLoc )
475 const Vector4& color = mTextParameters->GetGradientColor();
476 program.SetUniform4f( gradientColorLoc, color.r, color.g, color.b, color.a );
478 Vector2 invTextSize( mGeometryExtent );
479 invTextSize.x = invTextSize.x > Math::MACHINE_EPSILON_1 ? 1.0f / invTextSize.x : 1.0f;
480 invTextSize.y = invTextSize.y > Math::MACHINE_EPSILON_1 ? 1.0f / invTextSize.y : 1.0f;
482 program.SetUniform2f( textSizeLoc, invTextSize.width, invTextSize.height );
486 // If we don't have a gradient present (mGradientEnabled) but the shader requires
487 // gradient information (gradientRequired), then we set
488 // uGradientLine.zw = vec2(0.0, 0.0) to force vColor = uColor in the expression.
489 // If we do have a gradient present, then we set up all information.
490 const GLint gradientLineLoc = program.GetUniformLocation( Program::UNIFORM_GRADIENT_LINE );
491 if( Program::UNIFORM_UNKNOWN != gradientLineLoc )
493 program.SetUniform4f( gradientLineLoc,
501 const GLint positionLoc = program.GetAttribLocation(Program::ATTRIB_POSITION);
502 const GLint texCoordLoc = program.GetAttribLocation(Program::ATTRIB_TEXCOORD);
504 mContext->EnableVertexAttributeArray( positionLoc );
505 mContext->EnableVertexAttributeArray( texCoordLoc );
508 DALI_ASSERT_DEBUG( mVertexBuffer->BufferIsValid() );
509 mVertexBuffer->Bind();
510 DALI_ASSERT_DEBUG( mIndexBuffer->BufferIsValid() );
511 mIndexBuffer->Bind();
514 mContext->VertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, sizeof(TextVertex2D), &v->mX);
515 mContext->VertexAttribPointer(texCoordLoc, 4, GL_FLOAT, GL_FALSE, sizeof(TextVertex2D), &v->mU);
517 const GLsizei indexCount = mIndexBuffer->GetBufferSize() / sizeof(GLushort); // compiler will optimize this to >> if possible
518 mContext->DrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (void *) 0);
520 mContext->DisableVertexAttributeArray( positionLoc );
521 mContext->DisableVertexAttributeArray( texCoordLoc );
525 TextRenderer::TextRenderer( NodeDataProvider& dataprovider )
526 : Renderer( dataprovider ),
534 mSmoothing( Dali::TextStyle::DEFAULT_SMOOTH_EDGE_DISTANCE_FIELD ),
539 } // namespace SceneGraph
541 } // namespace Internal