Excellent! No, you’re not doing anything wrong. That was a nice find. The sample code was very helpful.
Matt
Excellent! No, you’re not doing anything wrong. That was a nice find. The sample code was very helpful.
Matt
It looks like I am getting a software image here though. Could it be the cause of the slowdown I am seeing compared to the JUCE 7 Direct2D branch?
Great job guys ![]()
Noticed small thing that if child components have been rotated (via an affine transform), i don’t get same behaviour with d2d renderer vs Software renderer, with d2d component get like ‘cliped’, not sure if there is something special to do maybe…
here quick vid ex of what i mean: i double clic to rotate child red comp, first software, then d2d. can post code in case made mistakes
https://youtu.be/DBk4D74fwXw
#pragma once
#include <JuceHeader.h>
class ChildComponent : public juce::Component
{
public:
ChildComponent(){};
~ChildComponent() {};
void paint(juce::Graphics& g){g.fillAll(juce::Colour{0xFFFF0000});}
};
class MainComponent : public juce::Component
{
public:
//==============================================================================
MainComponent()
{
addAndMakeVisible(child_comp);
setSize(600, 400);
};
~MainComponent() {};
//==============================================================================
void paint(juce::Graphics& g)
{
g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
}
void resized()
{
child_comp.setSize(600, 100);
};
bool is_rotated = false;
bool use_d2d = true;
void mouseDoubleClick(const juce::MouseEvent& e)
{
if (is_rotated)
{
child_comp.setSize(600, 100);
child_comp.setTopLeftPosition({ 0,0 });
child_comp.setTransform({});
}
else{ rotate_comp(child_comp, false, { 100,600 });}
is_rotated = !is_rotated;
}
void parentHierarchyChanged() override
{
if (use_d2d) return;
if (auto peer = getPeer())
{
int wanted_engine = 0; // software?
peer->setCurrentRenderingEngine(wanted_engine);
}
}
static void rotate_comp(juce::Component& component, bool clockWiseRotation, juce::Rectangle<int> verticalBounds)
{
auto angle = juce::MathConstants<float>::pi / 2.0f;
if (!clockWiseRotation) angle *= -1.0f;
component.setTransform({});
component.setSize(verticalBounds.getHeight(), verticalBounds.getWidth());
component.setCentrePosition(0, 0);
component.setTransform(juce::AffineTransform::rotation(angle).translated(verticalBounds.getCentreX(), verticalBounds.getCentreY()));
}
ChildComponent child_comp;
private:
//==============================================================================
// Your private member variables go here...
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
Thanks for the detailed report. Please go into JUCE\modules\juce_graphics\native, open juce_Direct2DGraphicsContext_windows.cpp. Find the setOrigin method and add a call to resetPendingClipList:
void Direct2DGraphicsContext::setOrigin (Point<int> o)
{
applyPendingClipList();
currentState->currentTransform.setOrigin (o);
resetPendingClipList(); // add this line
}
Matt
The renderer is presenting you with a software image so you can manipulate the bitmap directly. The image should be converted to a Direct2D GPU-stored bitmap once you release the bitmap data. We still need to investigate the performance issues.
Matt
Yeahh! fixed all my issues, you’re a master
thanks!
Very good to hear!
Matt
I’m still struggling to repro this. Is the assertion definitely present with the example code that you provided? If so, it might be helpful to see a stack trace at the point of the assertion. If you can work out what’s being drawn when the assertion fires, that might be helpful too.
And can we get a few warnings in choc silenced:
Hi @reuk,
It was indeed not present in my example because I had removed the buttons that select the renderer, which appear to be the cause of the problem! Sorry about that.
Here is an example code that does trigger the assertion:
#pragma once
#include <JuceHeader.h>
class MainComponent : public juce::Component {
public:
MainComponent() {
setSize(600, 200);
addAndMakeVisible(button);
setDirect2D(false);
button.setButtonText("button_text");
//button.setButtonText("different_button_text");
}
void resized() override {
button.setBounds(getLocalBounds().removeFromTop(30).removeFromLeft(getWidth() / 4));
}
private:
void setDirect2D(bool enable) {
juce::Timer::callAfterDelay(0, [this, enable] {
jassert(getPeer());
if (!getPeer()) return;
getPeer()->setCurrentRenderingEngine(enable ? 1 : 0);
});
}
juce::TextButton button;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
The problem appears when you resize the window and the text gets squashed to fit the button (I guess it could trigger with drawFittedText directly). It does appear when the text cannot be squashed anymore, and the height is supposed to be reduced. It only happens with certain strings.
Here are the effects when this happens, for different combinations that you can set in the example code:
Here is the call stack:
> NewProject.exe!juce::Path::applyTransform(const juce::AffineTransform & transform) Line 831 C++
NewProject.exe!juce::Typeface::getLayersForGlyph(juce::TypefaceMetricsKind kind, int glyphNumber, const juce::AffineTransform & transform, float __formal) Line 547 C++
NewProject.exe!<lambda_86464f4924c676fe45f23c05370598e3>::operator()<juce::RenderingHelpers::GlyphCache::Key>(const juce::RenderingHelpers::GlyphCache::Key & key) Line 209 C++
NewProject.exe!juce::LruCache<juce::RenderingHelpers::GlyphCache::Key,std::vector<juce::GlyphLayer,std::allocator<juce::GlyphLayer>>,128>::get<<lambda_86464f4924c676fe45f23c05370598e3>>(juce::RenderingHelpers::GlyphCache::Key key, juce::RenderingHelpers::GlyphCache::get::__l2::<lambda_86464f4924c676fe45f23c05370598e3> && fn) Line 59 C++
NewProject.exe!juce::RenderingHelpers::GlyphCache::get(const juce::Font & font, const int glyphNumber) Line 205 C++
NewProject.exe!juce::RenderingHelpers::StackBasedLowLevelGraphicsContext<juce::RenderingHelpers::SoftwareRendererSavedState>::drawGlyph::__l2::<lambda>() Line 2652 C++
NewProject.exe!juce::RenderingHelpers::StackBasedLowLevelGraphicsContext<juce::RenderingHelpers::SoftwareRendererSavedState>::drawGlyph(unsigned short i, const juce::AffineTransform & t) Line 2660 C++
NewProject.exe!juce::RenderingHelpers::StackBasedLowLevelGraphicsContext<juce::RenderingHelpers::SoftwareRendererSavedState>::drawGlyphs(juce::Span<unsigned short const ,-1> glyphs, juce::Span<juce::Point<float> const ,-1> positions, const juce::AffineTransform & t) Line 2621 C++
NewProject.exe!juce::GlyphArrangement::draw(const juce::Graphics & g, juce::AffineTransform transform) Line 635 C++
NewProject.exe!juce::`anonymous namespace'::ConfiguredArrangement::draw(const juce::Graphics & g) Line 63 C++
NewProject.exe!juce::Graphics::drawFittedText(const juce::String & text, juce::Rectangle<int> area, juce::Justification justification, int maximumNumberOfLines, float minimumHorizontalScale) Line 487 C++
NewProject.exe!juce::Graphics::drawFittedText(const juce::String & text, int x, int y, int width, int height, juce::Justification justification, int maximumNumberOfLines, float minimumHorizontalScale) Line 499 C++
NewProject.exe!juce::LookAndFeel_V2::drawButtonText(juce::Graphics & g, juce::TextButton & button, bool __formal, bool __formal) Line 289 C++
NewProject.exe!juce::TextButton::paintButton(juce::Graphics & g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) Line 64 C++
NewProject.exe!juce::Button::paint(juce::Graphics & g) Line 463 C++
NewProject.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g) Line 1640 C++
NewProject.exe!juce::Component::paintEntireComponent(juce::Graphics & g, bool ignoreAlphaLevel) Line 1754 C++
NewProject.exe!juce::Component::paintWithinParentContext(juce::Graphics & g) Line 1611 C++
NewProject.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g) Line 1682 C++
NewProject.exe!juce::Component::paintEntireComponent(juce::Graphics & g, bool ignoreAlphaLevel) Line 1754 C++
NewProject.exe!juce::Component::paintWithinParentContext(juce::Graphics & g) Line 1611 C++
NewProject.exe!juce::Component::paintComponentAndChildren(juce::Graphics & g) Line 1682 C++
NewProject.exe!juce::Component::paintEntireComponent(juce::Graphics & g, bool ignoreAlphaLevel) Line 1754 C++
NewProject.exe!juce::ComponentPeer::handlePaint(juce::LowLevelGraphicsContext & contextToPaintTo) Line 170 C++
NewProject.exe!juce::GDIContext::performPaint(HDC__ * dc, HRGN__ * rgn, int regionType, tagPAINTSTRUCT & paintStruct) Line 4570 C++
NewProject.exe!juce::GDIContext::handlePaintMessage() Line 4414 C++
NewProject.exe!juce::HWNDComponentPeer::handlePaintMessage() Line 2589 C++
NewProject.exe!juce::HWNDComponentPeer::peerWindowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3600 C++
NewProject.exe!juce::HWNDComponentPeer::windowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3540 C++
[External Code]
NewProject.exe!juce::HWNDComponentPeer::peerWindowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3985 C++
NewProject.exe!juce::HWNDComponentPeer::windowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3540 C++
[External Code]
NewProject.exe!juce::HWNDComponentPeer::peerWindowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3985 C++
NewProject.exe!juce::HWNDComponentPeer::windowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3540 C++
[External Code]
NewProject.exe!juce::InternalMessageQueue::dispatchNextMessage(bool returnIfNoPendingMessages) Line 155 C++
NewProject.exe!juce::detail::dispatchNextMessageOnSystemQueue(bool returnIfNoPendingMessages) Line 274 C++
NewProject.exe!juce::MessageManager::runDispatchLoop() Line 124 C++
NewProject.exe!juce::JUCEApplicationBase::main() Line 281 C++
NewProject.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal) Line 105 C++
[External Code]
So far I have no need to delete animators, but I just thought if there was a chain of different animations, it would be much more convenient to delete them with “clearAll()” and then add new ones with a new durationMs and “Easing”.
Btw. I did not found a way to change durationMs or Easing without constructing it from the beginning.
Despite this, I continue to be pleased with how smoothly the Component now works
I made a slide effect that moves the Component when the tab is pressed. If you change through setBounds() it will not be smooth (and logical, because there is int)
So I did this:
.withValueChangedCallback([this](auto value) {
...
myComponent.setTransform(juce::AffineTransform::translation(currentX_, 0.0F));
}
And it’s rock!
I’m noticing some buggy behavior with the webview2 component on Windows 11.
After navigating away from the initial page loaded with goToURL(), then calling goToURL again, the page will no longer display after having been hidden with setVisible.

class MainComponent : public juce::Component
{
public:
MainComponent()
{
webView.reset(new juce::WebBrowserComponent(
juce::WebBrowserComponent::Options().withBackend(
juce::WebBrowserComponent::Options::Backend::webview2)));
addAndMakeVisible(webView.get());
addAndMakeVisible(loadButton);
loadButton.onClick = [this] { webView->goToURL("https://github.com/MishaalRahmanGH/Ultra_HDR_Samples"); };
addAndMakeVisible(hideButton);
hideButton.onClick = [this] { webView->setVisible(!webView->isVisible()); };
webView->goToURL("https://github.com/MishaalRahmanGH/Ultra_HDR_Samples");
setSize(600, 400);
};
~MainComponent() override = default;
void resized() override
{
webView->setBounds(10, 45, getWidth() - 20, getHeight() - 55);
hideButton.setBounds(10, 10, 70, 25);
loadButton.setBounds(90, 10, 70, 25);
};
private:
std::unique_ptr<juce::WebBrowserComponent> webView;
juce::TextButton loadButton{ "Load", "Load" }, hideButton{ "Hide", "Hide" };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
As an aside, in case anyone is interested, the component is properly rendering Ultra HDR JPEG files on a HDR monitor. ![]()
Hey Guys, thanks for the exciting release.
What about MIDI 2.0 and/or sample accurate automation? As far as i know it was intended to be part of Juce 8…
Best,
Thomas
I don’t know about sample accurate automation,
But the JUCE team stated already in many occasions that MIDI 2.0 will be coming in a future release. One of the reasons is that as I understand it, some OSes aren’t fully implementing it and they prefer to introduce it when it’s available.
A few more warnings need to be silenced for Clang on Windows
BR: SVGs that draw fine in JUCE 7 don’t draw under JUCE 8 DirectX, instead they are completely blank.
In pathToGeometrySink where you have the jassert (figureStarted), if figureStarted is false, then you need to skip adding anything to the sink.
Code to reproduce:
What I see in JUCE 8:

What I see in JUCE 7:

Agreed! Thanks for catching this and for the sample code; most helpful.
Matt
This bug has enormous ramifications. For our Cloud app, we rendered each SVG icon into a bitmap for faster drawing, but for Nexus5, we’re drawing the SVG directly.
Now, whenever that icon comes into view (it’s a scrollable list), the entire UI doesn’t draw. All you see is the shadow that Windows draws behind the Window. The entire rectangle is completely empty. Not even a background color. You see the desktop.
The same SVG renders correctly everywhere, in all browsers, viewers, editors, etc.
We’re aware, there’s a fix on the way.
How do you guys deal with testing JUCE 8 with Projects that use Projucer? I cloned JUCE 8 into a separate folder and for projects using CMake it is super simple to switch them from the state I regularly use (a heavily patched JUCE 6) to JUCE 8 by just changing the path to the JUCE folder and running CMake again.
However with Projucer it’s really tricky as the path to JUCE and to the modules is global and stored in the machine’s Projucer settings. How do you deal with this? I really would like to easily switch single .jucer projects back and forth but can’t come up with a convenient way. I’m thinking about renaming the JUCE 8 Projucer into Projucer8 to get separate settings… but maybe that’s a recipe for disaster.
Update: yes, it is. Preferences file names are hardcoded in various places of Projucer ![]()