mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +00:00
Added rounded corner support to the image, video, animation, carousel and grid elements
This commit is contained in:
parent
ed22fc7aa5
commit
170d8e3791
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ private:
|
|||
int mPlayCount;
|
||||
bool mTargetIsMax;
|
||||
|
||||
float mCornerRadius;
|
||||
unsigned int mColorShift;
|
||||
unsigned int mColorShiftEnd;
|
||||
bool mColorGradientHorizontal;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ private:
|
|||
int mPlayCount;
|
||||
bool mTargetIsMax;
|
||||
|
||||
float mCornerRadius;
|
||||
unsigned int mColorShift;
|
||||
unsigned int mColorShiftEnd;
|
||||
bool mColorGradientHorizontal;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
BIN
resources/graphics/white.png
Normal file
BIN
resources/graphics/white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue