Merge branch 'tizen' of platform/core/uifw/dali-core into devel/new_mesh
[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/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>
38
39 #if defined(DEBUG_ENABLED)
40 namespace
41 {
42 Debug::Filter* gTextFilter = Debug::Filter::New(Debug::Concise, false, "LOG_SCENE_GRAPH_TEXT_RENDERER");
43 }
44 #endif
45
46 namespace Dali
47 {
48
49 namespace Internal
50 {
51
52 namespace SceneGraph
53 {
54
55 TextRenderer* TextRenderer::New( NodeDataProvider& dataprovider )
56 {
57   return new TextRenderer( dataprovider );
58 }
59
60 TextRenderer::~TextRenderer()
61 {
62   if(mTextureId > 0)
63   {
64     mTextureCache->RemoveObserver(mTextureId, this);
65   }
66
67   GlCleanup();
68
69   delete mTextColor;
70 }
71
72 void TextRenderer::TextureDiscarded( ResourceId textureId )
73 {
74   DALI_ASSERT_DEBUG( mTextureId == textureId || mTextureId == 0 );
75
76   mTextureId = 0;
77   mTexture = NULL;
78 }
79
80 void TextRenderer::AllocateTextParameters()
81 {
82   if( !mTextParameters )
83   {
84     mTextParameters = new TextParameters;
85   }
86 }
87
88 void TextRenderer::SetTextureId( ResourceId textureId )
89 {
90   DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::SetTextureId(%d)\n", textureId );
91
92   if(mTextureId > 0)
93   {
94     mTextureCache->RemoveObserver(mTextureId, this);
95   }
96
97   mTextureId = textureId;
98   mTexture = NULL;
99
100   if(textureId > 0)
101   {
102     mTextureCache->AddObserver(textureId, this);
103   }
104 }
105
106 void TextRenderer::UpdateIndexBuffer( std::size_t size )
107 {
108   if( size == 0)
109   {
110     return;
111   }
112   DALI_ASSERT_DEBUG( (size % 4 == 0) && "Invalid vertex length");
113
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).
116
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);
121
122   std::size_t numberQuads =  size >> 2;  // quads = verts / 4
123
124   GLushort* indices = new GLushort[ numberIndices  ];
125
126   std::size_t n = 0;
127   for( std::size_t i = 0; i < numberQuads; ++i )
128   {
129     /*
130      * 1 --- 2
131      * |    /|
132      * |  A  |
133      * | /   |
134      * 0 --- 3
135      *
136      * Draw 2 triangles with clock wise winding: 0->1->2 and 0->2->3
137      */
138     std::size_t vertIndex = i << 2;  // vert index = i * 4
139
140     indices[ n++ ] = 0 + vertIndex;
141     indices[ n++ ] = 1 + vertIndex;
142     indices[ n++ ] = 2 + vertIndex;
143
144     indices[ n++ ] = 0 + vertIndex;
145     indices[ n++ ] = 2 + vertIndex;
146     indices[ n++ ] = 3 + vertIndex;
147   }
148
149   mIndexBuffer->UpdateDataBuffer( numberIndices * sizeof(GLushort),indices );
150
151   delete []indices;
152 }
153
154 void TextRenderer::SetVertexData( TextVertexBuffer* vertexData )
155 {
156   DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::SetVertexData(this: %p, vertexData: %p)\n", this, vertexData );
157
158   if( !vertexData )
159   {
160     DALI_ASSERT_DEBUG( 0 && "No vertex data");  // vertex data structure is required even for empty strings
161     return;
162   }
163   if( vertexData->mVertices.size() > 0 )
164   {
165     SetTextureId(vertexData->mTextureId);
166
167     if ( !mVertexBuffer )
168     {
169       mVertexBuffer = new GpuBuffer( *mContext, GpuBuffer::ARRAY_BUFFER, GpuBuffer::DYNAMIC_DRAW );
170     }
171
172     if ( !mIndexBuffer )
173     {
174       mIndexBuffer = new GpuBuffer( *mContext, GpuBuffer::ELEMENT_ARRAY_BUFFER, GpuBuffer::STATIC_DRAW );
175     }
176
177     mVertexBuffer->UpdateDataBuffer( vertexData->mVertices.size() * sizeof(TextVertex2D), &vertexData->mVertices[0] );
178
179     UpdateIndexBuffer( vertexData->mVertices.size() ); // Used in DoRender()
180
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;
185   }
186   else
187   {
188     // no text to display, delete the GPU buffers, this will stop anything rendering.
189     mVertexBuffer.Reset();
190     mIndexBuffer.Reset();
191   }
192   // vertex data no longer required.
193   delete vertexData;
194 }
195
196 void TextRenderer::SetFontSize( float pixelSize )
197 {
198   mPixelSize = pixelSize;
199 }
200
201 void TextRenderer::SetGradient( const Vector4& color, const Vector2& startPoint, const Vector2& endPoint )
202 {
203   AllocateTextParameters();
204   mTextParameters->SetGradient( color, startPoint, endPoint );
205 }
206
207 void TextRenderer::SetTextColor( const Vector4& color )
208 {
209   if( NULL == mTextColor )
210   {
211     mTextColor = new Vector4( color );
212   }
213   else
214   {
215     *mTextColor = color;
216   }
217 }
218
219 void TextRenderer::SetOutline( const bool enable, const Vector4& color, const Vector2& params )
220 {
221   AllocateTextParameters();
222
223   mTextParameters->SetOutline( enable, color, params );
224 }
225
226 void TextRenderer::SetGlow( const bool enable, const Vector4& color, const float params )
227 {
228   AllocateTextParameters();
229
230   mTextParameters->SetGlow( enable, color, params );
231 }
232
233 void TextRenderer::SetDropShadow( const bool enable, const Vector4& color, const Vector2& offset, const float size )
234 {
235   AllocateTextParameters();
236
237   mTextParameters->SetShadow( enable, color, offset, size );
238 }
239
240 void TextRenderer::SetSmoothEdge( float params )
241 {
242   mSmoothing = params;
243 }
244
245 void TextRenderer::GlContextDestroyed()
246 {
247   if( mVertexBuffer )
248   {
249     mVertexBuffer->GlContextDestroyed();
250   }
251   if( mIndexBuffer )
252   {
253     mIndexBuffer->GlContextDestroyed();
254   }
255 }
256
257 void TextRenderer::GlCleanup()
258 {
259   mVertexBuffer.Reset();
260   mIndexBuffer.Reset();
261 }
262
263 bool TextRenderer::RequiresDepthTest() const
264 {
265   return false;
266 }
267
268 bool TextRenderer::CheckResources()
269 {
270   if ( ! ( mVertexBuffer && mIndexBuffer ) )
271   {
272     // This character has no geometry, must be a white space
273     return true;
274   }
275
276   if( !mVertexBuffer->BufferIsValid() )
277   {
278     return false;
279   }
280
281   if( mTexture == NULL )
282   {
283     mTexture = mTextureCache->GetTexture( mTextureId );
284
285     if( mTexture == NULL )
286     {
287       // texture atlas hasn't been created yet
288       return false;
289     }
290   }
291   if( mTexture->GetTextureId() == 0 )
292   {
293     return false;
294   }
295
296   return true;
297 }
298
299 void TextRenderer::ResolveGeometryTypes( BufferIndex bufferIndex, GeometryType& outType, ShaderSubTypes& outSubType )
300 {
301   outType = GEOMETRY_TYPE_TEXT;
302
303   // If we have a color gradient, then we cannot use the default shader.
304
305   outSubType = SHADER_DEFAULT;
306   if( mTextParameters )
307   {
308     if( mTextParameters->IsOutlineEnabled() )
309     {
310       if( mTextParameters->IsGlowEnabled() )
311       {
312         outSubType = SHADER_GRADIENT_OUTLINE_GLOW;
313       }
314       else
315       {
316         outSubType = SHADER_GRADIENT_OUTLINE;
317       }
318     }
319     else if( mTextParameters->IsGlowEnabled() )
320     {
321       outSubType = SHADER_GRADIENT_GLOW;
322     }
323     else if( mTextParameters->IsDropShadowEnabled() )
324     {
325       outSubType = SHADER_GRADIENT_SHADOW;
326     }
327     else
328     {
329       outSubType = SHADER_GRADIENT;
330     }
331   }
332 }
333
334 bool TextRenderer::IsOutsideClipSpace( const Matrix& modelMatrix, const Matrix& modelViewProjectionMatrix )
335 {
336   mContext->IncrementRendererCount();
337
338   Rect<float> boundingBox(mGeometryExtent.width*-0.5f, mGeometryExtent.height*-0.5f, mGeometryExtent.width, mGeometryExtent.height);
339   DEBUG_BOUNDING_BOX( *mContext, boundingBox, modelViewProjectionMatrix );
340
341   if(Is2dBoxOutsideClipSpace( modelMatrix, modelViewProjectionMatrix, boundingBox ) )
342   {
343     mContext->IncrementCulledCount();
344     return true;
345   }
346   return false;
347 }
348
349 void TextRenderer::DoRender( BufferIndex bufferIndex, Program& program, const Matrix& modelViewMatrix, const Matrix& viewMatrix )
350 {
351   DALI_ASSERT_DEBUG( NULL != mTexture && "TextRenderer::DoRender. mTexture == NULL." );
352   if( NULL == mTexture )
353   {
354     // Nothing to render.
355     return;
356   }
357
358   DALI_LOG_INFO( gTextFilter, Debug::General, "TextRenderer::DoRender(this: %p) textureId:%d\n", this, mTextureId );
359
360   mTextureCache->BindTexture( mTexture, mTextureId, GL_TEXTURE_2D, TEXTURE_UNIT_TEXT );
361   if( mTexture->GetTextureId() == 0 )
362   {
363     return; // early out if we haven't got a GL texture yet (e.g. due to context loss)
364   }
365
366   // Set sampler uniform
367   const GLint samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER );
368   if( Program::UNIFORM_UNKNOWN != samplerLoc )
369   {
370     // set the uniform
371     program.SetUniform1i( samplerLoc, TEXTURE_UNIT_TEXT );
372   }
373
374   mTexture->ApplySampler( TEXTURE_UNIT_TEXT, mSamplerBitfield );
375
376   const float SMOOTHING_ADJUSTMENT( 12.0f );
377   const float SMOOTHING_ADJUSTMENT_PIXEL_SIZE( 32.0f );
378
379   float smoothWidth = SMOOTHING_ADJUSTMENT / mPixelSize;
380   float smoothing = mSmoothing;
381
382   const GLint smoothingLoc = program.GetUniformLocation( Program::UNIFORM_SMOOTHING );
383   if( Program::UNIFORM_UNKNOWN != smoothingLoc )
384   {
385     smoothWidth = std::min( std::min(mSmoothing, 1.0f - mSmoothing), smoothWidth );
386
387     if( mPixelSize < SMOOTHING_ADJUSTMENT_PIXEL_SIZE )
388     {
389       smoothing *= Lerp( mPixelSize / SMOOTHING_ADJUSTMENT_PIXEL_SIZE, 0.5f, 1.0f );
390     }
391
392     program.SetUniform2f( smoothingLoc, std::max(0.0f, smoothing - smoothWidth), std::min(1.0f, smoothing + smoothWidth) );
393   }
394
395   if( mTextParameters )
396   {
397     if( mTextParameters->IsOutlineEnabled() )
398     {
399       const GLint outlineLoc = program.GetUniformLocation( Program::UNIFORM_OUTLINE );
400       const GLint outlineColorLoc = program.GetUniformLocation( Program::UNIFORM_OUTLINE_COLOR );
401
402       if( Program::UNIFORM_UNKNOWN != outlineLoc && Program::UNIFORM_UNKNOWN != outlineColorLoc )
403       {
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 );
409
410         program.SetUniform2f(outlineLoc, outlineStart, outlineEnd);
411         program.SetUniform4f(outlineColorLoc, outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a);
412       }
413     }
414
415     if( mTextParameters->IsGlowEnabled() )
416     {
417       const GLint glowLoc = program.GetUniformLocation( Program::UNIFORM_GLOW );
418       const GLint glowColorLoc = program.GetUniformLocation( Program::UNIFORM_GLOW_COLOR );
419
420       if( Program::UNIFORM_UNKNOWN != glowLoc && Program::UNIFORM_UNKNOWN != glowColorLoc )
421       {
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);
426       }
427     }
428
429     if( mTextParameters->IsDropShadowEnabled() )
430     {
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 );
434
435       if( Program::UNIFORM_UNKNOWN != shadowLoc && Program::UNIFORM_UNKNOWN != shadowColorLoc && Program::UNIFORM_UNKNOWN != shadowSmoothingLoc )
436       {
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) );
444       }
445     }
446   }
447
448   // Set the text color uniform
449   const GLint textColorLoc = program.GetUniformLocation( Program::UNIFORM_TEXT_COLOR );
450   if( Program::UNIFORM_UNKNOWN != textColorLoc )
451   {
452     Vector4 textColor( (NULL != mTextColor) ? *mTextColor : TextStyle::DEFAULT_TEXT_COLOR );
453
454     program.SetUniform4f(textColorLoc, textColor.r, textColor.g, textColor.b, textColor.a);
455   }
456
457   if( mTextParameters )
458   {
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
466     {
467       projection /= projection.LengthSquared();
468
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 );
472
473       if( Program::UNIFORM_UNKNOWN != gradientColorLoc && Program::UNIFORM_UNKNOWN != textSizeLoc )
474       {
475         const Vector4& color = mTextParameters->GetGradientColor();
476         program.SetUniform4f( gradientColorLoc, color.r, color.g, color.b, color.a );
477
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;
481
482         program.SetUniform2f( textSizeLoc, invTextSize.width, invTextSize.height );
483       }
484     }
485
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 )
492     {
493       program.SetUniform4f( gradientLineLoc,
494                             startPoint.x - 0.5f,
495                             startPoint.y - 0.5f,
496                             projection.x,
497                             projection.y );
498     }
499   }
500
501   const GLint positionLoc = program.GetAttribLocation(Program::ATTRIB_POSITION);
502   const GLint texCoordLoc = program.GetAttribLocation(Program::ATTRIB_TEXCOORD);
503
504   mContext->EnableVertexAttributeArray( positionLoc );
505   mContext->EnableVertexAttributeArray( texCoordLoc );
506
507   // bind the buffers
508   DALI_ASSERT_DEBUG( mVertexBuffer->BufferIsValid() );
509   mVertexBuffer->Bind();
510   DALI_ASSERT_DEBUG( mIndexBuffer->BufferIsValid() );
511   mIndexBuffer->Bind();
512
513   TextVertex2D* v = 0;
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);
516
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);
519
520   mContext->DisableVertexAttributeArray( positionLoc );
521   mContext->DisableVertexAttributeArray( texCoordLoc );
522
523 }
524
525 TextRenderer::TextRenderer( NodeDataProvider& dataprovider )
526 : Renderer( dataprovider ),
527   mTexture( NULL ),
528   mTextColor( NULL ),
529   mVertexBuffer(),
530   mIndexBuffer(),
531   mTextParameters(),
532   mGeometryExtent(),
533   mTextureId( 0 ),
534   mSmoothing( Dali::TextStyle::DEFAULT_SMOOTH_EDGE_DISTANCE_FIELD ),
535   mPixelSize(0.0f)
536 {
537 }
538
539 } // namespace SceneGraph
540
541 } // namespace Internal
542
543 } // namespace Dali