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:
Leon Styhre 2022-10-28 00:08:41 +02:00
parent ab11f36ece
commit 3c82bb4dfb
16 changed files with 71 additions and 67 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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; }

View file

@ -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);

View file

@ -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 {

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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 =

View file

@ -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);

View file

@ -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;

View file

@ -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));
}

View file

@ -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(

View file

@ -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);
}
}

View file

@ -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());
}

View file

@ -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;
}