Skip to content

Implement HQ pen #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/ProjectPlayer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ProjectScene {
property alias cloneLimit: loader.cloneLimit
property alias spriteFencing: loader.spriteFencing
property alias mute: loader.mute
property alias hqPen: projectPenLayer.hqPen
property bool showLoadingProgress: true
readonly property bool loading: priv.loading
readonly property int downloadedAssets: loader.downloadedAssets
Expand Down Expand Up @@ -141,9 +142,9 @@ ProjectScene {
engine: loader.engine
anchors.top: parent.top
anchors.left: parent.left
width: stageWidth
height: stageHeight
scale: stageScale
width: hqPen ? parent.width : stageWidth
height: hqPen ? parent.height : stageHeight
scale: hqPen ? 1 : stageScale
transformOrigin: Item.TopLeft
visible: !priv.loading
}
Expand Down
87 changes: 70 additions & 17 deletions src/penlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine)

if (m_engine && QOpenGLContext::currentContext()) {
m_projectPenLayers[m_engine] = this;
QOpenGLFramebufferObjectFormat fboFormat;
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
m_fbo = std::make_unique<QOpenGLFramebufferObject>(m_engine->stageWidth(), m_engine->stageHeight(), fboFormat);
Q_ASSERT(m_fbo->isValid());
m_texture = Texture(m_fbo->texture(), m_fbo->size());
createFbo();

if (!m_painter)
m_painter = std::make_unique<QNanoPainter>();
Expand Down Expand Up @@ -106,6 +102,21 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine)
emit engineChanged();
}

bool PenLayer::hqPen() const
{
return m_hqPen;
}

void PenLayer::setHqPen(bool newHqPen)
{
if (m_hqPen == newHqPen)
return;

m_hqPen = newHqPen;
createFbo();
emit hqPenChanged();
}

void scratchcpprender::PenLayer::clear()
{
if (!m_fbo)
Expand Down Expand Up @@ -138,16 +149,23 @@ void scratchcpprender::PenLayer::drawLine(const PenAttributes &penAttributes, do

m_painter->beginFrame(m_fbo->width(), m_fbo->height());

// Apply scale (HQ pen)
x0 *= m_scale;
y0 *= m_scale;
x1 *= m_scale;
y1 *= m_scale;

// Translate to Scratch coordinate system
double stageWidthHalf = m_engine->stageWidth() / 2;
double stageHeightHalf = m_engine->stageHeight() / 2;
double stageWidthHalf = width() / 2;
double stageHeightHalf = height() / 2;
x0 += stageWidthHalf;
y0 = stageHeightHalf - y0;
x1 += stageWidthHalf;
y1 = stageHeightHalf - y1;

// Set pen attributes
m_painter->setLineWidth(penAttributes.diameter);
const double diameter = penAttributes.diameter * m_scale;
m_painter->setLineWidth(diameter);
m_painter->setStrokeStyle(penAttributes.color);
m_painter->setFillStyle(penAttributes.color);
m_painter->setLineJoin(QNanoPainter::JOIN_ROUND);
Expand All @@ -156,11 +174,11 @@ void scratchcpprender::PenLayer::drawLine(const PenAttributes &penAttributes, do
m_painter->beginPath();

// Width 1 and 3 lines need to be offset by 0.5
const double offset = (std::fmod(std::max(4 - penAttributes.diameter, 0.0), 2)) / 2;
const double offset = (std::fmod(std::max(4 - diameter, 0.0), 2)) / 2;

// If the start and end coordinates are the same, draw a point, otherwise draw a line
if (x0 == x1 && y0 == y1) {
m_painter->circle(x0 + offset, y0 + offset, penAttributes.diameter / 2);
m_painter->circle(x0 + offset, y0 + offset, diameter / 2);
m_painter->fill();
} else {
m_painter->moveTo(x0 + offset, y0 + offset);
Expand Down Expand Up @@ -223,6 +241,9 @@ void PenLayer::stamp(IRenderedTarget *target)
} else
costume = target->stageModel()->stage()->currentCostume();

// Apply scale (HQ pen)
scale *= m_scale;

const double bitmapRes = costume->bitmapResolution();
const double centerX = costume->rotationCenterX() / bitmapRes;
const double centerY = costume->rotationCenterY() / bitmapRes;
Expand All @@ -234,8 +255,11 @@ void PenLayer::stamp(IRenderedTarget *target)

const double textureScale = texture.width() / static_cast<double>(target->costumeWidth());

// Apply scale (HQ pen)
x *= m_scale;
y *= m_scale;

// Translate the coordinates
// TODO: Apply scale (HQ pen)
x = std::floor(x + m_texture.width() / 2.0);
y = std::floor(-y + m_texture.height() / 2.0);

Expand Down Expand Up @@ -352,8 +376,11 @@ QRgb PenLayer::colorAtScratchPoint(double x, double y) const
const double width = m_texture.width();
const double height = m_texture.height();

// Apply scale (HQ pen)
x *= m_scale;
y *= m_scale;

// Translate the coordinates
// TODO: Apply scale
x = std::floor(x + width / 2.0);
y = std::floor(-y + height / 2.0);

Expand Down Expand Up @@ -393,7 +420,6 @@ const libscratchcpp::Rect &PenLayer::getBounds() const
}

for (const QPointF &point : points) {
// TODO: Apply scale
double x = point.x() - width / 2;
double y = -point.y() + height / 2;

Expand All @@ -410,10 +436,10 @@ const libscratchcpp::Rect &PenLayer::getBounds() const
bottom = y;
}

m_bounds.setLeft(left);
m_bounds.setTop(top);
m_bounds.setRight(right + 1);
m_bounds.setBottom(bottom - 1);
m_bounds.setLeft(left / m_scale);
m_bounds.setTop(top / m_scale);
m_bounds.setRight(right / m_scale + 1);
m_bounds.setBottom(bottom / m_scale - 1);
}

return m_bounds;
Expand All @@ -439,6 +465,33 @@ QNanoQuickItemPainter *PenLayer::createItemPainter() const
return new PenLayerPainter;
}

