text: /*
   * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
  
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
  
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
  
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  #include "tvgGlRenderer.h"
  #include "tvgGlGpuBuffer.h"
  #include "tvgGlGeometry.h"
  #include "tvgGlPropertyInterface.h"
  
  /************************************************************************/
  /* Internal Class Implementation                                        */
  /************************************************************************/
  static int32_t initEngineCnt = false;
  static int32_t rendererCnt = 0;
  
  
  static void _termEngine()
  {
      if (rendererCnt > 0) return;
  
      //TODO: Clean up global resources
  }
  
  /************************************************************************/
  /* External Class Implementation                                        */
  /************************************************************************/
  
  #define NOISE_LEVEL 0.5f
  
  bool GlRenderer::clear()
  {
      //TODO: (Request) to clear target
      // Will be adding glClearColor for input buffer
      return true;
  }
  
  
  bool GlRenderer::target(TVG_UNUSED uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h)
  {
      assert(w > 0 && h > 0);
  
      surface.stride = stride;
      surface.w = w;
      surface.h = h;
  
      return true;
  }
  
  
  bool GlRenderer::sync()
  {
      GL_CHECK(glFinish());
      GlRenderTask::unload();
      return true;
  }
  
  
  RenderRegion GlRenderer::region(TVG_UNUSED RenderData data)
  {
      return {0, 0, 0, 0};
  }
  
  
  bool GlRenderer::preRender()
  {
      if (mRenderTasks.size() == 0)
      {
          initShaders();
      }
      GlRenderTask::unload();
  
      // Blend function for straight alpha
      GL_CHECK(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
      GL_CHECK(glEnable(GL_BLEND));
      return true;
  }
  
  
  bool GlRenderer::postRender()
  {
      //TODO: called just after render()
  
      return true;
  }
  
  
  Compositor* GlRenderer::target(TVG_UNUSED const RenderRegion& region)
  {
      //TODO: Prepare frameBuffer & Setup render target for composition
      return nullptr;
  }
  
  
  bool GlRenderer::beginComposite(TVG_UNUSED Compositor* cmp, CompositeMethod method, uint32_t opacity)
  {
      //TODO: delete the given compositor and restore the context
      return false;
  }
  
  
  bool GlRenderer::endComposite(TVG_UNUSED Compositor* cmp)
  {
      //TODO: delete the given compositor and restore the context
      return false;
  }
  
  
  bool GlRenderer::renderImage(TVG_UNUSED void* data)
  {
      return false;
  }
  
  
  bool GlRenderer::renderShape(RenderData data)
  {
      auto sdata = static_cast<GlShape*>(data);
      if (!sdata) return false;
  
      uint8_t r, g, b, a;
      size_t flags = static_cast<size_t>(sdata->updateFlag);
  
      GL_CHECK(glViewport(0, 0, sdata->viewWd, sdata->viewHt));
  
      uint32_t primitiveCount = sdata->geometry->getPrimitiveCount();
      for (uint32_t i = 0; i < primitiveCount; ++i)
      {
          if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform))
          {
              const Fill* gradient = sdata->shape->fill();
              if (gradient != nullptr)
              {
  		drawPrimitive(*sdata, gradient, i, RenderUpdateFlag::Gradient);
  	    }
          }
  
          if(flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Transform))
          {
              sdata->shape->fillColor(&r, &g, &b, &a);
              if (a > 0)
              {
                  drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Color);
              }
          }
  
          if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform))
          {
              sdata->shape->strokeColor(&r, &g, &b, &a);
              if (a > 0)
              {
                  drawPrimitive(*sdata, r, g, b, a, i, RenderUpdateFlag::Stroke);
              }
          }
      }
  
      return true;
  }
  
  
  bool GlRenderer::dispose(RenderData data)
  {
      auto sdata = static_cast<GlShape*>(data);
      if (!sdata) return false;
  
      delete sdata;
      return true;
  }
  
  
  RenderData GlRenderer::prepare(TVG_UNUSED const Picture& picture, TVG_UNUSED RenderData data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED Array<RenderData>& clips, TVG_UNUSED RenderUpdateFlag flags)
  {
      //TODO:
      return nullptr;
  }
  
  
  RenderData GlRenderer::prepare(const Shape& shape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
  {
      //prepare shape data
      GlShape* sdata = static_cast<GlShape*>(data);
      if (!sdata) {
          sdata = new GlShape;
          if (!sdata) return nullptr;
          sdata->shape = &shape;
      }
  
      sdata->viewWd = static_cast<float>(surface.w);
      sdata->viewHt = static_cast<float>(surface.h);
      sdata->updateFlag = flags;
  
      if (sdata->updateFlag == RenderUpdateFlag::None) return sdata;
  
      sdata->geometry = make_unique<GlGeometry>();
  
      //invisible?
      uint8_t alphaF, alphaS;
      shape.fillColor(nullptr, nullptr, nullptr, &alphaF);
      shape.strokeColor(nullptr, nullptr, nullptr, &alphaS);
      auto strokeWd = shape.strokeWidth();
  
      if ( ((sdata->updateFlag & RenderUpdateFlag::Gradient) == 0) &&
           ((sdata->updateFlag & RenderUpdateFlag::Color) && alphaF == 0) &&
           ((sdata->updateFlag & RenderUpdateFlag::Stroke) && alphaS == 0) )
      {
          return sdata;
      }
  
      sdata->geometry->updateTransform(transform, sdata->viewWd, sdata->viewHt);
  
      if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Stroke | RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform) )
      {
          if (!sdata->geometry->decomposeOutline(shape)) return sdata;
          if (!sdata->geometry->generateAAPoints(shape, static_cast<float>(strokeWd), sdata->updateFlag)) return sdata;
          if (!sdata->geometry->tesselate(shape, sdata->viewWd, sdata->viewHt, sdata->updateFlag)) return sdata;
      }
      return sdata;
  }
  
  
  RenderRegion GlRenderer::viewport()
  {
      return {0, 0, UINT32_MAX, UINT32_MAX};
  }
  
  
  bool GlRenderer::viewport(TVG_UNUSED const RenderRegion& vp)
  {
      //TODO:
      return true;
  }
  
  
  int GlRenderer::init(uint32_t threads)
  {
      if ((initEngineCnt++) > 0) return true;
  
      //TODO:
  
      return true;
  }
  
  
  int32_t GlRenderer::init()
  {
      return initEngineCnt;
  }
  
  
  int GlRenderer::term()
  {
      if ((--initEngineCnt) > 0) return true;
  
      initEngineCnt = 0;
  
     _termEngine();
  
      return true;
  }
  
  
  GlRenderer* GlRenderer::gen()
  {
      return new GlRenderer();
  }
  
  
  GlRenderer::~GlRenderer()
  {
      mRenderTasks.clear();
  
      --rendererCnt;
  
      if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
  }
  
  
  void GlRenderer::initShaders()
  {
      // Solid Color Renderer
      mRenderTasks.push_back(GlColorRenderTask::gen());
  
      // Linear Gradient Renderer
      mRenderTasks.push_back(GlLinearGradientRenderTask::gen());
  
      // Radial Gradient Renderer
      mRenderTasks.push_back(GlRadialGradientRenderTask::gen());
  }
  
  
  void GlRenderer::drawPrimitive(GlShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint32_t primitiveIndex, RenderUpdateFlag flag)
  {
      GlColorRenderTask* renderTask = static_cast<GlColorRenderTask*>(mRenderTasks[GlRenderTask::RenderTypes::RT_Color].get());
      assert(renderTask);
      renderTask->load();
      float* matrix = sdata.geometry->getTransforMatrix();
      PropertyInterface::clearData(renderTask);
      renderTask->setColor(r, g, b, a);
      renderTask->setTransform(FORMAT_SIZE_MAT_4x4, matrix);
      int32_t vertexLoc = renderTask->getLocationPropertyId();
      renderTask->uploadValues();
      sdata.geometry->draw(vertexLoc, primitiveIndex, flag);
      sdata.geometry->disableVertex(vertexLoc);
  
  }
  
  
  void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, uint32_t primitiveIndex, RenderUpdateFlag flag)
  {
      const Fill::ColorStop* stops = nullptr;
      auto stopCnt = fill->colorStops(&stops);
      if (stopCnt < 2) return;
  
      GlGradientRenderTask* rTask = nullptr;
      auto size = sdata.geometry->getPrimitiveSize(primitiveIndex);
      auto matrix = sdata.geometry->getTransforMatrix();
  
      switch (fill->id()) {
          case TVG_CLASS_ID_LINEAR: {
              float x1, y1, x2, y2;
              GlLinearGradientRenderTask *renderTask = static_cast<GlLinearGradientRenderTask*>(mRenderTasks[GlRenderTask::RenderTypes::RT_LinGradient].get());
              assert(renderTask);
              rTask = renderTask;
              renderTask->load();
              PropertyInterface::clearData(renderTask);
              const LinearGradient* grad = static_cast<const LinearGradient*>(fill);
              grad->linear(&x1, &y1, &x2, &y2);
              renderTask->setStartPosition(x1, y1);
              renderTask->setEndPosition(x2, y2);
              break;
          }
          case TVG_CLASS_ID_RADIAL: {
              float x1, y1, r1;
              GlRadialGradientRenderTask *renderTask = static_cast<GlRadialGradientRenderTask*>(mRenderTasks[GlRenderTask::RenderTypes::RT_RadGradient].get());
              assert(renderTask);
              rTask = renderTask;
              renderTask->load();
              PropertyInterface::clearData(renderTask);
              const RadialGradient* grad = static_cast<const RadialGradient*>(fill);
              grad->radial(&x1, &y1, &r1);
              renderTask->setStartPosition(x1, y1);
              renderTask->setStartRadius(r1);
              break;
          }
      }
      if (rTask) {
          auto vertexLoc = rTask->getLocationPropertyId();
          rTask->setPrimitveSize(size.x, size.y);
          rTask->setCanvasSize(sdata.viewWd, sdata.viewHt);
          rTask->setNoise(NOISE_LEVEL);
          rTask->setStopCount((int)stopCnt);
          rTask->setTransform(FORMAT_SIZE_MAT_4x4, matrix);
  
          for (uint32_t i = 0; i < stopCnt; ++i) {
              rTask->setStopColor(i, stops[i].offset, stops[i].r, stops[i].g, stops[i].b, stops[i].a);
          }
  
          rTask->uploadValues();
          sdata.geometry->draw(vertexLoc, primitiveIndex, flag);
          sdata.geometry->disableVertex(vertexLoc);
      }
  }
  
  