Skip to content

Commit 89e11d3

Browse files
committed
Add toRgba() method to Value
1 parent acff91c commit 89e11d3

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

include/scratchcpp/value.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ class LIBSCRATCHCPP_EXPORT Value
139139
return ret;
140140
}
141141

142+
/*! Converts the value to an RGBA quadruplet. */
143+
Rgb toRgba() { return value_toRgba(&m_data); }
144+
142145
/*! Adds the given value to the value. */
143146
void add(const Value &v) { value_add(&m_data, &v.m_data, &m_data); }
144147

include/scratchcpp/value_functions.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,45 @@
55
namespace libscratchcpp
66
{
77

8+
/*! A typedef for unsigned int. Holds the RGBA values. */
9+
using Rgb = unsigned int;
10+
11+
/*! Returns the red component of the quadruplet rgb. */
12+
inline constexpr int red(Rgb rgb)
13+
{
14+
return ((rgb >> 16) & 0xff);
15+
}
16+
17+
/*! Returns the green component of the quadruplet rgb. */
18+
inline constexpr int green(Rgb rgb)
19+
{
20+
return ((rgb >> 8) & 0xff);
21+
}
22+
23+
/*! Returns the blue component of the quadruplet rgb. */
24+
inline constexpr int blue(Rgb rgb)
25+
{
26+
return (rgb & 0xff);
27+
}
28+
29+
/*! Returns the alpha component of the quadruplet rgb. */
30+
inline constexpr int alpha(Rgb rgb)
31+
{
32+
return rgb >> 24;
33+
}
34+
35+
/*! Creates an RGB triplet from the given color components. */
36+
inline constexpr Rgb rgb(int r, int g, int b)
37+
{
38+
return (0xffu << 24) | ((r & 0xffu) << 16) | ((g & 0xffu) << 8) | (b & 0xffu);
39+
}
40+
41+
/*! Creates an RGBA quadruplet from the given color components. */
42+
inline constexpr Rgb rgba(int r, int g, int b, int a)
43+
{
44+
return ((a & 0xffu) << 24) | ((r & 0xffu) << 16) | ((g & 0xffu) << 8) | (b & 0xffu);
45+
}
46+
847
extern "C"
948
{
1049
LIBSCRATCHCPP_EXPORT void value_free(ValueData *v);
@@ -33,6 +72,7 @@ extern "C"
3372
LIBSCRATCHCPP_EXPORT void value_toString(const ValueData *v, std::string *dst);
3473
LIBSCRATCHCPP_EXPORT char *value_toCString(const ValueData *v);
3574
LIBSCRATCHCPP_EXPORT void value_toUtf16(const ValueData *v, std::u16string *dst);
75+
LIBSCRATCHCPP_EXPORT Rgb value_toRgba(const ValueData *v);
3676

3777
LIBSCRATCHCPP_EXPORT bool value_doubleIsInt(double v);
3878

src/scratch/value_functions.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,63 @@ extern "C"
288288
dst->assign(utf8::utf8to16(s));
289289
}
290290

291+
/*! Returns the RGBA quadruplet from the given color value. */
292+
Rgb value_toRgba(const ValueData *v)
293+
{
294+
// https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L92-L103
295+
char *string = nullptr;
296+
size_t stringLen = 0;
297+
298+
if (v->type == ValueType::Number)
299+
return v->numberValue;
300+
else if (v->type == ValueType::String) {
301+
string = value_toCString(v);
302+
stringLen = strlen(string);
303+
} else if (v->type == ValueType::Bool)
304+
return v->boolValue;
305+
306+
if (stringLen > 0 && string[0] == '#') {
307+
// https://github.com/scratchfoundation/scratch-vm/blob/a4f095db5e03e072ba222fe721eeeb543c9b9c15/src/util/color.js#L60-L69
308+
// (this implementation avoids regex)
309+
310+
// Handle shorthand hex (e.g., "abc" -> "aabbcc")
311+
char expandedHex[7] = { 0 };
312+
char *ptr;
313+
314+
if (stringLen == 4) {
315+
expandedHex[0] = string[1];
316+
expandedHex[1] = string[1];
317+
expandedHex[2] = string[2];
318+
expandedHex[3] = string[2];
319+
expandedHex[4] = string[3];
320+
expandedHex[5] = string[3];
321+
ptr = expandedHex;
322+
} else if (stringLen == 7)
323+
ptr = string + 1; // skip '#'
324+
else {
325+
free(string);
326+
return rgb(0, 0, 0);
327+
}
328+
329+
// Convert hex components to integers
330+
int r, g, b;
331+
332+
if (std::sscanf(ptr, "%2x%2x%2x", &r, &g, &b) == 3) {
333+
free(string);
334+
return rgb(r, g, b);
335+
}
336+
337+
free(string);
338+
} else if (stringLen > 0) {
339+
const double ret = value_stringToDouble(string);
340+
free(string);
341+
return ret;
342+
} else if (string)
343+
free(string);
344+
345+
return rgb(0, 0, 0);
346+
}
347+
291348
/*! Returns true if the given number represents a round integer. */
292349
bool value_doubleIsInt(double v)
293350
{

test/scratch_classes/value_test.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,60 @@ TEST(ValueTest, ToString)
16161616
free(s);
16171617
}
16181618

1619+
TEST(ValueTest, ToRgba)
1620+
{
1621+
Value v = "#ff0000";
1622+
ASSERT_EQ(v.toRgba(), rgba(255, 0, 0, 255));
1623+
1624+
v = "#00ff00";
1625+
ASSERT_EQ(v.toRgba(), rgba(0, 255, 0, 255));
1626+
1627+
v = "#0000ff";
1628+
ASSERT_EQ(v.toRgba(), rgba(0, 0, 255, 255));
1629+
1630+
v = "#800000";
1631+
ASSERT_EQ(v.toRgba(), rgba(128, 0, 0, 255));
1632+
1633+
v = "#008000";
1634+
ASSERT_EQ(v.toRgba(), rgba(0, 128, 0, 255));
1635+
1636+
v = "#000080";
1637+
ASSERT_EQ(v.toRgba(), rgba(0, 0, 128, 255));
1638+
1639+
v = "#FF0080FF";
1640+
ASSERT_EQ(v.toRgba(), rgba(0, 0, 0, 255));
1641+
1642+
v = "#abc";
1643+
ASSERT_EQ(v.toRgba(), rgba(170, 187, 204, 255));
1644+
1645+
v = "00ff00";
1646+
ASSERT_EQ(v.toRgba(), 0);
1647+
1648+
v = "123";
1649+
ASSERT_EQ(v.toRgba(), 123);
1650+
1651+
v = "#12345";
1652+
ASSERT_EQ(v.toRgba(), rgba(0, 0, 0, 255));
1653+
1654+
v = "";
1655+
ASSERT_EQ(v.toRgba(), rgba(0, 0, 0, 255));
1656+
1657+
v = "#gggggg";
1658+
ASSERT_EQ(v.toRgba(), rgba(0, 0, 0, 255));
1659+
1660+
v = 2164195583;
1661+
ASSERT_EQ(v.toRgba(), 2164195583);
1662+
1663+
v = 0;
1664+
ASSERT_EQ(v.toRgba(), 0);
1665+
1666+
v = true;
1667+
ASSERT_EQ(v.toRgba(), 1);
1668+
1669+
v = false;
1670+
ASSERT_EQ(v.toRgba(), 0);
1671+
}
1672+
16191673
TEST(ValueTest, AddFunction)
16201674
{
16211675
Value v = 50;
@@ -3121,3 +3175,39 @@ TEST(ValueTest, StringToBool)
31213175
ASSERT_TRUE(value_stringToBool("0b100112001"));
31223176
ASSERT_TRUE(value_stringToBool("0b10011001.1"));
31233177
}
3178+
3179+
TEST(ValueTest, RedComponent)
3180+
{
3181+
Rgb color = rgba(255, 100, 50, 255);
3182+
ASSERT_EQ(red(color), 255);
3183+
}
3184+
3185+
TEST(ValueTest, GreenComponent)
3186+
{
3187+
Rgb color = rgba(255, 100, 50, 255);
3188+
ASSERT_EQ(green(color), 100);
3189+
}
3190+
3191+
TEST(ValueTest, BlueComponent)
3192+
{
3193+
Rgb color = rgba(255, 100, 50, 255);
3194+
ASSERT_EQ(blue(color), 50);
3195+
}
3196+
3197+
TEST(ValueTest, AlphaComponent)
3198+
{
3199+
Rgb color = rgba(255, 100, 50, 128);
3200+
ASSERT_EQ(alpha(color), 128);
3201+
}
3202+
3203+
TEST(ValueTest, RGB)
3204+
{
3205+
Rgb color = rgb(255, 100, 50);
3206+
ASSERT_EQ(color, 0xFFFF6432);
3207+
}
3208+
3209+
TEST(ValueTest, RGBA)
3210+
{
3211+
Rgb color = rgba(255, 100, 50, 128);
3212+
ASSERT_EQ(color, 0x80FF6432);
3213+
}

0 commit comments

Comments
 (0)