void PenLayer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
if (m_hqPen && newGeometry != oldGeometry)
createFbo();

QNanoQuickItem::geometryChange(newGeometry, oldGeometry);
}

void PenLayer::createFbo()
{
if (!QOpenGLContext::currentContext() || !m_engine)
return;

QOpenGLFramebufferObjectFormat fboFormat;
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);

QOpenGLFramebufferObject *newFbo = new QOpenGLFramebufferObject(width(), height(), fboFormat);
Q_ASSERT(newFbo->isValid());

if (m_fbo)
QOpenGLFramebufferObject::blitFramebuffer(newFbo, m_fbo.get());

m_fbo.reset(newFbo);
m_texture = Texture(m_fbo->texture(), m_fbo->size());
m_scale = width() / m_engine->stageWidth();
}

void PenLayer::updateTexture()
{
if (!m_fbo)
Expand Down
9 changes: 9 additions & 0 deletions src/penlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class PenLayer : public IPenLayer
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(libscratchcpp::IEngine *engine READ engine WRITE setEngine NOTIFY engineChanged)
Q_PROPERTY(bool hqPen READ hqPen WRITE setHqPen NOTIFY hqPenChanged)

public:
PenLayer(QNanoQuickItem *parent = nullptr);
Expand All @@ -30,6 +31,9 @@ class PenLayer : public IPenLayer
libscratchcpp::IEngine *engine() const override;
void setEngine(libscratchcpp::IEngine *newEngine) override;

bool hqPen() const;
void setHqPen(bool newHqPen);

void clear() override;
void drawPoint(const PenAttributes &penAttributes, double x, double y) override;
void drawLine(const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) override;
Expand All @@ -45,17 +49,22 @@ class PenLayer : public IPenLayer

signals:
void engineChanged();
void hqPenChanged();

protected:
QNanoQuickItemPainter *createItemPainter() const override;
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;

private:
void createFbo();
void updateTexture();

static std::unordered_map<libscratchcpp::IEngine *, IPenLayer *> m_projectPenLayers;
bool m_antialiasingEnabled = true;
libscratchcpp::IEngine *m_engine = nullptr;
bool m_hqPen = false;
std::unique_ptr<QOpenGLFramebufferObject> m_fbo;
double m_scale = 1;
std::unique_ptr<QNanoPainter> m_painter;
std::unique_ptr<QOpenGLExtraFunctions> m_glF;
Texture m_texture;
Expand Down
Binary file added test/lines_hq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading