361 lines
12 KiB
C++
361 lines
12 KiB
C++
/******************************************************************************
|
|
* Copyright (c) 2018(-2023) STMicroelectronics.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the TouchGFX 4.21.2 distribution.
|
|
*
|
|
* This software is licensed under terms that can be found in the LICENSE file in
|
|
* the root directory of this software component.
|
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <math.h>
|
|
#include <touchgfx/Drawable.hpp>
|
|
#include <touchgfx/Math3D.hpp>
|
|
#include <touchgfx/TextureMapTypes.hpp>
|
|
#include <touchgfx/Utils.hpp>
|
|
#include <touchgfx/hal/HAL.hpp>
|
|
#include <touchgfx/lcd/LCD.hpp>
|
|
#include <touchgfx/transforms/DisplayTransformation.hpp>
|
|
#include <touchgfx/widgets/TextureMapper.hpp>
|
|
|
|
namespace touchgfx
|
|
{
|
|
TextureMapper::TextureMapper(const Bitmap& bmp /*= Bitmap()*/)
|
|
: Image(bmp),
|
|
currentRenderingAlgorithm(NEAREST_NEIGHBOR),
|
|
xBitmapPosition(0.0f),
|
|
yBitmapPosition(0.0f),
|
|
xAngle(0.0f),
|
|
yAngle(0.0f),
|
|
zAngle(0.0f),
|
|
scale(1.0f),
|
|
xOrigo(0.0f),
|
|
yOrigo(0.0f),
|
|
zOrigo(1000.0f),
|
|
xCamera(0.0f),
|
|
yCamera(0.0f),
|
|
cameraDistance(1000.0f),
|
|
imageX0(0.0f),
|
|
imageY0(0.0f),
|
|
imageZ0(1.0f),
|
|
imageX1(0.0f),
|
|
imageY1(0.0f),
|
|
imageZ1(1.0f),
|
|
imageX2(0.0f),
|
|
imageY2(0.0f),
|
|
imageZ2(1.0f),
|
|
imageX3(0.0f),
|
|
imageY3(0.0f),
|
|
imageZ3(1.0f),
|
|
subDivisionSize(12)
|
|
{
|
|
}
|
|
|
|
void TextureMapper::setBitmap(const Bitmap& bmp)
|
|
{
|
|
Image::setBitmap(bmp);
|
|
applyTransformation();
|
|
}
|
|
|
|
void TextureMapper::applyTransformation()
|
|
{
|
|
const uint8_t n = 4;
|
|
|
|
int imgWidth = Bitmap(bitmap).getWidth() + 1;
|
|
int imgHeight = Bitmap(bitmap).getHeight() + 1;
|
|
|
|
Point4 vertices[n] = {
|
|
Point4(xBitmapPosition - 1, yBitmapPosition - 1, cameraDistance),
|
|
Point4((xBitmapPosition - 1) + imgWidth, yBitmapPosition - 1, cameraDistance),
|
|
Point4((xBitmapPosition - 1) + imgWidth, (yBitmapPosition - 1) + imgHeight, cameraDistance),
|
|
Point4(xBitmapPosition - 1, (yBitmapPosition - 1) + imgHeight, cameraDistance),
|
|
};
|
|
Point4 transformed[n];
|
|
|
|
Vector4 tm_center(xOrigo, yOrigo, zOrigo);
|
|
|
|
Matrix4x4 translateToCenter;
|
|
translateToCenter.concatenateXTranslation(-tm_center.getX()).concatenateYTranslation(-tm_center.getY()).concatenateZTranslation(-tm_center.getZ());
|
|
|
|
Matrix4x4 rotateAroundCenter;
|
|
rotateAroundCenter.concatenateXRotation(xAngle).concatenateYRotation(yAngle).concatenateZRotation(zAngle);
|
|
|
|
Matrix4x4 scaleAroundCenter;
|
|
scaleAroundCenter.concatenateXScale(scale).concatenateYScale(scale).concatenateZScale(scale);
|
|
|
|
Matrix4x4 translateFromCenter;
|
|
translateFromCenter.concatenateXTranslation(tm_center.getX()).concatenateYTranslation(tm_center.getY()).concatenateZTranslation(tm_center.getZ());
|
|
|
|
Matrix4x4 transform = translateFromCenter * scaleAroundCenter * rotateAroundCenter * translateToCenter;
|
|
|
|
Matrix4x4 translateToCamera;
|
|
translateToCamera.concatenateXTranslation(-xCamera);
|
|
translateToCamera.concatenateYTranslation(-yCamera);
|
|
|
|
Matrix4x4 perspectiveProject;
|
|
perspectiveProject.setViewDistance(cameraDistance);
|
|
|
|
Matrix4x4 translateFromCamera;
|
|
translateFromCamera.concatenateXTranslation(xCamera).concatenateYTranslation(yCamera);
|
|
|
|
transform = translateFromCamera * perspectiveProject * translateToCamera * transform;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
transformed[i] = transform * vertices[i];
|
|
}
|
|
|
|
imageX0 = ((float)transformed[0].getX() * cameraDistance / (float)transformed[0].getZ());
|
|
imageY0 = ((float)transformed[0].getY() * cameraDistance / (float)transformed[0].getZ());
|
|
imageZ0 = ((float)transformed[0].getZ());
|
|
|
|
imageX1 = ((float)transformed[1].getX() * cameraDistance / (float)transformed[1].getZ());
|
|
imageY1 = ((float)transformed[1].getY() * cameraDistance / (float)transformed[1].getZ());
|
|
imageZ1 = ((float)transformed[1].getZ());
|
|
|
|
imageX2 = ((float)transformed[2].getX() * cameraDistance / (float)transformed[2].getZ());
|
|
imageY2 = ((float)transformed[2].getY() * cameraDistance / (float)transformed[2].getZ());
|
|
imageZ2 = ((float)transformed[2].getZ());
|
|
|
|
imageX3 = ((float)transformed[3].getX() * cameraDistance / (float)transformed[3].getZ());
|
|
imageY3 = ((float)transformed[3].getY() * cameraDistance / (float)transformed[3].getZ());
|
|
imageZ3 = ((float)transformed[3].getZ());
|
|
}
|
|
|
|
Rect TextureMapper::getBoundingRect() const
|
|
{
|
|
// MIN and MAX are macros so do not insert them into each other
|
|
float minXf = MIN(imageX0, imageX1);
|
|
minXf = MIN(minXf, imageX2);
|
|
minXf = floorf(MIN(minXf, imageX3));
|
|
int16_t minX = (int16_t)(MAX(0, minXf));
|
|
|
|
float maxXf = MAX(imageX0, imageX1);
|
|
maxXf = MAX(maxXf, imageX2);
|
|
maxXf = ceilf(MAX(maxXf, imageX3));
|
|
int16_t maxX = getWidth();
|
|
maxX = (int16_t)(MIN(maxX, maxXf));
|
|
|
|
float minYf = MIN(imageY0, imageY1);
|
|
minYf = MIN(minYf, imageY2);
|
|
minYf = floorf(MIN(minYf, imageY3));
|
|
int16_t minY = (int16_t)(MAX(0, minYf));
|
|
|
|
float maxYf = MAX(imageY0, imageY1);
|
|
maxYf = MAX(maxYf, imageY2);
|
|
maxYf = ceilf(MAX(maxYf, imageY3));
|
|
int16_t maxY = getHeight();
|
|
maxY = (int16_t)(MIN(maxY, maxYf));
|
|
|
|
return Rect(minX, minY, maxX - minX, maxY - minY);
|
|
}
|
|
|
|
void TextureMapper::setAngles(float newXAngle, float newYAngle, float newZAngle)
|
|
{
|
|
xAngle = newXAngle;
|
|
yAngle = newYAngle;
|
|
zAngle = newZAngle;
|
|
|
|
applyTransformation();
|
|
}
|
|
|
|
void TextureMapper::updateAngles(float newXAngle, float newYAngle, float newZAngle)
|
|
{
|
|
invalidateContent();
|
|
setAngles(newXAngle, newYAngle, newZAngle);
|
|
invalidateContent();
|
|
}
|
|
|
|
void TextureMapper::setScale(float newScale)
|
|
{
|
|
scale = newScale;
|
|
|
|
applyTransformation();
|
|
}
|
|
|
|
void TextureMapper::updateScale(float newScale)
|
|
{
|
|
invalidateContent();
|
|
setScale(newScale);
|
|
invalidateContent();
|
|
}
|
|
|
|
void TextureMapper::invalidateBoundingRect() const
|
|
{
|
|
Rect r = getBoundingRect();
|
|
invalidateRect(r);
|
|
}
|
|
|
|
void TextureMapper::draw(const Rect& invalidatedArea) const
|
|
{
|
|
if (!alpha)
|
|
{
|
|
return;
|
|
}
|
|
uint16_t* fb = 0;
|
|
|
|
// Setup texture coordinates
|
|
float right = (float)(bitmap.getWidth());
|
|
float bottom = (float)(bitmap.getHeight());
|
|
float textureU0 = -1.0f;
|
|
float textureV0 = -1.0f;
|
|
float textureU1 = right;
|
|
float textureV1 = -1.0f;
|
|
float textureU2 = right;
|
|
float textureV2 = bottom;
|
|
float textureU3 = -1.0f;
|
|
float textureV3 = bottom;
|
|
if (HAL::DISPLAY_ROTATION == rotate90)
|
|
{
|
|
textureU0 = -1.0f;
|
|
textureV0 = right;
|
|
textureU1 = -1.0f;
|
|
textureV1 = -1.0f;
|
|
textureU2 = bottom;
|
|
textureV2 = -1.0f;
|
|
textureU3 = bottom;
|
|
textureV3 = right;
|
|
}
|
|
|
|
float triangleXs[4];
|
|
float triangleYs[4];
|
|
float triangleZs[4];
|
|
float triangleUs[4];
|
|
float triangleVs[4];
|
|
|
|
// Determine winding order
|
|
Vector4 zeroToOne(imageX1 - imageX0, imageY1 - imageY0, imageZ1 - imageZ0);
|
|
Vector4 zeroToTwo(imageX2 - imageX0, imageY2 - imageY0, imageZ2 - imageZ0);
|
|
Vector4 normal = zeroToOne.crossProduct(zeroToTwo);
|
|
|
|
if (normal.getZ() > 0)
|
|
{
|
|
triangleXs[0] = imageX0;
|
|
triangleXs[1] = imageX1;
|
|
triangleXs[2] = imageX2;
|
|
triangleXs[3] = imageX3;
|
|
triangleYs[0] = imageY0;
|
|
triangleYs[1] = imageY1;
|
|
triangleYs[2] = imageY2;
|
|
triangleYs[3] = imageY3;
|
|
triangleZs[0] = imageZ0;
|
|
triangleZs[1] = imageZ1;
|
|
triangleZs[2] = imageZ2;
|
|
triangleZs[3] = imageZ3;
|
|
|
|
triangleUs[0] = textureU0;
|
|
triangleUs[1] = textureU1;
|
|
triangleUs[2] = textureU2;
|
|
triangleUs[3] = textureU3;
|
|
triangleVs[0] = textureV0;
|
|
triangleVs[1] = textureV1;
|
|
triangleVs[2] = textureV2;
|
|
triangleVs[3] = textureV3;
|
|
}
|
|
else
|
|
{
|
|
// invert due to the triangles winding order (showing backface of the triangle)
|
|
triangleXs[1] = imageX0;
|
|
triangleXs[0] = imageX1;
|
|
triangleXs[2] = imageX3;
|
|
triangleXs[3] = imageX2;
|
|
triangleYs[1] = imageY0;
|
|
triangleYs[0] = imageY1;
|
|
triangleYs[2] = imageY3;
|
|
triangleYs[3] = imageY2;
|
|
triangleZs[1] = imageZ0;
|
|
triangleZs[0] = imageZ1;
|
|
triangleZs[2] = imageZ3;
|
|
triangleZs[3] = imageZ2;
|
|
|
|
triangleUs[1] = textureU0;
|
|
triangleUs[0] = textureU1;
|
|
triangleUs[2] = textureU3;
|
|
triangleUs[3] = textureU2;
|
|
triangleVs[1] = textureV0;
|
|
triangleVs[0] = textureV1;
|
|
triangleVs[2] = textureV3;
|
|
triangleVs[3] = textureV2;
|
|
}
|
|
|
|
drawQuad(invalidatedArea, fb, triangleXs, triangleYs, triangleZs, triangleUs, triangleVs);
|
|
}
|
|
|
|
void TextureMapper::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 TextureMapper.
|
|
Rect dirtyArea = Rect(0, 0, getWidth(), getHeight()) & invalidatedArea;
|
|
|
|
// Absolute position of the TextureMapper.
|
|
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];
|
|
float y1 = triangleYs[1];
|
|
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());
|
|
|
|
const Point3D point0 = { floatToFixed28_4(x0), floatToFixed28_4(y0), triangleZs[0], triangleUs[0], triangleVs[0] };
|
|
const Point3D point1 = { floatToFixed28_4(x1), floatToFixed28_4(y1), triangleZs[1], triangleUs[1], triangleVs[1] };
|
|
const Point3D point2 = { floatToFixed28_4(x2), floatToFixed28_4(y2), triangleZs[2], triangleUs[2], triangleVs[2] };
|
|
const Point3D point3 = { floatToFixed28_4(x3), floatToFixed28_4(y3), triangleZs[3], triangleUs[3], triangleVs[3] };
|
|
|
|
const Point3D vertices[4] = { point0, point1, point2, point3 };
|
|
|
|
DrawingSurface dest = { fb, HAL::FRAME_BUFFER_WIDTH };
|
|
TextureSurface src = { textmap, bitmap.getExtraData(), bitmap.getWidth(), bitmap.getHeight(), bitmap.getWidth() };
|
|
|
|
uint16_t subDivs = subDivisionSize;
|
|
if (point0.Z == point1.Z && point1.Z == point2.Z) //lint !e777
|
|
{
|
|
subDivs = 0xFFFF; // Max: One sweep
|
|
}
|
|
HAL::lcd().drawTextureMapQuad(dest, vertices, src, absoluteRect, dirtyAreaAbsolute, lookupRenderVariant(), alpha, subDivs);
|
|
}
|
|
|
|
RenderingVariant TextureMapper::lookupRenderVariant() const
|
|
{
|
|
RenderingVariant renderVariant;
|
|
if (currentRenderingAlgorithm == NEAREST_NEIGHBOR)
|
|
{
|
|
renderVariant = lookupNearestNeighborRenderVariant(bitmap);
|
|
}
|
|
else
|
|
{
|
|
renderVariant = lookupBilinearRenderVariant(bitmap);
|
|
}
|
|
return renderVariant;
|
|
}
|
|
|
|
Rect TextureMapper::getSolidRect() const
|
|
{
|
|
return Rect();
|
|
}
|
|
} // namespace touchgfx
|