Added rounded corner support to the image, video, animation, carousel and grid elements

This commit is contained in:
Leon Styhre 2023-08-20 19:41:07 +02:00
parent ed22fc7aa5
commit 170d8e3791
22 changed files with 219 additions and 60 deletions

View file

@ -118,6 +118,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"itemAxisRotation", FLOAT}, {"itemAxisRotation", FLOAT},
{"imageFit", STRING}, {"imageFit", STRING},
{"imageInterpolation", STRING}, {"imageInterpolation", STRING},
{"imageCornerRadius", FLOAT},
{"imageColor", COLOR}, {"imageColor", COLOR},
{"imageColorEnd", COLOR}, {"imageColorEnd", COLOR},
{"imageGradientType", STRING}, {"imageGradientType", STRING},
@ -184,6 +185,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"unfocusedItemDimming", FLOAT}, {"unfocusedItemDimming", FLOAT},
{"imageFit", STRING}, {"imageFit", STRING},
{"imageRelativeScale", FLOAT}, {"imageRelativeScale", FLOAT},
{"imageCornerRadius", FLOAT},
{"imageColor", COLOR}, {"imageColor", COLOR},
{"imageColorEnd", COLOR}, {"imageColorEnd", COLOR},
{"imageGradientType", STRING}, {"imageGradientType", STRING},
@ -194,11 +196,13 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"imageSaturation", FLOAT}, {"imageSaturation", FLOAT},
{"backgroundImage", PATH}, {"backgroundImage", PATH},
{"backgroundRelativeScale", FLOAT}, {"backgroundRelativeScale", FLOAT},
{"backgroundCornerRadius", FLOAT},
{"backgroundColor", COLOR}, {"backgroundColor", COLOR},
{"backgroundColorEnd", COLOR}, {"backgroundColorEnd", COLOR},
{"backgroundGradientType", STRING}, {"backgroundGradientType", STRING},
{"selectorImage", PATH}, {"selectorImage", PATH},
{"selectorRelativeScale", FLOAT}, {"selectorRelativeScale", FLOAT},
{"selectorCornerRadius", FLOAT},
{"selectorLayer", STRING}, {"selectorLayer", STRING},
{"selectorColor", COLOR}, {"selectorColor", COLOR},
{"selectorColorEnd", COLOR}, {"selectorColorEnd", COLOR},
@ -282,6 +286,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"tileHorizontalAlignment", STRING}, {"tileHorizontalAlignment", STRING},
{"tileVerticalAlignment", STRING}, {"tileVerticalAlignment", STRING},
{"interpolation", STRING}, {"interpolation", STRING},
{"cornerRadius", FLOAT},
{"color", COLOR}, {"color", COLOR},
{"colorEnd", COLOR}, {"colorEnd", COLOR},
{"gradientType", STRING}, {"gradientType", STRING},
@ -309,6 +314,8 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"onIterationsDone", STRING}, {"onIterationsDone", STRING},
{"audio", BOOLEAN}, {"audio", BOOLEAN},
{"interpolation", STRING}, {"interpolation", STRING},
{"imageCornerRadius", FLOAT},
{"videoCornerRadius", FLOAT},
{"color", COLOR}, {"color", COLOR},
{"colorEnd", COLOR}, {"colorEnd", COLOR},
{"gradientType", STRING}, {"gradientType", STRING},
@ -337,6 +344,7 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"direction", STRING}, {"direction", STRING},
{"iterationCount", UNSIGNED_INTEGER}, {"iterationCount", UNSIGNED_INTEGER},
{"interpolation", STRING}, {"interpolation", STRING},
{"cornerRadius", FLOAT},
{"color", COLOR}, {"color", COLOR},
{"colorEnd", COLOR}, {"colorEnd", COLOR},
{"gradientType", STRING}, {"gradientType", STRING},

View file

@ -45,6 +45,7 @@ GIFAnimComponent::GIFAnimComponent()
, mIterationCount {0} , mIterationCount {0}
, mPlayCount {0} , mPlayCount {0}
, mTargetIsMax {false} , mTargetIsMax {false}
, mCornerRadius {0.0f}
, mColorShift {0xFFFFFFFF} , mColorShift {0xFFFFFFFF}
, mColorShiftEnd {0xFFFFFFFF} , mColorShiftEnd {0xFFFFFFFF}
, mColorGradientHorizontal {true} , mColorGradientHorizontal {true}
@ -372,6 +373,10 @@ void GIFAnimComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
mIterationCount *= 2; mIterationCount *= 2;
} }
if (elem->has("cornerRadius"))
mCornerRadius =
glm::clamp(elem->get<float>("cornerRadius"), 0.0f, 0.5f) * mRenderer->getScreenWidth();
if (elem->has("interpolation")) { if (elem->has("interpolation")) {
const std::string& interpolation {elem->get<std::string>("interpolation")}; const std::string& interpolation {elem->get<std::string>("interpolation")};
if (interpolation == "linear") { if (interpolation == "linear") {
@ -588,6 +593,11 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
vertices->dimming = mDimming; vertices->dimming = mDimming;
vertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED; vertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED;
if (mCornerRadius > 0.0f) {
vertices->cornerRadius = mCornerRadius;
vertices->shaderFlags = vertices->shaderFlags | Renderer::ShaderFlags::ROUNDED_CORNERS;
}
// Render it. // Render it.
mRenderer->drawTriangleStrips(&vertices[0], 4); mRenderer->drawTriangleStrips(&vertices[0], 4);
} }

View file

@ -104,6 +104,7 @@ private:
int mPlayCount; int mPlayCount;
bool mTargetIsMax; bool mTargetIsMax;
float mCornerRadius;
unsigned int mColorShift; unsigned int mColorShift;
unsigned int mColorShiftEnd; unsigned int mColorShiftEnd;
bool mColorGradientHorizontal; bool mColorGradientHorizontal;

View file

@ -29,7 +29,9 @@ ImageComponent::ImageComponent(bool forceLoad, bool dynamic)
, mColorShiftEnd {0xFFFFFFFF} , mColorShiftEnd {0xFFFFFFFF}
, mColorGradientHorizontal {true} , mColorGradientHorizontal {true}
, mFadeOpacity {0.0f} , mFadeOpacity {0.0f}
, mCornerRadius {0.0f}
, mReflectionsFalloff {0.0f} , mReflectionsFalloff {0.0f}
, mCornerAntiAliasing {true}
, mFading {false} , mFading {false}
, mForceLoad {forceLoad} , mForceLoad {forceLoad}
, mDynamic {dynamic} , mDynamic {dynamic}
@ -332,7 +334,6 @@ void ImageComponent::setSaturation(float saturation)
void ImageComponent::setDimming(float dimming) void ImageComponent::setDimming(float dimming)
{ {
// Set dimming value.
mDimming = dimming; mDimming = dimming;
} }
@ -432,6 +433,18 @@ void ImageComponent::render(const glm::mat4& parentTrans)
mVertices->dimming = mDimming; mVertices->dimming = mDimming;
mVertices->reflectionsFalloff = mReflectionsFalloff; mVertices->reflectionsFalloff = mReflectionsFalloff;
if (mCornerRadius > 0.0f) {
mVertices->cornerRadius = mCornerRadius;
if (mCornerAntiAliasing) {
mVertices->shaderFlags =
mVertices->shaderFlags | Renderer::ShaderFlags::ROUNDED_CORNERS;
}
else {
mVertices->shaderFlags =
mVertices->shaderFlags | Renderer::ShaderFlags::ROUNDED_CORNERS_NO_AA;
}
}
mVertices->shaderFlags = mVertices->shaderFlags | Renderer::ShaderFlags::PREMULTIPLIED; mVertices->shaderFlags = mVertices->shaderFlags | Renderer::ShaderFlags::PREMULTIPLIED;
mRenderer->drawTriangleStrips(&mVertices[0], 4); mRenderer->drawTriangleStrips(&mVertices[0], 4);
} }
@ -535,6 +548,10 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
if (elem->has("cornerRadius"))
mCornerRadius =
glm::clamp(elem->get<float>("cornerRadius"), 0.0f, 0.5f) * mRenderer->getScreenWidth();
if (properties && elem->has("imageType")) { if (properties && elem->has("imageType")) {
std::string imageTypes {elem->get<std::string>("imageType")}; std::string imageTypes {elem->get<std::string>("imageType")};
for (auto& character : imageTypes) { for (auto& character : imageTypes) {

View file

@ -89,6 +89,8 @@ public:
void setSaturation(float saturation) override; void setSaturation(float saturation) override;
void setDimming(float dimming) override; void setDimming(float dimming) override;
void setClipRegion(const glm::vec4& clipRegion); void setClipRegion(const glm::vec4& clipRegion);
void setCornerRadius(float radius) { mCornerRadius = radius; }
void setCornerAntiAliasing(bool state) { mCornerAntiAliasing = state; }
void setReflectionsFalloff(float falloff) override { mReflectionsFalloff = falloff; } void setReflectionsFalloff(float falloff) override { mReflectionsFalloff = falloff; }
void setFlipX(bool state) override; // Mirror on the X axis. void setFlipX(bool state) override; // Mirror on the X axis.
@ -159,7 +161,9 @@ private:
std::shared_ptr<TextureResource> mTexture; std::shared_ptr<TextureResource> mTexture;
float mFadeOpacity; float mFadeOpacity;
float mCornerRadius;
float mReflectionsFalloff; float mReflectionsFalloff;
bool mCornerAntiAliasing;
bool mFading; bool mFading;
bool mForceLoad; bool mForceLoad;
bool mDynamic; bool mDynamic;

View file

@ -40,6 +40,7 @@ LottieAnimComponent::LottieAnimComponent()
, mIterationCount {0} , mIterationCount {0}
, mPlayCount {0} , mPlayCount {0}
, mTargetIsMax {false} , mTargetIsMax {false}
, mCornerRadius {0.0f}
, mColorShift {0xFFFFFFFF} , mColorShift {0xFFFFFFFF}
, mColorShiftEnd {0xFFFFFFFF} , mColorShiftEnd {0xFFFFFFFF}
, mColorGradientHorizontal {true} , mColorGradientHorizontal {true}
@ -342,6 +343,10 @@ void LottieAnimComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
mIterationCount *= 2; mIterationCount *= 2;
} }
if (elem->has("cornerRadius"))
mCornerRadius =
glm::clamp(elem->get<float>("cornerRadius"), 0.0f, 0.5f) * mRenderer->getScreenWidth();
if (properties & COLOR) { if (properties & COLOR) {
if (elem->has("color")) { if (elem->has("color")) {
mColorShift = elem->get<unsigned int>("color"); mColorShift = elem->get<unsigned int>("color");
@ -579,6 +584,11 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans)
vertices->dimming = mDimming; vertices->dimming = mDimming;
vertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED; vertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED;
if (mCornerRadius > 0.0f) {
vertices->cornerRadius = mCornerRadius;
vertices->shaderFlags = vertices->shaderFlags | Renderer::ShaderFlags::ROUNDED_CORNERS;
}
// Render it. // Render it.
mRenderer->drawTriangleStrips(&vertices[0], 4); mRenderer->drawTriangleStrips(&vertices[0], 4);
} }

View file

@ -90,6 +90,7 @@ private:
int mPlayCount; int mPlayCount;
bool mTargetIsMax; bool mTargetIsMax;
float mCornerRadius;
unsigned int mColorShift; unsigned int mColorShift;
unsigned int mColorShiftEnd; unsigned int mColorShiftEnd;
bool mColorGradientHorizontal; bool mColorGradientHorizontal;

View file

@ -16,14 +16,16 @@
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
#define SCREENSAVER_FADE_IN_TIME 1100 #define SCREENSAVER_FADE_IN_TIME 900
#define MEDIA_VIEWER_FADE_IN_TIME 600 #define MEDIA_VIEWER_FADE_IN_TIME 600
VideoComponent::VideoComponent() VideoComponent::VideoComponent()
: mVideoWidth {0} : mRenderer {Renderer::getInstance()}
, mVideoWidth {0}
, mVideoHeight {0} , mVideoHeight {0}
, mColorShift {0xFFFFFFFF} , mColorShift {0xFFFFFFFF}
, mColorShiftEnd {0xFFFFFFFF} , mColorShiftEnd {0xFFFFFFFF}
, mVideoCornerRadius {0.0f}
, mColorGradientHorizontal {true} , mColorGradientHorizontal {true}
, mTargetSize {0.0f, 0.0f} , mTargetSize {0.0f, 0.0f}
, mVideoAreaPos {0.0f, 0.0f} , mVideoAreaPos {0.0f, 0.0f}
@ -131,7 +133,7 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
glm::vec2 scale {getParent() ? glm::vec2 scale {getParent() ?
getParent()->getSize() : getParent()->getSize() :
glm::vec2 {Renderer::getScreenWidth(), Renderer::getScreenHeight()}}; glm::vec2 {mRenderer->getScreenWidth(), mRenderer->getScreenHeight()}};
if (properties & ThemeFlags::SIZE) { if (properties & ThemeFlags::SIZE) {
if (elem->has("size")) { if (elem->has("size")) {
@ -225,6 +227,14 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
if (elem->has("imageCornerRadius"))
mStaticImage.setCornerRadius(glm::clamp(elem->get<float>("imageCornerRadius"), 0.0f, 0.5f) *
mRenderer->getScreenWidth());
if (elem->has("videoCornerRadius"))
mVideoCornerRadius = glm::clamp(elem->get<float>("videoCornerRadius"), 0.0f, 0.5f) *
mRenderer->getScreenWidth();
if (elem->has("default")) { if (elem->has("default")) {
const std::string defaultVideo {elem->get<std::string>("default")}; const std::string defaultVideo {elem->get<std::string>("default")};
if (ResourceManager::getInstance().fileExists(defaultVideo)) { if (ResourceManager::getInstance().fileExists(defaultVideo)) {
@ -332,6 +342,14 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("pillarboxes")) if (elem->has("pillarboxes"))
mDrawPillarboxes = elem->get<bool>("pillarboxes"); mDrawPillarboxes = elem->get<bool>("pillarboxes");
// The black frame is rendered behind all videos and may be expanded to render pillarboxes
// or letterboxes.
mBlackFrame.setZIndex(mZIndex);
mBlackFrame.setCornerRadius(mVideoCornerRadius);
mBlackFrame.setCornerAntiAliasing(false);
mBlackFrame.setColorShift(0x000000FF);
mBlackFrame.setImage(":/graphics/white.png");
if (elem->has("pillarboxThreshold")) { if (elem->has("pillarboxThreshold")) {
const glm::vec2 pillarboxThreshold {elem->get<glm::vec2>("pillarboxThreshold")}; const glm::vec2 pillarboxThreshold {elem->get<glm::vec2>("pillarboxThreshold")};
mPillarboxThreshold.x = glm::clamp(pillarboxThreshold.x, 0.2f, 1.0f); mPillarboxThreshold.x = glm::clamp(pillarboxThreshold.x, 0.2f, 1.0f);

View file

@ -99,12 +99,15 @@ protected:
IMAGE IMAGE
}; };
Renderer* mRenderer;
ImageComponent mStaticImage; ImageComponent mStaticImage;
ImageComponent mBlackFrame;
unsigned mVideoWidth; unsigned mVideoWidth;
unsigned mVideoHeight; unsigned mVideoHeight;
unsigned int mColorShift; unsigned int mColorShift;
unsigned int mColorShiftEnd; unsigned int mColorShiftEnd;
float mVideoCornerRadius;
bool mColorGradientHorizontal; bool mColorGradientHorizontal;
glm::vec2 mTargetSize; glm::vec2 mTargetSize;
glm::vec2 mVideoAreaPos; glm::vec2 mVideoAreaPos;

View file

@ -30,8 +30,7 @@
#endif #endif
VideoFFmpegComponent::VideoFFmpegComponent() VideoFFmpegComponent::VideoFFmpegComponent()
: mRenderer {Renderer::getInstance()} : mBlackFrameOffset {0.0f, 0.0f}
, mRectangleOffset {0.0f, 0.0f}
, mFrameProcessingThread {nullptr} , mFrameProcessingThread {nullptr}
, mFormatContext {nullptr} , mFormatContext {nullptr}
, mVideoStream {nullptr} , mVideoStream {nullptr}
@ -176,31 +175,23 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
if (mIsPlaying && mFormatContext) { if (mIsPlaying && mFormatContext) {
Renderer::Vertex vertices[4]; Renderer::Vertex vertices[4];
mRenderer->setMatrix(trans);
unsigned int rectColor {0x000000FF}; if (!mScreensaverMode && !mMediaViewerMode) {
mBlackFrame.setOpacity(mOpacity * mThemeOpacity);
if (!mGeneralFade && mThemeOpacity != 1.0f) mBlackFrame.render(trans);
rectColor = static_cast<int>(mThemeOpacity * 255.0f);
if (mGeneralFade && (mOpacity != 1.0f || mThemeOpacity != 1.0f))
rectColor = static_cast<int>(mFadeIn * mOpacity * mThemeOpacity * 255.0f);
// Render the black rectangle behind the video.
if (mVideoRectangleCoords.size() == 4) {
mRenderer->drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1],
mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break.
rectColor, rectColor);
} }
mRenderer->setMatrix(trans);
// This is needed to avoid a slight gap before the video starts playing. // This is needed to avoid a slight gap before the video starts playing.
if (!mDecodedFrame) if (!mDecodedFrame)
return; return;
// clang-format off // clang-format off
vertices[0] = {{0.0f + mRectangleOffset.x, 0.0f + mRectangleOffset.y }, {mTopLeftCrop.x, 1.0f - mBottomRightCrop.y}, 0xFFFFFFFF}; vertices[0] = {{0.0f + mBlackFrameOffset.x, 0.0f + mBlackFrameOffset.y }, {mTopLeftCrop.x, 1.0f - mBottomRightCrop.y}, 0xFFFFFFFF};
vertices[1] = {{0.0f + mRectangleOffset.x, mSize.y + mRectangleOffset.y }, {mTopLeftCrop.x, 1.0f - mTopLeftCrop.y }, 0xFFFFFFFF}; vertices[1] = {{0.0f + mBlackFrameOffset.x, mSize.y + mBlackFrameOffset.y }, {mTopLeftCrop.x, 1.0f - mTopLeftCrop.y }, 0xFFFFFFFF};
vertices[2] = {{mSize.x + mRectangleOffset.x, 0.0f + + mRectangleOffset.y }, {mBottomRightCrop.x * 1.0f, 1.0f - mBottomRightCrop.y}, 0xFFFFFFFF}; vertices[2] = {{mSize.x + mBlackFrameOffset.x, 0.0f + + mBlackFrameOffset.y }, {mBottomRightCrop.x * 1.0f, 1.0f - mBottomRightCrop.y}, 0xFFFFFFFF};
vertices[3] = {{mSize.x + mRectangleOffset.x, mSize.y + + mRectangleOffset.y}, {mBottomRightCrop.x * 1.0f, 1.0f - mTopLeftCrop.y }, 0xFFFFFFFF}; vertices[3] = {{mSize.x + mBlackFrameOffset.x, mSize.y + + mBlackFrameOffset.y}, {mBottomRightCrop.x * 1.0f, 1.0f - mTopLeftCrop.y }, 0xFFFFFFFF};
// clang-format on // clang-format on
vertices[0].color = mColorShift; vertices[0].color = mColorShift;
@ -213,12 +204,22 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
vertices[i].position = glm::round(vertices[i].position); vertices[i].position = glm::round(vertices[i].position);
if (mFadeIn < 1.0f || mThemeOpacity < 1.0f) if (mFadeIn < 1.0f || mThemeOpacity < 1.0f)
vertices->opacity = mFadeIn * mThemeOpacity; vertices->opacity = mOpacity * mThemeOpacity;
vertices->brightness = mBrightness; vertices->brightness = mBrightness;
vertices->saturation = mSaturation * mThemeSaturation; vertices->saturation = mSaturation * mThemeSaturation;
vertices->saturation = 1.0f;
vertices->dimming = mDimming; vertices->dimming = mDimming;
if (mVideoCornerRadius > 0.0f) {
// We don't want to apply anti-aliasing to rounded corners as the black frame is
// rendered behind the video and that would generate ugly edge artifacts for any
// videos with lighter content.
vertices->cornerRadius = mVideoCornerRadius;
vertices->shaderFlags =
vertices->shaderFlags | Renderer::ShaderFlags::ROUNDED_CORNERS_NO_AA;
}
std::unique_lock<std::mutex> pictureLock {mPictureMutex}; std::unique_lock<std::mutex> pictureLock {mPictureMutex};
if (!mOutputPicture.hasBeenRendered) { if (!mOutputPicture.hasBeenRendered) {
@ -266,6 +267,9 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
if (mRenderScanlines) if (mRenderScanlines)
vertices[0].shaders = Renderer::Shader::SCANLINES; vertices[0].shaders = Renderer::Shader::SCANLINES;
} }
else {
vertices[0].opacity = mFadeIn;
}
mRenderer->drawTriangleStrips(&vertices[0], 4, Renderer::BlendFactor::SRC_ALPHA, mRenderer->drawTriangleStrips(&vertices[0], 4, Renderer::BlendFactor::SRC_ALPHA,
Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA); Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA);
@ -985,22 +989,24 @@ void VideoFFmpegComponent::outputFrames()
mEndOfVideo = true; mEndOfVideo = true;
} }
void VideoFFmpegComponent::calculateBlackRectangle() void VideoFFmpegComponent::calculateBlackFrame()
{ {
// Calculate the position and size for the black rectangle that will be rendered behind // Calculate the position and size for the black frame image that will be rendered behind
// videos. If the option to display pillarboxes (and letterboxes) is enabled, then this // videos. If the option to display pillarboxes (and letterboxes) is enabled, then this
// would extend to the entire video area (if above the threshold as defined below) or // would extend to the entire video area (if above the threshold as explained below) or
// otherwise it will exactly match the video size. The reason to add a black rectangle // otherwise it will exactly match the video size. The reason to add a black frame behind
// behind videos in this second instance is that the scanline rendering will make the // videos in this second instance is that the scanline rendering will make the video
// video partially transparent so this may avoid some unforseen issues with some themes. // partially transparent so this may avoid some unforseen issues with some themes.
// Another reason is that it always take a short while to initiate the video player which
// means no video texture is rendered for that brief moment, and it looks better to draw
// the black frame during this time period as most game videos also fade in from black.
// In general, adding very narrow pillarboxes or letterboxes doesn't look good, so by // In general, adding very narrow pillarboxes or letterboxes doesn't look good, so by
// default this is not done unless the size of the video vs the overall video area is // default this is not done unless the size of the video vs the overall video area is
// above the threshold defined by mPillarboxThreshold. By default this is set to 0.85 // above the threshold defined by mPillarboxThreshold. By default this is set to 0.85
// for the X axis and 0.90 for the Y axis, but this is theme-controllable via the // for the X axis and 0.90 for the Y axis, but this is theme-controllable via the
// pillarboxThreshold property. // pillarboxThreshold property.
if (mVideoAreaPos != glm::vec2 {0.0f, 0.0f} && mVideoAreaSize != glm::vec2 {0.0f, 0.0f}) { if (mVideoAreaPos != glm::vec2 {0.0f, 0.0f} && mVideoAreaSize != glm::vec2 {0.0f, 0.0f}) {
mVideoRectangleCoords.clear(); mBlackFrameOffset = {0.0f, 0.0f};
mRectangleOffset = {0.0f, 0.0f};
if (mDrawPillarboxes) { if (mDrawPillarboxes) {
float rectHeight {0.0f}; float rectHeight {0.0f};
@ -1037,26 +1043,23 @@ void VideoFFmpegComponent::calculateBlackRectangle()
// the video correctly. // the video correctly.
if (mOrigin != glm::vec2 {0.5f, 0.5f}) { if (mOrigin != glm::vec2 {0.5f, 0.5f}) {
if (rectWidth > mSize.x) if (rectWidth > mSize.x)
mRectangleOffset.x -= (rectWidth - mSize.x) * (mOrigin.x - 0.5f); mBlackFrameOffset.x -= (rectWidth - mSize.x) * (mOrigin.x - 0.5f);
else if (rectHeight > mSize.y) else if (rectHeight > mSize.y)
mRectangleOffset.y -= (rectHeight - mSize.y) * (mOrigin.y - 0.5f); mBlackFrameOffset.y -= (rectHeight - mSize.y) * (mOrigin.y - 0.5f);
} }
// Populate the rectangle coordinates to be used in render(). // Set the black frame position and size.
const float offsetX {rectWidth - mSize.x}; const float offsetX {rectWidth - mSize.x};
const float offsetY {rectHeight - mSize.y}; const float offsetY {rectHeight - mSize.y};
mVideoRectangleCoords.emplace_back(std::round((-offsetX / 2.0f) + mRectangleOffset.x)); mBlackFrame.setPosition((-offsetX / 2.0f) + mBlackFrameOffset.x,
mVideoRectangleCoords.emplace_back(std::round((-offsetY / 2.0f) + mRectangleOffset.y)); (-offsetY / 2.0f) + mBlackFrameOffset.y);
mVideoRectangleCoords.emplace_back(std::round(rectWidth)); mBlackFrame.setResize(rectWidth, rectHeight);
mVideoRectangleCoords.emplace_back(std::round(rectHeight));
} }
// If the option to display pillarboxes is disabled, then make the rectangle equivalent
// to the size of the video.
else { else {
mVideoRectangleCoords.emplace_back(0.0f); // If the option to display pillarboxes is disabled, then set the black frame to the
mVideoRectangleCoords.emplace_back(0.0f); // same position and size as the video.
mVideoRectangleCoords.emplace_back(std::round(mSize.x)); mBlackFrame.setPosition(0.0f, 0.0f);
mVideoRectangleCoords.emplace_back(std::round(mSize.y)); mBlackFrame.setResize(mSize.x, mSize.y);
} }
} }
} }
@ -1553,8 +1556,7 @@ void VideoFFmpegComponent::startVideoStream()
// the video screeensaver. // the video screeensaver.
resize(); resize();
// Calculate pillarbox/letterbox sizes. calculateBlackFrame();
calculateBlackRectangle();
mFadeIn = 0.0f; mFadeIn = 0.0f;
} }

View file

@ -82,15 +82,14 @@ private:
// Output frames to AudioManager and to the video surface (via the main thread). // Output frames to AudioManager and to the video surface (via the main thread).
void outputFrames(); void outputFrames();
// Calculate the black rectangle that is shown behind videos with non-standard aspect ratios. // Calculate the black frame that is rendered behind all videos and which may also be
void calculateBlackRectangle(); // adding pillarboxes/letterboxes.
void calculateBlackFrame();
// Detect and initialize the hardware decoder. // Detect and initialize the hardware decoder.
static void detectHWDecoder(); static void detectHWDecoder();
bool decoderInitHW(); bool decoderInitHW();
Renderer* mRenderer;
// clang-format off // clang-format off
static inline enum AVHWDeviceType sDeviceType {AV_HWDEVICE_TYPE_NONE}; static inline enum AVHWDeviceType sDeviceType {AV_HWDEVICE_TYPE_NONE};
static inline enum AVPixelFormat sPixelFormat {AV_PIX_FMT_NONE}; static inline enum AVPixelFormat sPixelFormat {AV_PIX_FMT_NONE};
@ -99,8 +98,7 @@ private:
static inline std::vector<std::string> sHWDecodedVideos; static inline std::vector<std::string> sHWDecodedVideos;
std::shared_ptr<TextureResource> mTexture; std::shared_ptr<TextureResource> mTexture;
std::vector<float> mVideoRectangleCoords; glm::vec2 mBlackFrameOffset;
glm::vec2 mRectangleOffset;
std::unique_ptr<std::thread> mFrameProcessingThread; std::unique_ptr<std::thread> mFrameProcessingThread;
std::mutex mPictureMutex; std::mutex mPictureMutex;

View file

@ -166,6 +166,7 @@ private:
bool mItemAxisHorizontal; bool mItemAxisHorizontal;
float mItemAxisRotation; float mItemAxisRotation;
bool mLinearInterpolation; bool mLinearInterpolation;
float mImageCornerRadius;
unsigned int mImageColorShift; unsigned int mImageColorShift;
unsigned int mImageColorShiftEnd; unsigned int mImageColorShiftEnd;
bool mImageColorGradientHorizontal; bool mImageColorGradientHorizontal;
@ -241,6 +242,7 @@ CarouselComponent<T>::CarouselComponent()
, mItemAxisHorizontal {false} , mItemAxisHorizontal {false}
, mItemAxisRotation {0.0f} , mItemAxisRotation {0.0f}
, mLinearInterpolation {false} , mLinearInterpolation {false}
, mImageCornerRadius {0.0f}
, mImageColorShift {0xFFFFFFFF} , mImageColorShift {0xFFFFFFFF}
, mImageColorShiftEnd {0xFFFFFFFF} , mImageColorShiftEnd {0xFFFFFFFF}
, mImageColorGradientHorizontal {true} , mImageColorGradientHorizontal {true}
@ -309,6 +311,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
item->setResize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f))); item->setResize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
else if (mImagefit == ImageFit::COVER) else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f))); item->setCroppedSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath); item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0) if (mImageBrightness != 0.0)
@ -339,6 +342,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
else if (mImagefit == ImageFit::COVER) else if (mImagefit == ImageFit::COVER)
mDefaultImage->setCroppedSize( mDefaultImage->setCroppedSize(
glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f))); glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
mDefaultImage->setCornerRadius(mImageCornerRadius);
mDefaultImage->setImage(entry.data.defaultImagePath); mDefaultImage->setImage(entry.data.defaultImagePath);
mDefaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL); mDefaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0) if (mImageBrightness != 0.0)
@ -413,6 +417,7 @@ void CarouselComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<Theme
item->setResize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f))); item->setResize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
else if (mImagefit == ImageFit::COVER) else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f))); item->setCroppedSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath); item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0) if (mImageBrightness != 0.0)
@ -1429,6 +1434,10 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
} }
if (elem->has("imageCornerRadius"))
mImageCornerRadius = glm::clamp(elem->get<float>("imageCornerRadius"), 0.0f, 0.5f) *
(mItemScale >= 1.0f ? mItemScale : 1.0f) * mRenderer->getScreenWidth();
mImageSelectedColor = mImageColorShift; mImageSelectedColor = mImageColorShift;
mImageSelectedColorEnd = mImageColorShiftEnd; mImageSelectedColorEnd = mImageColorShiftEnd;

