2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
18 #include <dali/internal/render/renderers/scene-graph-image-renderer.h>
21 #include <dali/public-api/common/dali-common.h>
22 #include <dali/internal/common/internal-constants.h>
23 #include <dali/internal/render/common/performance-monitor.h>
24 #include <dali/internal/render/common/vertex.h>
25 #include <dali/internal/render/gl-resources/gpu-buffer.h>
26 #include <dali/internal/render/gl-resources/texture.h>
27 #include <dali/internal/render/gl-resources/texture-cache.h>
28 #include <dali/internal/render/shaders/program.h>
29 #include <dali/internal/render/shaders/shader.h>
30 #include <dali/internal/update/controllers/scene-controller.h>
37 * VertexToTextureCoord
38 * Represents a mapping between a 1 dimensional vertex coordinate
39 * and a 1 dimensional texture coordinate.
41 struct VertexToTextureCoord
44 * @param[in] xVertex Vertex coordinate
45 * @param[in] uTexture Texture coordinate
47 VertexToTextureCoord(float xVertex, float uTexture)
53 float x; ///< 1D Vertex position
54 float u; ///< 1D Texture position
58 * Generates a list of equally spaced intervals along a line, including
59 * intervals at the points specified in insertionList.
60 * The line starts from insertionList.begin() and ends at insertionList.end()-1
61 * The number of intervals and spacing of these intervals is specified by
63 * @param[out] intervalList An empty vector to be populated with the list of intervals.
64 * @param[in] intervals The number of intervals to be generated.
65 * @param[in] insertionList A vector containing the points on the line to be inserted.
67 void GenerateIntervals(std::vector<VertexToTextureCoord>& intervalList, int intervals, const std::vector<VertexToTextureCoord>& insertionList)
69 DALI_ASSERT_DEBUG(insertionList.size() >= 2);
70 DALI_ASSERT_DEBUG(intervals > 0);
72 std::vector<VertexToTextureCoord>::const_iterator iter = insertionList.begin();
73 std::vector<VertexToTextureCoord>::const_iterator end = insertionList.end()-1;
75 const float length = end->x - iter->x;
76 const float intervalSize = length / static_cast<float>(intervals);
79 for(;iter!=end;++iter)
86 for(;x<x1;x+=intervalSize)
88 float progress = (x - x0) / (x1 - x0); // progress value between current interval and next.
89 float u = u0 + (u1 - u0) * progress; // u 1D texture coordinate value for this x position.
90 intervalList.push_back( VertexToTextureCoord( x, u ) );
92 intervalList.push_back( VertexToTextureCoord( x1, u1 ) );
107 ImageRenderer* ImageRenderer::New( RenderDataProvider& dataprovider )
109 return new ImageRenderer( dataprovider );
112 ImageRenderer::~ImageRenderer()
114 if ( mTextureId > 0 )
116 mTextureCache->RemoveObserver(mTextureId, this);
122 void ImageRenderer::SetTextureId( ResourceId textureId )
124 if ( mTextureId > 0 )
126 mTextureCache->RemoveObserver(mTextureId, this);
129 mTextureId = textureId;
134 mTextureCache->AddObserver(textureId, this);
138 void ImageRenderer::SetPixelArea( const ImageRenderer::PixelArea& pixelArea )
140 mUsePixelArea = true;
141 mPixelArea = pixelArea;
142 mIsMeshGenerated = false;
145 void ImageRenderer::SetNinePatchBorder( const Vector4& border, bool inPixels )
148 mBorderInPixels = inPixels;
149 mIsMeshGenerated = false;
152 void ImageRenderer::CalculateMeshData( MeshType type, const Vector2& targetSize, bool usePixelArea )
155 mGeometrySize = targetSize;
156 mUsePixelArea = usePixelArea;
157 mIsMeshGenerated = false;
160 void ImageRenderer::TextureDiscarded( ResourceId textureId )
162 DALI_ASSERT_DEBUG( mTextureId == textureId || mTextureId == 0 );
168 void ImageRenderer::GlCleanup()
172 mVertexBuffer.Reset();
177 mIndexBuffer.Reset();
181 bool ImageRenderer::RequiresDepthTest() const
186 bool ImageRenderer::CheckResources()
188 if( mTexture == NULL )
190 mTexture = mTextureCache->GetTexture( mTextureId );
193 if( mTexture == NULL )
198 if( ( mTexture->GetWidth() <= 0u ) ||
199 ( mTexture->GetHeight() <= 0u ) )
204 Integration::ResourceId shaderTextureId = mShader->GetTextureIdToRender() ;
206 if( shaderTextureId && mTextureCache->GetTexture( shaderTextureId ) == NULL )
214 void ImageRenderer::DoRender( BufferIndex bufferIndex, const Matrix& modelViewMatrix, const Matrix& modelMatrix, const Matrix& viewMatrix, const Matrix& projectionMatrix, const Vector4& color )
216 DALI_ASSERT_DEBUG( 0 != mTextureId && "ImageRenderer::DoRender. mTextureId == 0." );
217 DALI_ASSERT_DEBUG( NULL != mTexture && "ImageRenderer::DoRender. mTexture == NULL." );
219 if(! mIsMeshGenerated )
221 GenerateMeshData( mTexture );
224 DALI_ASSERT_DEBUG( mVertexBuffer );
226 mTextureCache->BindTexture( mTexture, mTextureId, GL_TEXTURE_2D, GL_TEXTURE0 );
228 // make sure the vertex is bound, this has to be done before
229 // we call VertexAttribPointer otherwise you get weird output on the display
230 mVertexBuffer->Bind();
232 ShaderSubTypes shaderType = SHADER_DEFAULT;
234 // Apply shader effect specific program and common uniforms
235 Program& program = mShader->Apply( *mContext, bufferIndex, GEOMETRY_TYPE_IMAGE, modelMatrix, viewMatrix, modelViewMatrix, projectionMatrix, color, shaderType );
237 // Set sampler uniform
238 GLint samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER );
239 if( -1 != samplerLoc )
242 program.SetUniform1i( samplerLoc, 0 );
245 samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER_RECT );
246 if( -1 != samplerLoc )
252 mTexture->GetTextureCoordinates( uv, &mPixelArea );
256 mTexture->GetTextureCoordinates( uv, NULL );
260 program.SetUniform4f( samplerLoc, uv.u0, uv.v0, uv.u2, uv.v2 );
263 // Check whether the program supports the expected attributes/uniforms
264 const GLint positionLoc = program.GetAttribLocation( Program::ATTRIB_POSITION );
265 const GLint texCoordLoc = program.GetAttribLocation( Program::ATTRIB_TEXCOORD );
267 if ( positionLoc != -1 )
269 mContext->EnableVertexAttributeArray( positionLoc );
271 const int stride = 4 * sizeof(float);
272 mContext->VertexAttribPointer( positionLoc, 2, GL_FLOAT, GL_FALSE, stride, 0 );
275 if ( texCoordLoc != -1 )
277 mContext->EnableVertexAttributeArray( texCoordLoc );
279 const int stride = 4 * sizeof(float);
280 mContext->VertexAttribPointer( texCoordLoc, 2, GL_FLOAT, GL_FALSE, stride, (const void*) (sizeof(float)*2) );
288 const GLsizei vertexCount = mVertexBuffer->GetBufferSize() / sizeof(Vertex2D); // compiler will optimize this to >> if possible
289 mContext->DrawArrays( GL_TRIANGLE_STRIP, 0, vertexCount );
290 DRAW_ARRAY_RECORD( vertexCount );
294 case GRID_NINE_PATCH:
296 const GLsizei indexCount = mIndexBuffer->GetBufferSize() / sizeof(GLushort); // compiler will optimize this to >> if possible
297 mIndexBuffer->Bind();
298 mContext->DrawElements( GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0 );
299 DRAW_ELEMENT_RECORD( indexCount );
304 if ( positionLoc != -1 )
306 mContext->DisableVertexAttributeArray( positionLoc );
309 if ( texCoordLoc != -1 )
311 mContext->DisableVertexAttributeArray( texCoordLoc );
315 void ImageRenderer::UpdateVertexBuffer( GLsizeiptr size, const GLvoid *data )
317 // create/destroy if needed/not needed.
318 if ( size && !mVertexBuffer )
320 mVertexBuffer = new GpuBuffer( *mContext, GpuBuffer::ARRAY_BUFFER, GpuBuffer::DYNAMIC_DRAW );
322 else if ( !size && mVertexBuffer )
324 mVertexBuffer.Reset();
330 mVertexBuffer->UpdateDataBuffer( size, data );
334 void ImageRenderer::UpdateIndexBuffer( GLsizeiptr size, const GLvoid *data )
336 // create/destroy if needed/not needed.
337 if ( size && !mIndexBuffer )
339 mIndexBuffer = new GpuBuffer( *mContext, GpuBuffer::ELEMENT_ARRAY_BUFFER, GpuBuffer::STATIC_DRAW );
341 else if ( !size && mIndexBuffer )
343 mIndexBuffer.Reset();
349 mIndexBuffer->UpdateDataBuffer(size,data);
353 void ImageRenderer::GenerateMeshData( Texture* texture )
355 const PixelArea* pixelArea = NULL;
358 pixelArea = &mPixelArea;
363 case ImageRenderer::QUAD:
365 SetQuadMeshData( texture, mGeometrySize, pixelArea );
368 case ImageRenderer::NINE_PATCH:
370 SetNinePatchMeshData( texture, mGeometrySize, mBorder, mBorderInPixels, pixelArea );
373 case ImageRenderer::GRID_QUAD:
375 SetGridMeshData( texture, mGeometrySize, NULL, false, pixelArea );
378 case ImageRenderer::GRID_NINE_PATCH:
380 SetGridMeshData( texture, mGeometrySize, &mBorder, mBorderInPixels, pixelArea );
384 mIsMeshGenerated = true;
387 void ImageRenderer::SetQuadMeshData( Texture* texture, const Vector2& size, const PixelArea* pixelArea )
389 const float x0 = -0.5f * size.x;
390 const float y0 = -0.5f * size.y;
391 const float x1 = 0.5f * size.x;
392 const float y1 = 0.5f * size.y;
395 * Here we render the square as a single square, as texture
396 * coordinates linearly interpolate between the 4 vertices.
398 * Note: a square (or a quad) is rendered as 2 triangles.
399 * Vertices 0,1,2 represent triangle A.
400 * Vertices 1,2,3 represent triangle B.
402 * No indices are needed as we tell GL to render in strip mode
403 * (GL_TRIANGLE_STRIP), which is faster, and consumes less
426 // We may only be displaying an area of the texture.
427 // Calling MapUV converts the u,v values to correct values for the pixel area
429 texture->MapUV( sizeof(verts)/sizeof(Vertex2D), verts, pixelArea );
431 UpdateVertexBuffer( sizeof(verts), verts );
432 UpdateIndexBuffer( 0, NULL );
435 void ImageRenderer::SetNinePatchMeshData( Texture* texture, const Vector2& size, const Vector4& border, bool borderInPixels, const PixelArea* pixelArea )
437 DALI_ASSERT_ALWAYS( mTexture->GetWidth() > 0.0f && "Invalid Texture width" );
438 DALI_ASSERT_ALWAYS( mTexture->GetHeight() > 0.0f && "Invalid Texture height" );
440 float textureWidth = mTexture->GetWidth();
441 float textureHeight = mTexture->GetHeight();
443 float borderLeft, borderTop, borderRight, borderBottom; // pixels from edge
444 float borderX0, borderY0, borderX1, borderY1; // In the range 0 -> 1
446 if ( borderInPixels )
448 borderLeft = border.x;
449 borderTop = border.y;
450 borderRight = border.z;
451 borderBottom = border.w;
453 borderX0 = border.x / textureWidth;
454 borderY0 = border.y / textureHeight;
455 borderX1 = 1.0f - ( border.z / textureWidth );
456 borderY1 = 1.0f - ( border.w / textureHeight );
460 borderLeft = textureWidth * border.x;
461 borderTop = textureHeight * border.y;
462 borderRight = textureWidth * (1.0f - border.z);
463 borderBottom = textureHeight * (1.0f - border.w);
471 const float u0 = 0.0f;
472 const float u3 = 1.0f;
473 const float u1 = borderX0;
474 const float u2 = borderX1;
476 const float v0 = 0.0f;
477 const float v3 = 1.0f;
478 const float v1 = borderY0;
479 const float v2 = borderY1;
481 const float x0 = size.x * -0.5;
482 const float x1 = x0 + borderLeft;
483 const float x2 = x0 + size.x - borderRight;
484 const float x3 = x0 + size.x;
486 const float y0 = size.y * -0.5;
487 const float y1 = y0 + borderTop;
488 const float y2 = y0 + size.y - borderBottom;
489 const float y3 = y0 + size.y;
492 * We're breaking a quad in to 9 smaller quads, so that when it's
493 * stretched the corners maintain their size.
494 * For speed the 9-patch is drawn with a single triangle span, the draw
495 * order of the span is 1->9.
496 * Previously it would draw three separate spans (1->3, 4->6, 7->9), but now it
497 * it does it one go by turning the corner when gets to the end of each row.
499 * No indices are needed as we tell GL to render in strip mode
500 * (GL_TRIANGLE_STRIP), which is faster, and consumes less
503 * |---|---------------|---|
505 * |---|---------------|---|
510 * |-------------------|---|
512 * |---|---------------|---|
563 const unsigned int vertexCount = sizeof( verts ) / sizeof( verts[0] );
565 texture->MapUV( vertexCount, verts, pixelArea );
566 UpdateVertexBuffer( sizeof(verts), verts );
567 UpdateIndexBuffer( 0, NULL );
570 void ImageRenderer::SetGridMeshData( Texture* texture, const Vector2& size, const Vector4* border, bool borderInPixels, const PixelArea* pixelArea )
574 * In Grid Mode, we tessellate the single quad into smaller quads
575 * at approximately (guideGridSize x guideGridSize) in size.
577 * Conversion of Quad to Gridded Quad.
579 * |-----------| |---|---|---|
581 * | | -> |---|---|---|
583 * |-----------| |---|---|---|
586 * In Grid Mode, we tessellate each quad of a 9-patch
587 * (see SetNinePatchMeshData) into smaller quads at approximately
588 * (guideGridSize x guideGridSize) in size.
590 * This satisfies the two requirements of a 9-patch with grid:
592 * 1. Texture coordinates within each section of the 9-patch
593 * should change linearly to that 9-patch's rules.
594 * 2. The image as as whole should provide Vertex points at
595 * approximate guideGridSize intervals.
597 * The result should be the horizontal and vertical lines of
598 * a 9-patch overlayed by the horizontal and vertical lines of
602 * | | | | | | | <- Grid Markers
603 * -|-------|-------------------|-------|
607 * |-------+-------------------+-------|
612 * -| 4 | 5 | 6 | <- 9 Patch.
617 * |-------+-------------------+-------|
621 * -|-------|-------------------|-------|
625 * | | | | | | | <- Grid Markers
626 * -|-------|-------------------|-------|
629 * -|-----|-|---|-----|-----|---|-|-----|
630 * |-------+-------------------+-------|
632 * -|-----|-|---|-----|-----|---|-|-----|
635 * -|-----|-|---|-----|-----|---|-|-----| <- 9 Patch.
638 * -|-----|-|---|-----|-----|---|-|-----|
640 * |-------+-------------------+-------|
641 * -|-----|-|---|-----|-----|---|-|-----|
644 * -|-------|-------------------|-------|
647 std::vector<VertexToTextureCoord> horizontalDivisions;
648 std::vector<VertexToTextureCoord> verticalDivisions;
650 const float guideGridSize = mShader->GetGridDensity();
652 const int textureWidth = texture->GetWidth();
653 const int textureHeight = texture->GetHeight();
655 const float halfWidth = size.width * 0.5f;
656 const float halfHeight = size.height * 0.5f;
658 // Determine how many rectangles across and down to tesselate image into.
659 const int guideRectX = (size.width / guideGridSize);
660 const int guideRectY = (size.height / guideGridSize);
662 // Build up list of points in X axis where vertices need to go.
663 std::vector<VertexToTextureCoord> insertionList;
664 insertionList.reserve(4);
665 insertionList.push_back( VertexToTextureCoord( -halfWidth, 0.0f ) );
667 // If 9-Patch Border exists, add additional border points in list.
670 float borderX0, borderX1, borderU0, borderU1;
672 if ( borderInPixels )
674 borderX0 = border->x - halfWidth;
675 borderX1 = halfWidth - border->z;
677 borderU0 = border->x / textureWidth;
678 borderU1 = 1.0f - (border->z / textureWidth);
682 borderX0 = border->x * textureWidth - halfWidth;
683 borderX1 = halfWidth - (1.0f - border->z) * textureWidth;
684 borderU0 = border->x;
685 borderU1 = border->z;
688 insertionList.push_back( VertexToTextureCoord( borderX0, borderU0 ) );
689 insertionList.push_back( VertexToTextureCoord( borderX1, borderU1 ) );
692 insertionList.push_back( VertexToTextureCoord( halfWidth, 1.0f ) );
693 GenerateIntervals(horizontalDivisions, guideRectX + 2, insertionList);
695 // Build up list of points in Y axis where vertices need to go.
696 insertionList.clear();
697 insertionList.push_back( VertexToTextureCoord( -halfHeight, 0.0f ) );
699 // If 9-Patch Border exists, add additional border points in list.
702 float borderY0, borderY1, borderU0, borderU1;
704 if ( borderInPixels )
706 borderY0 = border->y - halfHeight;
707 borderY1 = halfHeight - border->w;
709 borderU0 = border->y / textureHeight;
710 borderU1 = 1.0f - (border->w / textureHeight);
714 borderY0 = border->y * textureHeight - halfHeight;
715 borderY1 = halfHeight - (1.0f - border->w) * textureHeight;
717 borderU0 = border->y;
718 borderU1 = border->w;
721 insertionList.push_back( VertexToTextureCoord( borderY0, borderU0 ) );
722 insertionList.push_back( VertexToTextureCoord( borderY1, borderU1 ) );
725 insertionList.push_back( VertexToTextureCoord( halfHeight, 1.0f ) );
726 GenerateIntervals(verticalDivisions, guideRectY + 2, insertionList);
728 // Now build up Vertex pattern based on the above X and Y lists.
729 const int totalVertices = horizontalDivisions.size() * verticalDivisions.size();
730 Vertex2D* vertices = new Vertex2D[totalVertices];
731 Vertex2D* vertex = vertices;
733 for(std::vector<VertexToTextureCoord>::const_iterator yIter = verticalDivisions.begin(); yIter != verticalDivisions.end(); ++yIter )
735 for(std::vector<VertexToTextureCoord>::const_iterator xIter = horizontalDivisions.begin(); xIter != horizontalDivisions.end(); ++xIter )
737 vertex->mX = xIter->x;
738 vertex->mU = xIter->u;
739 vertex->mY = yIter->x;
740 vertex->mV = yIter->u;
745 // Build up Triangle indicies, very predictable pattern.
746 const size_t rectX = horizontalDivisions.size() - 1;
747 const size_t rectY = verticalDivisions.size() - 1;
748 const size_t totalIndices = rectX * rectY * 6; // 2 triangles per quad (rect) and 3 points to define each triangle.
749 GLushort* indices = new GLushort[totalIndices];
751 GenerateMeshIndices(indices, rectX, rectY);
753 texture->MapUV( totalVertices, vertices, pixelArea );
755 UpdateVertexBuffer( totalVertices * sizeof(Vertex2D) , vertices );
756 UpdateIndexBuffer( totalIndices * sizeof(GLushort), indices );
762 void ImageRenderer::GenerateMeshIndices(GLushort* indices, int rectanglesX, int rectanglesY)
764 GLushort *i = indices;
765 const int meshEndIndex = rectanglesY * (rectanglesX + 1);
768 while(index < meshEndIndex)
770 const int rowEndIndex = index + rectanglesX;
771 for (; index < rowEndIndex; index++ )
774 *i++ = index + 1 + rectanglesX;
778 *i++ = index + 1 + rectanglesX;
779 *i++ = index + 2 + rectanglesX;
781 index++; // one extra vertex per row than rects.
785 ImageRenderer::ImageRenderer( RenderDataProvider& dataprovider )
786 : Renderer( dataprovider ),
788 mBorder( 0.45, 0.45, 0.1, 0.1 ),
792 mMeshType( ImageRenderer::QUAD ),
793 mIsMeshGenerated( false ),
794 mBorderInPixels( false ),
795 mUsePixelArea( false )
799 } // namespace SceneGraph
801 } // namespace Internal