Display dynamic texture in slate

Hello everyone

I’m trying to get a dynamic texture to display in slate. Previously I’ve just been pushing it into the HUD, but that’s a bit useless when you’re not doing PIE.

I found some older examples, I’m wondering if they’re outdated, because I can’t get them to build:

Headers


#include "SlateBasics.h"
#include "SlateBrush.h"
#include "SCompoundWidget.h"
#include "SImage.h"
#include "SSpinningImage.h"




class SPreview : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS(SPreview)
        : _ItemImage(nullptr)
    {}


    SLATE_ARGUMENT(UTexture2D*, ItemImage)


    SLATE_END_ARGS()


    /** Constructs this widget. */
    void Construct(const FArguments& InArgs);


    FSlateBrush ItemBrush;
}

C++


#include "Preview.h"

#include "SlateBasics.h"
#include "SlateBrush.h"
#include "SCompoundWidget.h"
#include "SImage.h"
#include "SSpinningImage.h"

void SPreview::Construct(const FArguments& InArgs)
{
    ItemBrush.SetResourceObject(InArgs._ItemImage);
    ItemBrush.ImageSize.X = InArgs._ItemImage->GetSurfaceWidth();
    ItemBrush.ImageSize.Y = InArgs._ItemImage->GetSurfaceHeight();
    ItemBrush.DrawAs = ESlateBrushDrawType::Image;

    ChildSlot
        
            SNew(SPreview)
            .ItemImage(&ItemBrush)
        ];
}

Errors


1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\ARFilter.h(6): error C2236: unexpected token 'struct'. Did you forget a ';'?1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\ARFilter.h(7): error C2143: syntax error : missing ';' before '{'
1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\ARFilter.h(7): error C2447: '{' : missing function header (old-style formal list?)
1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\IAssetRegistry.h(79): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\IAssetRegistry.h(79): error C2143: syntax error : missing ',' before '&'
1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\IAssetRegistry.h(126): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\program files\epic games\4.8\engine\source\runtime\assetregistry\public\IAssetRegistry.h(126): error C2143: syntax error : missing ',' before '&'

What have I done that’s causing engine code to have errors? I have the Slate and SlateCore modules included. I went on to try a bunch of different things and they all cause errors in existing engine code. Argh.

The code doesn’t make any sense, it’s recursive. The construct method of SPreview is constructing an SPreview within itself.

Is there some reason you can’t do what you want just using an SImage? You can pass any texture or material to the image brush.

Hmm, good point. That was a last minute typo, it didn’t work with the correct SImage in there either. I copied it almost verbatim from here: Having trouble loading a texture into SImage - C++ - Unreal Engine Forums

I seemed to need to include the TextureEditor module and half the editor header files just to get it to even try compiling.

I’ve already abandoned that attempt in any case. I did try using an SImage, but I’m still trying to get it working using this post and some code from the previous link: How do I assign a FslateBrush to an SImage object? - UI - Unreal Engine Forums

The whole FSlateBrush thing is confusing. Are there any better examples?

Thanks for your help!

It should be:


SNew(SImage).Image(&PreviewImageBrush)

Get rid of the following:


SImage::FArguments PreviewImageArgs = SImage::FArguments();
PreviewImageArgs.Image(PreviewImageBrush);

You also need to store your PreviewImageBrush somewhere that will persist, since the SImage holds a pointer to it. So it should probably be a class member variable, definitely not a local variable.

The errors you posted seem to be unrelated though. If you still get them, you’d have to post more code for me to see where they’re coming from.

Sorry, I keep editing my original reply as I make progress. I’ve switched to a delegate now, but I haven’t persisted the brush anywhere. I’ll get on top of that now.

Alright, this should be my last question. This has me stumped as well.


const FSlateBrush UPreviewOutput::PreviewImageBrush;

const FSlateBrush * UPreviewOutput::GetPreviewImage()
{
	FSlateBrush * Brush = new FSlateBrush(PreviewImageBrush);
	Brush->SetResourceObject(UPreviewOutput::Texture);
	Brush->ImageSize.X = UPreviewOutput::Texture->GetSurfaceWidth();
	Brush->ImageSize.Y = UPreviewOutput::Texture->GetSurfaceHeight();
	Brush->DrawAs = ESlateBrushDrawType::Image;
	return Brush;
}


1>C:\Program Files\Epic Games\4.8\Engine\Source\Runtime\Core\Public\Misc\Attribute.h(44): error C2440: 'initializing' : cannot convert from 'const FSlateBrush *(__cdecl *const )(void)' to 'const FSlateBrush *'
1>C:\Program Files\Epic Games\4.8\Engine\Source\Runtime\Core\Public\Misc\Attribute.h(44): error C2439: 'TAttribute<const FSlateBrush *>::Value' : member could not be initialized

You’re not showing the code that is triggering the error, so it’s a bit hard to diagnose. :wink:
But it looks like you’re probably not assigning the delegate correctly. If you’re assigning it within a method of UPreviewOutput, it should look something like:


