Skip to content

Commit 7e17240

Browse files
committed
Apply graphics effects to images
1 parent 81d9f29 commit 7e17240

File tree

6 files changed

+126
-8
lines changed

6 files changed

+126
-8
lines changed

include/scratchcpp/costume.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace libscratchcpp
1111
{
1212

1313
class Broadcast;
14+
class IGraphicsEffect;
1415
class CostumePrivate;
1516

1617
/*! \brief The Costume class represents a Scratch costume. */
@@ -40,6 +41,8 @@ class LIBSCRATCHCPP_EXPORT Costume : public Asset
4041

4142
Rgb **bitmap() const;
4243

44+
void setGraphicsEffect(IGraphicsEffect *effect, double value);
45+
4346
Broadcast *broadcast();
4447

4548
protected:

src/scratch/costume.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ Rgb **Costume::bitmap() const
107107
return impl->bitmap;
108108
}
109109

110+
/*! Sets the value of the given graphics effect. */
111+
void Costume::setGraphicsEffect(IGraphicsEffect *effect, double value)
112+
{
113+
auto it = impl->graphicsEffects.find(effect);
114+
bool update = ((it == impl->graphicsEffects.cend()) || (it->second != value));
115+
116+
if (value == 0)
117+
impl->graphicsEffects.erase(effect);
118+
else
119+
impl->graphicsEffects[effect] = value;
120+
121+
if (update)
122+
impl->updateImage();
123+
}
124+
110125
/*!
111126
* Returns the Broadcast linked with this costume.
112127
* \note This is used by the "switch backdrop to and wait" block.

src/scratch/costume_p.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include <scratchcpp/igraphicseffect.h>
4+
35
#include "costume_p.h"
46

57
using namespace libscratchcpp;
@@ -37,15 +39,15 @@ void CostumePrivate::updateImage()
3739
oldHeight = scaledHeight;
3840
}
3941

40-
for (unsigned int i = 0; i < scaledHeight; i++) {
41-
for (unsigned int j = 0; j < scaledWidth; j++) {
42-
Rgb color = image->colorAt(mirrorHorizontally ? (scaledWidth - 1 - j) : j, i, scale / bitmapResolution);
43-
44-
// TODO: Apply graphics effects here
42+
double actualScale = scale / bitmapResolution;
4543

46-
bitmap[i][j] = color;
47-
}
44+
for (unsigned int i = 0; i < scaledHeight; i++) {
45+
for (unsigned int j = 0; j < scaledWidth; j++)
46+
bitmap[i][j] = image->colorAt(mirrorHorizontally ? (scaledWidth - 1 - j) : j, i, actualScale);
4847
}
48+
49+
for (const auto &[effect, value] : graphicsEffects)
50+
effect->apply(bitmap, scaledWidth, scaledHeight, value);
4951
}
5052

5153
void CostumePrivate::freeImage()

src/scratch/costume_p.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44

55
#include <scratchcpp/broadcast.h>
66
#include <scratchcpp/iimageformat.h>
7+
#include <unordered_map>
78

89
namespace libscratchcpp
910
{
1011

11-
class IImageFormat;
12+
class IGraphicsEffect;
1213

1314
struct CostumePrivate
1415
{
@@ -30,6 +31,7 @@ struct CostumePrivate
3031
double oldScale = 1;
3132
double scale = 1;
3233
bool mirrorHorizontally = false;
34+
std::unordered_map<IGraphicsEffect *, double> graphicsEffects;
3335
Broadcast broadcast;
3436
};
3537

test/assets/costume_test.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
#include <scratchcpp/broadcast.h>
44
#include <imageformatfactorymock.h>
55
#include <imageformatmock.h>
6+
#include <graphicseffectmock.h>
67

78
#include "../common.h"
89

910
using namespace libscratchcpp;
1011

1112
using ::testing::Return;
13+
using ::testing::_;
14+
using ::testing::SaveArg;
1215

1316
class CostumeTest : public testing::Test
1417
{
@@ -322,6 +325,86 @@ TEST_F(CostumeTest, ScaledBitmap)
322325
ASSERT_EQ(bitmap[3][5], 1);
323326
}
324327

328+
TEST_F(CostumeTest, BitmapWithGraphicsEffects)
329+
{
330+
EXPECT_CALL(*m_imageFormatFactory, createInstance()).WillOnce(Return(m_imageFormat));
331+
EXPECT_CALL(*m_imageFormat, width()).Times(3).WillRepeatedly(Return(0));
332+
EXPECT_CALL(*m_imageFormat, height()).Times(3).WillRepeatedly(Return(0));
333+
Costume costume("costume1", "a", "test");
334+
335+
GraphicsEffectMock effect1, effect2;
336+
costume.setGraphicsEffect(&effect1, 54.12);
337+
costume.setGraphicsEffect(&effect2, -89.03);
338+
339+
EXPECT_CALL(*m_imageFormat, width()).WillOnce(Return(4));
340+
EXPECT_CALL(*m_imageFormat, height()).WillOnce(Return(3));
341+
342+
EXPECT_CALL(*m_imageFormat, colorAt(0, 0, 1.5)).WillOnce(Return(5));
343+
EXPECT_CALL(*m_imageFormat, colorAt(1, 0, 1.5)).WillOnce(Return(5));
344+
EXPECT_CALL(*m_imageFormat, colorAt(2, 0, 1.5)).WillOnce(Return(3));
345+
EXPECT_CALL(*m_imageFormat, colorAt(3, 0, 1.5)).WillOnce(Return(8));
346+
EXPECT_CALL(*m_imageFormat, colorAt(4, 0, 1.5)).WillOnce(Return(8));
347+
EXPECT_CALL(*m_imageFormat, colorAt(5, 0, 1.5)).WillOnce(Return(1));
348+
349+
EXPECT_CALL(*m_imageFormat, colorAt(0, 1, 1.5)).WillOnce(Return(5));
350+
EXPECT_CALL(*m_imageFormat, colorAt(1, 1, 1.5)).WillOnce(Return(5));
351+
EXPECT_CALL(*m_imageFormat, colorAt(2, 1, 1.5)).WillOnce(Return(3));
352+
EXPECT_CALL(*m_imageFormat, colorAt(3, 1, 1.5)).WillOnce(Return(8));
353+
EXPECT_CALL(*m_imageFormat, colorAt(4, 1, 1.5)).WillOnce(Return(8));
354+
EXPECT_CALL(*m_imageFormat, colorAt(5, 1, 1.5)).WillOnce(Return(1));
355+
356+
EXPECT_CALL(*m_imageFormat, colorAt(0, 2, 1.5)).WillOnce(Return(9));
357+
EXPECT_CALL(*m_imageFormat, colorAt(1, 2, 1.5)).WillOnce(Return(9));
358+
EXPECT_CALL(*m_imageFormat, colorAt(2, 2, 1.5)).WillOnce(Return(10));
359+
EXPECT_CALL(*m_imageFormat, colorAt(3, 2, 1.5)).WillOnce(Return(5));
360+
EXPECT_CALL(*m_imageFormat, colorAt(4, 2, 1.5)).WillOnce(Return(5));
361+
EXPECT_CALL(*m_imageFormat, colorAt(5, 2, 1.5)).WillOnce(Return(8));
362+
363+
EXPECT_CALL(*m_imageFormat, colorAt(0, 3, 1.5)).WillOnce(Return(1));
364+
EXPECT_CALL(*m_imageFormat, colorAt(1, 3, 1.5)).WillOnce(Return(1));
365+
EXPECT_CALL(*m_imageFormat, colorAt(2, 3, 1.5)).WillOnce(Return(9));
366+
EXPECT_CALL(*m_imageFormat, colorAt(3, 3, 1.5)).WillOnce(Return(4));
367+
EXPECT_CALL(*m_imageFormat, colorAt(4, 3, 1.5)).WillOnce(Return(4));
368+
EXPECT_CALL(*m_imageFormat, colorAt(5, 3, 1.5)).WillOnce(Return(8));
369+
370+
Rgb **bitmapPtr1, **bitmapPtr2;
371+
EXPECT_CALL(effect1, apply(_, 6, 4, 54.12)).WillOnce(SaveArg<0>(&bitmapPtr1));
372+
EXPECT_CALL(effect2, apply(_, 6, 4, -89.03)).WillOnce(SaveArg<0>(&bitmapPtr2));
373+
costume.setScale(1.5);
374+
375+
auto bitmap = costume.bitmap();
376+
ASSERT_TRUE(bitmap);
377+
ASSERT_EQ(bitmap, bitmapPtr1);
378+
ASSERT_EQ(bitmap, bitmapPtr2);
379+
ASSERT_EQ(bitmap[0][0], 5);
380+
ASSERT_EQ(bitmap[0][1], 5);
381+
ASSERT_EQ(bitmap[0][2], 3);
382+
ASSERT_EQ(bitmap[0][3], 8);
383+
ASSERT_EQ(bitmap[0][4], 8);
384+
ASSERT_EQ(bitmap[0][5], 1);
385+
386+
ASSERT_EQ(bitmap[1][0], 5);
387+
ASSERT_EQ(bitmap[1][1], 5);
388+
ASSERT_EQ(bitmap[1][2], 3);
389+
ASSERT_EQ(bitmap[1][3], 8);
390+
ASSERT_EQ(bitmap[1][4], 8);
391+
ASSERT_EQ(bitmap[1][5], 1);
392+
393+
ASSERT_EQ(bitmap[2][0], 9);
394+
ASSERT_EQ(bitmap[2][1], 9);
395+
ASSERT_EQ(bitmap[2][2], 10);
396+
ASSERT_EQ(bitmap[2][3], 5);
397+
ASSERT_EQ(bitmap[2][4], 5);
398+
ASSERT_EQ(bitmap[2][5], 8);
399+
400+
ASSERT_EQ(bitmap[3][0], 1);
401+
ASSERT_EQ(bitmap[3][1], 1);
402+
ASSERT_EQ(bitmap[3][2], 9);
403+
ASSERT_EQ(bitmap[3][3], 4);
404+
ASSERT_EQ(bitmap[3][4], 4);
405+
ASSERT_EQ(bitmap[3][5], 8);
406+
}
407+
325408
TEST_F(CostumeTest, Broadcast)
326409
{
327410
Costume costume("costume1", "a", "svg");

test/mocks/graphicseffectmock.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include <scratchcpp/igraphicseffect.h>
4+
#include <gmock/gmock.h>
5+
6+
using namespace libscratchcpp;
7+
8+
class GraphicsEffectMock : public IGraphicsEffect
9+
{
10+
public:
11+
MOCK_METHOD(std::string, name, (), (const, override));
12+
MOCK_METHOD(void, apply, (Rgb **, unsigned int, unsigned int, double), (const, override));
13+
};

0 commit comments

Comments
 (0)