Copy Freetype Bitmap into Magick::Image at specific offset

107 Views Asked by At

In my game engine, I have a texture loading API which wraps low level libraries like OpenGL, DirectX, etc. This API uses Magick++ because I found it to be a convenient cross-platform solution and allows me to create procedural textures fairly easily.

I'm now adding a text rendering system using freetype where I want to use this texture API to dynamically generate a texture atlas for any given font where all the glyphs are stored horizontally adjacent.

I have been able to get this to work in the past by buffering the bitmaps directly into OpenGL. But now I want to accomplish this in a platform independent way, using this API.

I've looked around for a few examples but I can't find anything quite like what I'm after so if there are any magick++ experts around, I'd really appreciate some pointers.

So in simple terms: I've got a freetype bitmap and I want to be able to copy its pixel buffer to a specific offset inside a Magick::Image.

This code might help to clarify:

auto texture = e6::textures->create(e6::texture::specification{}, [name, totalWidth, maxHeight](){

      // Initialises Freetype
      FT_Face face;
      FT_Library ft;

      if (FT_Init_FreeType(&ft)) {
        std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
      }

      if (int error = FT_New_Face(ft, path(name.c_str()).c_str(), 0, &face)) {
        std::cout << "Failed to initialise fonts: " << name << std::endl;
        throw std::exception();
      }

      // Sets the size of the font
      FT_Set_Pixel_Sizes(face, 0, 100);

      unsigned int cursor = 0; // Keeps track of the horizontal offset.

      // Preallocate an image buffer
      // totalWidth and maxHeight is the size of the entire atlas
      Magick::Image image(Magick::Geometry(totalWidth, maxHeight), "BLACK");
      image.type(Magick::GrayscaleType);
      image.magick("BMP");
      image.depth(8);
      image.modifyImage();
      Magick::Pixels view(image);

      // Loops through a subset of the ASCII codes
      for (uint8_t c = 32; c < 128; c++) {
        if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
          std::cout << "Failed to load glyph: " << c << std::endl;
          continue;
        }

        // Just for clarification...
        unsigned int width = face->glyph->bitmap.width;
        unsigned int height = face->glyph->bitmap.rows;
        unsigned char* image_data = face->glyph->bitmap.buffer;

        // This is the problem part.
        // How can I copy the image_data into `image` at the cursor position?

        cursor += width; // Advance the cursor
      }

      image.write(std::string(TEXTURES) + "font-test.bmp"); // Write to filesystem

      // Clean up freetype
      FT_Done_Face(face);
      FT_Done_FreeType(ft);

      return image;
    }, "font-" + name);

I tried using a pixel cache which the documentation demonstrates:

Magick::Quantum *pixels = view.get(cursor, 0, width, height);
*pixels = *image_data;
view.sync();

But this leaves me with a completely black image, I think because the image_data goes out of scope.

1

There are 1 best solutions below

0
AudioBubble On

I was hoping there'd be a way to modify the image data directly but after a lot of trial and error, I ended up just creating an image for each glyph and compositing them together:

...

Magick::Image glyph (Magick::Geometry(), "BLACK");
glyph.type(MagickCore::GrayscaleType);
glyph.magick("BMP");
glyph.depth(8);
glyph.read(width, height, "R", Magick::StorageType::CharPixel, image_data);

image.composite(glyph, cursor, 0);

cursor += width;

At the very least, I hope this helps to prevent someone else going down the same rabbit hole I did.