2007-08-02 23:10:38 +04:00
|
|
|
/*
|
|
|
|
* 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,
|
2007-08-03 05:11:27 +04:00
|
|
|
float advanceX, float advanceY, float insetLeft, float insetRight)
|
2007-08-02 23:10:38 +04:00
|
|
|
{
|
|
|
|
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;
|
2007-08-03 05:11:27 +04:00
|
|
|
glyph->inset_left = insetLeft;
|
|
|
|
glyph->inset_right = insetRight;
|
2007-08-02 23:10:38 +04:00
|
|
|
|
|
|
|
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
|
2007-08-03 05:11:27 +04:00
|
|
|
FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const
|
2007-08-02 23:10:38 +04:00
|
|
|
{
|
|
|
|
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(),
|
2007-08-03 05:11:27 +04:00
|
|
|
fEngine.AdvanceX(), fEngine.AdvanceY(),
|
|
|
|
fEngine.InsetLeft(), fEngine.InsetRight());
|
2007-08-02 23:10:38 +04:00
|
|
|
|
|
|
|
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) {
|
2007-08-02 23:10:38 +04:00
|
|
|
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
|
|
|
}
|
2007-08-02 23:10:38 +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++;
|
|
|
|
}
|
|
|
|
|