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

View file

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

View file

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

View file

@ -29,7 +29,9 @@ ImageComponent::ImageComponent(bool forceLoad, bool dynamic)
, mColorShiftEnd {0xFFFFFFFF}
, mColorGradientHorizontal {true}
, mFadeOpacity {0.0f}
, mCornerRadius {0.0f}
, mReflectionsFalloff {0.0f}
, mCornerAntiAliasing {true}
, mFading {false}
, mForceLoad {forceLoad}
, mDynamic {dynamic}
@ -332,7 +334,6 @@ void ImageComponent::setSaturation(float saturation)
void ImageComponent::setDimming(float dimming)
{
// Set dimming value.
mDimming = dimming;
}
@ -432,6 +433,18 @@ void ImageComponent::render(const glm::mat4& parentTrans)
mVertices->dimming = mDimming;
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;
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")) {
std::string imageTypes {elem->get<std::string>("imageType")};
for (auto& character : imageTypes) {

View file

@ -89,6 +89,8 @@ public:
void setSaturation(float saturation) override;
void setDimming(float dimming) override;
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 setFlipX(bool state) override; // Mirror on the X axis.
@ -159,7 +161,9 @@ private:
std::shared_ptr<TextureResource> mTexture;
float mFadeOpacity;
float mCornerRadius;
float mReflectionsFalloff;
bool mCornerAntiAliasing;
bool mFading;
bool mForceLoad;
bool mDynamic;

View file

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

View file

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

View file

@ -16,14 +16,16 @@
#include <SDL2/SDL_timer.h>
#define SCREENSAVER_FADE_IN_TIME 1100
#define SCREENSAVER_FADE_IN_TIME 900
#define MEDIA_VIEWER_FADE_IN_TIME 600
VideoComponent::VideoComponent()
: mVideoWidth {0}
: mRenderer {Renderer::getInstance()}
, mVideoWidth {0}
, mVideoHeight {0}
, mColorShift {0xFFFFFFFF}
, mColorShiftEnd {0xFFFFFFFF}
, mVideoCornerRadius {0.0f}
, mColorGradientHorizontal {true}
, mTargetSize {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() ?
getParent()->getSize() :
glm::vec2 {Renderer::getScreenWidth(), Renderer::getScreenHeight()}};
glm::vec2 {mRenderer->getScreenWidth(), mRenderer->getScreenHeight()}};
if (properties & ThemeFlags::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")) {
const std::string defaultVideo {elem->get<std::string>("default")};
if (ResourceManager::getInstance().fileExists(defaultVideo)) {
@ -332,6 +342,14 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("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")) {
const glm::vec2 pillarboxThreshold {elem->get<glm::vec2>("pillarboxThreshold")};
mPillarboxThreshold.x = glm::clamp(pillarboxThreshold.x, 0.2f, 1.0f);

View file

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

View file

@ -30,8 +30,7 @@
#endif
VideoFFmpegComponent::VideoFFmpegComponent()
: mRenderer {Renderer::getInstance()}
, mRectangleOffset {0.0f, 0.0f}
: mBlackFrameOffset {0.0f, 0.0f}
, mFrameProcessingThread {nullptr}
, mFormatContext {nullptr}
, mVideoStream {nullptr}
@ -176,31 +175,23 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
if (mIsPlaying && mFormatContext) {
Renderer::Vertex vertices[4];
mRenderer->setMatrix(trans);
unsigned int rectColor {0x000000FF};
if (!mGeneralFade && mThemeOpacity != 1.0f)
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);
if (!mScreensaverMode && !mMediaViewerMode) {
mBlackFrame.setOpacity(mOpacity * mThemeOpacity);
mBlackFrame.render(trans);
}
mRenderer->setMatrix(trans);
// This is needed to avoid a slight gap before the video starts playing.
if (!mDecodedFrame)
return;
// clang-format off
vertices[0] = {{0.0f + mRectangleOffset.x, 0.0f + mRectangleOffset.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[2] = {{mSize.x + mRectangleOffset.x, 0.0f + + mRectangleOffset.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[0] = {{0.0f + mBlackFrameOffset.x, 0.0f + mBlackFrameOffset.y }, {mTopLeftCrop.x, 1.0f - mBottomRightCrop.y}, 0xFFFFFFFF};
vertices[1] = {{0.0f + mBlackFrameOffset.x, mSize.y + mBlackFrameOffset.y }, {mTopLeftCrop.x, 1.0f - mTopLeftCrop.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 + mBlackFrameOffset.x, mSize.y + + mBlackFrameOffset.y}, {mBottomRightCrop.x * 1.0f, 1.0f - mTopLeftCrop.y }, 0xFFFFFFFF};
// clang-format on
vertices[0].color = mColorShift;
@ -213,12 +204,22 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
vertices[i].position = glm::round(vertices[i].position);
if (mFadeIn < 1.0f || mThemeOpacity < 1.0f)
vertices->opacity = mFadeIn * mThemeOpacity;
vertices->opacity = mOpacity * mThemeOpacity;
vertices->brightness = mBrightness;
vertices->saturation = mSaturation * mThemeSaturation;
vertices->saturation = 1.0f;
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};
if (!mOutputPicture.hasBeenRendered) {
@ -266,6 +267,9 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
if (mRenderScanlines)
vertices[0].shaders = Renderer::Shader::SCANLINES;
}
else {
vertices[0].opacity = mFadeIn;
}
mRenderer->drawTriangleStrips(&vertices[0], 4, Renderer::BlendFactor::SRC_ALPHA,
Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA);
@ -985,22 +989,24 @@ void VideoFFmpegComponent::outputFrames()
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
// would extend to the entire video area (if above the threshold as defined below) or
// otherwise it will exactly match the video size. The reason to add a black rectangle
// behind videos in this second instance is that the scanline rendering will make the
// video partially transparent so this may avoid some unforseen issues with some themes.
// 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 frame behind
// videos in this second instance is that the scanline rendering will make the video
// 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
// 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
// for the X axis and 0.90 for the Y axis, but this is theme-controllable via the
// pillarboxThreshold property.
if (mVideoAreaPos != glm::vec2 {0.0f, 0.0f} && mVideoAreaSize != glm::vec2 {0.0f, 0.0f}) {
mVideoRectangleCoords.clear();
mRectangleOffset = {0.0f, 0.0f};
mBlackFrameOffset = {0.0f, 0.0f};
if (mDrawPillarboxes) {
float rectHeight {0.0f};
@ -1037,26 +1043,23 @@ void VideoFFmpegComponent::calculateBlackRectangle()
// the video correctly.
if (mOrigin != glm::vec2 {0.5f, 0.5f}) {
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)
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 offsetY {rectHeight - mSize.y};
mVideoRectangleCoords.emplace_back(std::round((-offsetX / 2.0f) + mRectangleOffset.x));
mVideoRectangleCoords.emplace_back(std::round((-offsetY / 2.0f) + mRectangleOffset.y));
mVideoRectangleCoords.emplace_back(std::round(rectWidth));
mVideoRectangleCoords.emplace_back(std::round(rectHeight));
mBlackFrame.setPosition((-offsetX / 2.0f) + mBlackFrameOffset.x,
(-offsetY / 2.0f) + mBlackFrameOffset.y);
mBlackFrame.setResize(rectWidth, rectHeight);
}
// If the option to display pillarboxes is disabled, then make the rectangle equivalent
// to the size of the video.
else {
mVideoRectangleCoords.emplace_back(0.0f);
mVideoRectangleCoords.emplace_back(0.0f);
mVideoRectangleCoords.emplace_back(std::round(mSize.x));
mVideoRectangleCoords.emplace_back(std::round(mSize.y));
// If the option to display pillarboxes is disabled, then set the black frame to the
// same position and size as the video.
mBlackFrame.setPosition(0.0f, 0.0f);
mBlackFrame.setResize(mSize.x, mSize.y);
}
}
}
@ -1553,8 +1556,7 @@ void VideoFFmpegComponent::startVideoStream()
// the video screeensaver.
resize();
// Calculate pillarbox/letterbox sizes.
calculateBlackRectangle();
calculateBlackFrame();
mFadeIn = 0.0f;
}

View file

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

View file

@ -166,6 +166,7 @@ private:
bool mItemAxisHorizontal;
float mItemAxisRotation;
bool mLinearInterpolation;
float mImageCornerRadius;
unsigned int mImageColorShift;
unsigned int mImageColorShiftEnd;
bool mImageColorGradientHorizontal;
@ -241,6 +242,7 @@ CarouselComponent<T>::CarouselComponent()
, mItemAxisHorizontal {false}
, mItemAxisRotation {0.0f}
, mLinearInterpolation {false}
, mImageCornerRadius {0.0f}
, mImageColorShift {0xFFFFFFFF}
, mImageColorShiftEnd {0xFFFFFFFF}
, 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)));
else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0)
@ -339,6 +342,7 @@ void CarouselComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeDat
else if (mImagefit == ImageFit::COVER)
mDefaultImage->setCroppedSize(
glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
mDefaultImage->setCornerRadius(mImageCornerRadius);
mDefaultImage->setImage(entry.data.defaultImagePath);
mDefaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL);
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)));
else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(glm::round(mItemSize * (mItemScale >= 1.0f ? mItemScale : 1.0f)));
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
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;
mImageSelectedColorEnd = mImageColorShiftEnd;