SNew(SImage).Image_UObject(this, &UPreviewOutput::GetPreviewImage)

Basically, .Image() is for assigning a static brush pointer, and the other forms are for assigning a dynamic brush via a delegate function. If your function is a member of a UObject-derived class, use .Image_UObject(), and if it’s a member or a regular class, use .Image_Raw().

Also, you shouldn’t be using the new operator to create your brush, you’re going to leak memory. Just make PreviewImageBrush a regular FSlateBrush member of your class - not const, not static, not a pointer. Then in your constructor or wherever is suitable, fill it with the data for the texture. Then inside of GetPreviewImage() all you need is:


return &PreviewImageBrush;

I wouldn’t have the foggiest what the code triggering the error is - it’s in engine code;

Okay, I’m trying out that! Thanks! :slight_smile:

This is pretty back to basics and I still don’t fully understand what it wants. This error in particular is really difficult to understand. I’ve tried returning pointers, references, actual brushes, nothing really works.

Although the class IS a UCLASS, it also has some static methods that I’m using here, so I’m using image_raw().


SNew(SImage).Image_Raw(this, &UPreviewOutput::GetPreviewImage)


FSlateBrush UPreviewOutput::PreviewImageBrush;

FSlateBrush * UPreviewOutput::GetPreviewImage()
{
    UPreviewOutput::PreviewImageBrush.SetResourceObject(UPreviewOutput::Texture);
    UPreviewOutput::PreviewImageBrush.ImageSize.X = UPreviewOutput::Texture->GetSurfaceWidth();
    UPreviewOutput::PreviewImageBrush.ImageSize.Y = UPreviewOutput::Texture->GetSurfaceHeight();
    UPreviewOutput::PreviewImageBrush.DrawAs = ESlateBrushDrawType::Image;
    return &UPreviewOutput::PreviewImageBrush;
}



1>C:\UnrealEngine\Projects\UEPreview002\Plugins\PreviewRenderPlugin\Source\PreviewRenderPlugin\Private\PreviewRenderPlugin.cpp(231): error C2664: 'SImage::FArguments::WidgetArgsType &SImage::FArguments::Image_Raw<FPreviewRenderPluginModule>(UserClass *,const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void) const)' : cannot convert argument 2 from 'FSlateBrush *(__cdecl *)(void)' to 'const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void) const'


After some poking around I’ve got it down to:

error C2664: ‘SImage::FArguments::WidgetArgsType &SImage::FArguments::Image_Raw<FOctaneRenderPluginModule>(UserClass ,const FSlateBrush (__cdecl FOctaneRenderPluginModule:: )(void) const)’ : cannot convert argument 2 from ‘const FSlateBrush (__cdecl )(void)’ to 'const FSlateBrush (__cdecl FOctaneRenderPluginModule:: )(void) const’

What’s the relevance of the bit after the __cdecl, and the extra const at the end?

I’m also confused because if I search the engine source for the string Image_Raw, I don’t find anything on github or my local checkout. Are slate method names constructed dynamically?

I moved my delegate method into the main plugin class, which improved the situation to:

> cannot convert argument 2 from ‘const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void)’ to ‘const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void) const’

Still missing that last const. Anyone know where that business comes from?

Sigh.


1>C:\UnrealEngine\Projects\UEPreview002\Plugins\PreviewRenderPlugin\Source\PreviewRenderPlugin\Private\PreviewRenderPlugin.cpp(231): error C2664: 'SImage::FArguments::WidgetArgsType &SImage::FArguments::Image_Raw<FPreviewRenderPluginModule>(UserClass *,const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void) const)' : cannot convert argument 2 from 'const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void) const' to 'const FSlateBrush (__cdecl FPreviewRenderPluginModule::* )(void) const'

Cannot convert
const FSlateBrush (__cdecl FPreviewRenderPluginModule:: )(void) const*’
to
const FSlateBrush (__cdecl FPreviewRenderPluginModule:: )(void) const*’

Well that’s bemusing.

(__cdecl FPreviewRenderPluginModule::* ) <- That asterisk, does that mean it covers all pointers? I would think you would need to have a pointer name tied to the asterisk no?

I do not know to much about programming but it looks like you are using as an argument, a pointer of type __cdecl FPreviewRenderPluginModule with no pointer name specified. also is the :: not used to call functions that are declared in the FPreviewRenderPluginModule class?

Also what is __cdecl ?

An asterisk doesn’t always mean a pointer. I believe the **__cdecl *FPreviewRenderPluginModule:: means that it must be delegated to a method from the FPreviewRenderPluginModule namespace. So that means __cdecl just ensures the namespace is a class declaration.

The first const means the return type has to be marked as const and the last const means the delegate function must be marked as const.

The (void) in that position just means there’s no input parameters to go into the function.

I’m pretty sure Image_Raw(), Image_Lambda(), Image_UObject(), etc. are all generated methods since they’re not in the source. But that also makes it hard to see what they’re doing, since I can’t find where the relevant code is.

Your last error is a bit perplexing to be honest. It also refers to FSlateBrush instead of FSlateBrush*.

Anyway, yeah I think you’re pretty close to seeing how all this works. Your last post is pretty much spot on. __cdecl is just the standard C++ calling convention.

You’re also correct that the various forms of the attribute functions are generated - they’re defined by macros. Try searching for ‘_Raw’ on the Github repo. If you instead search for a more common attribute like ‘Text_Raw’, you’ll see examples in the codebase of how it’s used.

To add to what I wrote in my last post, if you want to bind a static method, you should use:


SNew(SImage).Image_Static()

However, it would be unusual to be using a static method in this context.

Generally, Slate has two kinds of delegates, though they can appear to be the same.

  1. Slate attributes.
    These are for any member that is of type TAttribute<…> - you can check this in the documentation for the widget class you’re using. So Image in SImage is an attribute. For these, your delegate has only one job - return a value of the type enclosed inside the TAttribute<…> declaration. In this case, that is *const FSlateBrush **. So your delegate function should in general have no parameters, and return exactly that type.

Then you have a choice of what kind of function you want to bind to the delegate:
.Image_Raw(this, &MyRegularClass::SomeFunction) - SomeFunction should be a regular (non-static) class method, marked as const.
.Image_UObject(this, &UMyUObjectClass::SomeFunction) - SomeFunction should be a regular (non-static) class method on a UObject-derived class, marked as const.
.Image_Static(&MyClass::SomeFunction) - SomeFunction should be a static class method or a global function.
.Image_Lambda(]{ … }) - This takes a C++ lambda. This is really convenient for simple delegates.

Note that if you don’t need the value to be dynamic, you don’t need a delegate at all. You can set the attribute directly by:
.Image(&MyBrush) - Since in this particular case the attribute is a pointer, make sure you don’t pass the address of a local variable.

  1. Slate events.
    These are for attaching handlers for various events generated by the widget. They have the same form in Slate declarative syntax (ie. you can do SNew(SImage).OnMouseButtonDown_Raw/UObject/Static/Lambda). However, they could have any function signature, and it generally can’t be found in the documentation. Your best bet is to search the engine code for examples usage to check what the signature is. Other than that, they are bound in the same way as attributes.

I’d recommend reading this overview in the docs, and these great posts from cmartel.

If you still have an issue, post up all the relevant code and I’ll have a look.

@kamrann,

I’m sorry to raise a side question on your feedback but why that?
“Also, you shouldn’t be using the new operator to create your brush, you’re going to leak memory”

Why the “new” will create memory leak?

Thanks,

The new operator allocates dynamic memory, so any time you use it, it has to be matched by a delete somewhere else to clean up the memory.

In the case above, the new call was inside a delegate function, which is being called by Slate whenever it wants to find out what image to use (i.e. every frame). So a new brush would get created every frame. Slate doesn’t take responsibility for ownership of brushes that you pass to it, it assumes their lifetime is managed by you. So, every frame a brush would end up getting leaked.

Ok I understand better now.
I’m used to have data managed by the garbagge collector, I forgot that Slate is plain C++ and not UObject like UMG ^^

thanks,

I’m still having the issue even with Image_Static(). This is my situation:

I have a Texture2D resource that I’ve created in code and am updating as it changes. This could be every frame, every other second, or just once and then never again. I need to display it somewhere. So far I’ve been using it in the HUD during PIE but I need it to be visible normally in the editor as well.

I have plenty of other delegates in my slate setup so I have no idea what I’m doing wrong. Even Intellisense doesn’t like this line:

Anyway, here’s code:

OctaneRenderPlugin.cpp


const FSlateBrush FOctaneRenderPluginModule::GetPreviewImage() const{
    return UOctaneOutput::PreviewImageBrush;
}

OctaneOutput.h (THIS IS A UCLASS AS WELL)


static const FSlateBrush PreviewImageBrush;

OctaneOutput.cpp


const FSlateBrush UOctaneOutput::PreviewImageBrush;

I haven’t tried to assign a texture resource to the brush yet as that also generates errors due to it being const. This is very frustrating.

I’ve used a static because a singleton makes more sense in the context. I’ve got no real entry point for editor stuff as there’s no editor-ready event, so I do as much as I can as static, then things that require the editor to be loaded get kicked off when the plugin settings tab shows. That’s where this texture resource will live, but it’s important that it exists before the plugin window is shown too, in case the user doesn’t preview the texture in question before generating and applying it.

Eh, changed the brush to a pointer and it worked. I guess I hadn’t tried that exact combination of directives until now. Definitely needed all that const stuff though. Sweet!

I’m still having this problem where I can’t modify a const member. I haven’t done much with const yet - is a const static a special thing that needs to be handled a specific way? Can you initialize a static?