root/src/servers/app/font/FontCacheEntry.cpp
/*
 * Copyright 2007-2009, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Maxim Shemanarev <mcseemagg@yahoo.com>
 *              Stephan Aßmus <superstippi@gmx.de>
 *              Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
 */

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//                      mcseemagg@yahoo.com
//                      http://www.antigrain.com
//----------------------------------------------------------------------------


#include "FontCacheEntry.h"

#include <string.h>

#include <new>

#include <Autolock.h>

#include <agg_array.h>
#include <utf8_functions.h>
#include <util/OpenHashTable.h>

#include "GlobalSubpixelSettings.h"


BLocker FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");


class FontCacheEntry::GlyphCachePool {
        // This class needs to be defined before any inline functions, as otherwise
        // gcc2 will barf in debug mode.
        struct GlyphHashTableDefinition {
                typedef uint32          KeyType;
                typedef GlyphCache      ValueType;

                size_t HashKey(uint32 key) const
                {
                        return key;
                }

                size_t Hash(GlyphCache* value) const
                {
                        return value->glyph_index;
                }

                bool Compare(uint32 key, GlyphCache* value) const
                {
                        return value->glyph_index == key;
                }

                GlyphCache*& GetLink(GlyphCache* value) const
                {
                        return value->hash_link;
                }
        };
public:
        GlyphCachePool()
        {
        }

        ~GlyphCachePool()
        {
                GlyphCache* glyph = fGlyphTable.Clear(true);
                while (glyph != NULL) {
                        GlyphCache* next = glyph->hash_link;
                        delete glyph;
                        glyph = next;
                }
        }

        status_t Init()
        {
                return fGlyphTable.Init();
        }

        const GlyphCache* FindGlyph(uint32 glyphIndex) const
        {
                return fGlyphTable.Lookup(glyphIndex);
        }

        GlyphCache* CacheGlyph(uint32 glyphIndex,
                uint32 dataSize, glyph_data_type dataType, const agg::rect_i& bounds,
                float advanceX, float advanceY, float preciseAdvanceX,
                float preciseAdvanceY, float insetLeft, float insetRight)
        {
                GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex);
                if (glyph != NULL)
                        return NULL;

                glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType,
                        bounds, advanceX, advanceY, preciseAdvanceX, preciseAdvanceY,
                        insetLeft, insetRight);
                if (glyph == NULL || glyph->data == NULL) {
                        delete glyph;
                        return NULL;
                }

                // TODO: The HashTable grows without bounds. We should cleanup
                // older entries from time to time.

                fGlyphTable.Insert(glyph);

                return glyph;
        }

private:
        typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable;

        GlyphTable      fGlyphTable;
};


// #pragma mark -


FontCacheEntry::FontCacheEntry()
        :
        MultiLocker("FontCacheEntry lock"),
        fGlyphCache(new(std::nothrow) GlyphCachePool()),
        fEngine(),
        fLastUsedTime(LONGLONG_MIN),
        fUseCounter(0)
{
}


FontCacheEntry::~FontCacheEntry()
{
//printf("~FontCacheEntry()\n");
}


bool
FontCacheEntry::Init(const ServerFont& font, bool forceVector)
{
        if (!fGlyphCache.IsSet())
                return false;

        glyph_rendering renderingType = _RenderTypeFor(font, forceVector);

        // TODO: encoding from font
        FT_Encoding charMap = FT_ENCODING_NONE;
        bool hinting = font.Hinting();

        bool success;
        if (font.FontData() != NULL)
                success = fEngine.Init(NULL, font.FaceIndex(), font.Size(), charMap,
                        renderingType, hinting, (const void*)font.FontData(), font.FontDataSize());
        else
                success = fEngine.Init(font.Path(), font.FaceIndex(), font.Size(), charMap,
                        renderingType, hinting);

        if (!success) {
                fprintf(stderr, "FontCacheEntry::Init() - some error loading font "
                        "file %s\n", font.Path());
                return false;
        }

        if (fGlyphCache->Init() != B_OK) {
                fprintf(stderr, "FontCacheEntry::Init() - failed to allocate "
                        "GlyphCache table for font file %s\n", font.Path());
                return false;
        }

        return true;
}


bool
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{
        uint32 glyphCode;
        const char* start = utf8String;
        while ((glyphCode = UTF8ToCharCode(&utf8String))) {
                if (fGlyphCache->FindGlyph(glyphCode) == NULL)
                        return false;
                if (utf8String - start + 1 > length)
                        break;
        }
        return true;
}


inline bool
render_as_space(uint32 glyphCode)
{
        // whitespace: render as space
        // as per Unicode PropList.txt: White_Space
        return (glyphCode >= 0x0009 && glyphCode <= 0x000d)
                        // control characters
                || (glyphCode == 0x0085)
                        // another control
                || (glyphCode == 0x00a0)
                        // no-break space
                || (glyphCode == 0x1680)
                        // ogham space mark
                || (glyphCode >= 0x2000 && glyphCode <= 0x200a)
                        // en quand, hair space
                || (glyphCode >= 0x2028 && glyphCode <= 0x2029)
                        // line and paragraph separators
                || (glyphCode == 0x202f)
                        // narrow no-break space
                || (glyphCode == 0x205f)
                        // medium math space
                || (glyphCode == 0x3000)
                        // ideographic space
                ;
}


inline bool
render_as_zero_width(uint32 glyphCode)
{
        // ignorable chars: render as invisible
        // as per Unicode DerivedCoreProperties.txt: Default_Ignorable_Code_Point.
        // We also don't want tofu for noncharacters if we ever get one.
        return (glyphCode == 0x00ad)
                        // soft hyphen
                || (glyphCode == 0x034f)
                        // combining grapheme joiner
                || (glyphCode == 0x061c)
                        // arabic letter mark
                || (glyphCode >= 0x115f && glyphCode <= 0x1160)
                        // hangul fillers
                || (glyphCode >= 0x17b4 && glyphCode <= 0x17b5)
                        // ignorable khmer vowels
                || (glyphCode >= 0x180b && glyphCode <= 0x180f)
                        // mongolian variation selectors and vowel separator
                || (glyphCode >= 0x200b && glyphCode <= 0x200f)
                        // zero width space, cursive joiners, ltr marks
                || (glyphCode >= 0x202a && glyphCode <= 0x202e)
                        // left to right embed, override
                || (glyphCode >= 0x2060 && glyphCode <= 0x206f)
                        // word joiner, invisible math operators, reserved
                || (glyphCode == 0x3164)
                        // hangul filler
                || (glyphCode >= 0xfe00 && glyphCode <= 0xfe0f)
                        // variation selectors
                || (glyphCode == 0xfeff)
                        // zero width no-break space
                || (glyphCode == 0xffa0)
                        // halfwidth hangul filler
                || (glyphCode >= 0xfff0 && glyphCode <= 0xfff8)
                        // reserved
                || (glyphCode >= 0x1bca0 && glyphCode <= 0x1bca3)
                        // shorthand format controls
                || (glyphCode >= 0x1d173 && glyphCode <= 0x1d17a)
                        // musical symbols
                || (glyphCode >= 0xe0000 && glyphCode <= 0xe01ef)
                        // variation selectors, tag space, reserved
                || (glyphCode >= 0xe01f0 && glyphCode <= 0xe0fff)
                        // reserved
                || ((glyphCode & 0xffff) >= 0xfffe)
                        // noncharacters
                || ((glyphCode >= 0xfdd0 && glyphCode <= 0xfdef)
                        && glyphCode != 0xfdd1)
                        // noncharacters; 0xfdd1 is used internally to force .notdef glyph
                ;
}


const GlyphCache*
FontCacheEntry::CachedGlyph(uint32 glyphCode)
{
        // Only requires a read lock.
        return fGlyphCache->FindGlyph(glyphCode);
}


bool
FontCacheEntry::CanCreateGlyph(uint32 glyphCode)
{
        // Note that this bypass any fallback or caching because it is used in
        // the fallback code itself.
        uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(glyphCode);
        return glyphIndex != 0;
}


const GlyphCache*
FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry)
{
        // We cache the glyph by the requested glyphCode. The FontEngine of this
        // FontCacheEntry may not contain a glyph for the given code, in which case
        // we ask the fallbackEntry for the code to index translation and let it
        // generate the glyph data. We will still use our own cache for storing the
        // glyph. The next time it will be found (by glyphCode).

        // NOTE: Both this and the fallback FontCacheEntry are expected to be
        // write-locked!

        const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode);
        if (glyph != NULL)
                return glyph;

        FontEngine* engine = &fEngine;
        uint32 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
        if (glyphIndex == 0 && fallbackEntry != NULL) {
                // Our FontEngine does not contain this glyph, but we can retry with
                // the fallbackEntry.
                engine = &fallbackEntry->fEngine;
                glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode);
        }

        if (glyphIndex == 0) {
                if (render_as_zero_width(glyphCode)) {
                        // cache and return a zero width glyph
                        return fGlyphCache->CacheGlyph(glyphCode, 0, glyph_data_invalid,
                                agg::rect_i(0, 0, -1, -1), 0, 0, 0, 0, 0, 0);
                }

                // reset to our engine
                engine = &fEngine;
                if (render_as_space(glyphCode)) {
                        // get the normal space glyph
                        glyphIndex = engine->GlyphIndexForGlyphCode(0x20 /* space */);
                }
        }

        if (engine->PrepareGlyph(glyphIndex)) {
                glyph = fGlyphCache->CacheGlyph(glyphCode,
                        engine->DataSize(), engine->DataType(), engine->Bounds(),
                        engine->AdvanceX(), engine->AdvanceY(),
                        engine->PreciseAdvanceX(), engine->PreciseAdvanceY(),
                        engine->InsetLeft(), engine->InsetRight());

                if (glyph != NULL)
                        engine->WriteGlyphTo(glyph->data);
        }

        return glyph;
}


void
FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
        double x, double y, GlyphMonoAdapter& monoAdapter,
        GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter,
        double scale)
{
        if (glyph == NULL)
                return;

        switch(glyph->data_type) {
                case glyph_data_mono:
                        monoAdapter.init(glyph->data, glyph->data_size, x, y);
                        break;

                case glyph_data_gray8:
                        gray8Adapter.init(glyph->data, glyph->data_size, x, y);
                        break;

                case glyph_data_subpix:
                        gray8Adapter.init(glyph->data, glyph->data_size, x, y);
                        break;

                case glyph_data_outline:
                        pathAdapter.init(glyph->data, glyph->data_size, x, y, scale);
                        break;

                default:
                        break;
        }
}


bool
FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2,
        double* x, double* y)
{
        return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
}


/*static*/ void
FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize,
        const ServerFont& font, bool forceVector)
{
        glyph_rendering renderingType = _RenderTypeFor(font, forceVector);

        // TODO: read more of these from the font
        FT_Encoding charMap = FT_ENCODING_NONE;
        bool hinting = font.Hinting();
        uint8 averageWeight = gSubpixelAverageWeight;

        snprintf(signature, signatureSize, "%" B_PRId32 ",%p,%u,%d,%d,%.1f,%d,%d",
                font.GetFamilyAndStyle(), font.Manager(), charMap,
                font.Face(), int(renderingType), font.Size(), hinting, averageWeight);
}


void
FontCacheEntry::UpdateUsage()
{
        // this is a static lock to prevent usage of too many semaphores,
        // but on the other hand, it is not so nice to be using a lock
        // here at all
        // the hope is that the time is so short to hold this lock, that
        // there is not much contention
        BAutolock _(sUsageUpdateLock);

        fLastUsedTime = system_time();
        fUseCounter++;
}


/*static*/ glyph_rendering
FontCacheEntry::_RenderTypeFor(const ServerFont& font, bool forceVector)
{
        glyph_rendering renderingType = gSubpixelAntialiasing ?
                glyph_ren_subpix : glyph_ren_native_gray8;

        if (forceVector || font.Rotation() != 0.0 || font.Shear() != 90.0
                || font.FalseBoldWidth() != 0.0
                || (font.Flags() & B_DISABLE_ANTIALIASING) != 0
                || font.Size() > 30
                || !font.Hinting()) {
                renderingType = glyph_ren_outline;
        }

        return renderingType;
}