View file

@ -162,6 +162,7 @@ private:
float mUnfocusedItemDimming;
ImageFit mImagefit;
float mImageRelativeScale;
float mImageCornerRadius;
unsigned int mImageColor;
unsigned int mImageColorEnd;
bool mImageColorGradientHorizontal;
@ -238,6 +239,7 @@ GridComponent<T>::GridComponent()
, mUnfocusedItemDimming {1.0f}
, mImagefit {ImageFit::CONTAIN}
, mImageRelativeScale {1.0f}
, mImageCornerRadius {0.0f}
, mImageColor {0xFFFFFFFF}
, mImageColorEnd {0xFFFFFFFF}
, mImageColorGradientHorizontal {true}
@ -306,6 +308,7 @@ void GridComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeData>&
item->setResize(mItemSize * mImageRelativeScale);
else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(mItemSize * mImageRelativeScale);
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0)
@ -338,6 +341,7 @@ void GridComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeData>&
mDefaultImage->setResize(mItemSize * mImageRelativeScale);
else if (mImagefit == ImageFit::COVER)
mDefaultImage->setCroppedSize(mItemSize * mImageRelativeScale);
mDefaultImage->setCornerRadius(mImageCornerRadius);
mDefaultImage->setImage(entry.data.defaultImagePath);
mDefaultImage->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0)
@ -395,6 +399,7 @@ void GridComponent<T>::updateEntry(Entry& entry, const std::shared_ptr<ThemeData
item->setResize(mItemSize * mImageRelativeScale);
else if (mImagefit == ImageFit::COVER)
item->setCroppedSize(mItemSize * mImageRelativeScale);
item->setCornerRadius(mImageCornerRadius);
item->setImage(entry.data.imagePath);
item->applyTheme(theme, "system", "", ThemeFlags::ALL);
if (mImageBrightness != 0.0)
@ -1121,6 +1126,12 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
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"));
mBackgroundImagePath = path;
}
@ -1146,6 +1157,12 @@ void GridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
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"));
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;
}
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")) {
mImageColor = elem->get<unsigned int>("imageColor");
mImageColorEnd = mImageColor;

