Categories
Programming

Generating and displaying a QR code in Unreal Engine

Figuring out how to turn any string into a QR code that can be displayed as an image in the UI using Unreal engine. It involves a little bit of C++, but don’t worry!

This week we’re spending lots of time working on Askutron. Since the Unreal engine is still pretty new to me I keep learning every day. This short post is about how I figured out how to generate and display a QR code.

If you want to save time you can try your luck with any of the QR code plugins available on the Unreal marketplace. The lack of reviews and my programmer’s pride prevented me from dishing out the 50 or so Euro though.

Steps

This is the plan and what’s covered here.

  1. Compute the QR code
  2. Generate a texture at runtime from the QR code
  3. Display that texture in the UI using an Image

1. Compute the QR code

Like you might expect my journey started out with a web search for “C++ qr code”. The first result yielded this:

Indeed the GitHub repo with its 1,600 stars looked promising from the start. Also the documentation is concise and, importantly, contained simple examples.

So I proceeded to nonchalantly copy the CPP files (QrCode.hpp and QrCode.cpp) into my Unreal project’s Source folder. This is always the critical moment for me. Will it compile? Or will I have to tear my hair out over cryptic C++ error messages. Lucky for me it compiled without any issues.

Following the examples from the website and some basic Unreal C++ knowledge, computing the QR code was as easy as pie:

UTexture2D* UWebBuzzers::GenerateQrCode(UObject* parent, FString string)
{    
    auto errorCorrectionLevel = qrcodegen::QrCode::Ecc::LOW;
    qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(TCHAR_TO_UTF8(*string), errorCorrectionLevel);

    uint8 size = qr.getSize();
    TArray<FColor> pixels;
    pixels.SetNumZeroed(size * size);

    FColor black = FColor::Black;
    FColor white = FColor::White;

    for (uint8 x = 0; x < size; x++)
    {
        for (uint8 y = 0; y < size; y++)
        {
            FColor color = qr.getModule(x, y) ? white : black;
            pixels[x + y * size] = color;
        }
    }

    // TODO: Generate texture from color array (see below)
}

The actual QR computation happens in one call to “encodeText”. Note that I chose to simply use the low error correction level, which makes the QR code smaller. That’s because displaying the code on the screen isn’t likely to be affected much by obstruction or distortion.

That was very simple and intuitive. Shout out to Nayuki for this great library!

2. Generate a texture at runtime from the QR code

The next step is turning that color array into a texture that can be displayed. That’s even simpler, at least at first:

    FCreateTexture2DParameters params;    
    UTexture2D* texture = FImageUtils::CreateTexture2D(size, size, pixels, parent, "QRCode", EObjectFlags::RF_Transient, params);

    // don't interpolate texels to get sharp edges (otherwise the image would look blurry)
    texture->Filter = TextureFilter::TF_Nearest;

I’m using FImageUtils::CreateTexture2D to generate the texture at runtime. Its documentation is sorely missing any example code, like the entire Unreal documentation in general. But I eventually figured out a set of parameters that work:

  • the first two parameters simply are the size in pixels, which I simply set to the size of the QR code (number of “modules”)
  • pixels is the FColor array from above
  • parent is any UObject that can be passed in for example via blueprint. This is important! If you don’t set a parent the texture will be immediately garbage collected, or at least that’s what I assume is happening. Not setting it will result in the editor crashing with an error like “object is not packaged”.
  • “QRCode” is the name of the texture and can be freely chosen
  • For flags I wasn’t far from sure what needs to be passed here. But based on the sparse documentation I chose RF_Transient – “Don’t save object.”, since the texture is only supposed to exist at runtime.
  • The last parameter, “params”, allows to change a few other options that I don’t know anything but, so I left them at the default values.

Setting the texture filter to “NEAREST” (Nearest-neighbor interpolation) is also important. The default filter uses some sort of interpolation between pixels, which means that as the texture is stretched out it becomes very, very blurry. The generated texture is only between 17×17 and 144×144 pixels in size, depending on the length of the encoded string and error correction level.

Works perfectly in the editor. We’re done, right? Sigh, I’m afraid not. While this code runs as expected in the editor it will actually crash in a packaged build with the following error: “constructtexture2D not supported on console”, as I literally just found out.

