Skip to content

Commit 2b35a88

Browse files
committed
Implement legacy color mixing
1 parent 2107459 commit 2b35a88

File tree

4 files changed

+60
-22
lines changed

4 files changed

+60
-22
lines changed

src/blocks/penblocks.cpp

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ unsigned int PenBlocks::changePenHueBy(libscratchcpp::VirtualMachine *vm)
134134

135135
if (model) {
136136
const double colorChange = vm->getInput(0, 1)->toDouble() / 2;
137-
setOrChangeColorParam(model->penAttributes(), ColorParam::COLOR, colorChange, true);
137+
setOrChangeColorParam(ColorParam::COLOR, colorChange, model->penState(), true, true);
138138
}
139139

140140
return 1;
@@ -147,7 +147,8 @@ unsigned int PenBlocks::setPenColorToColor(libscratchcpp::VirtualMachine *vm)
147147
if (model) {
148148
const Value *value = vm->getInput(0, 1);
149149
std::string stringValue;
150-
PenAttributes &attributes = model->penAttributes();
150+
PenState &penState = model->penState();
151+
QColor newColor;
151152

152153
if (value->isString())
153154
stringValue = value->toString();
@@ -157,19 +158,24 @@ unsigned int PenBlocks::setPenColorToColor(libscratchcpp::VirtualMachine *vm)
157158

158159
if (stringValue.size() <= 7) // #RRGGBB
159160
{
160-
attributes.color = QColor::fromString(stringValue);
161-
valid = attributes.color.isValid();
161+
newColor = QColor::fromString(stringValue);
162+
valid = newColor.isValid();
162163
}
163164

164165
if (!valid)
165-
attributes.color = QColor(0, 0, 0);
166+
newColor = Qt::black;
166167

167168
} else {
168-
attributes.color = QColor::fromRgba(static_cast<QRgb>(value->toLong()));
169+
newColor = QColor::fromRgba(static_cast<QRgb>(value->toLong()));
169170

170-
if (attributes.color.alpha() == 0)
171-
attributes.color.setAlpha(255);
171+
if (newColor.alpha() == 0)
172+
newColor.setAlpha(255);
172173
}
174+
175+
penState.setColor(newColor);
176+
177+
// Set the legacy "shade" value the same way Scratch 2 did.
178+
penState.shade = penState.brightness / 2;
173179
}
174180

175181
return 1;
@@ -187,22 +193,31 @@ SpriteModel *PenBlocks::getSpriteModel(libscratchcpp::VirtualMachine *vm)
187193
return model;
188194
}
189195

190-
void PenBlocks::setOrChangeColorParam(PenAttributes &penAttributes, ColorParam param, double value, bool change)
196+
void PenBlocks::setOrChangeColorParam(ColorParam param, double value, PenState &penState, bool change, bool legacy)
191197
{
192-
PenState penState;
193-
penState.setColor(penAttributes.color);
194-
195198
switch (param) {
196199
case ColorParam::COLOR:
197200
penState.color = wrapClamp(value + (change ? penState.color : 0), 0, 100);
198201
break;
199202
}
200203

201-
const int h = std::round(std::fmod(penState.color * 360 / 100, 360.0));
202-
const int s = std::round(penState.saturation * 2.55);
203-
const int v = std::round(penState.brightness * 2.55);
204-
const int a = std::round((100 - penState.transparency) * 2.55);
205-
penAttributes.color = QColor::fromHsv(h, s, v, a);
204+
penState.updateColor();
205+
206+
if (legacy) {
207+
// https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/extensions/scratch3_pen/index.js#L750-L767
208+
// Create the new color in RGB using the scratch 2 "shade" model
209+
QColor rgb = penState.penAttributes.color.toRgb();
210+
const double shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade;
211+
212+
if (shade < 50)
213+
rgb = mixRgb(QColor(Qt::black), rgb, (10 + shade) / 60);
214+
else
215+
rgb = mixRgb(rgb, QColor(Qt::white), (shade - 50) / 60);
216+
217+
// Update the pen state according to new color
218+
const QColor hsv = rgb.toHsv();
219+
penState.setColor(hsv);
220+
}
206221
}
207222

208223
double PenBlocks::wrapClamp(double n, double min, double max)
@@ -211,3 +226,15 @@ double PenBlocks::wrapClamp(double n, double min, double max)
211226
const double range = max - min + 1;
212227
return n - (std::floor((n - min) / range) * range);
213228
}
229+
230+
QColor PenBlocks::mixRgb(const QColor &rgb0, const QColor &rgb1, double fraction1)
231+
{
232+
// https://github.com/scratchfoundation/scratch-vm/blob/a4f095db5e03e072ba222fe721eeeb543c9b9c15/src/util/color.js#L192-L201
233+
if (fraction1 <= 0)
234+
return rgb0;
235+
if (fraction1 >= 1)
236+
return rgb1;
237+
238+
const double fraction0 = 1 - fraction1;
239+
return QColor((fraction0 * rgb0.red()) + (fraction1 * rgb1.red()), (fraction0 * rgb0.green()) + (fraction1 * rgb1.green()), (fraction0 * rgb0.blue()) + (fraction1 * rgb1.blue()));
240+
}

src/blocks/penblocks.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
#pragma once
44

5-
#include <QColor>
65
#include <scratchcpp/iblocksection.h>
76

7+
class QColor;
8+
89
namespace scratchcpprender
910
{
1011

1112
class SpriteModel;
12-
class PenAttributes;
13+
class PenState;
1314

1415
class PenBlocks : public libscratchcpp::IBlockSection
1516
{
@@ -48,8 +49,9 @@ class PenBlocks : public libscratchcpp::IBlockSection
4849
};
4950

5051
static SpriteModel *getSpriteModel(libscratchcpp::VirtualMachine *vm);
51-
static void setOrChangeColorParam(PenAttributes &penAttributes, ColorParam param, double value, bool change);
52+
static void setOrChangeColorParam(ColorParam param, double value, PenState &penState, bool change, bool legacy = false);
5253
static double wrapClamp(double n, double min, double max);
54+
static QColor mixRgb(const QColor &rgb0, const QColor &rgb1, double fraction1);
5355
};
5456

5557
} // namespace scratchcpprender

src/penstate.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,18 @@ struct PenState
2626
this->saturation = hsvColor.saturationF() * 100;
2727
this->brightness = hsvColor.valueF() * 100;
2828
this->transparency = 100 * (1 - hsvColor.alphaF());
29+
30+
penAttributes.color = color;
2931
}
3032

3133
void updateColor()
3234
{
33-
const int h = std::round(std::fmod(color * 360 / 100, 360.0));
35+
int h = std::round(color * 360 / 100);
36+
h %= 360;
37+
38+
if (h < 0)
39+
h += 360;
40+
3441
const int s = std::round(saturation * 2.55);
3542
const int v = std::round(brightness * 2.55);
3643
const int a = std::round((100 - transparency) * 2.55);

test/blocks/pen_blocks_test.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,9 @@ TEST_F(PenBlocksTest, ChangePenHueByImpl)
467467
static Value constValues[] = { 125.7, -114.09 };
468468

469469
SpriteModel model;
470-
model.penAttributes().color.setAlpha(150);
470+
QColor color = model.penAttributes().color;
471+
color.setAlpha(150);
472+
model.penState().setColor(color);
471473
Sprite sprite;
472474
sprite.setInterface(&model);
473475

0 commit comments

Comments
 (0)