From 3c82bb4dfb53ba09a1faa92802baec3629ec9eec Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 28 Oct 2022 00:08:41 +0200 Subject: [PATCH] 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. --- es-core/src/ImageIO.cpp | 13 ++---- es-core/src/Window.cpp | 2 +- es-core/src/Window.h | 2 +- es-core/src/components/GIFAnimComponent.cpp | 6 ++- es-core/src/components/ImageComponent.cpp | 3 +- .../src/components/LottieAnimComponent.cpp | 2 +- es-core/src/components/NinePatchComponent.cpp | 1 + .../src/components/VideoFFmpegComponent.cpp | 5 ++- .../components/primary/CarouselComponent.h | 13 ++---- es-core/src/renderers/Renderer.cpp | 17 +++----- es-core/src/renderers/Renderer.h | 4 +- es-core/src/renderers/RendererOpenGL.cpp | 18 +++++++-- es-core/src/renderers/RendererOpenGL.h | 2 +- es-core/src/resources/Font.cpp | 5 ++- es-core/src/resources/TextureData.cpp | 5 +-- resources/shaders/glsl/core.glsl | 40 +++++++++++-------- 16 files changed, 71 insertions(+), 67 deletions(-) diff --git a/es-core/src/ImageIO.cpp b/es-core/src/ImageIO.cpp index d66426fc9..aa9ae36f5 100644 --- a/es-core/src/ImageIO.cpp +++ b/es-core/src/ImageIO.cpp @@ -39,6 +39,8 @@ std::vector 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 ImageIO::loadFromMemoryRGBA32(const unsigned char* da const BYTE* scanLine {FreeImage_GetScanLine(fiBitmap, static_cast(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(tempData)[i]}; - RGBQUAD rgba; - rgba.rgbBlue = bgra.rgbRed; - rgba.rgbGreen = bgra.rgbGreen; - rgba.rgbRed = bgra.rgbBlue; - rgba.rgbReserved = bgra.rgbReserved; - reinterpret_cast(tempData)[i] = rgba; - } + rawData = std::vector {tempData, tempData + width * height * 4}; // Free bitmap data. FreeImage_Unload(fiBitmap); diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index b79920ec8..7d3e8bd48 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -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); diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 92b4e32b0..2b5348979 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -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; } diff --git a/es-core/src/components/GIFAnimComponent.cpp b/es-core/src/components/GIFAnimComponent.cpp index 28f6ca231..7386f5cb2 100644 --- a/es-core/src/components/GIFAnimComponent.cpp +++ b/es-core/src/components/GIFAnimComponent.cpp @@ -190,10 +190,11 @@ void GIFAnimComponent::setAnimation(const std::string& path) mSize.x = static_cast(width); mSize.y = static_cast(height); + FreeImage_PreMultiplyWithAlpha(mFrame); mPictureRGBA.resize(mFileWidth * mFileHeight * 4); FreeImage_ConvertToRawBits(reinterpret_cast(&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(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); diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index d2117f2d1..80677adb2 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -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 { diff --git a/es-core/src/components/LottieAnimComponent.cpp b/es-core/src/components/LottieAnimComponent.cpp index 2fb5c73eb..c170c752f 100644 --- a/es-core/src/components/LottieAnimComponent.cpp +++ b/es-core/src/components/LottieAnimComponent.cpp @@ -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); diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index f70251f25..1bce309b5 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -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); } diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index fa318a70f..05a80b638 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -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); diff --git a/es-core/src/components/primary/CarouselComponent.h b/es-core/src/components/primary/CarouselComponent.h index 1311782c5..7702221a8 100644 --- a/es-core/src/components/primary/CarouselComponent.h +++ b/es-core/src/components/primary/CarouselComponent.h @@ -898,19 +898,14 @@ template void CarouselComponent::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(mReflectionsOpacity * 255.0f)); float falloff {glm::clamp(mReflectionsFalloff, 0.0f, 1.0f)}; falloff = mReflectionsOpacity * (1.0f - falloff); - comp->setColorShiftEnd(0xFFFFFF00 | static_cast(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::applyTheme(const std::shared_ptr& theme, mReflectionsOpacity = glm::clamp(elem->get("reflectionsOpacity"), 0.1f, 1.0f); if (elem->has("reflectionsFalloff")) - mReflectionsFalloff = glm::clamp(elem->get("reflectionsFalloff"), 0.0f, 5.0f); + mReflectionsFalloff = glm::clamp(elem->get("reflectionsFalloff"), 0.0f, 10.0f); if (elem->has("unfocusedItemOpacity")) mUnfocusedItemOpacity = diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index ef2da3ef7..5d92c2b80 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -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(rawData.data()), static_cast(width), static_cast(height), - 32, static_cast(width * 4), rmask, gmask, bmask, amask)}; + 32, static_cast(width * 4), bmask, gmask, rmask, amask)}; if (logoSurface != nullptr) { SDL_SetWindowIcon(mSDLWindow, logoSurface); diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index c07962ef9..8295b9f0f 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -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; diff --git a/es-core/src/renderers/RendererOpenGL.cpp b/es-core/src/renderers/RendererOpenGL.cpp index 2dcce598a..8907cc1f5 100644 --- a/es-core/src/renderers/RendererOpenGL.cpp +++ b/es-core/src/renderers/RendererOpenGL.cpp @@ -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(getScreenWidth()), static_cast(getScreenHeight()), nullptr); - mPostProcTexture2 = createTexture(TextureType::RGBA, false, false, false, false, + mPostProcTexture2 = createTexture(TextureType::BGRA, false, false, false, false, static_cast(getScreenWidth()), static_cast(getScreenHeight()), nullptr); @@ -376,8 +376,13 @@ unsigned int RendererOpenGL::createTexture(const TextureType type, linearMagnify ? static_cast(GL_LINEAR) : static_cast(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)); } diff --git a/es-core/src/renderers/RendererOpenGL.h b/es-core/src/renderers/RendererOpenGL.h index d6574e0dc..dc540ad92 100644 --- a/es-core/src/renderers/RendererOpenGL.h +++ b/es-core/src/renderers/RendererOpenGL.h @@ -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( diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 51d21e8e5..05cd00b42 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -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(it->verts.size())); + mRenderer->drawTriangleStrips( + &it->verts[0], static_cast(it->verts.size()), + Renderer::BlendFactor::SRC_ALPHA, Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA); } } diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index d46bf3069..70ddc4e82 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -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(mWidth), static_cast(mHeight), mDataRGBA.data()); } diff --git a/resources/shaders/glsl/core.glsl b/resources/shaders/glsl/core.glsl index b8ffb6302..44c2a726c 100644 --- a/resources/shaders/glsl/core.glsl +++ b/resources/shaders/glsl/core.glsl @@ -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; }