Skip to content

Commit 8a909ff

Browse files
committed
Add SVG scale limit
1 parent 4a33d22 commit 8a909ff

File tree

3 files changed

+128
-11
lines changed

3 files changed

+128
-11
lines changed

ScratchCPPGui/renderedtarget.cpp

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,30 @@
1313
using namespace scratchcppgui;
1414
using namespace libscratchcpp;
1515

16+
static const double SVG_SCALE_LIMIT = 0.25; // the maximum viewport dimensions are multiplied by this
17+
1618
RenderedTarget::RenderedTarget(QNanoQuickItem *parent) :
1719
IRenderedTarget(parent)
1820
{
21+
// Get maximum viewport dimensions
22+
QOpenGLContext context;
23+
context.create();
24+
Q_ASSERT(context.isValid());
25+
26+
if (context.isValid()) {
27+
QOffscreenSurface surface;
28+
surface.create();
29+
Q_ASSERT(surface.isValid());
30+
31+
if (surface.isValid()) {
32+
context.makeCurrent(&surface);
33+
GLint dims[2];
34+
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims);
35+
m_maximumWidth = dims[0] * SVG_SCALE_LIMIT;
36+
m_maximumHeight = dims[1] * SVG_SCALE_LIMIT;
37+
context.doneCurrent();
38+
}
39+
}
1940
}
2041

2142
void RenderedTarget::loadProperties()
@@ -56,10 +77,11 @@ void RenderedTarget::loadProperties()
5677
// Coordinates
5778
m_size = sprite->size() / 100;
5879
updateCostumeData();
59-
m_x = static_cast<double>(m_engine->stageWidth()) / 2 + sprite->x() - m_costume->rotationCenterX() * m_size / m_costume->bitmapResolution() * (m_newMirrorHorizontally ? -1 : 1);
60-
m_y = static_cast<double>(m_engine->stageHeight()) / 2 - sprite->y() - m_costume->rotationCenterY() * m_size / m_costume->bitmapResolution();
61-
m_originX = m_costume->rotationCenterX() * m_size / m_costume->bitmapResolution();
62-
m_originY = m_costume->rotationCenterY() * m_size / m_costume->bitmapResolution();
80+
double clampedSize = std::min(m_size, m_maxSize);
81+
m_x = static_cast<double>(m_engine->stageWidth()) / 2 + sprite->x() - m_costume->rotationCenterX() * clampedSize / m_costume->bitmapResolution() * (m_newMirrorHorizontally ? -1 : 1);
82+
m_y = static_cast<double>(m_engine->stageHeight()) / 2 - sprite->y() - m_costume->rotationCenterY() * clampedSize / m_costume->bitmapResolution();
83+
m_originX = m_costume->rotationCenterX() * clampedSize / m_costume->bitmapResolution();
84+
m_originY = m_costume->rotationCenterY() * clampedSize / m_costume->bitmapResolution();
6385

6486
// Layer
6587
m_z = sprite->layerOrder();
@@ -105,6 +127,12 @@ void RenderedTarget::updateProperties()
105127
setHeight(m_height);
106128
setRotation(m_rotation);
107129
setTransformOriginPoint(QPointF(m_originX, m_originY));
130+
Q_ASSERT(m_maxSize > 0);
131+
132+
if (!m_stageModel && (m_size > m_maxSize) && (m_maxSize != 0))
133+
setScale(m_size / m_maxSize);
134+
else
135+
setScale(1);
108136

109137
if (m_newMirrorHorizontally != m_mirrorHorizontally) {
110138
m_mirrorHorizontally = m_newMirrorHorizontally;
@@ -260,7 +288,7 @@ void RenderedTarget::paintSvg(QNanoPainter *painter)
260288
QSurface *oldSurface = context->surface();
261289
context->makeCurrent(&surface);
262290

263-
const QRectF drawRect(0, 0, width(), height());
291+
const QRectF drawRect(0, 0, std::min(width(), m_maximumWidth), std::min(height(), m_maximumHeight));
264292
const QSize drawRectSize = drawRect.size().toSize();
265293

266294
/*QOpenGLFramebufferObjectFormat fboFormat;
@@ -282,11 +310,13 @@ void RenderedTarget::calculateSize(Target *target, double costumeWidth, double c
282310
{
283311
if (m_costume) {
284312
double bitmapRes = m_costume->bitmapResolution();
313+
m_maxSize = std::min(m_maximumWidth / costumeWidth, m_maximumHeight / costumeHeight);
285314
Sprite *sprite = dynamic_cast<Sprite *>(target);
286315

287316
if (sprite) {
288-
m_width = costumeWidth * m_size / bitmapRes;
289-
m_height = costumeHeight * m_size / bitmapRes;
317+
double clampedSize = std::min(m_size, m_maxSize);
318+
m_width = costumeWidth * clampedSize / bitmapRes;
319+
m_height = costumeHeight * clampedSize / bitmapRes;
290320
} else {
291321
m_width = costumeWidth / bitmapRes;
292322
m_height = costumeHeight / bitmapRes;

ScratchCPPGui/renderedtarget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class RenderedTarget : public IRenderedTarget
8888
bool m_imageChanged = false;
8989
bool m_visible = true;
9090
double m_size = 1;
91+
double m_maxSize = 1;
9192
double m_x = 0;
9293
double m_y = 0;
9394
double m_z = 0;
@@ -98,6 +99,8 @@ class RenderedTarget : public IRenderedTarget
9899
bool m_newMirrorHorizontally = false;
99100
double m_originX = 0;
100101
double m_originY = 0;
102+
qreal m_maximumWidth = std::numeric_limits<double>::infinity();
103+
qreal m_maximumHeight = std::numeric_limits<double>::infinity();
101104
};
102105

103106
} // namespace scratchcppgui

test/renderedtarget/renderedtarget_test.cpp

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,90 @@ TEST_F(RenderedTargetTest, LoadPngCostume)
304304

305305
TEST_F(RenderedTargetTest, LoadSvgCostume)
306306
{
307+
// Get maximum viewport dimensions
308+
QOpenGLContext context;
309+
context.create();
310+
Q_ASSERT(context.isValid());
311+
312+
QOffscreenSurface surface;
313+
surface.create();
314+
Q_ASSERT(surface.isValid());
315+
316+
context.makeCurrent(&surface);
317+
GLint dims[2];
318+
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims);
319+
double maxWidth = dims[0] * 0.25;
320+
double maxHeight = dims[1] * 0.25;
321+
double maxSize = std::min(maxWidth / (1143 / 90.0), maxHeight / (1143 / 90.0));
322+
context.doneCurrent();
323+
307324
std::string str = readFileStr("image.svg");
308-
Costume costume("", "abc", "svg");
309-
costume.setData(str.size(), static_cast<void *>(const_cast<char *>(str.c_str())));
310-
costume.setBitmapResolution(3);
325+
auto costume = std::make_shared<Costume>("", "abc", "svg");
326+
costume->setData(str.size(), static_cast<void *>(const_cast<char *>(str.c_str())));
327+
costume->setBitmapResolution(1);
328+
329+
EngineMock engine;
330+
SpriteModel model;
331+
Sprite sprite;
332+
sprite.setSize(maxSize * 100);
333+
sprite.setX(49.7);
334+
sprite.setY(-64.15);
335+
costume->setRotationCenterX(-84);
336+
costume->setRotationCenterY(53);
337+
model.init(&sprite);
311338

312339
RenderedTarget target;
340+
target.setEngine(&engine);
341+
target.setSpriteModel(&model);
313342

314-
target.loadCostume(&costume);
343+
target.loadCostume(costume.get());
344+
ASSERT_TRUE(target.isSvg());
345+
ASSERT_FALSE(target.bitmapBuffer()->isOpen());
346+
target.bitmapBuffer()->open(QBuffer::ReadOnly);
347+
ASSERT_TRUE(target.bitmapBuffer()->readAll().toStdString().empty());
348+
ASSERT_TRUE(target.bitmapUniqueKey().toStdString().empty());
349+
target.bitmapBuffer()->close();
350+
351+
EXPECT_CALL(engine, stageWidth()).WillOnce(Return(480));
352+
EXPECT_CALL(engine, stageHeight()).WillOnce(Return(360));
353+
target.loadProperties();
354+
ASSERT_TRUE(target.isSvg());
355+
ASSERT_FALSE(target.bitmapBuffer()->isOpen());
356+
target.bitmapBuffer()->open(QBuffer::ReadOnly);
357+
ASSERT_TRUE(target.bitmapBuffer()->readAll().toStdString().empty());
358+
ASSERT_TRUE(target.bitmapUniqueKey().toStdString().empty());
359+
target.bitmapBuffer()->close();
360+
361+
target.updateProperties();
362+
ASSERT_TRUE(target.isSvg());
363+
ASSERT_FALSE(target.bitmapBuffer()->isOpen());
364+
target.bitmapBuffer()->open(QBuffer::ReadOnly);
365+
ASSERT_TRUE(target.bitmapBuffer()->readAll().toStdString().empty());
366+
ASSERT_TRUE(target.bitmapUniqueKey().toStdString().empty());
367+
target.bitmapBuffer()->close();
368+
369+
ASSERT_EQ(std::round(target.width() * 100) / 100, maxWidth);
370+
ASSERT_EQ(std::round(target.height() * 100) / 100, maxHeight);
371+
ASSERT_EQ(target.scale(), 1);
372+
ASSERT_EQ(std::round(target.x() * 100) / 100, 27381.35);
373+
ASSERT_EQ(std::round(target.y() * 100) / 100, -16849.39);
374+
ASSERT_EQ(std::round(target.transformOriginPoint().x() * 100) / 100, -27091.65);
375+
ASSERT_EQ(std::round(target.transformOriginPoint().y() * 100) / 100, 17093.54);
376+
377+
// Test scale limit
378+
sprite.setSize(maxSize * 250);
379+
380+
target.loadCostume(costume.get());
381+
ASSERT_TRUE(target.isSvg());
382+
ASSERT_FALSE(target.bitmapBuffer()->isOpen());
383+
target.bitmapBuffer()->open(QBuffer::ReadOnly);
384+
ASSERT_TRUE(target.bitmapBuffer()->readAll().toStdString().empty());
385+
ASSERT_TRUE(target.bitmapUniqueKey().toStdString().empty());
386+
target.bitmapBuffer()->close();
387+
388+
EXPECT_CALL(engine, stageWidth()).WillOnce(Return(480));
389+
EXPECT_CALL(engine, stageHeight()).WillOnce(Return(360));
390+
target.loadProperties();
315391
ASSERT_TRUE(target.isSvg());
316392
ASSERT_FALSE(target.bitmapBuffer()->isOpen());
317393
target.bitmapBuffer()->open(QBuffer::ReadOnly);
@@ -326,6 +402,14 @@ TEST_F(RenderedTargetTest, LoadSvgCostume)
326402
ASSERT_TRUE(target.bitmapBuffer()->readAll().toStdString().empty());
327403
ASSERT_TRUE(target.bitmapUniqueKey().toStdString().empty());
328404
target.bitmapBuffer()->close();
405+
406+
ASSERT_EQ(std::round(target.width() * 100) / 100, maxWidth);
407+
ASSERT_EQ(std::round(target.height() * 100) / 100, maxHeight);
408+
ASSERT_EQ(target.scale(), 2.5);
409+
ASSERT_EQ(std::round(target.x() * 100) / 100, 27381.35);
410+
ASSERT_EQ(std::round(target.y() * 100) / 100, -16849.39);
411+
ASSERT_EQ(std::round(target.transformOriginPoint().x() * 100) / 100, -27091.65);
412+
ASSERT_EQ(std::round(target.transformOriginPoint().y() * 100) / 100, 17093.54);
329413
}
330414

331415
TEST_F(RenderedTargetTest, PaintSvg)

0 commit comments

Comments
 (0)