Skip to content

Commit 34a9bcf

Browse files
committed
Rewrite skin rendering
Fixes rendering in Qt 6.7
1 parent aa73263 commit 34a9bcf

20 files changed

+52
-78
lines changed

src/bitmapskin.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ BitmapSkin::BitmapSkin(libscratchcpp::Costume *costume) :
2828
m_image.load(&buffer, format);
2929

3030
// Paint the image into a texture
31-
m_texture = createAndPaintTexture(m_image.width(), m_image.height(), false);
31+
m_texture = createAndPaintTexture(m_image.width(), m_image.height());
3232
m_textureSize.setWidth(m_image.width());
3333
m_textureSize.setHeight(m_image.height());
3434

src/skin.cpp

+36-63
Original file line numberDiff line numberDiff line change
@@ -9,83 +9,56 @@ using namespace scratchcpprender;
99

1010
Skin::Skin()
1111
{
12+
QOpenGLContext *context = QOpenGLContext::currentContext();
13+
Q_ASSERT(context);
14+
15+
if (context) {
16+
QObject::connect(context, &QOpenGLContext::aboutToBeDestroyed, &m_signalHandler, [this]() {
17+
// Destroy textures
18+
m_textures.clear();
19+
});
20+
}
1221
}
1322

14-
Texture Skin::createAndPaintTexture(int width, int height, bool multisampled)
23+
Texture Skin::createAndPaintTexture(int width, int height)
1524
{
1625
QOpenGLContext *context = QOpenGLContext::currentContext();
1726

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

21-
QOpenGLFunctions glF(context);
30+
QOpenGLExtraFunctions glF(context);
2231
glF.initializeOpenGLFunctions();
2332

24-
// Create offscreen surface
25-
QOffscreenSurface surface;
26-
surface.setFormat(context->format());
27-
surface.create();
28-
Q_ASSERT(surface.isValid());
29-
30-
// Save old surface
31-
QSurface *oldSurface = context->surface();
32-
33-
// Make context active on the surface
34-
context->makeCurrent(&surface);
35-
36-
const QRectF drawRect(0, 0, width, height);
37-
const QSize drawRectSize = drawRect.size().toSize();
38-
39-
// Create multisampled FBO (if the multisampled parameter is set)
40-
QOpenGLFramebufferObjectFormat format;
41-
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
42-
43-
if (multisampled)
44-
format.setSamples(16);
33+
// Render to QImage
34+
QImage image(width, height, QImage::Format_RGBA8888);
4535

46-
QOpenGLFramebufferObject fbo(drawRectSize, format);
47-
fbo.bind();
36+
// Clear the image to be fully transparent
37+
image.fill(Qt::transparent);
4838

49-
// Create paint device
50-
QOpenGLPaintDevice device(drawRectSize);
51-
QPainter painter(&device);
52-
painter.beginNativePainting();
53-
painter.setRenderHint(QPainter::Antialiasing, false);
54-
glF.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
55-
glF.glClear(GL_COLOR_BUFFER_BIT);
56-
57-
// Call the skin-specific paint method
58-
paint(&painter);
59-
60-
// Done with the painting
61-
painter.endNativePainting();
39+
QPainter painter(&image);
40+
paint(&painter); // Custom paint function
6241
painter.end();
63-
fbo.release();
64-
65-
GLuint textureHandle;
66-
67-
if (multisampled) {
68-
// Create non-multisampled FBO (we can't take the texture from the multisampled FBO)
69-
format.setSamples(0);
70-
71-
QOpenGLFramebufferObject targetFbo(drawRectSize, format);
72-
targetFbo.bind();
73-
74-
// Blit the multisampled FBO to target FBO
75-
QOpenGLFramebufferObject::blitFramebuffer(&targetFbo, &fbo);
76-
77-
// Take the texture (will call targetFbo.release())
78-
textureHandle = targetFbo.takeTexture();
79-
} else {
80-
// Take the texture
81-
textureHandle = fbo.takeTexture();
42+
image.mirror();
43+
44+
// Premultiply alpha
45+
for (int y = 0; y < image.height(); ++y) {
46+
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
47+
for (int x = 0; x < image.width(); ++x) {
48+
QColor color = QColor::fromRgba(line[x]);
49+
color.setRedF(color.redF() * color.alphaF());
50+
color.setGreenF(color.greenF() * color.alphaF());
51+
color.setBlueF(color.blueF() * color.alphaF());
52+
line[x] = color.rgba();
53+
}
8254
}
8355

84-
// Restore old surface
85-
context->doneCurrent();
86-
87-
if (oldSurface)
88-
context->makeCurrent(oldSurface);
56+
// Create final texture from the image
57+
auto texture = std::make_shared<QOpenGLTexture>(image);
58+
m_textures.push_back(texture);
59+
texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
60+
texture->setMagnificationFilter(QOpenGLTexture::Linear);
8961

90-
return Texture(textureHandle, drawRectSize);
62+
// Texture(texture, width, height).toImage().save("/home/adazem009/test.png");
63+
return Texture(texture->textureId(), width, height);
9164
}

src/skin.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ class Skin
2222
virtual double getTextureScale(const Texture &texture) const = 0;
2323

2424
protected:
25-
Texture createAndPaintTexture(int width, int height, bool multisampled);
25+
Texture createAndPaintTexture(int width, int height);
2626
virtual void paint(QPainter *painter) = 0;
27+
28+
private:
29+
std::vector<std::shared_ptr<QOpenGLTexture>> m_textures;
30+
QObject m_signalHandler; // for disconnecting signals after destroyed
2731
};
2832

2933
} // namespace scratchcpprender

src/svgskin.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ using namespace scratchcpprender;
99
static const int MAX_TEXTURE_DIMENSION = 2048;
1010
static const int INDEX_OFFSET = 8;
1111

12-
SVGSkin::SVGSkin(libscratchcpp::Costume *costume, bool antialiasing) :
13-
Skin(),
14-
m_antialiasing(antialiasing)
12+
SVGSkin::SVGSkin(libscratchcpp::Costume *costume) :
13+
Skin()
1514
{
1615
if (!costume)
1716
return;
@@ -86,7 +85,7 @@ Texture SVGSkin::createScaledTexture(int index)
8685
return Texture();
8786
}
8887

89-
const Texture texture = createAndPaintTexture(viewBox.width() * scale, viewBox.height() * scale, m_antialiasing);
88+
const Texture texture = createAndPaintTexture(viewBox.width() * scale, viewBox.height() * scale);
9089

9190
if (texture.isValid()) {
9291
m_textures[index] = texture.handle();

src/svgskin.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace scratchcpprender
2020
class SVGSkin : public Skin
2121
{
2222
public:
23-
SVGSkin(libscratchcpp::Costume *costume, bool antialiasing = true);
23+
SVGSkin(libscratchcpp::Costume *costume);
2424
~SVGSkin();
2525

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

4342
} // namespace scratchcpprender

test/mocks/enginemock.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class EngineMock : public IEngine
1919
MOCK_METHOD(void, start, (), (override));
2020
MOCK_METHOD(void, stop, (), (override));
2121
MOCK_METHOD(VirtualMachine *, startScript, (std::shared_ptr<Block>, Target *), (override));
22-
MOCK_METHOD(void, broadcast, (unsigned int), (override));
22+
MOCK_METHOD(void, broadcast, (int), (override));
2323
MOCK_METHOD(void, broadcastByPtr, (Broadcast *), (override));
2424
MOCK_METHOD(void, startBackdropScripts, (Broadcast *), (override));
2525
MOCK_METHOD(void, stopScript, (VirtualMachine *), (override));
@@ -105,7 +105,7 @@ class EngineMock : public IEngine
105105
MOCK_METHOD(const std::vector<std::shared_ptr<Broadcast>> &, broadcasts, (), (const, override));
106106
MOCK_METHOD(void, setBroadcasts, (const std::vector<std::shared_ptr<Broadcast>> &), (override));
107107
MOCK_METHOD(std::shared_ptr<Broadcast>, broadcastAt, (int), (const, override));
108-
MOCK_METHOD(int, findBroadcast, (const std::string &), (const, override));
108+
MOCK_METHOD(std::vector<int>, findBroadcasts, (const std::string &), (const, override));
109109
MOCK_METHOD(int, findBroadcastById, (const std::string &), (const, override));
110110

111111
MOCK_METHOD(void, addWhenTouchingObjectScript, (std::shared_ptr<Block>), (override));

test/skins/bitmapskin_test.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class BitmapSkinTest : public testing::Test
3333
void TearDown() override
3434
{
3535
ASSERT_EQ(m_context.surface(), &m_surface);
36+
m_jpegSkin.reset();
37+
m_pngSkin.reset();
3638
m_context.doneCurrent();
3739
}
3840

test/skins/svgskin_test.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ class SVGSkinTest : public testing::Test
2222
Costume costume("", "", "");
2323
std::string costumeData = readFileStr("image.svg");
2424
costume.setData(costumeData.size(), costumeData.data());
25-
m_skin = std::make_unique<SVGSkin>(&costume, false);
25+
m_skin = std::make_unique<SVGSkin>(&costume);
2626
}
2727

2828
void TearDown() override
2929
{
3030
ASSERT_EQ(m_context.surface(), &m_surface);
31+
m_skin.reset();
3132
m_context.doneCurrent();
3233
}
3334

@@ -59,10 +60,6 @@ TEST_F(SVGSkinTest, Textures)
5960
ASSERT_EQ(m_skin->getTextureScale(texture), scale);
6061
}
6162

62-
// Skip images 12, 13 and 15 because they're different on xvfb
63-
if (i == 12 || i == 13 || i >= 15)
64-
continue;
65-
6663
QBuffer buffer;
6764
texture.toImage().save(&buffer, "png");
6865
QFile ref("svg_texture_results/" + QString::number(std::min(i, 15)) + ".png");

test/stamp.png

134 Bytes
Loading

test/svg_texture_results/10.png

318 Bytes
Loading

test/svg_texture_results/11.png

635 Bytes
Loading

test/svg_texture_results/12.png

2.13 KB
Loading

test/svg_texture_results/13.png

4.55 KB
Loading

test/svg_texture_results/14.png

5.24 KB
Loading

test/svg_texture_results/15.png

25.7 KB
Loading

test/svg_texture_results/5.png

0 Bytes
Loading

test/svg_texture_results/6.png

14 Bytes
Loading

test/svg_texture_results/7.png

39 Bytes
Loading

test/svg_texture_results/8.png

101 Bytes
Loading

test/svg_texture_results/9.png

181 Bytes
Loading

0 commit comments

Comments
 (0)