text: /**
    ******************************************************************************
    * This file is part of the TouchGFX 4.16.1 distribution.
    *
    * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
    * All rights reserved.</center></h2>
    *
    * This software component is licensed by ST under Ultimate Liberty license
    * SLA0044, the "License"; You may not use this file except in compliance with
    * the License. You may obtain a copy of the License at:
    *                             www.st.com/SLA0044
    *
    ******************************************************************************
    */
  
  #include <touchgfx/TextureMapTypes.hpp>
  #include <touchgfx/hal/HAL.hpp>
  #include <touchgfx/transforms/DisplayTransformation.hpp>
  #include <touchgfx/widgets/ScalableImage.hpp>
  
  namespace touchgfx
  {
  ScalableImage::ScalableImage(const Bitmap& bitmap /*= Bitmap() */)
      : Image(bitmap),
        currentScalingAlgorithm(BILINEAR_INTERPOLATION)
  {
  }
  
  void ScalableImage::setScalingAlgorithm(ScalingAlgorithm algorithm)
  {
      currentScalingAlgorithm = algorithm;
  }
  
  ScalableImage::ScalingAlgorithm ScalableImage::getScalingAlgorithm()
  {
      return currentScalingAlgorithm;
  }
  
  void ScalableImage::drawQuad(const Rect& invalidatedArea, uint16_t* fb, const float* triangleXs, const float* triangleYs, const float* triangleZs, const float* triangleUs, const float* triangleVs) const
  {
      // Area to redraw. Relative to the scalableImage.
      Rect dirtyArea = Rect(0, 0, getWidth(), getHeight()) & invalidatedArea;
  
      // Absolute position of the scalableImage.
      Rect dirtyAreaAbsolute = dirtyArea;
      translateRectToAbsolute(dirtyAreaAbsolute);
  
      Rect absoluteRect = getAbsoluteRect();
      DisplayTransformation::transformDisplayToFrameBuffer(absoluteRect);
  
      // Transform rects to match framebuffer coordinates
      // This is needed if the display is rotated compared to the framebuffer
      DisplayTransformation::transformDisplayToFrameBuffer(dirtyArea, this->getRect());
      DisplayTransformation::transformDisplayToFrameBuffer(dirtyAreaAbsolute);
  
      // Get a pointer to the bitmap data, return if no bitmap found
      const uint16_t* textmap = (const uint16_t*)bitmap.getData();
      if (!textmap)
      {
          return;
      }
  
      float x0 = triangleXs[0];
      float x1 = triangleXs[1];
      float x2 = triangleXs[2];
      float x3 = triangleXs[3];
      float y0 = triangleYs[0]; //lint !e578
      float y1 = triangleYs[1]; //lint !e578
      float y2 = triangleYs[2];
      float y3 = triangleYs[3];
  
      DisplayTransformation::transformDisplayToFrameBuffer(x0, y0, this->getRect());
      DisplayTransformation::transformDisplayToFrameBuffer(x1, y1, this->getRect());
      DisplayTransformation::transformDisplayToFrameBuffer(x2, y2, this->getRect());
      DisplayTransformation::transformDisplayToFrameBuffer(x3, y3, this->getRect());
  
      Point3D vertices[4];
      Point3D point0 = { floatToFixed28_4(x0), floatToFixed28_4(y0), (float)(triangleZs[0]), (float)(triangleUs[0]), (float)(triangleVs[0]) };
      Point3D point1 = { floatToFixed28_4(x1), floatToFixed28_4(y1), (float)(triangleZs[1]), (float)(triangleUs[1]), (float)(triangleVs[1]) };
      Point3D point2 = { floatToFixed28_4(x2), floatToFixed28_4(y2), (float)(triangleZs[2]), (float)(triangleUs[2]), (float)(triangleVs[2]) };
      Point3D point3 = { floatToFixed28_4(x3), floatToFixed28_4(y3), (float)(triangleZs[3]), (float)(triangleUs[3]), (float)(triangleVs[3]) };
  
      vertices[0] = point0;
      vertices[1] = point1;
      vertices[2] = point2;
      vertices[3] = point3;
  
      DrawingSurface dest = { fb, HAL::FRAME_BUFFER_WIDTH };
      TextureSurface src = { textmap, bitmap.getExtraData(), bitmap.getWidth(), bitmap.getHeight(), bitmap.getWidth() };
  
      HAL::lcd().drawTextureMapQuad(dest, vertices, src, absoluteRect, dirtyAreaAbsolute, lookupRenderVariant(), alpha, 0xFFFF);
  }
  
  RenderingVariant ScalableImage::lookupRenderVariant() const
  {
      RenderingVariant renderVariant;
      if (currentScalingAlgorithm == NEAREST_NEIGHBOR)
      {
          renderVariant = lookupNearestNeighborRenderVariant(bitmap);
      }
      else
      {
          renderVariant = lookupBilinearRenderVariant(bitmap);
      }
      return renderVariant;
  }
  
  void ScalableImage::draw(const Rect& invalidatedArea) const
  {
      if (!alpha)
      {
          return;
      }
      uint16_t* fb = 0;
  
      float triangleXs[4];
      float triangleYs[4];
      float triangleZs[4];
      float triangleUs[4];
      float triangleVs[4];
  
      float imageX0 = 0;
      float imageY0 = 0;
      float imageX1 = imageX0 + getWidth();
      float imageY1 = imageY0;
      float imageX2 = imageX1;
      float imageY2 = imageY0 + getHeight();
      float imageX3 = imageX0;
      float imageY3 = imageY2;
  
      triangleZs[0] = 100.f;
      triangleZs[1] = 100.f;
      triangleZs[2] = 100.f;
      triangleZs[3] = 100.f;
  
      // Setup texture coordinates
      float right = (float)(bitmap.getWidth());
      float bottom = (float)(bitmap.getHeight());
      float textureU0 = 0.0f;
      float textureV0 = 0.0f;
      float textureU1 = right;
      float textureV1 = 0.0f;
      float textureU2 = right;
      float textureV2 = bottom;
      float textureU3 = 0.0f;
      float textureV3 = bottom;
      if (HAL::DISPLAY_ROTATION == rotate90)
      {
          textureU0 = 0.0f;
          textureV0 = right;
          textureU1 = 0.0f;
          textureV1 = 0.0f;
          textureU2 = bottom;
          textureV2 = 0.0f;
          textureU3 = bottom;
          textureV3 = right;
      }
  
      triangleXs[0] = imageX0;
      triangleXs[1] = imageX1;
      triangleXs[2] = imageX2;
      triangleXs[3] = imageX3;
      triangleYs[0] = imageY0;
      triangleYs[1] = imageY1;
      triangleYs[2] = imageY2;
      triangleYs[3] = imageY3;
  
      triangleUs[0] = textureU0;
      triangleUs[1] = textureU1;
      triangleUs[2] = textureU2;
      triangleUs[3] = textureU3;
      triangleVs[0] = textureV0;
      triangleVs[1] = textureV1;
      triangleVs[2] = textureV2;
      triangleVs[3] = textureV3;
  
      drawQuad(invalidatedArea, fb, triangleXs, triangleYs, triangleZs, triangleUs, triangleVs);
  }
  
  Rect ScalableImage::getSolidRect() const
  {
      if (alpha < 255)
      {
          return Rect(0, 0, 0, 0);
      }
  
      // If original image is completely solid the scaled image will also be
      if ((bitmap.getSolidRect().width == bitmap.getWidth()) && (bitmap.getSolidRect().height == bitmap.getHeight()))
      {
          return bitmap.getSolidRect();
      }
      return Rect(0, 0, 0, 0);
  }
  } // namespace touchgfx
  