Skip to content

Commit 7944c4d

Browse files
committed
Drastically improve quality of JUCE rendered text and components by aligning image to pixel grid
1 parent fe6b43f commit 7944c4d

File tree

17 files changed

+81
-54
lines changed

17 files changed

+81
-54
lines changed

Libraries/nanovg

Source/Canvas.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ void Canvas::parentHierarchyChanged()
499499

500500
void Canvas::updateFramebuffers(NVGcontext* nvg)
501501
{
502-
auto const pixelScale = getRenderScale();
502+
auto const pixelScale = editor->getRenderScale();
503503
auto zoom = getValue<float>(zoomScale);
504504

505505
constexpr int resizerLogicalSize = 9;
@@ -642,7 +642,7 @@ void Canvas::performRender(NVGcontext* nvg, Rectangle<int> invalidRegion)
642642
constexpr auto pos = Point<int>(halfSize, halfSize);
643643

644644
auto scaledStrokeSize = zoom < 1.0f ? jmap(zoom, 1.0f, 0.25f, 1.5f, 4.0f) : 1.5f;
645-
if (zoom < 0.3f && getRenderScale() <= 1.0f)
645+
if (zoom < 0.3f && editor->getRenderScale() <= 1.0f)
646646
scaledStrokeSize = jmap(zoom, 0.3f, 0.25f, 4.0f, 8.0f);
647647

648648
if (bg) {
@@ -817,11 +817,6 @@ void Canvas::performRender(NVGcontext* nvg, Rectangle<int> invalidRegion)
817817
}
818818
}
819819

820-
float Canvas::getRenderScale() const
821-
{
822-
return editor->nvgSurface.getRenderScale();
823-
}
824-
825820
void Canvas::renderAllObjects(NVGcontext* nvg, Rectangle<int> const area)
826821
{
827822
for (auto* obj : objects) {

Source/Canvas.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,6 @@ class Canvas final : public Component
151151

152152
void zoomToFitAll();
153153

154-
float getRenderScale() const;
155-
156154
bool autoscroll(MouseEvent const& e);
157155

158156
// Multi-dragger functions

Source/NVGSurface.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,12 +307,29 @@ class NVGImage {
307307

308308
void renderJUCEComponent(NVGcontext* nvg, Component& component, float const scale)
309309
{
310-
Image componentImage = component.createComponentSnapshot(Rectangle<int>(0, 0, component.getWidth(), component.getHeight()), false, scale);
311-
if (componentImage.isNull())
312-
return;
313-
310+
nvgSave(nvg);
311+
nvgScale(nvg, 1.0f / scale, 1.0f / scale);
312+
313+
Point<float> offset;
314+
nvgTransformGetSubpixelOffset(nvg, &offset.x, &offset.y);
315+
316+
auto w = roundToInt (scale * (float) component.getWidth()) + 1;
317+
auto h = roundToInt (scale * (float) component.getHeight()) + 1;
318+
319+
Image componentImage (component.isOpaque() ? Image::RGB : Image::ARGB, w, h, true);
320+
{
321+
Graphics g (componentImage);
322+
g.addTransform(AffineTransform::translation(offset.x, offset.y));
323+
g.addTransform(AffineTransform::scale(scale, scale));
324+
component.paintEntireComponent (g, true);
325+
}
326+
314327
loadJUCEImage(nvg, componentImage);
315-
render(nvg, { 0, 0, component.getWidth(), component.getHeight() });
328+
329+
// Make sure image pixel grid aligns with physical pixels
330+
nvgTransformQuantize(nvg);
331+
render(nvg, { 0, 0, w, h });
332+
nvgRestore(nvg);
316333
}
317334

318335

Source/Object.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ void Object::render(NVGcontext* nvg)
12061206

12071207
if (newObjectEditor) {
12081208
nvgDrawRoundedRect(nvg, 0, 0, b.getWidth(), b.getHeight(), cnv->textObjectBackgroundCol, isSelected() ? cnv->selectedOutlineCol : cnv->objectOutlineCol, Corners::objectCornerRadius);
1209-
textEditorRenderer.renderJUCEComponent(nvg, *newObjectEditor, getValue<float>(cnv->zoomScale) * cnv->getRenderScale());
1209+
textEditorRenderer.renderJUCEComponent(nvg, *newObjectEditor, getValue<float>(cnv->zoomScale) * editor->getRenderScale());
12101210
}
12111211

12121212
// If autoconnect is about to happen, draw a fake inlet with a dotted outline
@@ -1295,7 +1295,7 @@ void Object::renderLabel(NVGcontext* nvg)
12951295
NVGScopedState scopedState(nvg);
12961296
nvgTranslate(nvg, label->getX(), label->getY());
12971297
if (label->isVisible()) {
1298-
label->renderLabel(nvg, cnv->getRenderScale() * 2.0f);
1298+
label->renderLabel(nvg, editor->getRenderScale() * 2.0f);
12991299
}
13001300
}
13011301
}

Source/Objects/CommentObject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class CommentObject final : public ObjectBase
4949
{
5050
if (!editor) {
5151
auto const textArea = border.subtractedFrom(getLocalBounds());
52-
textRenderer.renderText(nvg, textArea, getImageScale());
52+
textRenderer.renderText(nvg, textArea.toFloat(), getImageScale());
5353
} else {
5454
imageRenderer.renderJUCEComponent(nvg, *editor, getImageScale());
5555
}

Source/Objects/GraphOnParent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ class GraphOnParent final : public ObjectBase {
396396
} else {
397397
auto const text = getText();
398398
if (text != "graph" && text.isNotEmpty()) {
399-
textRenderer.renderText(nvg, Rectangle<int>(5, 0, getWidth() - 5, 16), getImageScale());
399+
textRenderer.renderText(nvg, Rectangle<float>(5, 0, getWidth() - 5, 16), getImageScale());
400400
}
401401
}
402402
}

Source/Objects/MessageObject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class MessageObject final : public ObjectBase
167167
if (editor) {
168168
imageRenderer.renderJUCEComponent(nvg, *editor, getImageScale());
169169
} else {
170-
textRenderer.renderText(nvg, border.subtractedFrom(getLocalBounds()), getImageScale());
170+
textRenderer.renderText(nvg, border.subtractedFrom(getLocalBounds()).toFloat(), getImageScale());
171171
}
172172
}
173173

Source/Objects/ObjectBase.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -498,18 +498,10 @@ float ObjectBase::getImageScale()
498498
}
499499
if (topLevel->editor->pluginMode) {
500500
auto const scale = std::sqrt(std::abs(topLevel->getTransform().getDeterminant()));
501-
return topLevel->getRenderScale() * std::max(1.0f, scale);
501+
return object->editor->getRenderScale() * std::max(1.0f, scale);
502502
}
503503

504-
// Use rng to gradually update them all as we zoom
505-
// For perfomance, it's not desirable (or necessary) to update them all at once
506-
// So we do it randomly, forcing a repaint if the different is larger than 0.15
507-
auto const randval = rand() % 4;
508-
auto const bestScale = topLevel->getRenderScale() * getValue<float>(topLevel->zoomScale);
509-
auto const newScale = topLevel->isZooming && randval != 0 && std::abs(bestScale - lastImageScale) < 0.15f ? lastImageScale : bestScale;
510-
lastImageScale = newScale; // TODO: getters shouldn't have side-effects!
511-
512-
return newScale;
504+
return object->editor->getRenderScale() * getValue<float>(topLevel->zoomScale);
513505
}
514506

515507
ObjectParameters ObjectBase::getParameters()

Source/Objects/ObjectBase.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ class ObjectBase : public Component
257257

258258
protected:
259259
String type;
260-
float lastImageScale = 2.0f;
261260
PropertyListener propertyListener;
262261

263262
NVGImage imageRenderer;

0 commit comments

Comments
 (0)