Skip to content

Rewrite skin rendering #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bitmapskin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ BitmapSkin::BitmapSkin(libscratchcpp::Costume *costume) :
m_image.load(&buffer, format);

// Paint the image into a texture
m_texture = createAndPaintTexture(m_image.width(), m_image.height(), false);
m_texture = createAndPaintTexture(m_image.width(), m_image.height());
m_textureSize.setWidth(m_image.width());
m_textureSize.setHeight(m_image.height());

Expand Down
99 changes: 36 additions & 63 deletions src/skin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,83 +9,56 @@ using namespace scratchcpprender;

Skin::Skin()
{
QOpenGLContext *context = QOpenGLContext::currentContext();
Q_ASSERT(context);

if (context) {
QObject::connect(context, &QOpenGLContext::aboutToBeDestroyed, &m_signalHandler, [this]() {
// Destroy textures
m_textures.clear();
});
}
}

Texture Skin::createAndPaintTexture(int width, int height, bool multisampled)
Texture Skin::createAndPaintTexture(int width, int height)
{
QOpenGLContext *context = QOpenGLContext::currentContext();

if (!context || !context->isValid() || (width <= 0 || height <= 0))
return Texture();

QOpenGLFunctions glF(context);
QOpenGLExtraFunctions glF(context);
glF.initializeOpenGLFunctions();

// Create offscreen surface
QOffscreenSurface surface;
surface.setFormat(context->format());
surface.create();
Q_ASSERT(surface.isValid());

// Save old surface
QSurface *oldSurface = context->surface();

// Make context active on the surface
context->makeCurrent(&surface);

const QRectF drawRect(0, 0, width, height);
const QSize drawRectSize = drawRect.size().toSize();

// Create multisampled FBO (if the multisampled parameter is set)
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);

if (multisampled)
format.setSamples(16);
// Render to QImage
QImage image(width, height, QImage::Format_RGBA8888);

QOpenGLFramebufferObject fbo(drawRectSize, format);
fbo.bind();
// Clear the image to be fully transparent
image.fill(Qt::transparent);

// Create paint device
QOpenGLPaintDevice device(drawRectSize);
QPainter painter(&device);
painter.beginNativePainting();
painter.setRenderHint(QPainter::Antialiasing, false);
glF.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glF.glClear(GL_COLOR_BUFFER_BIT);

// Call the skin-specific paint method
paint(&painter);

// Done with the painting
painter.endNativePainting();
QPainter painter(&image);
paint(&painter); // Custom paint function
painter.end();
fbo.release();

GLuint textureHandle;

if (multisampled) {
// Create non-multisampled FBO (we can't take the texture from the multisampled FBO)
format.setSamples(0);

QOpenGLFramebufferObject targetFbo(drawRectSize, format);
targetFbo.bind();

// Blit the multisampled FBO to target FBO
QOpenGLFramebufferObject::blitFramebuffer(&targetFbo, &fbo);

// Take the texture (will call targetFbo.release())
textureHandle = targetFbo.takeTexture();
} else {
// Take the texture
textureHandle = fbo.takeTexture();
image.mirror();

// Premultiply alpha
for (int y = 0; y < image.height(); ++y) {
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
for (int x = 0; x < image.width(); ++x) {
QColor color = QColor::fromRgba(line[x]);
color.setRedF(color.redF() * color.alphaF());
color.setGreenF(color.greenF() * color.alphaF());
color.setBlueF(color.blueF() * color.alphaF());
line[x] = color.rgba();
}
}

// Restore old surface
context->doneCurrent();

if (oldSurface)
context->makeCurrent(oldSurface);
// Create final texture from the image
auto texture = std::make_shared<QOpenGLTexture>(image);
m_textures.push_back(texture);
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
texture->setMagnificationFilter(QOpenGLTexture::Linear);

return Texture(textureHandle, drawRectSize);
// Texture(texture, width, height).toImage().save("/home/adazem009/test.png");
return Texture(texture->textureId(), width, height);
}
6 changes: 5 additions & 1 deletion src/skin.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ class Skin
virtual double getTextureScale(const Texture &texture) const = 0;

protected:
Texture createAndPaintTexture(int width, int height, bool multisampled);
Texture createAndPaintTexture(int width, int height);
virtual void paint(QPainter *painter) = 0;

private:
std::vector<std::shared_ptr<QOpenGLTexture>> m_textures;
QObject m_signalHandler; // for disconnecting signals after destroyed
};

} // namespace scratchcpprender
7 changes: 3 additions & 4 deletions src/svgskin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ using namespace scratchcpprender;
static const int MAX_TEXTURE_DIMENSION = 2048;
static const int INDEX_OFFSET = 8;

SVGSkin::SVGSkin(libscratchcpp::Costume *costume, bool antialiasing) :
Skin(),
m_antialiasing(antialiasing)
SVGSkin::SVGSkin(libscratchcpp::Costume *costume) :
Skin()
{
if (!costume)
return;
Expand Down Expand Up @@ -86,7 +85,7 @@ Texture SVGSkin::createScaledTexture(int index)
return Texture();
}

const Texture texture = createAndPaintTexture(viewBox.width() * scale, viewBox.height() * scale, m_antialiasing);
const Texture texture = createAndPaintTexture(viewBox.width() * scale, viewBox.height() * scale);

if (texture.isValid()) {
m_textures[index] = texture.handle();
Expand Down
3 changes: 1 addition & 2 deletions src/svgskin.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace scratchcpprender
class SVGSkin : public Skin
{
public:
SVGSkin(libscratchcpp::Costume *costume, bool antialiasing = true);
SVGSkin(libscratchcpp::Costume *costume);
~SVGSkin();

Texture getTexture(double scale) const override;
Expand All @@ -37,7 +37,6 @@ class SVGSkin : public Skin
std::unordered_map<GLuint, Texture> m_textureObjects;
QSvgRenderer m_svgRen;
int m_maxIndex = 0;
bool m_antialiasing = false;
};

} // namespace scratchcpprender
2 changes: 2 additions & 0 deletions test/skins/bitmapskin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class BitmapSkinTest : public testing::Test
void TearDown() override
{
ASSERT_EQ(m_context.surface(), &m_surface);
m_jpegSkin.reset();
m_pngSkin.reset();
m_context.doneCurrent();
}

Expand Down
7 changes: 2 additions & 5 deletions test/skins/svgskin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ class SVGSkinTest : public testing::Test
Costume costume("", "", "");
std::string costumeData = readFileStr("image.svg");
costume.setData(costumeData.size(), costumeData.data());
m_skin = std::make_unique<SVGSkin>(&costume, false);
m_skin = std::make_unique<SVGSkin>(&costume);
}

void TearDown() override
{
ASSERT_EQ(m_context.surface(), &m_surface);
m_skin.reset();
m_context.doneCurrent();
}

Expand Down Expand Up @@ -59,10 +60,6 @@ TEST_F(SVGSkinTest, Textures)
ASSERT_EQ(m_skin->getTextureScale(texture), scale);
}

// Skip images 12, 13 and 15 because they're different on xvfb
if (i == 12 || i == 13 || i >= 15)
continue;

QBuffer buffer;
texture.toImage().save(&buffer, "png");
QFile ref("svg_texture_results/" + QString::number(std::min(i, 15)) + ".png");
Expand Down
Binary file modified test/stamp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/svg_texture_results/12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/svg_texture_results/13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/svg_texture_results/15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/svg_texture_results/9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading