Skip to content

Commit b2f15f7

Browse files
authored
Merge pull request #308 from scratchcpp/graphics_effects_api
Add API for graphics effects
2 parents 5c35857 + 40ac744 commit b2f15f7

17 files changed

+331
-8
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ target_sources(scratchcpp
5151
include/scratchcpp/iimageformat.h
5252
include/scratchcpp/iimageformatfactory.h
5353
include/scratchcpp/rect.h
54+
include/scratchcpp/igraphicseffect.h
5455
)
5556

5657
add_library(zip SHARED

docs/Graphics effects.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
\page graphicsEffects Graphics effects
2+
3+
libscratchcpp provides API for implementing custom graphics effects.
4+
No effects are currently included with libscratchcpp, but that is planned to change.
5+
6+
In case you've implemented some of the effects yourself, feel free to make pull requests!
7+
These are the issues (tasks) for the default graphics effects:
8+
9+
- Color: https://github.com/scratchcpp/libscratchcpp/issues/283
10+
- Fisheye: https://github.com/scratchcpp/libscratchcpp/issues/284
11+
- Whirl: https://github.com/scratchcpp/libscratchcpp/issues/285
12+
- Pixelate: https://github.com/scratchcpp/libscratchcpp/issues/286
13+
- Mosaic: https://github.com/scratchcpp/libscratchcpp/issues/287
14+
- Brightness: https://github.com/scratchcpp/libscratchcpp/issues/288
15+
- Ghost: https://github.com/scratchcpp/libscratchcpp/issues/289
16+
17+
# Implementing a graphics effect
18+
To implement a graphics effect that libscratchcpp doesn't support,
19+
subclass \link libscratchcpp::IGraphicsEffect IGraphicsEffect \endlink
20+
and override all of the methods.
21+
22+
It's recommended to use the libscratchcpp namespace like this in your class:
23+
```cpp
24+
using namespace libscratchcpp;
25+
```
26+
27+
The `name()` method should return the name of the effect which is
28+
used by the set and change effect blocks, for example `ghost` or `fisheye`.
29+
30+
The `apply()` method is used to apply the effect on a bitmap. The bitmap
31+
can be accessed using the `bitmap` parameter and is writable.
32+
33+
# Registering the graphics effect
34+
To register the graphics effect, use \link libscratchcpp::ScratchConfiguration::registerGraphicsEffect() ScratchConfiguration::registerGraphicsEffect() \endlink.
35+
36+
```cpp
37+
libscratchcpp::ScratchConfiguration::registerGraphicsEffect(std::make_shared<MyGraphicsEffect>());
38+
```

include/scratchcpp/costume.h

+4
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,9 @@ class LIBSCRATCHCPP_EXPORT Costume : public Asset
4041

4142
Rgb **bitmap() const;
4243

44+
double graphicsEffectValue(IGraphicsEffect *effect) const;
45+
void setGraphicsEffectValue(IGraphicsEffect *effect, double value);
46+
4347
Broadcast *broadcast();
4448

4549
protected:

include/scratchcpp/igraphicseffect.h

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <string>
6+
7+
#include "iimageformat.h"
8+
9+
namespace libscratchcpp
10+
{
11+
12+
/*! \brief The IGraphicsEffects class is an interface for implementing custom graphics effects. */
13+
class LIBSCRATCHCPP_EXPORT IGraphicsEffect
14+
{
15+
public:
16+
virtual ~IGraphicsEffect() { }
17+
18+
/*! Returns the name of the graphics effect. */
19+
virtual std::string name() const = 0;
20+
21+
/*! Applies the effect on the given bitmap. */
22+
virtual void apply(Rgb **bitmap, unsigned int width, unsigned int height, double value) const = 0;
23+
};
24+
25+
} // namespace libscratchcpp

include/scratchcpp/scratchconfiguration.h

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace libscratchcpp
1313
class IExtension;
1414
class IImageFormat;
1515
class IImageFormatFactory;
16+
class IGraphicsEffect;
1617
class ScratchConfigurationPrivate;
1718

1819
/*! \brief The ScratchConfiguration class provides methods for adding custom extensions. */
@@ -41,6 +42,10 @@ class LIBSCRATCHCPP_EXPORT ScratchConfiguration
4142
static void removeImageFormat(const std::string &name);
4243
static std::shared_ptr<IImageFormat> createImageFormat(const std::string &name);
4344

45+
static void registerGraphicsEffect(std::shared_ptr<IGraphicsEffect> effect);
46+
static void removeGraphicsEffect(const std::string &name);
47+
static IGraphicsEffect *getGraphicsEffect(const std::string &name);
48+
4449
private:
4550
static const std::vector<std::shared_ptr<IExtension>> getExtensions();
4651

include/scratchcpp/sprite.h

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace libscratchcpp
99

1010
class ISpriteHandler;
1111
class Rect;
12+
class IGraphicsEffect;
1213
class SpritePrivate;
1314

1415
/*! \brief The Sprite class represents a Scratch sprite. */
@@ -65,6 +66,9 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
6566

6667
Rect boundingRect() const;
6768

69+
double graphicsEffectValue(IGraphicsEffect *effect) const;
70+
void setGraphicsEffectValue(IGraphicsEffect *effect, double value);
71+
6872
private:
6973
Target *dataSource() const override;
7074
void setXY(double x, double y);

src/scratch/costume.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,32 @@ Rgb **Costume::bitmap() const
107107
return impl->bitmap;
108108
}
109109

