|
3 | 3 | #include <scratchcpp/iengine.h>
|
4 | 4 | #include <scratchcpp/costume.h>
|
5 | 5 | #include <scratchcpp/rect.h>
|
| 6 | +#include <scratchcpp/value.h> |
6 | 7 | #include <QtSvg/QSvgRenderer>
|
7 | 8 | #include <qnanopainter.h>
|
8 | 9 |
|
@@ -584,6 +585,7 @@ bool RenderedTarget::containsScratchPoint(double x, double y) const
|
584 | 585 |
|
585 | 586 | QRgb RenderedTarget::colorAtScratchPoint(double x, double y) const
|
586 | 587 | {
|
| 588 | + // NOTE: Only this target is processed! Use sampleColor3b() to get the final color. |
587 | 589 | if (!m_engine || !m_cpuTexture.isValid())
|
588 | 590 | return qRgba(0, 0, 0, 0);
|
589 | 591 |
|
@@ -638,6 +640,48 @@ bool RenderedTarget::touchingClones(const std::vector<libscratchcpp::Sprite *> &
|
638 | 640 | return false;
|
639 | 641 | }
|
640 | 642 |
|
| 643 | +bool RenderedTarget::touchingColor(const Value &color) const |
| 644 | +{ |
| 645 | + // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L775-L841 |
| 646 | + QRgb rgb = convertColor(color); |
| 647 | + |
| 648 | + std::vector<Target *> targets; |
| 649 | + getVisibleTargets(targets); |
| 650 | + |
| 651 | + QRectF myRect = touchingBounds(); |
| 652 | + std::vector<IRenderedTarget *> candidates; |
| 653 | + QRectF bounds = candidatesBounds(myRect, targets, candidates); |
| 654 | + |
| 655 | + if (colorMatches(rgb, qRgb(255, 255, 255))) { |
| 656 | + // The color we're checking for is the background color which spans the entire stage |
| 657 | + bounds = myRect; |
| 658 | + |
| 659 | + if (bounds.isEmpty()) |
| 660 | + return false; |
| 661 | + } else if (candidates.empty()) { |
| 662 | + // If not checking for the background color, we can return early if there are no candidate drawables |
| 663 | + return false; |
| 664 | + } |
| 665 | + |
| 666 | + QPointF point; |
| 667 | + |
| 668 | + // Loop through the points of the union |
| 669 | + for (int y = bounds.top(); y <= bounds.bottom(); y++) { |
| 670 | + for (int x = bounds.left(); x <= bounds.right(); x++) { |
| 671 | + if (this->containsScratchPoint(x, y)) { |
| 672 | + point.setX(x); |
| 673 | + point.setY(y); |
| 674 | + QRgb pixelColor = sampleColor3b(point, candidates); |
| 675 | + |
| 676 | + if (colorMatches(rgb, pixelColor)) |
| 677 | + return true; |
| 678 | + } |
| 679 | + } |
| 680 | + } |
| 681 | + |
| 682 | + return false; |
| 683 | +} |
| 684 | + |
641 | 685 | void RenderedTarget::calculatePos()
|
642 | 686 | {
|
643 | 687 | if (!m_skin || !m_costume || !m_engine)
|
@@ -810,6 +854,38 @@ CpuTextureManager *RenderedTarget::textureManager() const
|
810 | 854 | return m_textureManager.get();
|
811 | 855 | }
|
812 | 856 |
|
| 857 | +void RenderedTarget::getVisibleTargets(std::vector<Target *> &dst) const |
| 858 | +{ |
| 859 | + dst.clear(); |
| 860 | + |
| 861 | + if (!m_engine) |
| 862 | + return; |
| 863 | + |
| 864 | + const auto &targets = m_engine->targets(); |
| 865 | + |
| 866 | + for (auto target : targets) { |
| 867 | + Q_ASSERT(target); |
| 868 | + |
| 869 | + if (target->isStage()) |
| 870 | + dst.push_back(target.get()); |
| 871 | + else { |
| 872 | + Sprite *sprite = static_cast<Sprite *>(target.get()); |
| 873 | + |
| 874 | + if (sprite->visible()) |
| 875 | + dst.push_back(target.get()); |
| 876 | + |
| 877 | + const auto &clones = sprite->clones(); |
| 878 | + |
| 879 | + for (auto clone : clones) { |
| 880 | + if (clone->visible()) |
| 881 | + dst.push_back(clone.get()); |
| 882 | + } |
| 883 | + } |
| 884 | + } |
| 885 | + |
| 886 | + std::sort(dst.begin(), dst.end(), [](Target *t1, Target *t2) { return t1->layerOrder() > t2->layerOrder(); }); |
| 887 | +} |
| 888 | + |
813 | 889 | QRectF RenderedTarget::touchingBounds() const
|
814 | 890 | {
|
815 | 891 | // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L1330-L1350
|
@@ -926,6 +1002,62 @@ void RenderedTarget::clampRect(Rect &rect, double left, double right, double bot
|
926 | 1002 | rect.setTop(std::max(rect.top(), bottom));
|
927 | 1003 | }
|
928 | 1004 |
|
| 1005 | +QRgb RenderedTarget::convertColor(const libscratchcpp::Value &color) |
| 1006 | +{ |
| 1007 | + // TODO: Remove this after libscratchcpp starts converting colors (it still needs to be converted to RGB here) |
| 1008 | + std::string stringValue; |
| 1009 | + |
| 1010 | + if (color.isString()) |
| 1011 | + stringValue = color.toString(); |
| 1012 | + |
| 1013 | + if (!stringValue.empty() && stringValue[0] == '#') { |
| 1014 | + bool valid = false; |
| 1015 | + QColor color; |
| 1016 | + |
| 1017 | + if (stringValue.size() <= 7) // #RRGGBB |
| 1018 | + { |
| 1019 | + color = QColor::fromString(stringValue); |
| 1020 | + valid = color.isValid(); |
| 1021 | + } |
| 1022 | + |
| 1023 | + if (!valid) |
| 1024 | + color = Qt::black; |
| 1025 | + |
| 1026 | + return color.rgb(); |
| 1027 | + |
| 1028 | + } else |
| 1029 | + return QColor::fromRgba(static_cast<QRgb>(color.toLong())).rgb(); |
| 1030 | +} |
| 1031 | + |
| 1032 | +bool RenderedTarget::colorMatches(QRgb a, QRgb b) |
| 1033 | +{ |
| 1034 | + // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L77-L81 |
| 1035 | + return (qRed(a) & 0b11111000) == (qRed(b) & 0b11111000) && (qGreen(a) & 0b11111000) == (qGreen(b) & 0b11111000) && (qBlue(a) & 0b11110000) == (qBlue(b) & 0b11110000); |
| 1036 | +} |
| 1037 | + |
| 1038 | +QRgb RenderedTarget::sampleColor3b(const QPointF &point, const std::vector<IRenderedTarget *> &targets) |
| 1039 | +{ |
| 1040 | + // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L1966-L1990 |
| 1041 | + double blendAlpha = 1; |
| 1042 | + QRgb blendColor; |
| 1043 | + int r = 0, g = 0, b = 0; |
| 1044 | + |
| 1045 | + for (int i = 0; blendAlpha != 0 && i < targets.size(); i++) { |
| 1046 | + Q_ASSERT(targets[i]); |
| 1047 | + blendColor = targets[i]->colorAtScratchPoint(point.x(), point.y()); |
| 1048 | + |
| 1049 | + r += qRed(blendColor) * blendAlpha; |
| 1050 | + g += qGreen(blendColor) * blendAlpha; |
| 1051 | + b += qBlue(blendColor) * blendAlpha; |
| 1052 | + blendAlpha *= (1.0 - (qAlpha(blendColor) / 255.0)); |
| 1053 | + } |
| 1054 | + |
| 1055 | + r += blendAlpha * 255; |
| 1056 | + g += blendAlpha * 255; |
| 1057 | + b += blendAlpha * 255; |
| 1058 | + return qRgb(r, g, b); |
| 1059 | +} |
| 1060 | + |
929 | 1061 | bool RenderedTarget::mirrorHorizontally() const
|
930 | 1062 | {
|
931 | 1063 | return m_mirrorHorizontally;
|
|
0 commit comments