Beispiel #1
0
void TextureAtlas::load(TextureManager& texture_manager,
                        const rainbow::Texture& texture,
                        const DataMap& data,
                        float scale)
{
    R_ASSERT(data, "Failed to load texture");

    const rainbow::Image& image = rainbow::Image::decode(data, scale);
    if (!image.data)
        return;

    switch (image.format)
    {
#ifdef GL_OES_compressed_ETC1_RGB8_texture
        case rainbow::Image::Format::ETC1:
            texture_manager.upload_compressed(
                texture, GL_ETC1_RGB8_OES, image.width, image.height,
                image.size, image.data);
            break;
#endif  // ETC1
#ifdef GL_IMG_texture_compression_pvrtc
        case rainbow::Image::Format::PVRTC: {
            R_ASSERT(image.depth == 2 || image.depth == 4, kInvalidColorDepth);
            R_ASSERT(image.channels == 3 || image.channels == 4,
                     "Invalid number of colour channels");
            GLint internal = 0;
            if (image.channels == 3)
            {
                internal = (image.depth == 2
                    ? GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
                    : GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG);
            }
            else
            {
                internal = (image.depth == 2
                    ? GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
                    : GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG);
            }
            texture_manager.upload_compressed(
                texture, internal, image.width, image.height, image.size,
                image.data);
            break;
        }
#endif  // PVRTC
        default: {
            GLint format = 0;
            GLint internal = 0;
            switch (image.channels)
            {
                case 1:
                    R_ASSERT(image.depth == 8, kInvalidColorDepth);
                    format = GL_LUMINANCE;
                    internal = GL_LUMINANCE;
                    break;
                case 2:
                    R_ASSERT(image.depth == 16, kInvalidColorDepth);
                    format = GL_LUMINANCE_ALPHA;
                    internal = GL_LUMINANCE_ALPHA;
                    break;
                case 3:
                    R_ASSERT(image.depth == 16 || image.depth == 24,
                             kInvalidColorDepth);
                    format = GL_RGB;
                    internal = (image.depth == 16 ? GL_RGBA4 : GL_RGBA8);
                    break;
                case 4:
                    R_ASSERT(image.depth == 16 || image.depth == 32,
                             kInvalidColorDepth);
                    format = GL_RGBA;
                    internal = (image.depth == 16 ? GL_RGBA4 : GL_RGBA8);
                    break;
            }
            texture_manager.upload(
                texture, internal, image.width, image.height, format,
                image.data);
            break;
        }
    }
}
Beispiel #2
0
void FontAtlas::load(TextureManager& texture_manager,
                     const rainbow::Texture& texture,
                     const Data& font)
{
    R_ASSERT(font, "Failed to load font");

    for (uint_t i = 0; i < kNumCharacters; ++i)
        charset_[i].code = i + kASCIIOffset;

#if FONTATLAS_EXTENDED > 0
    const unsigned int characters[]{
        0x00c5,  // LATIN CAPITAL LETTER A WITH RING ABOVE
        0x00c6,  // LATIN CAPITAL LETTER AE
        0x00d8,  // LATIN CAPITAL LETTER O WITH STROKE
        0x00e5,  // LATIN SMALL LETTER A WITH RING ABOVE
        0x00e6,  // LATIN SMALL LETTER AE
        0x00f8,  // LATIN SMALL LETTER O WITH STROKE
    };
    for (size_t i = 0; i < array_size(characters); ++i)
        charset_[kNumCharacters + i].code = characters[i];
#endif

    FontLibrary library;
    if (!library)
        return;

    FontFace face(library, font);
    if (!face)
        return;

    FT_Set_Char_Size(face, 0, pt_ * kPixelFormat, kDPI, kDPI);
    const Vec2u& max = max_glyph_size(face, charset_, array_size(charset_));
    if (max.is_zero())
        return;
    const Vec2u size(rainbow::ceil_pow2(max.x * kNumGlyphsPerColRow),
                     rainbow::ceil_pow2(max.y * kNumGlyphsPerColRow));

    // GL_LUMINANCE8_ALPHA8 buffer
    Vec2u offset(size.x * size.y * 2, 0);
    auto buffer = std::make_unique<GLubyte[]>(offset.x);
    std::fill_n(buffer.get(), offset.x, 0);

    // Copy all glyph bitmaps to our texture.
    offset.x = 0;
    const Vec2f pixel(1.0f / size.x, 1.0f / size.y);
    for (auto& glyph : charset_)
    {
        FT_Load_Char(face, glyph.code, FT_LOAD_RENDER);

        const FT_GlyphSlot& slot = face->glyph;
        const FT_Bitmap& bitmap = slot->bitmap;

        // Make sure bitmap data has enough space to the right. Otherwise, start
        // a new line.
        const uint_t width = bitmap.width + kGlyphPadding2;
        if (offset.x + width > size.x)
        {
            offset.x = 0;
            offset.y += max.y;
        }

        // Copy bitmap data to texture.
        if (bitmap.buffer)
        {
            R_ASSERT(bitmap.num_grays == 256, "");
            R_ASSERT(bitmap.pixel_mode == FT_PIXEL_MODE_GRAY, "");
            copy_bitmap_into(buffer.get(), size, offset + kGlyphPadding,
                             bitmap.buffer, Vec2u(bitmap.width, bitmap.rows));
        }

        // Save font glyph data.
        glyph.advance = slot->advance.x / kPixelFormat;
        glyph.left = slot->bitmap_left;

        SpriteVertex* vx = glyph.quad;

        vx[0].position.x = -kGlyphMargin;
        vx[0].position.y = static_cast<float>(slot->bitmap_top -
                                              static_cast<int>(bitmap.rows)) -
                           kGlyphMargin;
        vx[1].position.x = static_cast<float>(bitmap.width) + kGlyphMargin;
        vx[1].position.y = vx[0].position.y;
        vx[2].position.x = vx[1].position.x;
        vx[2].position.y = static_cast<float>(slot->bitmap_top) + kGlyphMargin;
        vx[3].position.x = vx[0].position.x;
        vx[3].position.y = vx[2].position.y;

        vx[0].texcoord.x = (kGlyphPadding - kGlyphMargin + offset.x) * pixel.x;
        vx[0].texcoord.y =
            (kGlyphPadding + bitmap.rows + kGlyphMargin + offset.y) * pixel.y;
        vx[1].texcoord.x =
            (kGlyphPadding + bitmap.width + kGlyphMargin + offset.x) * pixel.x;
        vx[1].texcoord.y = vx[0].texcoord.y;
        vx[2].texcoord.x = vx[1].texcoord.x;
        vx[2].texcoord.y = (kGlyphPadding - kGlyphMargin + offset.y) * pixel.y;
        vx[3].texcoord.x = vx[0].texcoord.x;
        vx[3].texcoord.y = vx[2].texcoord.y;

#ifdef FONTATLAS_KERNING
        if (FT_HAS_KERNING(face))
        {
            int i = -1;
            FT_Vector kerning;
            for (auto& left : charset_)
            {
                FT_Get_Kerning(
                    face, left.code, glyph.code, FT_KERNING_DEFAULT, &kerning);
                glyph.kern[++i] = static_cast<short>(kerning.x / kPixelFormat);
            }
        }
#endif

        // Advance to the next "slot" in our texture.
        offset.x += width;
    }

    height_ = face->size->metrics.height / kPixelFormat;

    texture_manager.upload(texture, GL_LUMINANCE_ALPHA, size.x, size.y,
                           GL_LUMINANCE_ALPHA, buffer.get());
}