The solution ain’t pretty and Unreal’s documentation was of no help at all, once again. So back to the web search I went. Unfortunately many questions regarding Unreal on forums or “AnswerHub” are left unanswered. The Unity community seems to be in much, much better shape. Regardless, I eventually found one post with a solution (though no explanation):

    UTexture2D* texture = UTexture2D::CreateTransient(size, size, EPixelFormat::PF_B8G8R8A8, "QRCode");
    void* data = texture->PlatformData->Mips[0].BulkData.Lock(LOCK_WRITE);
    FMemory::Memcpy(data, pixels.GetData(), size * size * 4);
    texture->PlatformData->Mips[0].BulkData.Unlock();
    texture->UpdateResource();

As someone who has programmed C++ using DirectX and OpenGL before I know what’s happening, but I’m left without any clue why the initial solution is not supported on “consoles”. Maybe it’s because of the non-square texture size, but I was too lazy to test that theory.

Anyway, this is the final code (see also this Gist):

#include "QrCode.hpp" // from https://github.com/nayuki/QR-Code-generator
#include "ImageUtils.h" // from Unreal Engine (4.24)

/// <summary>Generates a QR code texture from a string.</summary>
/// <param name="parent">UE parent (required)</param>
/// <param name="string">String to encode</param>
UTexture2D* UWebBuzzers::GenerateQrCode(UObject* parent, FString string)
{    
    qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(TCHAR_TO_UTF8(*string), qrcodegen::QrCode::Ecc::LOW);

    uint8 size = qr.getSize();
    TArray<FColor> pixels;
    pixels.SetNumZeroed(size * size);

    FColor black = FColor::Black;
    FColor white = FColor::White;

    for (uint8 x = 0; x < size; x++)
    {
        for (uint8 y = 0; y < size; y++)
        {
            FColor color = qr.getModule(x, y) ? white : black;
            pixels[x + y * size] = color;
        }
    }

    UTexture2D* texture = UTexture2D::CreateTransient(size, size, EPixelFormat::PF_B8G8R8A8, "QRCode");
    void* data = texture->PlatformData->Mips[0].BulkData.Lock(LOCK_WRITE);
    FMemory::Memcpy(data, pixels.GetData(), size * size * 4);
    texture->PlatformData->Mips[0].BulkData.Unlock();
    texture->UpdateResource();

    texture->Filter = TextureFilter::TF_Nearest;

    return texture;
}

What I haven’t shown here is that I exposed this function as part of a blueprint code library. How to do that I’m not going to cover here, as that’s kind of a standard Unreal thing that should be covered by documentation.

3. Display that texture in the UI using an Image

To display the resulting QR code texture in the UI just grab an image, call the blueprint function from above and set the result as the texture of the image using “Set Brush from Texture”.

Done and done! That’s how you generate and display a QR code in Unreal engine as someone who knows nearly nothing about the Unreal engine. If some day I learn a better way to do this I’m going to make sure to update this post.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
lukassoo
lukassoo
1 year ago

Thank you for your work!
I would have probably spent many hours figuring this out from scratch.

I have used your example and managed to get a working QR encoded TOTP URIs for Authenticator apps.

Google Authenticator works flawlessly but I also tried Authy and it didn’t want to work for some reason.
I went through my logic and found no issues, then I compared the resulting QR codes from Unreal and the test app that google links to.

(Result in attached image)

Seeing this I knew what was going on, Google Authenticator sees the code and understands both inverted and regular versions but Authy does not.

So i went into the GenerateQRCode function and found this line:

FColor color = qr.getModule(x, y) ? white : black;

Simply swapping colors does the job:

FColor color = qr.getModule(x, y) ? black : white

And now the generated QR codes match and both Authenticators work.

After fixing this I also found out that the function actually says that it works differently than usual (second part of the attachment).

Thanks again for doing most of the work 🙂

attachment.png
Laura
Laura
1 year ago

Hi there, thanks so much for your blog post, I am trying to use QR codes for a project. There is one issue with the library you are using because it throws exceptions. When you try to build this for console it fails because exceptions are disabled and games in general should be exception safe. I might have to modify the library to remove those or use another one. Thanks again!