haiku/src/servers/app/FontCacheEntry.cpp

245 lines
5.9 KiB
C++
Raw Normal View History

/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Maxim Shemanarev <mcseemagg@yahoo.com>
* Stephan Aßmus <superstippi@gmx.de>
*/
//----------------------------------------------------------------------------
// 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 <agg_array.h>
#include <Autolock.h>
#include "utf8_functions.h"
BLocker
FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock");
class FontCacheEntry::GlyphCachePool {
public:
enum block_size_e { block_size = 16384-16 };
GlyphCachePool()
: fAllocator(block_size)
{
memset(fGlyphs, 0, sizeof(fGlyphs));
}
const GlyphCache* FindGlyph(uint16 glyphCode) const
{
unsigned msb = (glyphCode >> 8) & 0xFF;
if (fGlyphs[msb])
return fGlyphs[msb][glyphCode & 0xFF];
return 0;
}
GlyphCache* CacheGlyph(uint16 glyphCode, unsigned glyphIndex,
unsigned dataSize, glyph_data_type dataType, const agg::rect_i& bounds,
float advanceX, float advanceY, float insetLeft, float insetRight)
{
unsigned msb = (glyphCode >> 8) & 0xFF;
if (fGlyphs[msb] == 0) {
fGlyphs[msb]
= (GlyphCache**)fAllocator.allocate(sizeof(GlyphCache*) * 256,
sizeof(GlyphCache*));
memset(fGlyphs[msb], 0, sizeof(GlyphCache*) * 256);
}
unsigned lsb = glyphCode & 0xFF;
if (fGlyphs[msb][lsb])
return 0; // already exists, do not overwrite
GlyphCache* glyph
= (GlyphCache*)fAllocator.allocate(sizeof(GlyphCache),
sizeof(double));
glyph->glyph_index = glyphIndex;
glyph->data = fAllocator.allocate(dataSize);
glyph->data_size = dataSize;
glyph->data_type = dataType;
glyph->bounds = bounds;
glyph->advance_x = advanceX;
glyph->advance_y = advanceY;
glyph->inset_left = insetLeft;
glyph->inset_right = insetRight;
return fGlyphs[msb][lsb] = glyph;
}
private:
agg::block_allocator fAllocator;
GlyphCache** fGlyphs[256];
};
// #pragma mark -
// constructor
FontCacheEntry::FontCacheEntry()
: MultiLocker("FontCacheEntry lock")
, Referenceable()
, fGlyphCache(new GlyphCachePool())
, fEngine()
, fLastUsedTime(LONGLONG_MIN)
, fUseCounter(0)
{
}
// destructor
FontCacheEntry::~FontCacheEntry()
{
//printf("~FontCacheEntry()\n");
delete fGlyphCache;
}
// Init
bool
FontCacheEntry::Init(const ServerFont& font)
{
glyph_rendering renderingType = glyph_ren_native_gray8;
if (font.Rotation() != 0.0 || font.Shear() != 90.0)
renderingType = glyph_ren_outline;
// TODO: encoding from font
FT_Encoding charMap = FT_ENCODING_NONE;
bool hinting = true; // TODO: font.Hinting();
if (!fEngine.Init(font.Path(), 0, font.Size(), charMap,
renderingType, hinting)) {
fprintf(stderr, "FontCacheEntry::Init() - some error loading font "
"file %s\n", font.Path());
return false;
}
return true;
}
// HasGlyphs
bool
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
{
uint32 charCode;
const char* start = utf8String;
while ((charCode = UTF8ToCharCode(&utf8String))) {
if (!fGlyphCache->FindGlyph(charCode))
return false;
if (utf8String - start + 1 > length)
break;
}
return true;
}
// Glyph
const GlyphCache*
FontCacheEntry::Glyph(uint16 glyphCode)
{
const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode);
if (glyph) {
return glyph;
} else {
if (fEngine.PrepareGlyph(glyphCode)) {
glyph = fGlyphCache->CacheGlyph(glyphCode,
fEngine.GlyphIndex(), fEngine.DataSize(),
fEngine.DataType(), fEngine.Bounds(),
fEngine.AdvanceX(), fEngine.AdvanceY(),
fEngine.InsetLeft(), fEngine.InsetRight());
fEngine.WriteGlyphTo(glyph->data);
return glyph;
}
}
return 0;
}
// InitAdaptors
void
FontCacheEntry::InitAdaptors(const GlyphCache* glyph,
double x, double y, GlyphMonoAdapter& monoAdapter,
GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter,
double scale)
{
if (!glyph)
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_outline:
pathAdapter.init(glyph->data, glyph->data_size, x, y, scale);
break;
default:
break;
}
}
// GetKerning
bool
FontCacheEntry::GetKerning(uint16 glyphCode1, uint16 glyphCode2,
double* x, double* y)
{
return fEngine.GetKerning(glyphCode1, glyphCode2, x, y);
}
// GenerateSignature
/*static*/ void
FontCacheEntry::GenerateSignature(char* signature, const ServerFont& font)
{
glyph_rendering renderingType = glyph_ren_native_gray8;
* after my last changes to font rendering, it was about 15% slower than before (although there should be much less lock contention) * with this change, there is quite a bit of cleanup, text drawing is now about 20% faster than before the original changes to font caching, mostly due to turning off the kerning feature, which at the moment gives absolutely no benefit. The correct way of doing it might be to use kerning depending on the provided glyph spacing mode * removed fPenLocation from Painter, the usage should be more correct now, since it is now consistently applied before the coordinate transformation from view to screen (also for DrawShape() now, before any view scaling and origin offset) * Painter no longer has it's own instance of a ServerFont, instead it uses its AGGTextRenderer instance, which was per Painter again after the last change, and not global anymore, made _UpdateFont() useless * When using GlyphLayoutEngine, it supports a second pass with the same FontCacheEntry through the introduction of a FontCacheReference. This speeds up DrawString a little, since it needs to calculate the bounding box for the string, and then draw the string in a second pass. This is now done with only one FontCacheEntry lookup * I also tried to optimize the on-the-fly conversion of UTF8->CharCode away, since it was done four times per DrawString, but surprisingly, it proofed to be a slight slowdown. * introduced a shortcut in DrawingEngine::DrawString() which avoids calculating the bounding box, we are now a tiny bit faster to figure out that we don't need to draw any string than Dano In the test environment (drawing to offscreen bitmap and blitting to screen eventually), text rendering is now about 3.7 times _faster_ than Dano text rendering (directly to screen), for untransformed text. Unfortunately I cannot test on the same machine in accelerant using version of the test environment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21822 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-08-04 15:37:16 +04:00
if (font.Rotation() != 0.0 || font.Shear() != 90.0
|| font.Flags() & B_DISABLE_ANTIALIASING
|| font.Size() > 30) {
renderingType = glyph_ren_outline;
* after my last changes to font rendering, it was about 15% slower than before (although there should be much less lock contention) * with this change, there is quite a bit of cleanup, text drawing is now about 20% faster than before the original changes to font caching, mostly due to turning off the kerning feature, which at the moment gives absolutely no benefit. The correct way of doing it might be to use kerning depending on the provided glyph spacing mode * removed fPenLocation from Painter, the usage should be more correct now, since it is now consistently applied before the coordinate transformation from view to screen (also for DrawShape() now, before any view scaling and origin offset) * Painter no longer has it's own instance of a ServerFont, instead it uses its AGGTextRenderer instance, which was per Painter again after the last change, and not global anymore, made _UpdateFont() useless * When using GlyphLayoutEngine, it supports a second pass with the same FontCacheEntry through the introduction of a FontCacheReference. This speeds up DrawString a little, since it needs to calculate the bounding box for the string, and then draw the string in a second pass. This is now done with only one FontCacheEntry lookup * I also tried to optimize the on-the-fly conversion of UTF8->CharCode away, since it was done four times per DrawString, but surprisingly, it proofed to be a slight slowdown. * introduced a shortcut in DrawingEngine::DrawString() which avoids calculating the bounding box, we are now a tiny bit faster to figure out that we don't need to draw any string than Dano In the test environment (drawing to offscreen bitmap and blitting to screen eventually), text rendering is now about 3.7 times _faster_ than Dano text rendering (directly to screen), for untransformed text. Unfortunately I cannot test on the same machine in accelerant using version of the test environment. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21822 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-08-04 15:37:16 +04:00
}
// TODO: read more of these from the font
FT_Encoding charMap = FT_ENCODING_NONE;
bool hinting = true; // TODO: font.Hinting();
sprintf(signature, "%ld%u%d%d%.1f%d",
font.GetFamilyAndStyle(), charMap,
font.Face(), int(renderingType), font.Size(), hinting);
}
// UpdateUsage
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++;
}