View file

@ -162,6 +162,7 @@ private:
float mUnfocusedItemDimming; float mUnfocusedItemDimming;
ImageFit mImagefit; ImageFit mImagefit;
float mImageRelativeScale; float mImageRelativeScale;
float mImageCornerRadius;
unsigned int mImageColor; unsigned int mImageColor;
unsigned int mImageColorEnd; unsigned int mImageColorEnd;
bool mImageColorGradientHorizontal; bool mImageColorGradientHorizontal;
@ -238,6 +239,7 @@ GridComponent<T>::GridComponent()
, mUnfocusedItemDimming {1.0f} , mUnfocusedItemDimming {1.0f}
, mImagefit {ImageFit::CONTAIN} , mImagefit {ImageFit::CONTAIN}
, mImageRelativeScale {1.0f} , mImageRelativeScale {1.0f}
, mImageCornerRadius {0.0f}
, mImageColor {0xFFFFFFFF} , mImageColor {0xFFFFFFFF}
, mImageColorEnd {0xFFFFFFFF} , mImageColorEnd {0xFFFFFFFF}
, mImageColorGradientHorizontal {true} , mImageColorGradientHorizontal {true}
@ -306,6 +308,7 @@ void GridComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeData>&
item->setResize(mItemSize * mImageRelativeScale); item->setResize(mItemSize * mImageRelativeScale);
else if (mImagefit == ImageFit::COVER) else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(mItemSize * mImageRelativeScale); item->setCroppedSize(mItemSize * mImageRelativeScale);
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath); item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0) if (mImageBrightness != 0.0)
@ -338,6 +341,7 @@ void GridComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeData>&
mDefaultImage->setResize(mItemSize * mImageRelativeScale); mDefaultImage->setResize(mItemSize * mImageRelativeScale);
else if (mImagefit == ImageFit::COVER) else if (mImagefit == ImageFit::COVER)
mDefaultImage->setCroppedSize(mItemSize * mImageRelativeScale); mDefaultImage->setCroppedSize(mItemSize * mImageRelativeScale);
mDefaultImage->setCornerRadius(mImageCornerRadius);
mDefaultImage->setImage(entry.data.defaultImagePath); mDefaultImage->setImage(entry.data.defaultImagePath);
mDefaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL); mDefaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0) if (mImageBrightness != 0.0)
@ -395,6 +399,7 @@ void GridComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<ThemeData
item->setResize(mItemSize * mImageRelativeScale); item->setResize(mItemSize * mImageRelativeScale);
else if (mImagefit == ImageFit::COVER) else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(mItemSize * mImageRelativeScale); item->setCroppedSize(mItemSize * mImageRelativeScale);
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath); item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL); item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0) if (mImageBrightness != 0.0)
@ -1121,6 +1126,12 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
mBackgroundImage->setColorGradientHorizontal(false); mBackgroundImage->setColorGradientHorizontal(false);
} }
} }
float backgroundCornerRadius {0.0f};
if (elem->has("backgroundCornerRadius"))
backgroundCornerRadius =
glm::clamp(elem->get<float>("backgroundCornerRadius"), 0.0f, 0.5f) *
(mItemScale >= 1.0f ? mItemScale : 1.0f) * mRenderer->getScreenWidth();
mBackgroundImage->setCornerRadius(backgroundCornerRadius);
mBackgroundImage->setImage(elem->get<std::string>("backgroundImage")); mBackgroundImage->setImage(elem->get<std::string>("backgroundImage"));
mBackgroundImagePath = path; mBackgroundImagePath = path;
} }
@ -1146,6 +1157,12 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
mSelectorImage->setColorGradientHorizontal(false); mSelectorImage->setColorGradientHorizontal(false);
} }
} }
float selectorCornerRadius {0.0f};
if (elem->has("selectorCornerRadius"))
selectorCornerRadius =
glm::clamp(elem->get<float>("selectorCornerRadius"), 0.0f, 0.5f) *
(mItemScale >= 1.0f ? mItemScale : 1.0f) * mRenderer->getScreenWidth();
mSelectorImage->setCornerRadius(selectorCornerRadius);
mSelectorImage->setImage(elem->get<std::string>("selectorImage")); mSelectorImage->setImage(elem->get<std::string>("selectorImage"));
mSelectorImagePath = path; mSelectorImagePath = path;
} }
@ -1236,6 +1253,10 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
mItemSpacing.y = ((mItemSize.y * mItemScale) - mItemSize.y) / 2.0f; mItemSpacing.y = ((mItemSize.y * mItemScale) - mItemSize.y) / 2.0f;
} }
if (elem->has("imageCornerRadius"))
mImageCornerRadius = glm::clamp(elem->get<float>("imageCornerRadius"), 0.0f, 0.5f) *
(mItemScale >= 1.0f ? mItemScale : 1.0f) * mRenderer->getScreenWidth();
if (elem->has("imageColor")) { if (elem->has("imageColor")) {
mImageColor = elem->get<unsigned int>("imageColor"); mImageColor = elem->get<unsigned int>("imageColor");
mImageColorEnd = mImageColor; mImageColorEnd = mImageColor;

View file

@ -49,11 +49,13 @@ public:
}; };
enum ShaderFlags { enum ShaderFlags {
PREMULTIPLIED = 0x00000001, PREMULTIPLIED = 0x00000001,
FONT_TEXTURE = 0x00000002, FONT_TEXTURE = 0x00000002,
POST_PROCESSING = 0x00000004, POST_PROCESSING = 0x00000004,
CLIPPING = 0x00000008, CLIPPING = 0x00000008,
ROTATED = 0x00000010 // Screen rotated 90 or 270 degrees. ROTATED = 0x00000010, // Screen rotated 90 or 270 degrees.
ROUNDED_CORNERS = 0x00000020,
ROUNDED_CORNERS_NO_AA = 0x00000040
}; };
// clang-format on // clang-format on
@ -66,6 +68,7 @@ public:
float opacity; float opacity;
float saturation; float saturation;
float dimming; float dimming;
float cornerRadius;
float reflectionsFalloff; float reflectionsFalloff;
float blurStrength; float blurStrength;
unsigned int shaders; unsigned int shaders;
@ -76,6 +79,7 @@ public:
, opacity {1.0f} , opacity {1.0f}
, saturation {1.0f} , saturation {1.0f}
, dimming {1.0f} , dimming {1.0f}
, cornerRadius {0.0f}
, reflectionsFalloff {0.0f} , reflectionsFalloff {0.0f}
, blurStrength {0.0f} , blurStrength {0.0f}
, shaders {0} , shaders {0}
@ -95,6 +99,7 @@ public:
, opacity {1.0f} , opacity {1.0f}
, saturation {1.0f} , saturation {1.0f}
, dimming {1.0f} , dimming {1.0f}
, cornerRadius {0.0f}
, reflectionsFalloff {0.0f} , reflectionsFalloff {0.0f}
, blurStrength {0.0f} , blurStrength {0.0f}
, shaders {0} , shaders {0}

View file

@ -505,6 +505,11 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
mCoreShader->setOpacity(vertices->opacity); mCoreShader->setOpacity(vertices->opacity);
mCoreShader->setSaturation(vertices->saturation); mCoreShader->setSaturation(vertices->saturation);
mCoreShader->setDimming(vertices->dimming); mCoreShader->setDimming(vertices->dimming);
if (vertices->shaderFlags & ShaderFlags::ROUNDED_CORNERS ||
vertices->shaderFlags & ShaderFlags::ROUNDED_CORNERS_NO_AA) {
mCoreShader->setTextureSize({width, height});
mCoreShader->setCornerRadius(vertices->cornerRadius);
}
mCoreShader->setReflectionsFalloff(vertices->reflectionsFalloff); mCoreShader->setReflectionsFalloff(vertices->reflectionsFalloff);
mCoreShader->setFlags(vertices->shaderFlags); mCoreShader->setFlags(vertices->shaderFlags);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));

View file

@ -23,6 +23,7 @@ ShaderOpenGL::ShaderOpenGL()
, mShaderOpacity {0} , mShaderOpacity {0}
, mShaderSaturation {0} , mShaderSaturation {0}
, mShaderDimming {0} , mShaderDimming {0}
, mCornerRadius {0}
, mShaderReflectionsFalloff {0} , mShaderReflectionsFalloff {0}
, mBlurStrength {0} , mBlurStrength {0}
, mShaderFlags {0} , mShaderFlags {0}
@ -128,6 +129,7 @@ void ShaderOpenGL::getVariableLocations(GLuint programID)
mShaderOpacity = glGetUniformLocation(mProgramID, "opacity"); mShaderOpacity = glGetUniformLocation(mProgramID, "opacity");
mShaderSaturation = glGetUniformLocation(mProgramID, "saturation"); mShaderSaturation = glGetUniformLocation(mProgramID, "saturation");
mShaderDimming = glGetUniformLocation(mProgramID, "dimming"); mShaderDimming = glGetUniformLocation(mProgramID, "dimming");
mCornerRadius = glGetUniformLocation(mProgramID, "cornerRadius");
mShaderReflectionsFalloff = glGetUniformLocation(mProgramID, "reflectionsFalloff"); mShaderReflectionsFalloff = glGetUniformLocation(mProgramID, "reflectionsFalloff");
mBlurStrength = glGetUniformLocation(mProgramID, "blurStrength"); mBlurStrength = glGetUniformLocation(mProgramID, "blurStrength");
mShaderFlags = glGetUniformLocation(mProgramID, "shaderFlags"); mShaderFlags = glGetUniformLocation(mProgramID, "shaderFlags");
@ -194,6 +196,12 @@ void ShaderOpenGL::setDimming(GLfloat dimming)
GL_CHECK_ERROR(glUniform1f(mShaderDimming, dimming)); GL_CHECK_ERROR(glUniform1f(mShaderDimming, dimming));
} }
void ShaderOpenGL::setCornerRadius(GLfloat cornerRadius)
{
if (mCornerRadius != -1)
GL_CHECK_ERROR(glUniform1f(mCornerRadius, cornerRadius));
}
void ShaderOpenGL::setReflectionsFalloff(GLfloat falloff) void ShaderOpenGL::setReflectionsFalloff(GLfloat falloff)
{ {
if (mShaderReflectionsFalloff != -1) if (mShaderReflectionsFalloff != -1)

View file

@ -73,6 +73,7 @@ public:
void setOpacity(GLfloat opacity); void setOpacity(GLfloat opacity);
void setSaturation(GLfloat saturation); void setSaturation(GLfloat saturation);
void setDimming(GLfloat dimming); void setDimming(GLfloat dimming);
void setCornerRadius(GLfloat cornerRadius);
void setReflectionsFalloff(GLfloat falloff); void setReflectionsFalloff(GLfloat falloff);
void setBlurStrength(GLfloat blurStrength); void setBlurStrength(GLfloat blurStrength);
void setFlags(GLuint flags); void setFlags(GLuint flags);
@ -101,6 +102,7 @@ private:
GLint mShaderOpacity; GLint mShaderOpacity;
GLint mShaderSaturation; GLint mShaderSaturation;
GLint mShaderDimming; GLint mShaderDimming;
GLint mCornerRadius;
GLint mShaderReflectionsFalloff; GLint mShaderReflectionsFalloff;
GLint mBlurStrength; GLint mBlurStrength;
GLint mShaderFlags; GLint mShaderFlags;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -39,6 +39,8 @@ out vec4 FragColor;
// 0x00000004 - Post processing // 0x00000004 - Post processing
// 0x00000008 - Clipping // 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees // 0x00000010 - Screen rotated 90 or 270 degrees
// 0x00000020 - Rounded corners
// 0x00000040 - Rounded corners with no anti-aliasing
void main() void main()
{ {

View file

@ -39,6 +39,8 @@ out vec4 FragColor;
// 0x00000004 - Post processing // 0x00000004 - Post processing
// 0x00000008 - Clipping // 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees // 0x00000010 - Screen rotated 90 or 270 degrees
// 0x00000020 - Rounded corners
// 0x00000040 - Rounded corners with no anti-aliasing
void main() void main()
{ {

View file

@ -3,8 +3,7 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// core.glsl // core.glsl
// //
// Core shader functionality: // Core shader functionality.
// Clipping, brightness, saturation, opacity, dimming and reflections falloff.
// //
// Vertex section of code: // Vertex section of code:
@ -38,11 +37,13 @@ in vec2 position;
in vec2 texCoord; in vec2 texCoord;
in vec4 color; in vec4 color;
uniform vec2 textureSize;
uniform vec4 clipRegion; uniform vec4 clipRegion;
uniform float brightness; uniform float brightness;
uniform float saturation; uniform float saturation;
uniform float opacity; uniform float opacity;
uniform float dimming; uniform float dimming;
uniform float cornerRadius;
uniform float reflectionsFalloff; uniform float reflectionsFalloff;
uniform uint shaderFlags; uniform uint shaderFlags;
@ -55,6 +56,8 @@ out vec4 FragColor;
// 0x00000004 - Post processing // 0x00000004 - Post processing
// 0x00000008 - Clipping // 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees // 0x00000010 - Screen rotated 90 or 270 degrees
// 0x00000020 - Rounded corners
// 0x00000040 - Rounded corners with no anti-aliasing
void main() void main()
{ {
@ -72,6 +75,34 @@ void main()
vec4 sampledColor = texture(textureSampler, texCoord); vec4 sampledColor = texture(textureSampler, texCoord);
// Rounded corners.
if (0x0u != (shaderFlags & 0x20u) || 0x0u != (shaderFlags & 0x40u)) {
float radius = cornerRadius;
// Don't go beyond half the width and height.
if (radius > textureSize.x / 2.0)
radius = textureSize.x / 2.0;
if (radius > textureSize.y / 2.0)
radius = textureSize.y / 2.0;
vec2 q = abs(position - textureSize / 2.0) -
(vec2(textureSize.x / 2.0, textureSize.y / 2.0) - radius);
float pixelDistance = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
if (pixelDistance > 0.0) {
discard;
}
else {
float pixelValue;
if (0x0u != (shaderFlags & 0x20u))
pixelValue = 1.0 - smoothstep(-0.75, 0.5, pixelDistance);
else
pixelValue = 1.0;
sampledColor.a *= pixelValue;
sampledColor.rgb *= pixelValue;
}
}
// Brightness. // Brightness.
if (brightness != 0.0) { if (brightness != 0.0) {
sampledColor.rgb /= sampledColor.a; sampledColor.rgb /= sampledColor.a;

View file

@ -100,6 +100,8 @@ uniform float OutputGamma;
// 0x00000004 - Post processing // 0x00000004 - Post processing
// 0x00000008 - Clipping // 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees // 0x00000010 - Screen rotated 90 or 270 degrees
// 0x00000020 - Rounded corners
// 0x00000040 - Rounded corners with no anti-aliasing
void main() void main()
{ {