Skip to content

Commit 2d80105

Browse files
committed
Add costume image API
1 parent 4f728b5 commit 2d80105

File tree

7 files changed

+444
-5
lines changed

7 files changed

+444
-5
lines changed

include/scratchcpp/costume.h

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

33
#pragma once
44

5+
#include "iimageformat.h"
56
#include "spimpl.h"
67

78
#include "asset.h"
@@ -28,8 +29,22 @@ class LIBSCRATCHCPP_EXPORT Costume : public Asset
2829
int rotationCenterY() const;
2930
void setRotationCenterY(int newRotationCenterY);
3031

32+
unsigned int width() const;
33+
unsigned int height() const;
34+
35+
double scale() const;
36+
void setScale(double scale);
37+
38+
bool mirrorHorizontally() const;
39+
void setMirrorHorizontally(bool mirror);
40+
41+
Rgb **bitmap() const;
42+
3143
Broadcast *broadcast();
3244

45+
protected:
46+
void processData(const char *data) override;
47+
3348
private:
3449
spimpl::unique_impl_ptr<CostumePrivate> impl;
3550
};

src/scratch/costume.cpp

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

33
#include <scratchcpp/costume.h>
4+
#include <scratchcpp/scratchconfiguration.h>
45

56
#include "costume_p.h"
67

@@ -11,6 +12,9 @@ Costume::Costume(const std::string &name, const std::string &id, const std::stri
1112
Asset(name, id, format),
1213
impl(spimpl::make_unique_impl<CostumePrivate>())
1314
{
15+
// NOTE: image is always initialized, so there's no need for null checks
16+
impl->image = ScratchConfiguration::createImageFormat(format);
17+
impl->updateImage();
1418
}
1519

1620
/*! Returns the reciprocal of the costume scaling factor for bitmap costumes. */
@@ -49,6 +53,56 @@ void Costume::setRotationCenterY(int newRotationCenterY)
4953
impl->rotationCenterY = newRotationCenterY;
5054
}
5155

56+
/*! Returns the costume width. */
57+
unsigned int Costume::width() const
58+
{
59+
return impl->image->width() * impl->scale;
60+
}
61+
62+
/*! Returns the costume height. */
63+
unsigned int Costume::height() const
64+
{
65+
return impl->image->height() * impl->scale;
66+
}
67+
68+
/*! Returns the image scale. */
69+
double Costume::scale() const
70+
{
71+
return impl->scale;
72+
}
73+
74+
/*! Sets the image scale (this is automatically set by the sprite). */
75+
void Costume::setScale(double scale)
76+
{
77+
if (impl->scale == scale)
78+
return;
79+
80+
impl->scale = scale;
81+
impl->updateImage();
82+
}
83+
84+
/*! Returns true if the costume image is mirrored horizontally. */
85+
bool Costume::mirrorHorizontally() const
86+
{
87+
return impl->mirrorHorizontally;
88+
}
89+
90+
/*! Sets whether the costume image is mirrored horizontally (this is automatically set by the sprite). */
91+
void Costume::setMirrorHorizontally(bool mirror)
92+
{
93+
if (impl->mirrorHorizontally == mirror)
94+
return;
95+
96+
impl->mirrorHorizontally = mirror;
97+
impl->updateImage();
98+
}
99+
100+
/*! Returns the bitmap of the costume (an array of pixel rows). */
101+
Rgb **Costume::bitmap() const
102+
{
103+
return impl->bitmap;
104+
}
105+
52106
/*!
53107
* Returns the Broadcast linked with this costume.
54108
* \note This is used by the "switch backdrop to and wait" block.
@@ -57,3 +111,10 @@ Broadcast *Costume::broadcast()
57111
{
58112
return &impl->broadcast;
59113
}
114+
115+
void Costume::processData(const char *data)
116+
{
117+
impl->image->setData(data);
118+
impl->freeImage();
119+
impl->updateImage();
120+
}

src/scratch/costume_p.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,52 @@ CostumePrivate::CostumePrivate() :
88
broadcast("", "")
99
{
1010
}
11+
12+
CostumePrivate::~CostumePrivate()
13+
{
14+
freeImage();
15+
}
16+
17+
void CostumePrivate::updateImage()
18+
{
19+
unsigned int scaledWidth = image->width() * scale;
20+
unsigned int scaledHeight = image->height() * scale;
21+
22+
if (scaledWidth == 0 || scaledHeight == 0) {
23+
freeImage();
24+
return;
25+
}
26+
27+
if (!bitmap || (scale != oldScale)) {
28+
freeImage();
29+
bitmap = static_cast<Rgb **>(malloc(sizeof(Rgb *) * scaledHeight));
30+
31+
for (unsigned int i = 0; i < scaledHeight; i++)
32+
bitmap[i] = static_cast<Rgb *>(malloc(sizeof(Rgb) * scaledWidth));
33+
34+
oldScale = scale;
35+
oldWidth = scaledWidth;
36+
oldHeight = scaledHeight;
37+
}
38+
39+
for (unsigned int i = 0; i < scaledHeight; i++) {
40+
for (unsigned int j = 0; j < scaledWidth; j++) {
41+
Rgb color = image->colorAt(mirrorHorizontally ? (scaledWidth - 1 - j) : j, i, scale);
42+
43+
// TODO: Apply graphics effects here
44+
45+
bitmap[i][j] = color;
46+
}
47+
}
48+
}
49+
50+
void CostumePrivate::freeImage()
51+
{
52+
if (bitmap) {
53+
for (unsigned int i = 0; i < oldHeight; i++)
54+
free(bitmap[i]);
55+
56+
free(bitmap);
57+
bitmap = nullptr;
58+
}
59+
}

src/scratch/costume_p.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,32 @@
33
#pragma once
44

55
#include <scratchcpp/broadcast.h>
6+
#include <scratchcpp/iimageformat.h>
67

78
namespace libscratchcpp
89
{
910

11+
class IImageFormat;
12+
1013
struct CostumePrivate
1114
{
1215
CostumePrivate();
1316
CostumePrivate(const CostumePrivate &) = delete;
17+
~CostumePrivate();
18+
19+
void updateImage();
20+
void freeImage();
1421

1522
double bitmapResolution = 1;
1623
int rotationCenterX = 0;
1724
int rotationCenterY = 0;
25+
std::shared_ptr<IImageFormat> image;
26+
Rgb **bitmap = nullptr;
27+
double oldWidth = 0;
28+
double oldHeight = 0;
29+
double oldScale = 1;
30+
double scale = 1;
31+
bool mirrorHorizontally = false;
1832
Broadcast broadcast;
1933
};
2034

test/assets/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ add_executable(
2323
target_link_libraries(
2424
costume_test
2525
GTest::gtest_main
26+
GTest::gmock_main
2627
scratchcpp
28+
scratchcpp_mocks
2729
)
2830

2931
gtest_discover_tests(costume_test)

0 commit comments

Comments
 (0)