mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-29 19:55:37 +00:00
Changed the renderer pixel format from RGBA to BGRA.
Also implemented premultiplied alpha for all images, animations and videos and improved the carousel reflection falloff logic.
This commit is contained in:
parent
ab11f36ece
commit
3c82bb4dfb
|
@ -39,6 +39,8 @@ std::vector<unsigned char> ImageIO::loadFromMemoryRGBA32(const unsigned char* da
|
|||
fiBitmap = fiConverted;
|
||||
}
|
||||
}
|
||||
FreeImage_PreMultiplyWithAlpha(fiBitmap);
|
||||
|
||||
if (fiBitmap != nullptr) {
|
||||
width = FreeImage_GetWidth(fiBitmap);
|
||||
height = FreeImage_GetHeight(fiBitmap);
|
||||
|
@ -49,16 +51,7 @@ std::vector<unsigned char> ImageIO::loadFromMemoryRGBA32(const unsigned char* da
|
|||
const BYTE* scanLine {FreeImage_GetScanLine(fiBitmap, static_cast<int>(i))};
|
||||
memcpy(tempData + (i * width * 4), scanLine, width * 4);
|
||||
}
|
||||
// Convert from BGRA to RGBA.
|
||||
for (size_t i = 0; i < width * height; ++i) {
|
||||
RGBQUAD bgra {reinterpret_cast<RGBQUAD*>(tempData)[i]};
|
||||
RGBQUAD rgba;
|
||||
rgba.rgbBlue = bgra.rgbRed;
|
||||
rgba.rgbGreen = bgra.rgbGreen;
|
||||
rgba.rgbRed = bgra.rgbBlue;
|
||||
rgba.rgbReserved = bgra.rgbReserved;
|
||||
reinterpret_cast<RGBQUAD*>(tempData)[i] = rgba;
|
||||
}
|
||||
|
||||
rawData = std::vector<unsigned char> {tempData, tempData + width * height * 4};
|
||||
// Free bitmap data.
|
||||
FreeImage_Unload(fiBitmap);
|
||||
|
|
|
@ -138,7 +138,7 @@ bool Window::init()
|
|||
mBackgroundOverlay->setImage(":/graphics/frame.png");
|
||||
mBackgroundOverlay->setResize(mRenderer->getScreenWidth(), mRenderer->getScreenHeight());
|
||||
|
||||
mPostprocessedBackground = TextureResource::get("");
|
||||
mPostprocessedBackground = TextureResource::get("", false, false, false, false, false);
|
||||
|
||||
mListScrollFont = Font::get(FONT_SIZE_LARGE);
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ public:
|
|||
void setAllowTextScrolling(bool value) { mAllowTextScrolling = value; }
|
||||
bool getAllowTextScrolling() { return mAllowTextScrolling; }
|
||||
|
||||
// For Lottie animations.
|
||||
// For GIF and Lottie animations.
|
||||
void setAllowFileAnimation(bool value) { mAllowFileAnimation = value; }
|
||||
bool getAllowFileAnimation() { return mAllowFileAnimation; }
|
||||
|
||||
|
|
|
@ -190,10 +190,11 @@ void GIFAnimComponent::setAnimation(const std::string& path)
|
|||
mSize.x = static_cast<float>(width);
|
||||
mSize.y = static_cast<float>(height);
|
||||
|
||||
FreeImage_PreMultiplyWithAlpha(mFrame);
|
||||
mPictureRGBA.resize(mFileWidth * mFileHeight * 4);
|
||||
|
||||
FreeImage_ConvertToRawBits(reinterpret_cast<BYTE*>(&mPictureRGBA.at(0)), mFrame, filePitch, 32,
|
||||
FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1);
|
||||
FI_RGBA_BLUE, FI_RGBA_GREEN, FI_RGBA_RED, 1);
|
||||
|
||||
mTexture->initFromPixels(&mPictureRGBA.at(0), mFileWidth, mFileHeight);
|
||||
|
||||
|
@ -453,6 +454,7 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
|
|||
FIF_GIF, &mAnimIO, static_cast<fi_handle>(mAnimFile), GIF_PLAYBACK);
|
||||
|
||||
mFrame = FreeImage_LockPage(mAnimation, mFrameNum);
|
||||
FreeImage_PreMultiplyWithAlpha(mFrame);
|
||||
mPictureRGBA.clear();
|
||||
mPictureRGBA.resize(mFrameSize);
|
||||
|
||||
|
@ -496,7 +498,7 @@ void GIFAnimComponent::render(const glm::mat4& parentTrans)
|
|||
vertices->saturation = mSaturation * mThemeSaturation;
|
||||
vertices->opacity = mOpacity * mThemeOpacity;
|
||||
vertices->dimming = mDimming;
|
||||
vertices->shaderFlags = Renderer::ShaderFlags::BGRA_TO_RGBA;
|
||||
vertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED;
|
||||
|
||||
// Render it.
|
||||
mRenderer->drawTriangleStrips(&vertices[0], 4);
|
||||
|
|
|
@ -350,7 +350,7 @@ void ImageComponent::render(const glm::mat4& parentTrans)
|
|||
mClipRegion.w - mClipRegion.y, 0xFF000033, 0xFF000033);
|
||||
}
|
||||
// An image with zero size would normally indicate a corrupt image file.
|
||||
if (mTexture->getSize() != glm::ivec2 {}) {
|
||||
if (mTexture->getSize() != glm::ivec2 {0, 0}) {
|
||||
// Actually draw the image.
|
||||
// The bind() function returns false if the texture is not currently loaded. A blank
|
||||
// texture is bound in this case but we want to handle a fade so it doesn't just
|
||||
|
@ -367,6 +367,7 @@ void ImageComponent::render(const glm::mat4& parentTrans)
|
|||
mVertices->dimming = mDimming;
|
||||
mVertices->reflectionsFalloff = mReflectionsFalloff;
|
||||
|
||||
mVertices->shaderFlags = mVertices->shaderFlags | Renderer::ShaderFlags::PREMULTIPLIED;
|
||||
mRenderer->drawTriangleStrips(&mVertices[0], 4);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -487,7 +487,7 @@ void LottieAnimComponent::render(const glm::mat4& parentTrans)
|
|||
vertices->saturation = mSaturation * mThemeSaturation;
|
||||
vertices->opacity = mOpacity * mThemeOpacity;
|
||||
vertices->dimming = mDimming;
|
||||
vertices->shaderFlags = Renderer::ShaderFlags::BGRA_TO_RGBA;
|
||||
vertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED;
|
||||
|
||||
// Render it.
|
||||
mRenderer->drawTriangleStrips(&vertices[0], 4);
|
||||
|
|
|
@ -134,6 +134,7 @@ void NinePatchComponent::render(const glm::mat4& parentTrans)
|
|||
if (mTexture && mVertices != nullptr) {
|
||||
mRenderer->setMatrix(trans);
|
||||
mVertices->opacity = mOpacity;
|
||||
mVertices->shaderFlags = Renderer::ShaderFlags::PREMULTIPLIED;
|
||||
mTexture->bind();
|
||||
mRenderer->drawTriangleStrips(&mVertices[0], 6 * 9);
|
||||
}
|
||||
|
|
|
@ -224,7 +224,8 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
|
|||
}
|
||||
}
|
||||
|
||||
mRenderer->drawTriangleStrips(&vertices[0], 4);
|
||||
mRenderer->drawTriangleStrips(&vertices[0], 4, Renderer::BlendFactor::SRC_ALPHA,
|
||||
Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
else {
|
||||
if (mVisible)
|
||||
|
@ -435,7 +436,7 @@ bool VideoFFmpegComponent::setupVideoFilters()
|
|||
}
|
||||
|
||||
filterDescription.append("format=pix_fmts=")
|
||||
.append(std::string(av_get_pix_fmt_name(AV_PIX_FMT_RGBA)));
|
||||
.append(std::string(av_get_pix_fmt_name(AV_PIX_FMT_BGRA)));
|
||||
|
||||
returnValue = avfilter_graph_parse_ptr(mVFilterGraph, filterDescription.c_str(),
|
||||
&mVFilterInputs, &mVFilterOutputs, nullptr);
|
||||
|
|
|
@ -898,19 +898,14 @@ template <typename T> void CarouselComponent<T>::render(const glm::mat4& parentT
|
|||
mEntries.at(renderItem.index).data.defaultItemPath != "")) {
|
||||
glm::mat4 reflectionTrans {glm::translate(
|
||||
renderItem.trans, glm::vec3 {0.0f, comp->getSize().y * renderItem.scale, 0.0f})};
|
||||
const unsigned int colorShift {comp->getColorShift()};
|
||||
comp->setColorGradientHorizontal(false);
|
||||
comp->setColorShift(0xFFFFFF00 | static_cast<int>(mReflectionsOpacity * 255.0f));
|
||||
float falloff {glm::clamp(mReflectionsFalloff, 0.0f, 1.0f)};
|
||||
falloff = mReflectionsOpacity * (1.0f - falloff);
|
||||
comp->setColorShiftEnd(0xFFFFFF00 | static_cast<int>(falloff * 255.0f));
|
||||
// "Extra" falloff as a value of 1.0 already fades the image completely.
|
||||
if (mReflectionsFalloff > 1.0f)
|
||||
comp->setReflectionsFalloff(mReflectionsFalloff - 1.0f);
|
||||
comp->setOpacity(comp->getOpacity() * mReflectionsOpacity);
|
||||
if (mReflectionsFalloff > 0.0f)
|
||||
comp->setReflectionsFalloff(comp->getSize().y / mReflectionsFalloff);
|
||||
comp->setFlipY(true);
|
||||
comp->render(reflectionTrans);
|
||||
comp->setFlipY(false);
|
||||
comp->setColorShift(colorShift);
|
||||
comp->setReflectionsFalloff(0.0f);
|
||||
}
|
||||
|
||||
|
@ -1146,7 +1141,7 @@ void CarouselComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
|||
mReflectionsOpacity = glm::clamp(elem->get<float>("reflectionsOpacity"), 0.1f, 1.0f);
|
||||
|
||||
if (elem->has("reflectionsFalloff"))
|
||||
mReflectionsFalloff = glm::clamp(elem->get<float>("reflectionsFalloff"), 0.0f, 5.0f);
|
||||
mReflectionsFalloff = glm::clamp(elem->get<float>("reflectionsFalloff"), 0.0f, 10.0f);
|
||||
|
||||
if (elem->has("unfocusedItemOpacity"))
|
||||
mUnfocusedItemOpacity =
|
||||
|
|
|
@ -37,22 +37,15 @@ void Renderer::setIcon()
|
|||
if (!rawData.empty()) {
|
||||
ImageIO::flipPixelsVert(rawData.data(), width, height);
|
||||
|
||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||
unsigned int rmask {0xFF000000};
|
||||
unsigned int gmask {0x00FF0000};
|
||||
unsigned int bmask {0x0000FF00};
|
||||
unsigned int amask {0x000000FF};
|
||||
#else
|
||||
unsigned int rmask {0x000000FF};
|
||||
unsigned int gmask {0x0000FF00};
|
||||
unsigned int bmask {0x00FF0000};
|
||||
unsigned int amask {0xFF000000};
|
||||
#endif
|
||||
constexpr unsigned int bmask {0x00FF0000};
|
||||
constexpr unsigned int gmask {0x0000FF00};
|
||||
constexpr unsigned int rmask {0x000000FF};
|
||||
constexpr unsigned int amask {0xFF000000};
|
||||
|
||||
// Try creating SDL surface from logo data.
|
||||
SDL_Surface* logoSurface {SDL_CreateRGBSurfaceFrom(
|
||||
static_cast<void*>(rawData.data()), static_cast<int>(width), static_cast<int>(height),
|
||||
32, static_cast<int>(width * 4), rmask, gmask, bmask, amask)};
|
||||
32, static_cast<int>(width * 4), bmask, gmask, rmask, amask)};
|
||||
|
||||
if (logoSurface != nullptr) {
|
||||
SDL_SetWindowIcon(mSDLWindow, logoSurface);
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
};
|
||||
|
||||
enum ShaderFlags {
|
||||
BGRA_TO_RGBA = 0x00000001,
|
||||
PREMULTIPLIED = 0x00000001,
|
||||
FONT_TEXTURE = 0x00000002,
|
||||
POST_PROCESSING = 0x00000004,
|
||||
CLIPPING = 0x00000008
|
||||
|
@ -201,7 +201,7 @@ public:
|
|||
virtual void drawTriangleStrips(
|
||||
const Vertex* vertices,
|
||||
const unsigned int numVertices,
|
||||
const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA,
|
||||
const BlendFactor srcBlendFactor = BlendFactor::ONE,
|
||||
const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA) = 0;
|
||||
virtual void setMatrix(const glm::mat4& matrix) = 0;
|
||||
virtual void setScissor(const Rect& scissor) = 0;
|
||||
|
|
|
@ -248,13 +248,13 @@ bool RendererOpenGL::createContext()
|
|||
GL_CHECK_ERROR(glBindVertexArray(mVertexBuffer2));
|
||||
|
||||
uint8_t data[4] {255, 255, 255, 255};
|
||||
mWhiteTexture = createTexture(TextureType::RGBA, false, false, false, true, 1, 1, data);
|
||||
mWhiteTexture = createTexture(TextureType::BGRA, false, false, false, true, 1, 1, data);
|
||||
|
||||
mPostProcTexture1 = createTexture(TextureType::RGBA, false, false, false, false,
|
||||
mPostProcTexture1 = createTexture(TextureType::BGRA, false, false, false, false,
|
||||
static_cast<unsigned int>(getScreenWidth()),
|
||||
static_cast<unsigned int>(getScreenHeight()), nullptr);
|
||||
|
||||
mPostProcTexture2 = createTexture(TextureType::RGBA, false, false, false, false,
|
||||
mPostProcTexture2 = createTexture(TextureType::BGRA, false, false, false, false,
|
||||
static_cast<unsigned int>(getScreenWidth()),
|
||||
static_cast<unsigned int>(getScreenHeight()), nullptr);
|
||||
|
||||
|
@ -376,8 +376,13 @@ unsigned int RendererOpenGL::createTexture(const TextureType type,
|
|||
linearMagnify ? static_cast<GLfloat>(GL_LINEAR) :
|
||||
static_cast<GLfloat>(GL_NEAREST)));
|
||||
|
||||
#if defined(USE_OPENGLES)
|
||||
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType,
|
||||
GL_UNSIGNED_BYTE, data));
|
||||
#else
|
||||
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, textureType,
|
||||
GL_UNSIGNED_BYTE, data));
|
||||
#endif
|
||||
|
||||
if (mipmapping)
|
||||
GL_CHECK_ERROR(glGenerateMipmap(GL_TEXTURE_2D));
|
||||
|
@ -626,7 +631,12 @@ void RendererOpenGL::shaderPostprocessing(unsigned int shaders,
|
|||
else
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mShaderFBO2));
|
||||
|
||||
GL_CHECK_ERROR(glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
|
||||
#if defined(USE_OPENGLES)
|
||||
GL_CHECK_ERROR(
|
||||
glReadPixels(0, 0, width, height, GL_BGRA_EXT, GL_UNSIGNED_BYTE, textureRGBA));
|
||||
#else
|
||||
GL_CHECK_ERROR(glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, textureRGBA));
|
||||
#endif
|
||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
void drawTriangleStrips(
|
||||
const Vertex* vertices,
|
||||
const unsigned int numVertices,
|
||||
const BlendFactor srcBlendFactor = BlendFactor::SRC_ALPHA,
|
||||
const BlendFactor srcBlendFactor = BlendFactor::ONE,
|
||||
const BlendFactor dstBlendFactor = BlendFactor::ONE_MINUS_SRC_ALPHA) override;
|
||||
|
||||
void shaderPostprocessing(
|
||||
|
|
|
@ -248,8 +248,9 @@ void Font::renderTextCache(TextCache* cache)
|
|||
it->verts[0].shaderFlags = Renderer::ShaderFlags::FONT_TEXTURE;
|
||||
|
||||
mRenderer->bindTexture(*it->textureIdPtr);
|
||||
mRenderer->drawTriangleStrips(&it->verts[0],
|
||||
static_cast<const unsigned int>(it->verts.size()));
|
||||
mRenderer->drawTriangleStrips(
|
||||
&it->verts[0], static_cast<const unsigned int>(it->verts.size()),
|
||||
Renderer::BlendFactor::SRC_ALPHA, Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ bool TextureData::initSVGFromMemory(const std::string& fileData)
|
|||
auto svgImage = lunasvg::Document::loadFromData(fileData);
|
||||
|
||||
if (svgImage == nullptr) {
|
||||
LOG(LogDebug) << "TextureData::initSVGFromMemory(): Couldn't parse SVG image \"" << mPath
|
||||
LOG(LogError) << "TextureData::initSVGFromMemory(): Couldn't parse SVG image \"" << mPath
|
||||
<< "\"";
|
||||
mInvalidSVGFile = true;
|
||||
return false;
|
||||
|
@ -107,7 +107,6 @@ bool TextureData::initSVGFromMemory(const std::string& fileData)
|
|||
|
||||
if (rasterize) {
|
||||
auto bitmap = svgImage->renderToBitmap(mWidth, mHeight);
|
||||
bitmap.convertToRGBA();
|
||||
mDataRGBA.insert(mDataRGBA.begin(), std::move(bitmap.data()),
|
||||
std::move(bitmap.data() + mWidth * mHeight * 4));
|
||||
|
||||
|
@ -221,7 +220,7 @@ bool TextureData::uploadAndBind()
|
|||
|
||||
// Upload texture.
|
||||
mTextureID =
|
||||
mRenderer->createTexture(Renderer::TextureType::RGBA, true, mLinearMagnify, mMipmapping,
|
||||
mRenderer->createTexture(Renderer::TextureType::BGRA, true, mLinearMagnify, mMipmapping,
|
||||
mTile, static_cast<const unsigned int>(mWidth),
|
||||
static_cast<const unsigned int>(mHeight), mDataRGBA.data());
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// core.glsl
|
||||
//
|
||||
// Core shader functionality:
|
||||
// clipping, opacity, saturation, dimming and BGRA to RGBA conversion.
|
||||
// Clipping, opacity, saturation, dimming and reflections falloff.
|
||||
//
|
||||
|
||||
// Vertex section of code:
|
||||
|
@ -24,7 +24,7 @@ void main(void)
|
|||
gl_Position = MVPMatrix * vec4(positionVertex.xy, 0.0, 1.0);
|
||||
position = positionVertex;
|
||||
texCoord = texCoordVertex;
|
||||
color.rgba = colorVertex.abgr;
|
||||
color.abgr = colorVertex.rgba;
|
||||
}
|
||||
|
||||
// Fragment section of code:
|
||||
|
@ -49,7 +49,7 @@ uniform sampler2D textureSampler;
|
|||
out vec4 FragColor;
|
||||
|
||||
// shaderFlags:
|
||||
// 0x00000001 - BGRA to RGBA conversion
|
||||
// 0x00000001 - Premultiplied alpha (BGRA)
|
||||
// 0x00000002 - Font texture
|
||||
// 0x00000004 - Post processing
|
||||
// 0x00000008 - Clipping
|
||||
|
@ -57,7 +57,7 @@ out vec4 FragColor;
|
|||
void main()
|
||||
{
|
||||
// Discard any pixels outside the clipping region.
|
||||
if (0u != (shaderFlags & 8u)) {
|
||||
if (0x0u != (shaderFlags & 0x8u)) {
|
||||
if (position.x < clipRegion.x)
|
||||
discard;
|
||||
else if (position.y < clipRegion.y)
|
||||
|
@ -71,19 +71,31 @@ void main()
|
|||
vec4 sampledColor = texture(textureSampler, texCoord);
|
||||
|
||||
// For fonts the alpha information is stored in the red channel.
|
||||
if (0u != (shaderFlags & 2u))
|
||||
if (0x0u != (shaderFlags & 0x2u))
|
||||
sampledColor = vec4(1.0, 1.0, 1.0, sampledColor.r);
|
||||
|
||||
sampledColor *= color;
|
||||
// Different color calculations depending on whether the texture contains premultiplied
|
||||
// alpha or straight alpha values.
|
||||
if (0x0u != (shaderFlags & 0x01u)) {
|
||||
sampledColor.rgb *= color.rgb;
|
||||
sampledColor *= color.a;
|
||||
}
|
||||
else {
|
||||
sampledColor *= color;
|
||||
}
|
||||
|
||||
// When post-processing we drop the alpha channel to avoid strange issues
|
||||
// with some graphics drivers.
|
||||
if (0u != (shaderFlags & 4u))
|
||||
// When post-processing we drop the alpha channel to avoid strange issues with some
|
||||
// graphics drivers.
|
||||
if (0x0u != (shaderFlags & 0x4u))
|
||||
sampledColor.a = 1.0;
|
||||
|
||||
// Opacity.
|
||||
if (opacity != 1.0)
|
||||
sampledColor.a = sampledColor.a * opacity;
|
||||
if (opacity != 1.0) {
|
||||
if (0x0u == (shaderFlags & 0x01u))
|
||||
sampledColor.a *= opacity;
|
||||
else
|
||||
sampledColor *= opacity;
|
||||
}
|
||||
|
||||
// Saturation.
|
||||
if (saturation != 1.0) {
|
||||
|
@ -98,13 +110,9 @@ void main()
|
|||
sampledColor *= dimColor;
|
||||
}
|
||||
|
||||
// BGRA to RGBA conversion.
|
||||
if (0u != (shaderFlags & 1u))
|
||||
sampledColor = sampledColor.bgra;
|
||||
|
||||
// Reflections falloff.
|
||||
if (reflectionsFalloff > 0.0)
|
||||
sampledColor.a = mix(sampledColor.a, sampledColor.a - reflectionsFalloff, texCoord.y);
|
||||
sampledColor.argb *= mix(0.0, 1.0, reflectionsFalloff - position.y) / reflectionsFalloff;
|
||||
|
||||
FragColor = sampledColor;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue