What's the best method for

Hi,

I’m trying to perform a simple zoom pan on a picture.
Let’s say I have a picture 640x800, I’m trying to zoom it to the whole screen width (1920 for my screen), and slowly pan it vertically.
Currently, I’m doing this:

 float width = (float)getWidth(), height = (float)getHeight();
 float iw = (float)currentImage->getWidth(), ih = (float)currentImage->getHeight();
 float bigH = ih * width / iw;
 // pos is a number going from 0 (top of picture), to 1 (bottom of picture). 
 g.drawImageTransformed(currentImage, currentImage->getBounds(), AffineTransform::scale(width / iw, width / iw).translated(0, -(bigH - height) * (float)pos));

I call this code repeatively, but it’s so slow to perform (in release mode, with image subsampling set to lowest, this takes 160ms on my Core2Duo, thus breaking any smooth effect).

How could I speed this up (I can’t use OpenGL) ?

yeah, that’s a lot of pixels to push around. The only way it could be a bit faster would be if you cached a rescaled version and moved that around, but it’s still a lot of data to copy around in memory.

This is why I’m trying to move towards more native rendering methods - hopefully eventually we’ll have hardware accelerated rendering running on all the platforms.

So, another question, is there a way to get the native pixels array of the graphic object ?
I’ve used libswscale in another project, and it was performing very well. I wonder how they achieve this, does the new Juce GIT does assembler tricks to copy bunch of pixels around, or is it just c++ ?

How can I force the component internal image format to be RGB (and not ARGB) ?
I’ve called “setOpaque(true)” but it’s still ARGB (so the blending is slower, right ?)

You can’t - it’s deliberately not possible because e.g. on the mac it’s using native methods that may be hardware-based.

The latest code doesn’t use any assembler, but if you’re just copying pixels it’s all done with memcpy anyway. If you wanted to get into the software renderer and find the bit that’s being used, then there might be ways to optimise it.

I don’t think it’s the case here. The graphic’s pixel format is ARGB, and my picture is RGB, so I can’t see how it would be possible to memcpy.
So back the question #2, how can I force graphic to use RGB only (so it can memcpy) ?

As long as the top-level window is opaque, it’ll render into an RGB image (this is Windows, right?)

You’re not using a component effect or anything, are you? If you did that it’d have to render to a temporary ARGB image. Or if you set the component to be buffered to an image.

Strange, my component is called like this:

    component = new MyComponent();
    // add the component to the desktop, which will remove it
    // from its current parent component..
    component->addToDesktop (ComponentPeer::windowIsTemporary);
    component->setOpaque (true); // Thought it would set it RGB ?
    component->setSize(Desktop::getInstance().getMainMonitorArea(false).getWidth(), Desktop::getInstance().getMainMonitorArea(false).getHeight());

While it’s not the main top left window (the main top level window is very classic:

    setResizable (true, false); // resizability is a property of ResizableWindow
    setResizeLimits (720, 576, 8192, 8192);
    juce::Rectangle rect = Desktop::getInstance().getMainMonitorArea(false);
    setBounds(rect);
    setTitleBarHeight(0);
    setOpaque(true);
    // Zero borders
    setFullScreen(true);
    setDropShadowEnabled(false);

Did I miss something ?
When I break into MyComponent::paint, and look into the Graphics object it’s an ARGB Image.

I’d certainly recommend adding it to the desktop after setting up its properties, but it should still work. If you want to dig into it a bit deeper, you could step through the actual paint call in the windowing code where it creates the image.

That was it, you MUST addToDesktop after the setOpaque(true) call.
So, for the curious, with RGB graphic internal format, the rendering time for drawing a 1920x2500 picture on a 1920x1200 screen is 20ms on a Core2Duo, but when the internal graphic is set to ARGB, rendering time goes to the roof to 160ms (more than 8x slower).

Thank you Jules, you’re the man.
I guess the blending code should get a assembler boost, don’t you think ? (Yes, I know it would break the current template based selection)

Also, I’m using this code to get my profiling:

#ifndef hpp_SmallCodeProfiler_hpp
#define hpp_SmallCodeProfiler_hpp

/** A simple scope based profiler class that outputs to the debug console.
     Usage is darn simple:
     @code
      {
              SimpleProfile profile(T("someTest"));
              [...] // Your actual code here
      } // Outputs: "[someTest] Profiled time: 0.003s" on the console
     @endcode
*/
class SimpleProfile
{
private:
    int64       start;
    const tchar * name; 

public:
    SimpleProfile(const tchar * name) : start(Time::getHighResolutionTicks()), name(name) {}
    ~SimpleProfile() 
    { 
        Logger::outputDebugPrintf(JUCE_T("[%s] Profiled time: %.06lfs"), name, Time::highResolutionTicksToSeconds(Time::getHighResolutionTicks() - start));
    }
};

#endif

I’m surprised that it wasn’t working. If you change the opacity, it should delete and re-build the window to reflect that.

Blending RGB onto RGB is very easy - just a memcpy, really. Blending onto ARGB is a bit more tricky, it needs to rearrange all the bytes. I’m sure there could be mmx ways of making it faster, but I’m hoping that eventually this code will just be a fallback implementation when there’s no hardware-accelerated version available.

To me, the code “addToDT / setOpaque” does this:

void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
{
    checkMessageManagerIsLocked

    if (! isOpaque())
        styleWanted |= ComponentPeer::windowIsSemiTransparent;  
// Here styleWanted get assigned the semiTransparent flag
[...]


void Component::setOpaque (const bool shouldBeOpaque) throw()
{
    if (shouldBeOpaque != flags.opaqueFlag)
    {
        flags.opaqueFlag = shouldBeOpaque;
        if (flags.hasHeavyweightPeerFlag)
        {
            const ComponentPeer* const peer = ComponentPeer::getPeerFor (this);

            if (peer != 0)
            {
                addToDesktop (peer->getStyleFlags()); // With semiTransparent flag

While the opposite works as intended… I guess it’s missing a

    if (! isOpaque())
        styleWanted |= ComponentPeer::windowIsSemiTransparent;
    else
        styleWanted &= ~ComponentPeer::windowIsSemiTransparent;

Anyway, it’s a detail.

Yes, thanks, I just found the same buggette.