View file

@ -53,7 +53,9 @@ public:
FONT_TEXTURE = 0x00000002,
POST_PROCESSING = 0x00000004,
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
@ -66,6 +68,7 @@ public:
float opacity;
float saturation;
float dimming;
float cornerRadius;
float reflectionsFalloff;
float blurStrength;
unsigned int shaders;
@ -76,6 +79,7 @@ public:
, opacity {1.0f}
, saturation {1.0f}
, dimming {1.0f}
, cornerRadius {0.0f}
, reflectionsFalloff {0.0f}
, blurStrength {0.0f}
, shaders {0}
@ -95,6 +99,7 @@ public:
, opacity {1.0f}
, saturation {1.0f}
, dimming {1.0f}
, cornerRadius {0.0f}
, reflectionsFalloff {0.0f}
, blurStrength {0.0f}
, shaders {0}

View file

@ -505,6 +505,11 @@ void RendererOpenGL::drawTriangleStrips(const Vertex* vertices,
mCoreShader->setOpacity(vertices->opacity);
mCoreShader->setSaturation(vertices->saturation);
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->setFlags(vertices->shaderFlags);
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices));

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

View file

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

View file

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

View file

@ -3,8 +3,7 @@
// EmulationStation Desktop Edition
// core.glsl
//
// Core shader functionality:
// Clipping, brightness, saturation, opacity, dimming and reflections falloff.
// Core shader functionality.
//
// Vertex section of code:
@ -38,11 +37,13 @@ in vec2 position;
in vec2 texCoord;
in vec4 color;
uniform vec2 textureSize;
uniform vec4 clipRegion;
uniform float brightness;
uniform float saturation;
uniform float opacity;
uniform float dimming;
uniform float cornerRadius;
uniform float reflectionsFalloff;
uniform uint shaderFlags;
@ -55,6 +56,8 @@ out vec4 FragColor;
// 0x00000004 - Post processing
// 0x00000008 - Clipping
// 0x00000010 - Screen rotated 90 or 270 degrees
// 0x00000020 - Rounded corners
// 0x00000040 - Rounded corners with no anti-aliasing
void main()
{
@ -72,6 +75,34 @@ void main()
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.
if (brightness != 0.0) {
sampledColor.rgb /= sampledColor.a;

View file

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