Skip to content

Commit 3a5c212

Browse files
committed
Refactor rendering to use skins
1 parent 550d528 commit 3a5c212

File tree

9 files changed

+319
-432
lines changed

9 files changed

+319
-432
lines changed

src/irenderedtarget.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#pragma once
44

5+
#include <QtOpenGL>
56
#include <qnanoquickitem.h>
67
#include <scratchcpp/sprite.h>
78

@@ -15,6 +16,7 @@ namespace scratchcpprender
1516
class StageModel;
1617
class SpriteModel;
1718
class SceneMouseArea;
19+
class Texture;
1820

1921
class IRenderedTarget : public QNanoQuickItem
2022
{
@@ -35,6 +37,9 @@ class IRenderedTarget : public QNanoQuickItem
3537
virtual void updateLayerOrder(int layerOrder) = 0;
3638
virtual void updateCostume(libscratchcpp::Costume *costume) = 0;
3739

40+
virtual bool costumesLoaded() const = 0;
41+
virtual void loadCostumes() = 0;
42+
3843
virtual void beforeRedraw() = 0;
3944

4045
virtual void deinitClone() = 0;
@@ -64,16 +69,12 @@ class IRenderedTarget : public QNanoQuickItem
6469

6570
virtual QPointF mapFromScene(const QPointF &point) const = 0;
6671

67-
virtual QBuffer *bitmapBuffer() = 0;
68-
virtual const QString &bitmapUniqueKey() const = 0;
69-
7072
virtual void lockCostume() = 0;
7173
virtual void unlockCostume() = 0;
7274

7375
virtual bool mirrorHorizontally() const = 0;
7476

75-
virtual bool isSvg() const = 0;
76-
virtual void paintSvg(QNanoPainter *painter) = 0;
77+
virtual Texture texture() const = 0;
7778

7879
virtual void updateHullPoints(QOpenGLFramebufferObject *fbo) = 0;
7980
virtual const std::vector<QPointF> &hullPoints() const = 0;

src/renderedtarget.cpp

Lines changed: 84 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "stagemodel.h"
1111
#include "spritemodel.h"
1212
#include "scenemousearea.h"
13+
#include "bitmapskin.h"
14+
#include "svgskin.h"
1315

1416
using namespace scratchcpprender;
1517
using namespace libscratchcpp;
@@ -19,25 +21,12 @@ static const double SVG_SCALE_LIMIT = 0.1; // the maximum viewport dimensions ar
1921
RenderedTarget::RenderedTarget(QNanoQuickItem *parent) :
2022
IRenderedTarget(parent)
2123
{
22-
// Get maximum viewport dimensions
23-
QOpenGLContext context;
24-
context.create();
25-
Q_ASSERT(context.isValid());
26-
27-
if (context.isValid()) {
28-
QOffscreenSurface surface;
29-
surface.create();
30-
Q_ASSERT(surface.isValid());
31-
32-
if (surface.isValid()) {
33-
context.makeCurrent(&surface);
34-
GLint dims[2];
35-
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims);
36-
m_maximumWidth = dims[0] * SVG_SCALE_LIMIT;
37-
m_maximumHeight = dims[1] * SVG_SCALE_LIMIT;
38-
context.doneCurrent();
39-
}
40-
}
24+
}
25+
26+
RenderedTarget::~RenderedTarget()
27+
{
28+
for (const auto &[costume, skin] : m_skins)
29+
delete skin;
4130
}
4231

4332
void RenderedTarget::updateVisibility(bool visible)
@@ -112,29 +101,13 @@ void RenderedTarget::updateCostume(Costume *costume)
112101
m_costumeMutex.lock();
113102
m_costume = costume;
114103

115-
if (m_costume->dataFormat() == "svg") {
116-
m_svgRenderer.load(QByteArray::fromRawData(static_cast<const char *>(m_costume->data()), m_costume->dataSize()));
117-
QRectF rect = m_svgRenderer.viewBoxF();
118-
m_costumeWidth = rect.width();
119-
m_costumeHeight = rect.height();
120-
} else {
121-
m_bitmapBuffer.open(QBuffer::WriteOnly);
122-
m_bitmapBuffer.write(static_cast<const char *>(m_costume->data()), m_costume->dataSize());
123-
m_bitmapBuffer.close();
124-
m_bitmapUniqueKey = QString::fromStdString(m_costume->id());
125-
const char *format;
126-
127-
{
128-
QImageReader reader(&m_bitmapBuffer);
129-
format = reader.format();
130-
}
104+
if (m_costumesLoaded) {
105+
auto it = m_skins.find(m_costume);
131106

132-
m_bitmapBuffer.close();
133-
m_costumeBitmap.load(&m_bitmapBuffer, format);
134-
QSize size = m_costumeBitmap.size();
135-
m_costumeWidth = std::max(0, size.width());
136-
m_costumeHeight = std::max(0, size.height());
137-
m_bitmapBuffer.close();
107+
if (it == m_skins.end())
108+
m_skin = nullptr;
109+
else
110+
m_skin = it->second;
138111
}
139112

140113
m_costumeMutex.unlock();
@@ -143,10 +116,59 @@ void RenderedTarget::updateCostume(Costume *costume)
143116
calculatePos();
144117
}
145118

119+
bool RenderedTarget::costumesLoaded() const
120+
{
121+
return m_costumesLoaded;
122+
}
123+
124+
void RenderedTarget::loadCostumes()
125+
{
126+
// Delete previous skins
127+
for (const auto &[costume, skin] : m_skins)
128+
delete skin;
129+
130+
m_skins.clear();
131+
132+
// Generate a skin for each costume
133+
Target *target = scratchTarget();
134+
135+
if (!target)
136+
return;
137+
138+
const auto &costumes = target->costumes();
139+
140+
for (auto costume : costumes) {
141+
Skin *skin = nullptr;
142+
if (costume->dataFormat() == "svg")
143+
skin = new SVGSkin(costume.get());
144+
else
145+
skin = new BitmapSkin(costume.get());
146+
147+
if (skin)
148+
m_skins[costume.get()] = skin;
149+
150+
if (m_costume && costume.get() == m_costume)
151+
m_skin = skin;
152+
}
153+
154+
m_costumesLoaded = true;
155+
156+
if (m_costume) {
157+
calculateSize();
158+
calculatePos();
159+
}
160+
}
161+
146162
void RenderedTarget::beforeRedraw()
147163
{
164+
// These properties must be set here to avoid unnecessary calls to update()
148165
setWidth(m_width);
149166
setHeight(m_height);
167+
168+
if (!m_oldTexture.isValid() || (m_texture.isValid() && m_texture != m_oldTexture)) {
169+
m_oldTexture = m_texture;
170+
update();
171+
}
150172
}
151173

152174
void RenderedTarget::deinitClone()
@@ -343,35 +365,9 @@ void RenderedTarget::mouseMoveEvent(QMouseEvent *event)
343365
}
344366
}
345367

346-
void RenderedTarget::paintSvg(QNanoPainter *painter)
368+
Texture RenderedTarget::texture() const
347369
{
348-
Q_ASSERT(painter);
349-
QOpenGLContext *context = QOpenGLContext::currentContext();
350-
Q_ASSERT(context);
351-
352-
if (!context)
353-
return;
354-
355-
QOffscreenSurface surface;
356-
surface.setFormat(context->format());
357-
surface.create();
358-
Q_ASSERT(surface.isValid());
359-
360-
QSurface *oldSurface = context->surface();
361-
context->makeCurrent(&surface);
362-
363-
const QRectF drawRect(0, 0, std::min(width(), m_maximumWidth), std::min(height(), m_maximumHeight));
364-
const QSize drawRectSize = drawRect.size().toSize();
365-
366-
QOpenGLPaintDevice device(drawRectSize);
367-
QPainter qPainter;
368-
qPainter.begin(&device);
369-
qPainter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
370-
m_svgRenderer.render(&qPainter, drawRect);
371-
qPainter.end();
372-
373-
context->doneCurrent();
374-
context->makeCurrent(oldSurface);
370+
return m_texture;
375371
}
376372

377373
void RenderedTarget::updateHullPoints(QOpenGLFramebufferObject *fbo)
@@ -458,27 +454,24 @@ bool RenderedTarget::contains(const QPointF &point) const
458454

459455
void RenderedTarget::calculatePos()
460456
{
461-
if (!m_costume || !m_engine)
457+
if (!m_skin || !m_costume || !m_engine)
462458
return;
463459

464-
if (m_spriteModel) {
465-
if (isVisible()) {
466-
double stageWidth = m_engine->stageWidth();
467-
double stageHeight = m_engine->stageHeight();
468-
setX(m_stageScale * (stageWidth / 2 + m_x - m_costume->rotationCenterX() * m_clampedSize / m_costume->bitmapResolution() * (m_mirrorHorizontally ? -1 : 1)));
469-
setY(m_stageScale * (stageHeight / 2 - m_y - m_costume->rotationCenterY() * m_clampedSize / m_costume->bitmapResolution()));
470-
qreal originX = m_costume->rotationCenterX() * m_clampedSize * m_stageScale / m_costume->bitmapResolution();
471-
qreal originY = m_costume->rotationCenterY() * m_clampedSize * m_stageScale / m_costume->bitmapResolution();
472-
setTransformOriginPoint(QPointF(originX, originY));
473-
}
474-
} else {
460+
if (isVisible() || m_stageModel) {
475461
double stageWidth = m_engine->stageWidth();
476462
double stageHeight = m_engine->stageHeight();
477-
setX(m_stageScale * (stageWidth / 2 - m_costume->rotationCenterX() / m_costume->bitmapResolution()));
478-
setY(m_stageScale * (stageHeight / 2 - m_costume->rotationCenterY() / m_costume->bitmapResolution()));
479-
qreal originX = m_costume->rotationCenterX() / m_costume->bitmapResolution();
480-
qreal originY = m_costume->rotationCenterY() / m_costume->bitmapResolution();
463+
setX(m_stageScale * (stageWidth / 2 + m_x - m_costume->rotationCenterX() * m_size / scale() / m_costume->bitmapResolution() * (m_mirrorHorizontally ? -1 : 1)));
464+
setY(m_stageScale * (stageHeight / 2 - m_y - m_costume->rotationCenterY() * m_size / scale() / m_costume->bitmapResolution()));
465+
qreal originX = m_costume->rotationCenterX() * m_stageScale * m_size / scale() / m_costume->bitmapResolution();
466+
qreal originY = m_costume->rotationCenterY() * m_stageScale * m_size / scale() / m_costume->bitmapResolution();
481467
setTransformOriginPoint(QPointF(originX, originY));
468+
469+
// Qt ignores the transform origin point if it's (0, 0),
470+
// so set the transform origin to top left in this case.
471+
if (originX == 0 && originY == 0)
472+
setTransformOrigin(QQuickItem::TopLeft);
473+
else
474+
setTransformOrigin(QQuickItem::Center);
482475
}
483476
}
484477

@@ -515,30 +508,13 @@ void RenderedTarget::calculateRotation()
515508

516509
void RenderedTarget::calculateSize()
517510
{
518-
if (m_costume) {
519-
double bitmapRes = m_costume->bitmapResolution();
520-
521-
if (m_costumeWidth == 0 || m_costumeHeight == 0)
522-
m_maxSize = 1;
523-
else
524-
m_maxSize = std::min(m_maximumWidth / (m_costumeWidth * m_stageScale), m_maximumHeight / (m_costumeHeight * m_stageScale));
525-
526-
if (m_spriteModel) {
527-
m_clampedSize = std::min(m_size, m_maxSize);
528-
m_width = m_costumeWidth * m_clampedSize * m_stageScale / bitmapRes;
529-
m_height = m_height = m_costumeHeight * m_clampedSize * m_stageScale / bitmapRes;
530-
} else {
531-
m_width = m_costumeWidth * m_stageScale / bitmapRes;
532-
m_height = m_height = m_costumeHeight * m_stageScale / bitmapRes;
533-
}
511+
if (m_skin && m_costume) {
512+
Texture texture = m_skin->getTexture(m_size * m_stageScale);
513+
m_texture = texture;
514+
m_width = texture.width();
515+
m_height = texture.height();
516+
setScale(m_size * m_stageScale / m_skin->getTextureScale(texture) / m_costume->bitmapResolution());
534517
}
535-
536-
Q_ASSERT(m_maxSize > 0);
537-
538-
if (!m_stageModel && (m_size > m_maxSize) && (m_maxSize != 0))
539-
setScale(m_size / m_maxSize);
540-
else
541-
setScale(1);
542518
}
543519

544520
void RenderedTarget::handleSceneMouseMove(qreal x, qreal y)
@@ -554,16 +530,6 @@ void RenderedTarget::handleSceneMouseMove(qreal x, qreal y)
554530
}
555531
}
556532

557-
QBuffer *RenderedTarget::bitmapBuffer()
558-
{
559-
return &m_bitmapBuffer;
560-
}
561-
562-
const QString &RenderedTarget::bitmapUniqueKey() const
563-
{
564-
return m_bitmapUniqueKey;
565-
}
566-
567533
void RenderedTarget::lockCostume()
568534
{
569535
m_costumeMutex.lock();
@@ -578,11 +544,3 @@ bool RenderedTarget::mirrorHorizontally() const
578544
{
579545
return m_mirrorHorizontally;
580546
}
581-
582-
bool RenderedTarget::isSvg() const
583-
{
584-
if (!m_costume)
585-
return false;
586-
587-
return (m_costume->dataFormat() == "svg");
588-
}

0 commit comments

Comments
 (0)