110+
/*! Returns the value of the given graphics effect. */
111+
double Costume::graphicsEffectValue(IGraphicsEffect *effect) const
112+
{
113+
auto it = impl->graphicsEffects.find(effect);
114+
115+
if (it == impl->graphicsEffects.cend())
116+
return 0;
117+
else
118+
return it->second;
119+
}
120+
121+
/*! Sets the value of the given graphics effect (this is automatically set by the sprite). */
122+
void Costume::setGraphicsEffectValue(IGraphicsEffect *effect, double value)
123+
{
124+
auto it = impl->graphicsEffects.find(effect);
125+
bool update = ((it == impl->graphicsEffects.cend()) || (it->second != value));
126+
127+
if (value == 0)
128+
impl->graphicsEffects.erase(effect);
129+
else
130+
impl->graphicsEffects[effect] = value;
131+
132+
if (update)
133+
impl->updateImage();
134+
}
135+
110136
/*!
111137
* Returns the Broadcast linked with this costume.
112138
* \note This is used by the "switch backdrop to and wait" block.

src/scratch/costume_p.cpp

+9-7
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

+3-1
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

src/scratch/sprite.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ void Sprite::setCurrentCostume(int newCostume)
212212
if (costume) {
213213
costume->setScale(impl->size / 100);
214214
costume->setMirrorHorizontally(impl->rotationStyle == RotationStyle::LeftRight);
215+
216+
for (const auto &[effect, value] : impl->graphicsEffects)
217+
costume->setGraphicsEffectValue(effect, value);
215218
}
216219

217220
Target::setCurrentCostume(newCostume);
@@ -309,6 +312,29 @@ Rect Sprite::boundingRect() const
309312
return ret;
310313
}
311314

315+
/*! Returns the value of the given graphics effect. */
316+
double Sprite::graphicsEffectValue(IGraphicsEffect *effect) const
317+
{
318+
auto it = impl->graphicsEffects.find(effect);
319+
320+
if (it == impl->graphicsEffects.cend())
321+
return 0;
322+
else
323+
return it->second;
324+
}
325+
326+
/*! Sets the value of the given graphics effect. */
327+
void Sprite::setGraphicsEffectValue(IGraphicsEffect *effect, double value)
328+
{
329+
impl->graphicsEffects[effect] = value;
330+
331+
// TODO: Make currentCostume() return the costume (not index)
332+
auto costume = costumeAt(currentCostume() - 1);
333+
334+
if (costume)
335+
costume->setGraphicsEffectValue(effect, value);
336+
}
337+
312338
Target *Sprite::dataSource() const
313339
{
314340
return impl->cloneRoot;

src/scratch/sprite_p.h

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#pragma once
44

55
#include <scratchcpp/sprite.h>
6+
#include <unordered_map>
67

78
namespace libscratchcpp
89
{
@@ -32,6 +33,7 @@ struct SpritePrivate
3233
double direction = 90;
3334
bool draggable = false;
3435
Sprite::RotationStyle rotationStyle = Sprite::RotationStyle::AllAround;
36+
std::unordered_map<IGraphicsEffect *, double> graphicsEffects;
3537
};
3638

3739
} // namespace libscratchcpp

src/scratchconfiguration.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <scratchcpp/scratchconfiguration.h>
44
#include <scratchcpp/iimageformatfactory.h>
5+
#include <scratchcpp/igraphicseffect.h>
56
#include <iostream>
67

78
#include "scratchconfiguration_p.h"
@@ -50,6 +51,32 @@ std::shared_ptr<IImageFormat> ScratchConfiguration::createImageFormat(const std:
5051
return it->second->createInstance();
5152
}
5253

54+
/*! Registers the given graphics effect. */
55+
void ScratchConfiguration::registerGraphicsEffect(std::shared_ptr<IGraphicsEffect> effect)
56+
{
57+
if (!effect)
58+
return;
59+
60+
impl->graphicsEffects[effect->name()] = effect;
61+
}
62+
63+
/*! Removes the given graphics effect. */
64+
void ScratchConfiguration::removeGraphicsEffect(const std::string &name)
65+
{
66+
impl->graphicsEffects.erase(name);
67+
}
68+
69+
/*! Returns the graphics effect with the given name, or nullptr if it isn't registered. */
70+
IGraphicsEffect *ScratchConfiguration::getGraphicsEffect(const std::string &name)
71+
{
72+
auto it = impl->graphicsEffects.find(name);
73+
74+
if (it == impl->graphicsEffects.cend())
75+
return nullptr;
76+
else
77+
return it->second.get();
78+
}
79+
5380
const std::vector<std::shared_ptr<IExtension>> ScratchConfiguration::getExtensions()
5481
{
5582
return impl->extensions;

src/scratchconfiguration_p.h

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace libscratchcpp
1414

1515
class IExtension;
1616
class IImageFormatFactory;
17+
class IGraphicsEffect;
1718

1819
struct ScratchConfigurationPrivate
1920
{
@@ -22,6 +23,7 @@ struct ScratchConfigurationPrivate
2223

2324
std::vector<std::shared_ptr<IExtension>> extensions = { std::make_shared<StandardBlocks>() };
2425
std::unordered_map<std::string, std::shared_ptr<IImageFormatFactory>> imageFormats;
26+
std::unordered_map<std::string, std::shared_ptr<IGraphicsEffect>> graphicsEffects;
2527
};
2628

2729
} // namespace libscratchcpp

0 commit comments

Comments
 (0)