1c2a857182a616864ea7b5b80f0f3a379bc14a09
[platform/core/uifw/dali-core.git] / dali / internal / render / renderers / scene-graph-text-renderer.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/render/renderers/scene-graph-text-renderer.h>
20
21 // INTERNAL INCLUDES
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/common/culling-algorithms.h>
29 #include <dali/internal/render/common/performance-monitor.h>
30 #include <dali/internal/render/common/vertex.h>
31 #include <dali/internal/render/renderers/scene-graph-renderer-debug.h>
32 #include <dali/internal/render/shaders/program.h>
33 #include <dali/internal/render/shaders/shader.h>
34 #include <dali/internal/render/gl-resources/texture-cache.h>
35 #include <dali/internal/render/gl-resources/texture.h>
36 #include <dali/internal/update/controllers/scene-controller.h>
37
38 using namespace std;
39
40 #if defined(DEBUG_ENABLED)
41 namespace
42 {
43 Debug::Filter* gTextFilter = Debug::Filter::New(Debug::Concise, false, "LOG_SCENE_GRAPH_TEXT_RENDERER");
44 }
45 #endif
46
47 namespace Dali
48 {
49
50 namespace Internal
51 {
52
53 namespace SceneGraph
54 {
55
56 TextRenderer* TextRenderer::New( RenderDataProvider& dataprovider )
57 {
58   return new TextRenderer( dataprovider );
59 }
60
61 TextRenderer::~TextRenderer()
62 {
63   if(mTextureId > 0)
64   {
65     mTextureCache->RemoveObserver(mTextureId, this);
66   }
67
68   GlCleanup();
69
70   delete mTextColor;
71 }
72
73 void TextRenderer::TextureDiscarded( ResourceId textureId )
74 {
75   DALI_ASSERT_DEBUG( mTextureId == textureId || mTextureId == 0 );
76
77   mTextureId = 0;
78   mTexture = NULL;
79 }
80
81 void TextRenderer::AllocateTextParameters()
82 {
83   if( !mTextParameters )
84   {
85     mTextParameters = new TextParameters;
86   }
87 }
88
89 void TextRenderer::SetTextureId( ResourceId textureId )
90 {
91   DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::SetTextureId(%d)\n", textureId );
92
93   if(mTextureId > 0)
94   {
95     mTextureCache->RemoveObserver(mTextureId, this);
96   }
97
98   mTextureId = textureId;
99   mTexture = NULL;
100
101   if(textureId > 0)
102   {
103     mTextureCache->AddObserver(textureId, this);
104   }
105 }
106
107 void TextRenderer::UpdateIndexBuffer( std::size_t size )
108 {
109   if( size == 0)
110   {
111     return;
112   }
113   DALI_ASSERT_DEBUG( (size % 4 == 0) && "Invalid vertex length");
114
115   // @todo need to create a simple gpu-buffer-manager class which allow us
116   // to use a single indice buffer for all text-renderers and image renderers (minus 9 patch).
117
118   // number of indices(points) = number of (vertices / 4 ) = number of quads * 6
119   // to display the quad as two triangles (each triangle has 3 points).
120   // equivalent to 1.5 * number verts, which can be done with bit shifting.
121   std::size_t numberIndices =  size + (size >> 1);
122
123   std::size_t numberQuads =  size >> 2;  // quads = verts / 4
124
125   GLushort* indices = new GLushort[ numberIndices  ];
126
127   std::size_t n = 0;
128   for( std::size_t i = 0; i < numberQuads; ++i )
129   {
130     /*
131      * 1 --- 2
132      * |    /|
133      * |  A  |
134      * | /   |
135      * 0 --- 3
136      *
137      * Draw 2 triangles with clock wise winding: 0->1->2 and 0->2->3
138      */
139     std::size_t vertIndex = i << 2;  // vert index = i * 4
140
141     indices[ n++ ] = 0 + vertIndex;
142     indices[ n++ ] = 1 + vertIndex;
143     indices[ n++ ] = 2 + vertIndex;
144
145     indices[ n++ ] = 0 + vertIndex;
146     indices[ n++ ] = 2 + vertIndex;
147     indices[ n++ ] = 3 + vertIndex;
148   }
149
150   mIndexBuffer->UpdateDataBuffer( numberIndices * sizeof(GLushort),indices );
151
152   delete []indices;
153 }
154
155 void TextRenderer::SetVertexData( TextVertexBuffer* vertexData )
156 {
157   DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::SetVertexData(this: %p, vertexData: %p)\n", this, vertexData );
158
159   if( !vertexData )
160   {
161     DALI_ASSERT_DEBUG( 0 && "No vertex data");  // vertex data structure is required even for empty strings
162     return;
163   }
164   if( vertexData->mVertices.size() > 0 )
165   {
166     SetTextureId(vertexData->mTextureId);
167
168     if ( !mVertexBuffer )
169     {
170       mVertexBuffer = new GpuBuffer( *mContext, GpuBuffer::ARRAY_BUFFER, GpuBuffer::DYNAMIC_DRAW );
171     }
172
173     if ( !mIndexBuffer )
174     {
175       mIndexBuffer = new GpuBuffer( *mContext, GpuBuffer::ELEMENT_ARRAY_BUFFER, GpuBuffer::STATIC_DRAW );
176     }
177
178     mVertexBuffer->UpdateDataBuffer( vertexData->mVertices.size() * sizeof(TextVertex2D), &vertexData->mVertices[0] );
179
180     UpdateIndexBuffer( vertexData->mVertices.size() ); // Used in DoRender()
181
182     // Get inverted text size, as this is faster for the shader to operate on,
183     // and shader won't throw any errors performing a multiplication rather than a divide by zero
184     // on a bad size value.
185     mGeometryExtent = vertexData->mGeometryExtent;
186   }
187   else
188   {
189     // no text to display, delete the GPU buffers, this will stop anything rendering.
190     mVertexBuffer.Reset();
191     mIndexBuffer.Reset();
192   }
193   // vertex data no longer required.
194   delete vertexData;
195 }
196
197 void TextRenderer::SetFontSize( float pixelSize )
198 {
199   mPixelSize = pixelSize;
200 }
201
202 void TextRenderer::SetGradient( const Vector4& color, const Vector2& startPoint, const Vector2& endPoint )
203 {
204   AllocateTextParameters();
205   mTextParameters->SetGradient( color, startPoint, endPoint );
206 }
207
208 void TextRenderer::SetTextColor( const Vector4& color )
209 {
210   if( NULL == mTextColor )
211   {
212     mTextColor = new Vector4( color );
213   }
214   else
215   {
216     *mTextColor = color;
217   }
218 }
219
220 void TextRenderer::SetOutline( const bool enable, const Vector4& color, const Vector2& params )
221 {
222   AllocateTextParameters();
223
224   mTextParameters->SetOutline( enable, color, params );
225 }
226
227 void TextRenderer::SetGlow( const bool enable, const Vector4& color, const float params )
228 {
229   AllocateTextParameters();
230
231   mTextParameters->SetGlow( enable, color, params );
232 }
233
234 void TextRenderer::SetDropShadow( const bool enable, const Vector4& color, const Vector2& offset, const float size )
235 {
236   AllocateTextParameters();
237
238   mTextParameters->SetShadow( enable, color, offset, size );
239 }
240
241 void TextRenderer::SetSmoothEdge( float params )
242 {
243   mSmoothing = params;
244 }
245
246 void TextRenderer::GlContextDestroyed()
247 {
248   if( mVertexBuffer )
249   {
250     mVertexBuffer->GlContextDestroyed();
251   }
252   if( mIndexBuffer )
253   {
254     mIndexBuffer->GlContextDestroyed();
255   }
256 }
257
258 void TextRenderer::GlCleanup()
259 {
260   mVertexBuffer.Reset();
261   mIndexBuffer.Reset();
262 }
263
264 bool TextRenderer::RequiresDepthTest() const
265 {
266   return false;
267 }
268
269 bool TextRenderer::CheckResources()
270 {
271   if ( ! ( mVertexBuffer && mIndexBuffer ) )
272   {
273     // This character has no geometry, must be a white space
274     return true;
275   }
276
277   if( !mVertexBuffer->BufferIsValid() )
278   {
279     return false;
280   }
281
282   if( mTexture == NULL )
283   {
284     mTexture = mTextureCache->GetTexture( mTextureId );
285
286     if( mTexture == NULL )
287     {
288       // texture atlas hasn't been created yet
289       return false;
290     }
291   }
292   if( mTexture->GetTextureId() == 0 )
293   {
294     return false;
295   }
296
297   return true;
298 }
299
300 void TextRenderer::ResolveGeometryTypes( BufferIndex bufferIndex, GeometryType& outType, ShaderSubTypes& outSubType )
301 {
302   outType = GEOMETRY_TYPE_TEXT;
303
304   // If we have a color gradient, then we cannot use the default shader.
305
306   outSubType = SHADER_DEFAULT;
307   if( mTextParameters )
308   {
309     if( mTextParameters->IsOutlineEnabled() )
310     {
311       if( mTextParameters->IsGlowEnabled() )
312       {
313         outSubType = SHADER_GRADIENT_OUTLINE_GLOW;
314       }
315       else
316       {
317         outSubType = SHADER_GRADIENT_OUTLINE;
318       }
319     }
320     else if( mTextParameters->IsGlowEnabled() )
321     {
322       outSubType = SHADER_GRADIENT_GLOW;
323     }
324     else if( mTextParameters->IsDropShadowEnabled() )
325     {
326       outSubType = SHADER_GRADIENT_SHADOW;
327     }
328     else
329     {
330       outSubType = SHADER_GRADIENT;
331     }
332   }
333 }
334
335 bool TextRenderer::IsOutsideClipSpace( const Matrix& modelMatrix, const Matrix& modelViewProjectionMatrix )
336 {
337   mContext->IncrementRendererCount();
338
339   Rect<float> boundingBox(mGeometryExtent.width*-0.5f, mGeometryExtent.height*-0.5f, mGeometryExtent.width, mGeometryExtent.height);
340   DEBUG_BOUNDING_BOX( *mContext, boundingBox, modelViewProjectionMatrix );
341
342   if(Is2dBoxOutsideClipSpace( modelMatrix, modelViewProjectionMatrix, boundingBox ) )
343   {
344     mContext->IncrementCulledCount();
345     return true;
346   }
347   return false;
348 }
349
350 void TextRenderer::DoRender( BufferIndex bufferIndex, Program& program, const Matrix& modelViewMatrix, const Matrix& viewMatrix )
351 {
352   DALI_ASSERT_DEBUG( NULL != mTexture && "TextRenderer::DoRender. mTexture == NULL." );
353
354   DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::DoRender(this: %p) textureId:%d\n", this, mTextureId );
355
356   // Set sampler uniform
357   const GLint samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER );
358   if( Program::UNIFORM_UNKNOWN != samplerLoc )
359   {
360     // set the uniform
361     program.SetUniform1i( samplerLoc, 0 );
362   }
363
364   const float SMOOTHING_ADJUSTMENT( 12.0f );
365   const float SMOOTHING_ADJUSTMENT_PIXEL_SIZE( 32.0f );
366
367   float smoothWidth = SMOOTHING_ADJUSTMENT / mPixelSize;
368   float smoothing = mSmoothing;
369
370   const GLint smoothingLoc = program.GetUniformLocation( Program::UNIFORM_SMOOTHING );
371   if( Program::UNIFORM_UNKNOWN != smoothingLoc )
372   {
373     smoothWidth = min( min(mSmoothing, 1.0f - mSmoothing), smoothWidth );
374
375     if( mPixelSize < SMOOTHING_ADJUSTMENT_PIXEL_SIZE )
376     {
377       smoothing *= Lerp( mPixelSize / SMOOTHING_ADJUSTMENT_PIXEL_SIZE, 0.5f, 1.0f );
378     }
379
380     program.SetUniform2f( smoothingLoc, std::max(0.0f, smoothing - smoothWidth), std::min(1.0f, smoothing + smoothWidth) );
381   }
382
383   if( mTextParameters )
384   {
385     if( mTextParameters->IsOutlineEnabled() )
386     {
387       const GLint outlineLoc = program.GetUniformLocation( Program::UNIFORM_OUTLINE );
388       const GLint outlineColorLoc = program.GetUniformLocation( Program::UNIFORM_OUTLINE_COLOR );
389
390       if( Program::UNIFORM_UNKNOWN != outlineLoc && Program::UNIFORM_UNKNOWN != outlineColorLoc )
391       {
392         const Vector2& outline = mTextParameters->GetOutlineThickness();
393         const Vector4& outlineColor = mTextParameters->GetOutlineColor();
394         float outlineWidth = outline[1] + smoothWidth;
395         float outlineStart = outline[0];
396         float outlineEnd = min( 1.0f, outlineStart + outlineWidth );
397
398         program.SetUniform2f(outlineLoc, outlineStart, outlineEnd);
399         program.SetUniform4f(outlineColorLoc, outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
400       }
401     }
402
403     if( mTextParameters->IsGlowEnabled() )
404     {
405       const GLint glowLoc = program.GetUniformLocation( Program::UNIFORM_GLOW );
406       const GLint glowColorLoc = program.GetUniformLocation( Program::UNIFORM_GLOW_COLOR );
407
408       if( Program::UNIFORM_UNKNOWN != glowLoc && Program::UNIFORM_UNKNOWN != glowColorLoc )
409       {
410         // if mGlow is > mSmoothing we get an inverted glyph, so clamp the value
411         program.SetUniform1f(glowLoc, std::min(mTextParameters->GetGlowIntensity(), mSmoothing));
412         const Vector4& glowColor = mTextParameters->GetGlowColor();
413         program.SetUniform4f(glowColorLoc, glowColor.r, glowColor.g, glowColor.b, glowColor.a);
414       }
415     }
416
417     if( mTextParameters->IsDropShadowEnabled() )
418     {
419       const GLint shadowLoc = program.GetUniformLocation( Program::UNIFORM_SHADOW );
420       const GLint shadowColorLoc = program.GetUniformLocation( Program::UNIFORM_SHADOW_COLOR );
421       const GLint shadowSmoothingLoc = program.GetUniformLocation( Program::UNIFORM_SHADOW_SMOOTHING );
422
423       if( Program::UNIFORM_UNKNOWN != shadowLoc && Program::UNIFORM_UNKNOWN != shadowColorLoc && Program::UNIFORM_UNKNOWN != shadowSmoothingLoc )
424       {
425         // convert shadow offset from tile to atlas coordinates
426         const Vector2& offset( mTextParameters->GetDropShadowOffset() / mTexture->GetWidth());
427         float shadowSmoothing = std::max(0.0f, smoothing - mTextParameters->GetDropShadowSize() );
428         program.SetUniform2f(shadowLoc, offset.x, offset.y);
429         const Vector4& dropShadowColor = mTextParameters->GetDropShadowColor();
430         program.SetUniform4f(shadowColorLoc, dropShadowColor.r, dropShadowColor.g, dropShadowColor.b, dropShadowColor.a);
431         program.SetUniform2f( shadowSmoothingLoc, std::max(0.0f, shadowSmoothing - smoothWidth), std::min(1.0f, shadowSmoothing + smoothWidth) );
432       }
433     }
434   }
435
436   // Set the text color uniform
437   const GLint textColorLoc = program.GetUniformLocation( Program::UNIFORM_TEXT_COLOR );
438   if( Program::UNIFORM_UNKNOWN != textColorLoc )
439   {
440     Vector4 textColor( (NULL != mTextColor) ? *mTextColor : TextStyle::DEFAULT_TEXT_COLOR );
441
442     program.SetUniform4f(textColorLoc, textColor.r, textColor.g, textColor.b, textColor.a);
443   }
444
445   if( mTextParameters )
446   {
447     // All shaders except default shader require the uGradientLine.zw uniform to be set
448     // at the very least. (setting it to vec2(0.0, 0.0) will disable gradient)
449     Vector2 projection( Vector2::ZERO );
450     Vector2 startPoint( TextStyle::DEFAULT_GRADIENT_START_POINT );
451     startPoint = mTextParameters->GetGradientStartPoint();
452     projection = mTextParameters->GetGradientEndPoint() - startPoint;
453     if( mTextParameters->IsGradientEnabled() ) // same as: mGradientEndPoint != mGradientStartPoint
454     {
455       projection /= projection.LengthSquared();
456
457       // For valid gradients Gradient Color and Text Size information must be set.
458       const GLint gradientColorLoc = program.GetUniformLocation( Program::UNIFORM_GRADIENT_COLOR );
459       const GLint textSizeLoc = program.GetUniformLocation( Program::UNIFORM_INVERSE_TEXT_SIZE );
460
461       if( Program::UNIFORM_UNKNOWN != gradientColorLoc && Program::UNIFORM_UNKNOWN != textSizeLoc )
462       {
463         const Vector4& color = mTextParameters->GetGradientColor();
464         program.SetUniform4f( gradientColorLoc, color.r, color.g, color.b, color.a );
465
466         Vector2 invTextSize( mGeometryExtent );
467         invTextSize.x = invTextSize.x > Math::MACHINE_EPSILON_1 ? 1.0f / invTextSize.x : 1.0f;
468         invTextSize.y = invTextSize.y > Math::MACHINE_EPSILON_1 ? 1.0f / invTextSize.y : 1.0f;
469
470         program.SetUniform2f( textSizeLoc, invTextSize.width, invTextSize.height );
471       }
472     }
473
474     // If we don't have a gradient present (mGradientEnabled) but the shader requires
475     // gradient information (gradientRequired), then we set
476     // uGradientLine.zw = vec2(0.0, 0.0) to force vColor = uColor in the expression.
477     // If we do have a gradient present, then we set up all information.
478     const GLint gradientLineLoc = program.GetUniformLocation( Program::UNIFORM_GRADIENT_LINE );
479     if( Program::UNIFORM_UNKNOWN != gradientLineLoc )
480     {
481       program.SetUniform4f( gradientLineLoc,
482                             startPoint.x - 0.5f,
483                             startPoint.y - 0.5f,
484                             projection.x,
485                             projection.y );
486     }
487   }
488
489   const GLint positionLoc = program.GetAttribLocation(Program::ATTRIB_POSITION);
490   const GLint texCoordLoc = program.GetAttribLocation(Program::ATTRIB_TEXCOORD);
491
492   mTexture->Bind(GL_TEXTURE_2D, GL_TEXTURE0);
493   mTexture->ApplySampler( mSamplerBitfield );
494
495   mContext->EnableVertexAttributeArray( positionLoc );
496   mContext->EnableVertexAttributeArray( texCoordLoc );
497
498   // bind the buffers
499   DALI_ASSERT_DEBUG( mVertexBuffer->BufferIsValid() );
500   mVertexBuffer->Bind();
501   DALI_ASSERT_DEBUG( mIndexBuffer->BufferIsValid() );
502   mIndexBuffer->Bind();
503
504   TextVertex2D* v = 0;
505   mContext->VertexAttribPointer(positionLoc,  2, GL_FLOAT, GL_FALSE, sizeof(TextVertex2D), &v->mX);
506   mContext->VertexAttribPointer(texCoordLoc,  4, GL_FLOAT, GL_FALSE, sizeof(TextVertex2D), &v->mU);
507
508   const GLsizei indexCount = mIndexBuffer->GetBufferSize() / sizeof(GLushort); // compiler will optimize this to >> if possible
509   mContext->DrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, (void *) 0);
510   DRAW_ELEMENT_RECORD( indexCount );
511
512   mContext->DisableVertexAttributeArray( positionLoc );
513   mContext->DisableVertexAttributeArray( texCoordLoc );
514
515 }
516
517 TextRenderer::TextRenderer( RenderDataProvider& dataprovider )
518 : Renderer( dataprovider ),
519   mTexture( NULL ),
520   mTextColor( NULL ),
521   mVertexBuffer(),
522   mIndexBuffer(),
523   mTextParameters(),
524   mGeometryExtent(),
525   mTextureId( 0 ),
526   mSmoothing( Dali::TextStyle::DEFAULT_SMOOTH_EDGE_DISTANCE_FIELD ),
527   mPixelSize(0.0f)
528 {
529 }
530
531 } // namespace SceneGraph
532
533 } // namespace Internal
534
535 } // namespace Dali