diff --git a/Ide/win/LgiIde.vcxproj b/Ide/win/LgiIde.vcxproj
--- a/Ide/win/LgiIde.vcxproj
+++ b/Ide/win/LgiIde.vcxproj
@@ -1,278 +1,278 @@
Debug
Win32
Debug
x64
Release
Win32
Release
x64
LgiIde
{967AF7EF-7C17-4FCC-997E-BED17F886F07}
LgiIde_vc8
Win32Proj
10.0
Application
v142
Unicode
true
Application
v142
Unicode
true
Application
v142
Unicode
Application
v142
Unicode
<_ProjectFileVersion>12.0.30501.0
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\..\..\CodeLib\libpng-1.6.26
Disabled
..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;..\resources;C:\Program Files (x86)\OpenSSL-Win32\include;%(AdditionalIncludeDirectories)
WIN32;_DEBUG;WINDOWS;%(PreprocessorDefinitions)
true
EnableFastChecks
MultiThreadedDebugDLL
Level2
ProgramDatabase
imm32.lib;Mincore.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
..\lib;%(AdditionalLibraryDirectories)
true
Windows
false
MachineX86
Disabled
..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;..\resources;C:\Program Files\OpenSSL-Win64\include;%(AdditionalIncludeDirectories)
WIN32;_DEBUG;WINDOWS;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDebugDLL
Level2
ProgramDatabase
imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
..\lib;%(AdditionalLibraryDirectories)
true
Windows
false
..\..\src\win\General\Lgi.manifest
..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;.\Resources;C:\Program Files (x86)\OpenSSL-Win32\include;%(AdditionalIncludeDirectories)
WIN32;NDEBUG;WINDOWS;%(PreprocessorDefinitions)
MultiThreadedDLL
Level2
ProgramDatabase
imm32.lib;Mincore.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
..\lib;%(AdditionalLibraryDirectories)
true
Windows
true
true
false
MachineX86
..\..\include;..\..\include\lgi\win;..\..\..\..\..\CodeLib\zlib;..\..\..\..\..\CodeLib\libpng;..\..\..\..\..\CodeLib\libpng\win32;..\..\..\..\..\CodeLib\zlib\win32;..\resources;C:\Program Files\OpenSSL-Win64\include;%(AdditionalIncludeDirectories)
WIN32;NDEBUG;WINDOWS;%(PreprocessorDefinitions)
MultiThreadedDLL
Level2
ProgramDatabase
imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
..\lib;%(AdditionalLibraryDirectories)
true
Windows
true
true
false
..\..\src\win\General\Lgi.manifest
-
+
{95df9ca4-6d37-4a85-a648-80c2712e0da1}
false
true
false
true
\ No newline at end of file
diff --git a/include/lgi/common/DisplayString.h b/include/lgi/common/DisplayString.h
--- a/include/lgi/common/DisplayString.h
+++ b/include/lgi/common/DisplayString.h
@@ -1,307 +1,299 @@
#ifndef _GDISPLAY_STRING_H_
#define _GDISPLAY_STRING_H_
#include "lgi/common/Font.h"
#if defined(LGI_SDL)
#elif defined(LINUX)
namespace Pango
{
#include "pango/pango.h"
#include "pango/pangocairo.h"
}
#endif
#if !defined(_MSC_VER) || defined(__GTK_H__)
#define LGI_DSP_STR_CACHE 1
#endif
/// \brief Cache for text measuring, glyph substitution and painting
///
/// To paint text onto the screen several stages need to be implemented to
/// properly support unicode on multiple platforms. This class addresses all
/// of those needs and then allows you to cache the results to reduce text
/// related workload.
///
/// The first stage is converting text into the native format for the
/// OS's API. This usually involved converting the text to wide characters for
/// Linux or Windows, or Utf-8 for Haiku. Then the text is converted into runs of
/// characters that can be rendered in the same font. If glyph substitution is
/// required to render the characters a separate run is used with a different
/// font ID. Finally you can measure or paint these runs of text. Also tab characters
/// are expanded to the current tab size setting.
class LgiClass LDisplayString
{
protected:
LSurface *pDC;
LFont *Font;
uint32_t x, y;
int xf, yf;
int DrawOffsetF;
// Flags
uint8_t LaidOut : 1;
uint8_t AppendDots : 1;
uint8_t VisibleTab : 1;
/* String data:
Str Wide
Windows: utf-16 utf-16
MacCarbon: utf-16 utf-32
MacCocoa: utf-16 utf-32
Gtk3: utf-8 utf-32
Haiku: utf-8 utf-32
*/
OsChar *Str;
ssize_t StrWords;
#if LGI_DSP_STR_CACHE
wchar_t *Wide;
ssize_t WideWords;
#endif
#if defined(LGI_SDL)
LAutoPtr Img;
#elif defined(__GTK_H__)
struct GDisplayStringPriv *d;
friend struct GDisplayStringPriv;
#elif defined(MAC)
#if USE_CORETEXT
CTLineRef Hnd;
CFAttributedStringRef AttrStr;
void CreateAttrStr();
#else
ATSUTextLayout Hnd;
ATSUTextMeasurement fAscent;
ATSUTextMeasurement fDescent;
#endif
#elif defined(WINNATIVE) || defined(HAIKU)
class CharInfo
{
public:
- OsChar *Str;
- int Len;
- int X;
- uint8_t FontId;
- int8 SizeDelta;
-
- CharInfo()
- {
- Str = 0;
- Len = 0;
- X = 0;
- FontId = 0;
- SizeDelta = 0;
- }
+ OsChar *Str = NULL;
+ int Len = 0;
+ int X = 0;
+ uint8_t FontId = 0;
+ int8_t SizeDelta = 0;
+ uint8_t Missing = false;
uint32_t First()
{
auto s = (const uint16*)Str;
ssize_t l = Len * sizeof(*Str);
return LgiUtf16To32(s, l);
}
};
LArray Info;
#endif
void Layout(bool Debug = false);
void DrawWhiteSpace(LSurface *pDC, char Ch, LRect &r);
public:
// Turn on debug logging...
bool _debug = false;
/// Constructor
LDisplayString
(
/// The base font. Must not be destroyed during the lifetime of this object.
LFont *f,
/// Utf-8 input string
const char *s,
/// Number of bytes in the input string or -1 for NULL terminated.
ssize_t l = -1,
LSurface *pdc = 0
);
/// Constructor
LDisplayString
(
/// The base font. Must not be destroyed during the lifetime of this object.
LFont *f,
/// A wide character input string
const char16 *s,
/// The number of characters in the input string (NOT the number of bytes) or -1 for NULL terminated
ssize_t l = -1,
LSurface *pdc = 0
);
#ifdef _MSC_VER
/// Constructor
LDisplayString
(
/// The base font. Must not be destroyed during the lifetime of this object.
LFont *f,
/// A wide character input string
const uint32_t *s,
/// The number of characters in the input string (NOT the number of bytes) or -1 for NULL terminated
ssize_t l = -1,
LSurface *pdc = 0
);
#endif
virtual ~LDisplayString();
LDisplayString &operator=(const LDisplayString &s)
{
LAssert(0);
return *this;
}
/// \returns the draw offset used to calculate tab spacing (in Px)
int GetDrawOffset();
/// Sets the draw offset used to calculate tab spacing (in Px)
void SetDrawOffset(int Px);
/// Returns the ShowVisibleTab setting.
/// Treats Unicode-2192 (left arrow) as a tab char
bool ShowVisibleTab();
/// Sets the ShowVisibleTab setting.
/// Treats Unicode-2192 (left arrow) as a tab char
void ShowVisibleTab(bool i);
/// \returns the font used to create the layout
LFont *GetFont() { return Font; };
/// Fits string into 'width' pixels, truncating with '...' if it's not going to fit
void TruncateWithDots
(
/// The number of pixels the string has to fit
int Width
);
/// Returns true if the string is trucated
bool IsTruncated();
/// Returns the words (not chars) in the OsChar string
ssize_t Length();
/*
/// Sets the number of words in the OsChar string
void Length(int NewLen);
*/
/// Returns the pointer to the native string
operator const char16*()
{
#if LGI_DSP_STR_CACHE
return Wide;
#else
return Str;
#endif
}
/// \returns the number of words in the wide string
ssize_t WideLength()
{
#if LGI_DSP_STR_CACHE
return WideWords;
#else
return StrWords;
#endif
}
// API that use full pixel sizes:
/// Returns the width of the whole string
int X();
/// Returns the height of the whole string
int Y();
/// Returns the width and height of the whole string
LPoint Size();
/// Returns the number of characters that fit in 'x' pixels.
ssize_t CharAt(int x, LPxToIndexType Type = LgiTruncate);
/// Draws the string onto a device surface
void Draw
(
/// The output device
LSurface *pDC,
/// The x coordinate of the top left corner of the output box
int x,
/// The y coordinate of the top left corner of the output box
int y,
/// An optional clipping rectangle. If the font is not transparent this rectangle will be filled with the background colour.
LRect *r = NULL,
/// Turn on debug logging
bool Debug = false
);
/// Draws the string onto a device surface
void DrawCenter
(
/// The output device
LSurface *pDC,
/// An rectangle to center in. If the font is not transparent this rectangle will be filled with the background colour.
LRect *r
)
{
if (r)
Draw(pDC, r->x1 + ((r->X() - X()) >> 1), r->y1 + ((r->Y() - Y()) >> 1), r);
}
// API that uses fractional pixel sizes.
enum
{
#if defined LGI_SDL
FShift = 6,
FScale = 1 << FShift,
#elif defined __GTK_H__
/// This is the value for 1 pixel.
FScale = PANGO_SCALE,
/// This is bitwise shift to convert between integer and fractional
FShift = 10
#elif defined MAC
/// This is the value for 1 pixel.
FScale = 0x10000,
/// This is bitwise shift to convert between integer and fractional
FShift = 16
#else
/// This is the value for 1 pixel.
FScale = 1,
/// This is bitwise shift to convert between integer and fractional
FShift = 0
#endif
};
/// \returns the draw offset used to calculate tab spacing (in fractional units)
int GetDrawOffsetF();
/// Sets the draw offset used to calculate tab spacing (in fractional units)
void SetDrawOffsetF(int Fpx);
/// \returns the fractional width of the string
int FX();
/// \returns the fractional height of the string
int FY();
/// \returns both the fractional width and height of the string
LPoint FSize();
/// Draws the string using fractional co-ordinates.
void FDraw
(
/// The output device
LSurface *pDC,
/// The fractional x coordinate of the top left corner of the output box
int fx,
/// The fractional y coordinate of the top left corner of the output box
int fy,
/// [Optional] fractional clipping rectangle. If the font is not transparent
/// this rectangle will be filled with the background colour.
LRect *frc = NULL,
/// [Optional] print debug info
bool Debug = false
);
};
#endif
diff --git a/src/common/Gdc2/Font/DisplayString.cpp b/src/common/Gdc2/Font/DisplayString.cpp
--- a/src/common/Gdc2/Font/DisplayString.cpp
+++ b/src/common/Gdc2/Font/DisplayString.cpp
@@ -1,2293 +1,2299 @@
//////////////////////////////////////////////////////////////////////
// Includes
#include
#include
#include
#include
#ifdef HAIKU
#include
#endif
#include "lgi/common/Lgi.h"
#include "lgi/common/Variant.h"
#include "lgi/common/FontSelect.h"
#include "lgi/common/GdiLeak.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/PixelRops.h"
#include "lgi/common/UnicodeString.h"
#ifdef FontChange
#undef FontChange
#endif
#ifdef LGI_SDL
#include "ftsynth.h"
#endif
#if WINNATIVE
static OsChar GDisplayStringDots[] = {'.', '.', '.', 0};
#endif
//345678123456781234567812345678
// 2nd
#define DEBUG_CHAR_AT 0
#define DEBUG_BOUNDS 0
#if defined(__GTK_H__) || (defined(MAC) && !defined(LGI_SDL))
#define DISPLAY_STRING_FRACTIONAL_NATIVE 1
#else
#define DISPLAY_STRING_FRACTIONAL_NATIVE 0
#endif
#if defined(__GTK_H__)
struct Block : public LRect
{
/// This points to somewhere in Ds->Str
OsChar *Str = NULL;
/// Bytes in this block
int Bytes = 0;
/// Utf-8 characters in this block
int Chars = 0;
/// Alternative font to get characters from (NULL if using the display string's font)
LFont *Fnt = NULL;
/// Layout for this block. Shouldn't ever be NULL. But shouldn't crash otherwise.
Gtk::PangoLayout *Hnd = NULL;
~Block()
{
if (Hnd)
g_object_unref(Hnd);
}
};
struct GDisplayStringPriv
{
LDisplayString *Ds;
LArray Blocks;
bool Debug;
int LastTabOffset;
GDisplayStringPriv(LDisplayString *str) : Ds(str)
{
#if 0
Debug = Stristr(Ds->Str, "(Jumping).wma") != 0;
#else
Debug = false;
#endif
LastTabOffset = -1;
}
~GDisplayStringPriv()
{
}
void Create(Gtk::GtkPrintContext *PrintCtx)
{
auto *Fs = LFontSystem::Inst();
auto *Fnt = Ds->Font;
auto Tbl = Fnt->GetGlyphMap();
int Chars = 0;
LUtf8Ptr p(Ds->Str);
auto *Start = p.GetPtr();
if (Tbl)
{
int32 w;
Block *b = NULL;
auto DisplayCtx = LFontSystem::Inst()->GetContext();
while ((w = (int32)p))
{
LFont *f;
if (w >= 0x80 && !_HasUnicodeGlyph(Tbl, w))
f = Fs->GetGlyph(w, Ds->Font);
else
f = Ds->Font;
if (!b || (f != NULL && f != Fnt))
{
// Finish old block
if (b)
{
b->Bytes = p.GetPtr() - Start;
b->Chars = Chars;
Chars = 0;
}
Start = p.GetPtr();
if (f)
{
// Start new block...
b = &Blocks.New();
b->Str = (char*)Start;
b->Bytes = -1; // unknown at this point
if (f != Ds->Font)
// External font
b->Fnt = f;
// Create a pango layout
if (PrintCtx)
b->Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx);
else
b->Hnd = Gtk::pango_layout_new(DisplayCtx);
}
// else no font supports glyph
Fnt = f;
}
// else no change in font
p++;
Chars++;
}
if (b)
{
// Finish the last block
b->Bytes = p.GetPtr() - Start;
b->Chars = Chars;
}
if (Debug)
{
// Print the block array
for (size_t i=0; iStrWords)
{
p++;
Chars++;
}
auto &b = Blocks.New();
b.Str = (char*)Start;
b.Bytes = p.GetPtr() - Start;
b.Chars = Chars;
if (PrintCtx)
b.Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx);
else
b.Hnd = Gtk::pango_layout_new(LFontSystem::Inst()->GetContext());
}
/* This could get stuck in an infinite loop. Leaving out for the moment.
for (auto &b: Blocks)
{
if (b.Hnd == NULL && b.Fnt == NULL)
{
Blocks.Length(0);
goto Start;
}
}
*/
}
void UpdateTabs(int Offset, int Size, bool Debug = false)
{
if (Ds->Font &&
Ds->Font->TabSize())
{
int Len = 16;
LastTabOffset = Offset;
Gtk::PangoTabArray *t = Gtk::pango_tab_array_new(Len, true);
if (t)
{
for (int i=0; i
bool StringConvert(Out *&out, ssize_t &OutWords, const In *in, ssize_t InLen)
{
if (!in)
{
out = 0;
OutWords = 0;
return false;
}
auto InSz = sizeof(In);
auto OutSz = sizeof(Out);
// Work out input size
ssize_t InWords;
if (InLen >= 0)
InWords = InLen;
else
for (InWords = 0; in[InWords]; InWords++)
;
if (InSz == OutSz)
{
// No conversion optimization
out = (Out*)Strdup(in, InWords);
OutWords = out ? InWords : 0;
return out != 0;
}
else
{
// Convert the string to new word size
static const char *Cp[] = { NULL, "utf-8", "utf-16", NULL, "utf-32"};
LAssert(OutSz <= 4 && InSz <= 4 && Cp[OutSz] && Cp[InSz]);
out = (Out*) LNewConvertCp(Cp[OutSz], in, Cp[InSz], InWords*sizeof(In));
OutWords = Strlen(out);
return out != NULL;
}
return false;
}
//////////////////////////////////////////////////////////////////////
#define SubtractPtr(a, b) ( ((a)-(b)) / sizeof(*a) )
#define VisibleTabChar 0x2192
#define IsTabChar(c) (c == '\t') // || (c == VisibleTabChar && VisibleTab))
#if USE_CORETEXT
#include
void LDisplayString::CreateAttrStr()
{
if (!Wide)
return;
if (AttrStr)
{
CFRelease(AttrStr);
AttrStr = NULL;
}
wchar_t *w = Wide;
CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)w, StrlenW(w) * sizeof(*w), kCFStringEncodingUTF32LE, false);
if (string)
{
CFDictionaryRef attributes = Font->GetAttributes();
if (attributes)
AttrStr = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
// else LAssert(0);
CFRelease(string);
}
}
#endif
LDisplayString::LDisplayString(LFont *f, const char *s, ssize_t l, LSurface *pdc)
{
pDC = pdc;
Font = f;
#if LGI_DSP_STR_CACHE
StringConvert(Wide, WideWords, s, l);
#endif
StringConvert(Str, StrWords, s, l);
x = y = 0;
xf = 0;
yf = 0;
DrawOffsetF = 0;
LaidOut = 0;
AppendDots = 0;
VisibleTab = 0;
#if defined __GTK_H__
d = new GDisplayStringPriv(this);
if (Font && Str)
{
LAssert(StrWords >= 0);
if (StrWords > 0)
d->Create(pDC ? pDC->GetPrintContext() : NULL);
}
#elif defined MAC && !defined(LGI_SDL)
Hnd = 0;
#if USE_CORETEXT
AttrStr = NULL;
#endif
if (Font && Str && StrWords > 0)
{
#if USE_CORETEXT
CreateAttrStr();
#else
ATSUCreateTextLayout(&Hnd);
#endif
}
#endif
}
LDisplayString::LDisplayString(LFont *f, const char16 *s, ssize_t l, LSurface *pdc)
{
pDC = pdc;
Font = f;
#if LGI_DSP_STR_CACHE
StringConvert(Wide, WideWords, s, l);
#endif
StringConvert(Str, StrWords, s, l);
x = y = 0;
xf = 0;
yf = 0;
DrawOffsetF = 0;
LaidOut = 0;
AppendDots = 0;
VisibleTab = 0;
#if defined __GTK_H__
d = new GDisplayStringPriv(this);
if (Font && Str && StrWords > 0)
d->Create(pDC ? pDC->GetPrintContext() : NULL);
#elif defined MAC && !defined(LGI_SDL)
Hnd = NULL;
#if USE_CORETEXT
AttrStr = NULL;
#endif
if (Font && Str && StrWords > 0)
{
#if USE_CORETEXT
CreateAttrStr();
#else
OSStatus e = ATSUCreateTextLayout(&Hnd);
if (e) printf("%s:%i - ATSUCreateTextLayout failed with %i.\n", _FL, (int)e);
#endif
}
#endif
}
#ifdef _MSC_VER
LDisplayString::LDisplayString(LFont *f, const uint32_t *s, ssize_t l, LSurface *pdc)
{
pDC = pdc;
Font = f;
x = y = 0;
xf = 0;
yf = 0;
DrawOffsetF = 0;
LaidOut = 0;
AppendDots = 0;
VisibleTab = 0;
#if LGI_DSP_STR_CACHE
StringConvert(Wide, WideWords, s, l);
#endif
StringConvert(Str, StrWords, s, l);
#if defined __GTK_H__
d = new GDisplayStringPriv(this);
if (Font && Str && StrWords > 0)
d->Create(pDC ? pDC->GetPrintContext() : NULL);
#endif
}
#endif
LDisplayString::~LDisplayString()
{
#if defined(LGI_SDL)
Img.Reset();
#elif defined __GTK_H__
DeleteObj(d);
#elif defined MAC
#if USE_CORETEXT
if (Hnd)
{
CFRelease(Hnd);
Hnd = NULL;
}
if (AttrStr)
{
CFRelease(AttrStr);
AttrStr = NULL;
}
#else
if (Hnd)
ATSUDisposeTextLayout(Hnd);
#endif
#endif
DeleteArray(Str);
#if LGI_DSP_STR_CACHE
DeleteArray(Wide);
#endif
}
void LDisplayString::DrawWhiteSpace(LSurface *pDC, char Ch, LRect &r)
{
if (Ch == '\t')
{
r.Inset(3, 3);
if (r.Y()/2 == 0)
r.y2++;
int Cy = (r.Y() >> 1);
pDC->Line(r.x1, r.y1+Cy, r.x2, r.y1+Cy);
pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y1);
pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y2);
}
else // Space
{
int x = r.x1 + (r.X()>>1) - 1;
int Cy = r.y1 + (int)ceil(Font->Ascent()) - 2;
pDC->Rectangle(x, Cy, x+1, Cy+1);
}
}
void LDisplayString::Layout(bool Debug)
{
if (LaidOut || !Font)
return;
LaidOut = 1;
#if defined(LGI_SDL)
FT_Face Fnt = Font->Handle();
FT_Error error;
if (!Fnt || !Str)
return;
// Create an array of glyph indexes
LArray Glyphs;
for (OsChar *s = Str; *s; s++)
{
FT_UInt index = FT_Get_Char_Index(Fnt, *s);
if (index)
Glyphs.Add(index);
}
// Measure the string...
LPoint Sz;
int FontHt = Font->GetHeight();
int AscentF = (int) (Font->Ascent() * FScale);
int LoadMode = FT_LOAD_FORCE_AUTOHINT;
for (unsigned i=0; iglyph->metrics.horiBearingY;
Sz.x += Fnt->glyph->metrics.horiAdvance;
Sz.y = MAX(Sz.y, PyF + Fnt->glyph->metrics.height);
}
}
// Create the memory context to draw into
xf = Sz.x;
x = ((Sz.x + FScale - 1) >> FShift) + 1;
yf = FontHt << FShift;
y = FontHt; // ((Sz.y + FScale - 1) >> FShift) + 1;
if (Img.Reset(new LMemDC(x, y, CsIndex8)))
{
// Clear the context to black
Img->Colour(0);
Img->Rectangle();
bool IsItalic = Font->Italic();
int CurX = 0;
int FBaseline = Fnt->size->metrics.ascender;
for (unsigned i=0; iglyph);
error = FT_Render_Glyph(Fnt->glyph, FT_RENDER_MODE_NORMAL);
if (error == 0)
{
FT_Bitmap &bmp = Fnt->glyph->bitmap;
if (bmp.buffer)
{
// Copy rendered glyph into our image memory
int Px = (CurX + (FScale >> 1)) >> FShift;
int PyF = AscentF - Fnt->glyph->metrics.horiBearingY;
int Py = PyF >> FShift;
int Skip = 0;
if (Fnt->glyph->format == FT_GLYPH_FORMAT_BITMAP)
{
Px += Fnt->glyph->bitmap_left;
Skip = Fnt->glyph->bitmap_left < 0 ? -Fnt->glyph->bitmap_left : 0;
Py = (AscentF >> FShift) - Fnt->glyph->bitmap_top;
}
LAssert(Px + bmp.width <= Img->X());
for (int y=0; y= 0);
out += Px+Skip;
memcpy(out, in+Skip, bmp.width-Skip);
}
/*
else
{
LAssert(!"No scanline?");
break;
}
*/
}
}
if (i < Glyphs.Length() - 1)
{
FT_Vector kerning;
FT_Get_Kerning(Fnt, Glyphs[i], Glyphs[i+1], FT_KERNING_DEFAULT, &kerning);
CurX += Fnt->glyph->metrics.horiAdvance + kerning.x;
}
else
{
CurX += Fnt->glyph->metrics.horiAdvance;
}
}
}
}
}
else LgiTrace("::Layout Create MemDC failed\n");
#elif defined(__GTK_H__)
y = Font->GetHeight();
yf = y * PANGO_SCALE;
if (!d->Blocks.Length() || !Font->Handle())
return;
LUtf8Ptr Utf(Str);
int32 Wide;
while (*Utf.GetPtr())
{
Wide = Utf;
if (!Wide)
{
LgiTrace("%s:%i - Not utf8\n", _FL);
return;
}
Utf++;
}
LFontSystem *FSys = LFontSystem::Inst();
Gtk::pango_context_set_font_description(FSys->GetContext(), Font->Handle());
int TabSizePx = Font->TabSize();
int TabSizeF = TabSizePx * FScale;
int TabOffsetF = DrawOffsetF % TabSizeF;
int OffsetF = TabOffsetF ? TabSizeF - TabOffsetF : 0;
d->UpdateTabs(OffsetF / FScale, Font->TabSize());
if (Font->Underline())
{
Gtk::PangoAttrList *attrs = Gtk::pango_attr_list_new();
Gtk::PangoAttribute *attr = Gtk::pango_attr_underline_new(Gtk::PANGO_UNDERLINE_SINGLE);
Gtk::pango_attr_list_insert(attrs, attr);
for (auto &b: d->Blocks)
Gtk::pango_layout_set_attributes(b.Hnd, attrs);
Gtk::pango_attr_list_unref(attrs);
}
int Fx = 0;
for (auto &b: d->Blocks)
{
int bx = 0, by = 0;
if (b.Hnd)
{
if (!LIsUtf8(b.Str, b.Bytes))
{
LgiTrace("Invalid UTF8: '%.*S'\n", (int)b.Bytes, b.Str);
}
else
{
Gtk::pango_layout_set_text(b.Hnd, b.Str, b.Bytes);
}
Gtk::pango_layout_get_size(b.Hnd, &bx, &by);
}
else if (b.Fnt)
{
b.Fnt->_Measure(bx, by, b.Str, b.Bytes);
bx <<= FShift;
by <<= FShift;
}
b.ZOff(bx-1, by-1);
b.Offset(Fx, 0);
xf += bx;
yf = MAX(yf, by);
}
x = (xf + PANGO_SCALE - 1) / PANGO_SCALE;
#if 1
y = Font->GetHeight();
#else
y = (yf + PANGO_SCALE - 1) / PANGO_SCALE;
#endif
if (y > Font->GetHeight())
{
printf("%s:%i - Height error: %i > %i\n", _FL, y, Font->GetHeight());
}
#elif defined MAC && !defined(LGI_SDL)
#if USE_CORETEXT
int height = Font->GetHeight();
y = height;
if (AttrStr)
{
LAssert(!Hnd);
Hnd = CTLineCreateWithAttributedString(AttrStr);
if (Hnd)
{
CGFloat ascent = 0.0;
CGFloat descent = 0.0;
CGFloat leading = 0.0;
double width = CTLineGetTypographicBounds(Hnd, &ascent, &descent, &leading);
x = ceil(width);
xf = width * FScale;
yf = height * FScale;
}
}
#else
if (!Hnd || !Str)
return;
OSStatus e = ATSUSetTextPointerLocation(Hnd, Str, 0, len, len);
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUSetTextPointerLocation failed with errorcode %i (%s)\n", _FL, (int)e, a);
DeleteArray(a);
return;
}
e = ATSUSetRunStyle(Hnd, Font->Handle(), 0, len);
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUSetRunStyle failed with errorcode %i (%s)\n", _FL, (int)e, a);
DeleteArray(a);
return;
}
ATSUTextMeasurement fTextBefore;
ATSUTextMeasurement fTextAfter;
if (pDC)
{
OsPainter dc = pDC->Handle();
ATSUAttributeTag Tags[1] = {kATSUCGContextTag};
ByteCount Sizes[1] = {sizeof(CGContextRef)};
ATSUAttributeValuePtr Values[1] = {&dc};
e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values);
if (e) printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e);
}
ATSUTab Tabs[32];
for (int i=0; iTabSize()) << 16;
Tabs[i].tabType = kATSULeftTab;
}
e = ATSUSetTabArray(Hnd, Tabs, CountOf(Tabs));
if (e) printf("%s:%i - ATSUSetTabArray failed (e=%i)\n", _FL, (int)e);
e = ATSUGetUnjustifiedBounds( Hnd,
kATSUFromTextBeginning,
kATSUToTextEnd,
&fTextBefore,
&fTextAfter,
&fAscent,
&fDescent);
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUGetUnjustifiedBounds failed with errorcode %i (%s)\n", _FL, (int)e, a);
DeleteArray(a);
return;
}
xf = fTextAfter - fTextBefore;
yf = fAscent + fDescent;
x = (xf + 0xffff) >> 16;
y = (yf + 0xffff) >> 16;
ATSUSetTransientFontMatching(Hnd, true);
#endif
#elif defined(HAIKU)
if (!Font)
{
LgiTrace("%s:%i - Missing pointer: %p\n", _FL, Font);
return;
}
BFont *fnt = Font->Handle();
if (!fnt)
{
LgiTrace("%s:%i - Missing handle. %p/%p\n", _FL, fnt);
return;
}
int tabSize = Font->TabSize() ? Font->TabSize() : 32;
font_height height = {0};
fnt->GetHeight(&height);
yf = y = height.ascent + height.descent + height.leading;
if (!Str)
return;
LUtf8Ptr start(Str);
int isTab = -1;
for (LUtf8Ptr p(Str); true; p++)
{
int32_t ch = p;
if (isTab < 0)
{
isTab = IsTabChar(ch);
}
else if (!ch || IsTabChar(ch) ^ isTab)
{
auto &l = Info.New();
l.Str = start.GetPtr();
l.Len = p.GetPtr() - start.GetPtr();
const char *strArr[] = { l.Str };
const int32 strLen[] = { l.Len };
float width[1] = { 0 };
fnt->GetStringWidths(strArr, strLen, 1, width);
if (isTab)
{
// Handle tab(s)
for (int t=0; tHandle())
Font->Create();
y = Font->GetHeight();
LFontSystem *Sys = LFontSystem::Inst();
if (Sys && Str)
{
LFont *f = Font;
bool GlyphSub = Font->SubGlyphs();
Info[i].Str = Str;
int TabSize = Font->TabSize() ? Font->TabSize() : 32;
bool WasTab = IsTabChar(*Str);
f = GlyphSub ? Sys->GetGlyph(*Str, Font) : Font;
if (f && f != Font)
{
f->Size(Font->Size());
f->SetWeight(Font->GetWeight());
if (!f->Handle())
f->Create();
}
bool Debug = WasTab;
uint32_t u32;
for (LUnicodeString u(Str, StrWords); true; u++)
{
u32 = *u;
- LFont *n = GlyphSub ? Sys->GetGlyph(u32, Font) : Font;
- bool Change = n != f || // The font changed
- (IsTabChar(u32) ^ WasTab) || // Entering/leaving a run of tabs
- !u32 || // Hit a NULL character
- (u.Get() - Info[i].Str) >= 1000; // This is to stop very long segments not rendering
+ LFont *n = u32 > ' ' && GlyphSub ? Sys->GetGlyph(u32, Font) : Font;
+ bool Change = n != f || // The font changed
+ (IsTabChar(u32) ^ WasTab) || // Entering/leaving a run of tabs
+ !u32 || // Hit a NULL character
+ (u.Get() - Info[i].Str) >= 1000; // This is to stop very long segments not rendering
if (Change)
{
// End last segment
if (n && n != Font)
{
n->Size(Font->Size());
n->SetWeight(Font->GetWeight());
if (!n->Handle())
n->Create();
}
- Info[i].Len = (int) (u.Get() - Info[i].Str);
- if (Info[i].Len)
+ auto &CurInfo = Info[i];
+ CurInfo.Len = (int) (u.Get() - CurInfo.Str);
+ if (CurInfo.Len)
{
if (WasTab)
{
// Handle tab(s)
- for (int t=0; tGetHeight() > Font->GetHeight()))
{
- Info[i].SizeDelta = -1;
- f->PointSize(Font->PointSize() + Info[i].SizeDelta);
+ CurInfo.SizeDelta = -1;
+ f->PointSize(Font->PointSize() + CurInfo.SizeDelta);
f->Create();
}
#endif
- if (!f)
+ if (u32 && !f)
{
// no font, so ? out the chars... as they aren't available anyway
- // printf("Font Cache Miss, Len=%i\n\t", Info[i].Len);
+ LgiTrace("MissingGlyph: %i 0x%x\n", u32, u32);
+ CurInfo.Missing = true;
+ #if 0
+ for (int n=0; n_Measure(sx, sy, Info[i].Str, Info[i].Len);
- x += Info[i].X = sx > 0xffff ? 0xffff : sx;
+
+ m->_Measure(sx, sy, CurInfo.Str, CurInfo.Len);
+ x += CurInfo.X = sx > 0xffff ? 0xffff : sx;
}
- auto Ch = Info[i].First();
- Info[i].FontId = !f || Font == f ? 0 : Sys->Lut[Ch];
+ auto Ch = CurInfo.First();
+ CurInfo.FontId = !f || Font == f ? 0 : Sys->Lut[Ch];
i++;
}
f = n;
// Start next segment
WasTab = IsTabChar(u32);
Info[i].Str = u.Get();
}
if (!u32) break;
}
if (Info.Length() > 0 && Info.Last().Len == 0)
{
Info.Length(Info.Length()-1);
}
}
xf = x;
yf = y;
#endif
}
int LDisplayString::GetDrawOffset()
{
return DrawOffsetF >> FShift;
}
void LDisplayString::SetDrawOffset(int Px)
{
if (LaidOut)
LAssert(!"No point setting TabOrigin after string is laid out.\n");
DrawOffsetF = Px << FShift;
}
bool LDisplayString::ShowVisibleTab()
{
return VisibleTab;
}
void LDisplayString::ShowVisibleTab(bool i)
{
VisibleTab = i;
}
bool LDisplayString::IsTruncated()
{
return AppendDots;
}
void LDisplayString::TruncateWithDots(int Width)
{
Layout();
#if defined __GTK_H__
int Fx = 0;
int Fwid = Width << FShift;
for (auto &b: d->Blocks)
{
if (Fwid < Fx + b.X())
{
if (b.Hnd)
{
Gtk::pango_layout_set_ellipsize(b.Hnd, Gtk::PANGO_ELLIPSIZE_END);
Gtk::pango_layout_set_width(b.Hnd, Fwid - Fx);
Gtk::pango_layout_set_single_paragraph_mode(b.Hnd, true);
}
else if (b.Fnt)
{
}
break;
}
Fx += b.X();
}
#elif WINNATIVE
if (Width < X() + 8)
{
ssize_t c = CharAt(Width);
if (c >= 0 && c < StrWords)
{
if (c > 0) c--; // fudge room for dots
if (c > 0) c--;
AppendDots = 1;
if (Info.Length())
{
int Width = 0;
int Pos = 0;
for (int i=0; i= Pos &&
c < Pos + Info[i].Len)
{
Info[i].Len = (int) (c - Pos);
Info[i].Str[Info[i].Len] = 0;
LFont *f = Font;
if (Info[i].FontId)
{
LFontSystem *Sys = LFontSystem::Inst();
f = Sys->Font[Info[i].FontId];
f->PointSize(Font->PointSize() + Info[i].SizeDelta);
if (!f->Handle())
{
f->Create();
}
}
else
{
f = Font;
}
if (f)
{
int Sx, Sy;
f->_Measure(Sx, Sy, Info[i].Str, Info[i].Len);
Info[i].X = Sx;
Width += Info[i].X;
}
Info.Length(i + 1);
break;
}
Pos += Info[i].Len;
Width += Info[i].X;
}
int DotsX, DotsY;
Font->_Measure(DotsX, DotsY, GDisplayStringDots, 3);
x = Width + DotsX;
}
}
}
#elif defined(LGI_SDL)
#elif defined(MAC)
#if USE_CORETEXT
if (Hnd)
{
/*
CFAttributedStringRef truncationString = CFAttributedStringCreate(NULL, CFSTR("\u2026"), Font->GetAttributes());
if (truncationString)
{
CTLineRef truncationToken = CTLineCreateWithAttributedString(truncationString);
CFRelease(truncationString);
if (truncationToken)
{
CTLineRef TruncatedLine = CTLineCreateTruncatedLine(Hnd, Width, kCTLineTruncationEnd, truncationToken);
CFRelease(truncationToken);
if (TruncatedLine)
{
CFRelease(Hnd);
Hnd = TruncatedLine;
}
}
}
*/
}
#endif
#endif
}
ssize_t LDisplayString::CharAt(int Px, LPxToIndexType Type)
{
Layout();
if (Px < 0)
{
return 0;
}
else if (Px >= (int)x)
{
#if defined __GTK_H__
if (Str)
{
LUtf8Str u(Str);
return u.GetChars();
}
return 0;
#else
#if LGI_DSP_STR_CACHE
return WideWords;
#else
return StrWords;
#endif
#endif
}
int Status = -1;
#if defined(__GTK_H__)
int Fx = 0;
int Fpos = Px << FShift;
Status = 0;
for (auto &b: d->Blocks)
{
int Index = 0, Trailing = 0;
int Foffset = Fpos - Fx;
if (b.Hnd && Gtk::pango_layout_xy_to_index(b.Hnd, Foffset, 0, &Index, &Trailing))
{
if (d->Debug)
printf("CharAt(%g) x=%g Status=%i Foffset=%g index=%i trailing=%i\n",
(double)Fpos/FScale, (double)b.X()/FScale, Status,
(double)Foffset/FScale, Index, Trailing);
LUtf8Str u(Str);
while ((OsChar*)u.GetPtr() < Str + Index + Trailing)
{
u++;
Status++;
}
return Status;
}
else
{
if (d->Debug)
printf("CharAt(%g) x=%g Status=%i Chars=%i\n",
(double)Fpos/FScale, (double)b.X()/FScale, Status, b.Chars);
Status += b.Chars;
}
Fx += b.X();
}
#elif defined(LGI_SDL)
LAssert(!"Impl me");
#elif defined(MAC)
if (Hnd && Str)
{
#if USE_CORETEXT
CGPoint pos = { (CGFloat)Px, 1.0f };
CFIndex utf16 = CTLineGetStringIndexForPosition(Hnd, pos);
// 'utf16' is in UTF-16, and the API needs to return a UTF-32 index...
// So convert between the 2 here...
LAssert(Str != NULL);
int utf32 = 0;
for (int i=0; Str[i] && iTabSize() ? Font->TabSize() : 32;
int Cx = 0;
int Char = 0;
#if DEBUG_CHAR_AT
printf("CharAt(%i) Str='%s'\n", Px, Str);
#endif
for (int i=0; i= Cx && Px < Cx + Info[i].X)
{
// The position is in this block of characters
if (IsTabChar(Info[i].Str[0]))
{
// Search through tab block
for (int t=0; t= Cx && Px < Cx + TabX)
{
Status = Char;
#if DEBUG_CHAR_AT
printf("\tIn tab block %i\n", i);
#endif
break;
}
Cx += TabX;
Char++;
}
}
else
{
// Find the pos in this block
LFont *f = Font;
#if defined(WIN32)
if (Info[i].FontId)
{
f = Sys->Font[Info[i].FontId];
f->Colour(Font->Fore(), Font->Back());
f->Size(Font->Size());
if (!f->Handle())
{
f->Create();
}
}
#endif
int Fit = f->_CharAt(Px - Cx, Info[i].Str, Info[i].Len, Type);
#if DEBUG_CHAR_AT
printf("\tNon tab block %i, Fit=%i, Px-Cx=%i-%i=%i, Str='%.5s'\n",
i, Fit, Px, Cx, Px-Cx, Info[i].Str);
#endif
if (Fit >= 0)
Status = Char + Fit;
else
Status = -1;
break;
}
}
else
{
// Not in this block, skip the whole lot
Cx += Info[i].X;
Char += Info[i].Len;
}
}
if (Status < 0)
{
Status = Char;
}
}
#endif
return Status;
}
ssize_t LDisplayString::Length()
{
return StrWords;
}
int LDisplayString::X()
{
Layout();
return x;
}
int LDisplayString::Y()
{
Layout();
return y;
}
LPoint LDisplayString::Size()
{
Layout();
return LPoint(x, y);
}
#if defined LGI_SDL
template
bool CompositeText8Alpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip)
{
OutPx map[256];
if (!Out || !In || !Font)
return false;
// FIXME, do blt clipping here...
// Create colour map of the foreground/background colours
uint8_t *Div255 = Div255Lut;
LColour fore = Font->Fore();
LRgb24 fore_px;
fore_px.r = fore.r();
fore_px.g = fore.g();
fore_px.b = fore.b();
if (Font->Transparent())
{
for (int a=0; a<256; a++)
{
map[a].r = Div255[a * fore_px.r];
map[a].g = Div255[a * fore_px.g];
map[a].b = Div255[a * fore_px.b];
map[a].a = a;
}
}
else
{
LColour back = Font->Back();
LRgb24 back_px;
back_px.r = back.r();
back_px.g = back.g();
back_px.b = back.b();
for (int a=0; a<256; a++)
{
int oma = 255 - a;
map[a].r = (oma * back_px.r) + (a * fore_px.r) / 255;
map[a].g = (oma * back_px.g) + (a * fore_px.g) / 255;
map[a].b = (oma * back_px.b) + (a * fore_px.b) / 255;
map[a].a = 255;
}
}
uint8_t *StartOfBuffer = (*Out)[0];
uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y());
for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++)
{
OutPx *d = ((OutPx*) (*Out)[py + y]) + Clip.DstClip.x1;
uint8_t *i = (*In)[y];
if (!i) return false;
uint8_t *e = i + Clip.DstClip.X();
LAssert((uint8_t*)d >= StartOfBuffer);
if (Font->Transparent())
{
uint8_t a, o;
OutPx *s;
while (i < e)
{
// Alpha blend map and output pixel
a = *i++;
switch (a)
{
case 0:
break;
case 255:
// Copy
*d = map[a];
break;
default:
// Blend
o = 255 - a;
s = map + a;
#define NonPreMulOver32NoAlpha(c) d->c = ((s->c * a) + (Div255[d->c * 255] * o)) / 255
NonPreMulOver32NoAlpha(r);
NonPreMulOver32NoAlpha(g);
NonPreMulOver32NoAlpha(b);
break;
}
d++;
}
}
else
{
while (i < e)
{
// Copy rop
*d++ = map[*i++];
}
}
LAssert((uint8_t*)d <= EndOfBuffer);
}
return true;
}
template
bool CompositeText8NoAlpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip)
{
LRgba32 map[256];
if (!Out || !In || !Font)
return false;
// FIXME, do blt clipping here...
// Create colour map of the foreground/background colours
uint8_t *DivLut = Div255Lut;
LColour fore = Font->Fore();
LRgb24 fore_px;
fore_px.r = fore.r();
fore_px.g = fore.g();
fore_px.b = fore.b();
if (Font->Transparent())
{
for (int a=0; a<256; a++)
{
map[a].r = DivLut[a * fore_px.r];
map[a].g = DivLut[a * fore_px.g];
map[a].b = DivLut[a * fore_px.b];
map[a].a = a;
}
}
else
{
LColour back = Font->Back();
LRgb24 back_px;
back_px.r = back.r();
back_px.g = back.g();
back_px.b = back.b();
for (int a=0; a<256; a++)
{
int oma = 255 - a;
map[a].r = DivLut[(oma * back_px.r) + (a * fore_px.r)];
map[a].g = DivLut[(oma * back_px.g) + (a * fore_px.g)];
map[a].b = DivLut[(oma * back_px.b) + (a * fore_px.b)];
map[a].a = 255;
}
}
uint8_t *StartOfBuffer = (*Out)[0];
uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y());
for (int y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++)
{
OutPx *dst = (OutPx*) (*Out)[py + y];
if (!dst)
continue;
dst += Clip.DstClip.x1;
if ((uint8_t*)dst < StartOfBuffer)
continue;
uint8_t *i = (*In)[y];
if (!i) return false;
uint8_t *e = i + Clip.DstClip.X();
LRgba32 *src;
LAssert((uint8_t*)dst >= StartOfBuffer);
if (Font->Transparent())
{
uint8_t a, oma;
while (i < e)
{
// Alpha blend map and output pixel
a = *i++;
src = map + a;
switch (a)
{
case 0:
break;
case 255:
// Copy
dst->r = src->r;
dst->g = src->g;
dst->b = src->b;
break;
default:
// Blend
OverPm32toPm24(src, dst);
break;
}
dst++;
}
}
else
{
while (i < e)
{
// Copy rop
src = map + *i++;
dst->r = src->r;
dst->g = src->g;
dst->b = src->b;
dst++;
}
}
LAssert((uint8_t*)dst <= EndOfBuffer);
}
return true;
}
template
bool CompositeText5NoAlpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip)
{
OutPx map[256];
if (!Out || !In || !Font)
return false;
// FIXME, do blt clipping here...
#define MASK_5BIT 0x1f
#define MASK_6BIT 0x3f
// Create colour map of the foreground/background colours
uint8_t *Div255 = Div255Lut;
LColour fore = Font->Fore();
LRgb24 fore_px;
fore_px.r = fore.r();
fore_px.g = fore.g();
fore_px.b = fore.b();
if (Font->Transparent())
{
for (int a=0; a<256; a++)
{
map[a].r = (int)Div255[a * fore_px.r] >> 3;
map[a].g = (int)Div255[a * fore_px.g] >> 2;
map[a].b = (int)Div255[a * fore_px.b] >> 3;
}
}
else
{
LColour back = Font->Back();
LRgb24 back_px;
back_px.r = back.r();
back_px.g = back.g();
back_px.b = back.b();
for (int a=0; a<256; a++)
{
int oma = 255 - a;
map[a].r = Div255[(oma * back_px.r) + (a * fore_px.r)] >> 3;
map[a].g = Div255[(oma * back_px.g) + (a * fore_px.g)] >> 2;
map[a].b = Div255[(oma * back_px.b) + (a * fore_px.b)] >> 3;
}
}
uint8_t *StartOfBuffer = (*Out)[0];
uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y());
for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++)
{
OutPx *dst = ((OutPx*) (*Out)[py + y]);
if (!dst)
continue;
dst += Clip.DstClip.x1;
uint8_t *i = (*In)[y];
if (!i) return false;
uint8_t *e = i + Clip.DstClip.X();
LAssert((uint8_t*)dst >= StartOfBuffer);
if (Font->Transparent())
{
uint8_t a;
OutPx *src;
while (i < e)
{
// Alpha blend map and output pixel
a = *i++;
switch (a)
{
case 0:
break;
case 255:
// Copy
*dst = map[a];
break;
default:
{
// Blend
#if 0
uint8_t oma = 255 - a;
src = map + a;
LRgb24 d = { G5bitTo8bit(dst->r),
G6bitTo8bit(dst->g),
G5bitTo8bit(dst->b)};
LRgb24 s = { G5bitTo8bit(src->r),
G6bitTo8bit(src->g),
G5bitTo8bit(src->b)};
dst->r = Div255[(oma * d.r) + (a * s.r)] >> 3;
dst->g = Div255[(oma * d.g) + (a * s.g)] >> 2;
dst->b = Div255[(oma * d.b) + (a * s.b)] >> 3;
#else
uint8_t a5 = a >> 3;
uint8_t a6 = a >> 2;
uint8_t oma5 = MASK_5BIT - a5;
uint8_t oma6 = MASK_6BIT - a6;
src = map + a;
dst->r = ((oma5 * (uint8_t)dst->r) + (a5 * (uint8_t)src->r)) / MASK_5BIT;
dst->g = ((oma6 * (uint8_t)dst->g) + (a6 * (uint8_t)src->g)) / MASK_6BIT;
dst->b = ((oma5 * (uint8_t)dst->b) + (a5 * (uint8_t)src->b)) / MASK_5BIT;
#endif
break;
}
}
dst++;
}
}
else
{
while (i < e)
{
// Copy rop
*dst++ = map[*i++];
}
}
LAssert((uint8_t*)dst <= EndOfBuffer);
}
return true;
}
#endif
void LDisplayString::Draw(LSurface *pDC, int px, int py, LRect *r, bool Debug)
{
Layout();
#if DISPLAY_STRING_FRACTIONAL_NATIVE
// GTK / Mac use fractional pixels, so call the fractional version:
LRect rc;
if (r)
{
rc = *r;
rc.x1 <<= FShift;
rc.y1 <<= FShift;
#if defined(MAC) && !defined(__GTK_H__)
rc.x2 <<= FShift;
rc.y2 <<= FShift;
#else
rc.x2 = (rc.x2 + 1) << FShift;
rc.y2 = (rc.y2 + 1) << FShift;
#endif
}
FDraw(pDC, px << FShift, py << FShift, r ? &rc : NULL, Debug);
#elif defined(HAIKU)
if (!Font || !pDC)
{
LgiTrace("%s:%i - No ptr: %p/%p.\n", _FL, Font, pDC);
return;
}
BFont *fnt = Font->Handle();
BView *view = pDC->Handle();
if (!fnt || !view)
{
LgiTrace("%s:%i - No handle: %p/%p(%s).\n", _FL, fnt, view, pDC->GetClass());
return;
}
font_height height = {0};
fnt->GetHeight(&height);
/*
if (_debug)
printf(" trans=%i height=%g,%g,%g\n",
Font->Transparent(),
height.ascent, height.descent, height.leading);
*/
if (!Font->Transparent())
{
view->SetHighColor(Font->Back());
if (r)
view->FillRect(*r);
else
view->FillRect(BRect(px, py, px+x, py+y));
}
auto locked = view->LockLooper();
view->SetFont(fnt);
view->SetHighColor(Font->Fore());
int cx = px;
for (auto &i: Info)
{
/*
if (_debug)
printf(" info=%.*s c=%i,%i\n", i.Len, i.Str, cx, py);
*/
view->DrawString(i.Str, i.Len, BPoint(cx, py + height.ascent));
cx += i.X;
}
BRegion region;
view->GetClippingRegion(®ion);
if (locked)
view->UnlockLooper();
#elif defined(LGI_SDL)
if (Img && pDC && pDC->Y() > 0 && (*pDC)[0])
{
int Ox = 0, Oy = 0;
pDC->GetOrigin(Ox, Oy);
LBlitRegions Clip(pDC, px-Ox, py-Oy, Img, r);
LColourSpace DstCs = pDC->GetColourSpace();
switch (DstCs)
{
#define DspStrCase(px_fmt, comp) \
case Cs##px_fmt: \
CompositeText##comp(pDC, Img, Font, px-Ox, py-Oy, Clip); \
break;
DspStrCase(Rgb16, 5NoAlpha)
DspStrCase(Bgr16, 5NoAlpha)
DspStrCase(Rgb24, 8NoAlpha)
DspStrCase(Bgr24, 8NoAlpha)
DspStrCase(Rgbx32, 8NoAlpha)
DspStrCase(Bgrx32, 8NoAlpha)
DspStrCase(Xrgb32, 8NoAlpha)
DspStrCase(Xbgr32, 8NoAlpha)
DspStrCase(Rgba32, 8Alpha)
DspStrCase(Bgra32, 8Alpha)
DspStrCase(Argb32, 8Alpha)
DspStrCase(Abgr32, 8Alpha)
default:
LgiTrace("%s:%i - LDisplayString::Draw Unsupported colour space.\n", _FL);
// LAssert(!"Unsupported colour space.");
break;
#undef DspStrCase
}
}
else
LgiTrace("::Draw argument error.\n");
#elif defined WINNATIVE
if (Info.Length() && pDC && Font)
{
LFontSystem *Sys = LFontSystem::Inst();
COLOUR Old = pDC->Colour();
int TabSize = Font->TabSize() ? Font->TabSize() : 32;
int Ox = px;
LColour cFore = Font->Fore();
LColour cBack = Font->Back();
LColour cWhitespace;
if (VisibleTab)
{
cWhitespace = Font->WhitespaceColour();
LAssert(cWhitespace.IsValid());
}
for (int i=0; iFont[Info[i].FontId];
f->Colour(cFore, cBack);
auto Sz = Font->Size();
Sz.Value += Info[i].SizeDelta;
f->Size(Sz);
f->Transparent(Font->Transparent());
f->Underline(Font->Underline());
if (!f->Handle())
{
f->Create();
}
}
else
{
f = Font;
+ if (Info[i].Missing)
+ f->Colour(LColour::Red.Mix(cFore), cBack);
}
if (f)
{
LRect b;
if (r)
{
b.x1 = i ? px : r->x1;
b.y1 = r->y1;
b.x2 = i < Info.Length() - 1 ? px + Info[i].X - 1 : r->x2;
b.y2 = r->y2;
}
else
{
b.x1 = px;
b.y1 = py;
b.x2 = px + Info[i].X - 1;
b.y2 = py + Y() - 1;
}
if (b.Valid())
{
if (IsTabChar(*Info[i].Str))
{
// Invisible tab... draw blank space
if (!Font->Transparent())
{
pDC->Colour(cBack);
pDC->Rectangle(&b);
}
if (VisibleTab)
{
int X = px;
for (int n=0; nColour(cWhitespace);
DrawWhiteSpace(pDC, '\t', r);
X += Dx;
}
}
}
else
{
// Draw the character(s)
LColour Fg = f->Fore();
LAssert(Fg.IsValid());
f->_Draw(pDC, px, py, Info[i].Str, Info[i].Len, &b, Fg);
if (VisibleTab)
{
OsChar *start = Info[i].Str;
OsChar *s = start;
OsChar *e = s + Info[i].Len;
int Sp = -1;
while (s < e)
{
if (*s == ' ')
{
int Sx, Sy;
if (Sp < 0) f->_Measure(Sp, Sy, s, 1);
f->_Measure(Sx, Sy, start, (int)(s - start));
LRect r(0, 0, Sp-1, Sy-1);
r.Offset(px + Sx, py);
pDC->Colour(cWhitespace);
DrawWhiteSpace(pDC, ' ', r);
}
s++;
}
}
}
}
}
// Inc my position
px += Info[i].X;
}
if (AppendDots)
{
int Sx, Sy;
Font->_Measure(Sx, Sy, GDisplayStringDots, 3);
LRect b;
if (r)
{
b.x1 = px;
b.y1 = r->y1;
b.x2 = min(px + Sx - 1, r->x2);
b.y2 = r->y2;
}
else
{
b.x1 = px;
b.y1 = py;
b.x2 = px + Sx - 1;
b.y2 = py + Y() - 1;
}
LColour Fg = Font->Fore();
Font->_Draw(pDC, px, py, GDisplayStringDots, 3, &b, Fg);
}
pDC->Colour(Old);
}
else if (r &&
Font &&
!Font->Transparent())
{
pDC->Colour(Font->Back());
pDC->Rectangle(r);
}
#endif
}
int LDisplayString::GetDrawOffsetF()
{
return DrawOffsetF;
}
void LDisplayString::SetDrawOffsetF(int Fpx)
{
if (LaidOut)
LAssert(!"No point setting TabOrigin after string is laid out.\n");
DrawOffsetF = Fpx;
}
int LDisplayString::FX()
{
Layout();
return xf;
}
int LDisplayString::FY()
{
Layout();
return yf;
}
LPoint LDisplayString::FSize()
{
Layout();
return LPoint(xf, yf);
}
void LDisplayString::FDraw(LSurface *pDC, int fx, int fy, LRect *frc, bool Debug)
{
Layout(Debug);
#if !DISPLAY_STRING_FRACTIONAL_NATIVE
// Windows doesn't use fractional pixels, so call the integer version:
LRect rc;
if (frc)
{
rc = *frc;
rc.x1 >>= FShift;
rc.y1 >>= FShift;
rc.x2 >>= FShift;
rc.y2 >>= FShift;
}
Draw(pDC, fx >> FShift, fy >> FShift, frc ? &rc : NULL, Debug);
#elif defined __GTK_H__
Gtk::cairo_t *cr = pDC->Handle();
if (!cr)
{
LAssert(!"Can't get cairo.");
return;
}
pango_context_set_font_description(LFontSystem::Inst()->GetContext(), Font->Handle());
cairo_save(cr);
LColour b = Font->Back();
double Dx = ((double)fx / FScale);
double Dy = ((double)fy / FScale);
double rx, ry, rw, rh;
if (!Font->Transparent())
{
// Background fill
cairo_set_source_rgb(cr, (double)b.r()/255.0, (double)b.g()/255.0, (double)b.b()/255.0); cairo_new_path(cr);
if (frc)
{
rx = ((double)frc->x1 / FScale);
ry = ((double)frc->y1 / FScale);
rw = (double)frc->X() / FScale;
rh = (double)frc->Y() / FScale;
}
else
{
rx = Dx;
ry = Dy;
rw = x;
rh = y;
}
cairo_rectangle(cr, rx, ry, rw, rh);
cairo_fill(cr);
if (frc)
{
cairo_rectangle(cr, rx, ry, rw, rh);
cairo_clip(cr);
}
}
else if (frc)
{
rx = ((double)frc->x1 / FScale);
ry = ((double)frc->y1 / FScale);
rw = (double)frc->X() / FScale;
rh = (double)frc->Y() / FScale;
cairo_rectangle(cr, rx, ry, rw, rh);
cairo_clip(cr);
}
cairo_translate(cr, Dx, Dy);
LColour f = Font->Fore();
for (auto &b: d->Blocks)
{
double Bx = ((double)b.X()) / FScale;
#if DEBUG_BOUNDS
double By = Font->GetHeight();
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
cairo_rectangle(cr, 0, 0, Bx, By);
cairo_rectangle(cr, 1, 1, Bx - 2.0, By - 2.0);
cairo_set_fill_rule(cr, Gtk::CAIRO_FILL_RULE_EVEN_ODD);
cairo_fill(cr);
#endif
if (b.Hnd)
{
cairo_set_source_rgb( cr,
(double)f.r()/255.0,
(double)f.g()/255.0,
(double)f.b()/255.0);
pango_cairo_show_layout(cr, b.Hnd);
if (VisibleTab && Str)
{
LUtf8Str Ptr(Str);
auto Ws = Font->WhitespaceColour();
pDC->Colour(Ws);
for (int32 u, Idx = 0 ; (u = Ptr); Idx++)
{
if (IsTabChar(u) || u == ' ')
{
Gtk::PangoRectangle pos;
Gtk::pango_layout_index_to_pos(b.Hnd, Idx, &pos);
LRect r(0, 0, pos.width / FScale, pos.height / FScale);
r.Offset(pos.x / FScale, pos.y / FScale);
DrawWhiteSpace(pDC, u, r);
}
Ptr++;
}
}
}
else if (b.Fnt)
{
b.Fnt->Transparent(Font->Transparent());
b.Fnt->Back(Font->Back());
b.Fnt->_Draw(pDC, 0, 0, b.Str, b.Bytes, NULL, f);
}
else LAssert(0);
cairo_translate(cr, Bx, 0);
}
cairo_restore(cr);
#elif defined MAC && !defined(LGI_SDL)
int Ox = 0, Oy = 0;
int px = fx >> FShift;
int py = fy >> FShift;
LRect rc;
if (frc)
rc.Set( frc->x1 >> FShift,
frc->y1 >> FShift,
frc->x2 >> FShift,
frc->y2 >> FShift);
if (pDC && !pDC->IsScreen())
pDC->GetOrigin(Ox, Oy);
if (pDC && !Font->Transparent())
{
LColour Old = pDC->Colour(Font->Back());
if (frc)
{
pDC->Rectangle(&rc);
}
else
{
LRect a(px, py, px + x - 1, py + y - 1);
pDC->Rectangle(&a);
}
pDC->Colour(Old);
}
if (Hnd && pDC && StrWords > 0)
{
OsPainter dc = pDC->Handle();
#if USE_CORETEXT
int y = (pDC->Y() - py + Oy);
CGContextSaveGState(dc);
pDC->Colour(Font->Fore());
if (pDC->IsScreen())
{
if (frc)
{
CGRect rect = rc;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
CGContextTranslateCTM(dc, 0, pDC->Y()-1);
CGContextScaleCTM(dc, 1.0, -1.0);
}
else
{
if (frc)
{
CGContextSaveGState(dc);
CGRect rect = rc;
rect.origin.x -= Ox;
rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
}
CGFloat Tx = (CGFloat)fx / FScale - Ox;
CGFloat Ty = (CGFloat)y - Font->Ascent();
CGContextSetTextPosition(dc, Tx, Ty);
CTLineDraw(Hnd, dc);
CGContextRestoreGState(dc);
#else
ATSUAttributeTag Tags[1] = {kATSUCGContextTag};
ByteCount Sizes[1] = {sizeof(CGContextRef)};
ATSUAttributeValuePtr Values[1] = {&dc};
e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values);
if (e)
{
printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e);
}
else
{
// Set style attr
ATSURGBAlphaColor c;
LColour Fore = Font->Fore();
c.red = (double) Fore.r() / 255.0;
c.green = (double) Fore.g() / 255.0;
c.blue = (double) Fore.b() / 255.0;
c.alpha = 1.0;
ATSUAttributeTag Tags[] = {kATSURGBAlphaColorTag};
ATSUAttributeValuePtr Values[] = {&c};
ByteCount Lengths[] = {sizeof(c)};
e = ATSUSetAttributes( Font->Handle(),
CountOf(Tags),
Tags,
Lengths,
Values);
if (e)
{
printf("%s:%i - Error setting font attr (e=%i)\n", _FL, (int)e);
}
else
{
int y = (pDC->Y() - py + Oy);
if (pDC->IsScreen())
{
CGContextSaveGState(dc);
if (frc)
{
CGRect rect = rc;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
CGContextTranslateCTM(dc, 0, pDC->Y()-1);
CGContextScaleCTM(dc, 1.0, -1.0);
e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, fx - Long2Fix(Ox), Long2Fix(y) - fAscent);
CGContextRestoreGState(dc);
}
else
{
if (frc)
{
CGContextSaveGState(dc);
CGRect rect = rc;
rect.origin.x -= Ox;
rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height;
rect.size.width += 1.0;
rect.size.height += 1.0;
CGContextClipToRect(dc, rect);
}
e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix(px - Ox), Long2Fix(y) - fAscent);
if (frc)
CGContextRestoreGState(dc);
}
if (e)
{
char *a = 0;
StringConvert(a, NULL, Str, len);
printf("%s:%i - ATSUDrawText failed with %i, len=%i, str=%.20s\n", _FL, (int)e, len, a);
DeleteArray(a);
}
}
}
#endif
}
#endif
}
diff --git a/LgiTest/Mac/Info.plist b/test/GuiTest/Mac/Info.plist
rename from LgiTest/Mac/Info.plist
rename to test/GuiTest/Mac/Info.plist
diff --git a/LgiTest/Mac/LgiTest.xcodeproj/project.pbxproj b/test/GuiTest/Mac/LgiTest.xcodeproj/project.pbxproj
rename from LgiTest/Mac/LgiTest.xcodeproj/project.pbxproj
rename to test/GuiTest/Mac/LgiTest.xcodeproj/project.pbxproj
diff --git a/LgiTest/Mac/LgiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/test/GuiTest/Mac/LgiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename from LgiTest/Mac/LgiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename to test/GuiTest/Mac/LgiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/LgiTest/Main.cpp b/test/GuiTest/Main.cpp
rename from LgiTest/Main.cpp
rename to test/GuiTest/Main.cpp
diff --git a/LgiTest/Test.sln b/test/GuiTest/Test.sln
rename from LgiTest/Test.sln
rename to test/GuiTest/Test.sln
diff --git a/LgiTest/Test.vcxproj b/test/GuiTest/Test.vcxproj
rename from LgiTest/Test.vcxproj
rename to test/GuiTest/Test.vcxproj
diff --git a/LgiTest/Test.vcxproj.filters b/test/GuiTest/Test.vcxproj.filters
rename from LgiTest/Test.vcxproj.filters
rename to test/GuiTest/Test.vcxproj.filters
diff --git a/HtmlEditor/CMakeLists.txt b/test/HtmlEditor/CMakeLists.txt
rename from HtmlEditor/CMakeLists.txt
rename to test/HtmlEditor/CMakeLists.txt
diff --git a/HtmlEditor/HtmlEdit.sln b/test/HtmlEditor/HtmlEdit.sln
rename from HtmlEditor/HtmlEdit.sln
rename to test/HtmlEditor/HtmlEdit.sln
diff --git a/HtmlEditor/HtmlEdit.vcxproj b/test/HtmlEditor/HtmlEdit.vcxproj
rename from HtmlEditor/HtmlEdit.vcxproj
rename to test/HtmlEditor/HtmlEdit.vcxproj
diff --git a/HtmlEditor/HtmlEdit.vcxproj.filters b/test/HtmlEditor/HtmlEdit.vcxproj.filters
rename from HtmlEditor/HtmlEdit.vcxproj.filters
rename to test/HtmlEditor/HtmlEdit.vcxproj.filters
diff --git a/HtmlEditor/HtmlEditor.xml b/test/HtmlEditor/HtmlEditor.xml
rename from HtmlEditor/HtmlEditor.xml
rename to test/HtmlEditor/HtmlEditor.xml
diff --git a/HtmlEditor/Makefile b/test/HtmlEditor/Makefile
rename from HtmlEditor/Makefile
rename to test/HtmlEditor/Makefile
diff --git a/HtmlEditor/Rich.cpp b/test/HtmlEditor/Rich.cpp
rename from HtmlEditor/Rich.cpp
rename to test/HtmlEditor/Rich.cpp
diff --git a/HtmlEditor/Rich.rc b/test/HtmlEditor/Rich.rc
rename from HtmlEditor/Rich.rc
rename to test/HtmlEditor/Rich.rc
diff --git a/HtmlEditor/Test/Reply.html b/test/HtmlEditor/Test/Reply.html
rename from HtmlEditor/Test/Reply.html
rename to test/HtmlEditor/Test/Reply.html
diff --git a/HtmlEditor/Test/Reply2.html b/test/HtmlEditor/Test/Reply2.html
rename from HtmlEditor/Test/Reply2.html
rename to test/HtmlEditor/Test/Reply2.html
diff --git a/HtmlEditor/TestCases/example.html b/test/HtmlEditor/TestCases/example.html
rename from HtmlEditor/TestCases/example.html
rename to test/HtmlEditor/TestCases/example.html
diff --git a/HtmlEditor/TestCases/hr.html b/test/HtmlEditor/TestCases/hr.html
rename from HtmlEditor/TestCases/hr.html
rename to test/HtmlEditor/TestCases/hr.html
diff --git a/HtmlEditor/TestCases/reply-test.html b/test/HtmlEditor/TestCases/reply-test.html
rename from HtmlEditor/TestCases/reply-test.html
rename to test/HtmlEditor/TestCases/reply-test.html
diff --git a/HtmlEditor/TestCases/sig.html b/test/HtmlEditor/TestCases/sig.html
rename from HtmlEditor/TestCases/sig.html
rename to test/HtmlEditor/TestCases/sig.html
diff --git a/HtmlEditor/TestCases/sig2.html b/test/HtmlEditor/TestCases/sig2.html
rename from HtmlEditor/TestCases/sig2.html
rename to test/HtmlEditor/TestCases/sig2.html
diff --git a/HtmlEditor/icon1.ico b/test/HtmlEditor/icon1.ico
rename from HtmlEditor/icon1.ico
rename to test/HtmlEditor/icon1.ico
diff --git a/HtmlEditor/resource.h b/test/HtmlEditor/resource.h
rename from HtmlEditor/resource.h
rename to test/HtmlEditor/resource.h
diff --git a/HtmlEditor/tools.bmp b/test/HtmlEditor/tools.bmp
rename from HtmlEditor/tools.bmp
rename to test/HtmlEditor/tools.bmp
diff --git a/HtmlTest/CMakeLists.txt b/test/HtmlViewer/CMakeLists.txt
rename from HtmlTest/CMakeLists.txt
rename to test/HtmlViewer/CMakeLists.txt
diff --git a/HtmlTest/HtmlTest.sln b/test/HtmlViewer/HtmlTest.sln
rename from HtmlTest/HtmlTest.sln
rename to test/HtmlViewer/HtmlTest.sln
diff --git a/HtmlTest/HtmlTest.vcxproj b/test/HtmlViewer/HtmlTest.vcxproj
rename from HtmlTest/HtmlTest.vcxproj
rename to test/HtmlViewer/HtmlTest.vcxproj
diff --git a/HtmlTest/HtmlTest.vcxproj.filters b/test/HtmlViewer/HtmlTest.vcxproj.filters
rename from HtmlTest/HtmlTest.vcxproj.filters
rename to test/HtmlViewer/HtmlTest.vcxproj.filters
diff --git a/HtmlTest/HtmlTestSuite.lr8 b/test/HtmlViewer/HtmlTestSuite.lr8
rename from HtmlTest/HtmlTestSuite.lr8
rename to test/HtmlViewer/HtmlTestSuite.lr8
diff --git a/HtmlTest/MacCocoa/Assets.xcassets/AppIcon.appiconset/Contents.json b/test/HtmlViewer/MacCocoa/Assets.xcassets/AppIcon.appiconset/Contents.json
rename from HtmlTest/MacCocoa/Assets.xcassets/AppIcon.appiconset/Contents.json
rename to test/HtmlViewer/MacCocoa/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/HtmlTest/MacCocoa/Assets.xcassets/Contents.json b/test/HtmlViewer/MacCocoa/Assets.xcassets/Contents.json
rename from HtmlTest/MacCocoa/Assets.xcassets/Contents.json
rename to test/HtmlViewer/MacCocoa/Assets.xcassets/Contents.json
diff --git a/HtmlTest/MacCocoa/Base.lproj/MainMenu.xib b/test/HtmlViewer/MacCocoa/Base.lproj/MainMenu.xib
rename from HtmlTest/MacCocoa/Base.lproj/MainMenu.xib
rename to test/HtmlViewer/MacCocoa/Base.lproj/MainMenu.xib
diff --git a/HtmlTest/MacCocoa/HtmlTest.entitlements b/test/HtmlViewer/MacCocoa/HtmlTest.entitlements
rename from HtmlTest/MacCocoa/HtmlTest.entitlements
rename to test/HtmlViewer/MacCocoa/HtmlTest.entitlements
diff --git a/HtmlTest/MacCocoa/HtmlTest.xcodeproj/project.pbxproj b/test/HtmlViewer/MacCocoa/HtmlTest.xcodeproj/project.pbxproj
rename from HtmlTest/MacCocoa/HtmlTest.xcodeproj/project.pbxproj
rename to test/HtmlViewer/MacCocoa/HtmlTest.xcodeproj/project.pbxproj
diff --git a/HtmlTest/MacCocoa/HtmlTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/test/HtmlViewer/MacCocoa/HtmlTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename from HtmlTest/MacCocoa/HtmlTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata
rename to test/HtmlViewer/MacCocoa/HtmlTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata
diff --git a/HtmlTest/MacCocoa/Info.plist b/test/HtmlViewer/MacCocoa/Info.plist
rename from HtmlTest/MacCocoa/Info.plist
rename to test/HtmlViewer/MacCocoa/Info.plist
diff --git a/HtmlTest/MacCocoa/create_project.py b/test/HtmlViewer/MacCocoa/create_project.py
rename from HtmlTest/MacCocoa/create_project.py
rename to test/HtmlViewer/MacCocoa/create_project.py
diff --git a/HtmlTest/Makefile b/test/HtmlViewer/Makefile
rename from HtmlTest/Makefile
rename to test/HtmlViewer/Makefile
diff --git a/HtmlTest/TestSuite.cpp b/test/HtmlViewer/TestSuite.cpp
rename from HtmlTest/TestSuite.cpp
rename to test/HtmlViewer/TestSuite.cpp
diff --git a/HtmlTest/TestSuite.xml b/test/HtmlViewer/TestSuite.xml
rename from HtmlTest/TestSuite.xml
rename to test/HtmlViewer/TestSuite.xml
diff --git a/HtmlTest/resdefs.h b/test/HtmlViewer/resdefs.h
rename from HtmlTest/resdefs.h
rename to test/HtmlViewer/resdefs.h
diff --git a/tests/UnitTests.sln b/test/UnitTests/UnitTests.sln
rename from tests/UnitTests.sln
rename to test/UnitTests/UnitTests.sln
diff --git a/tests/UnitTests.vcxproj b/test/UnitTests/UnitTests.vcxproj
rename from tests/UnitTests.vcxproj
rename to test/UnitTests/UnitTests.vcxproj
diff --git a/tests/UnitTests.vcxproj.filters b/test/UnitTests/UnitTests.vcxproj.filters
rename from tests/UnitTests.vcxproj.filters
rename to test/UnitTests/UnitTests.vcxproj.filters
diff --git a/tests/src/AutoPtrTest.cpp b/test/UnitTests/src/AutoPtrTest.cpp
rename from tests/src/AutoPtrTest.cpp
rename to test/UnitTests/src/AutoPtrTest.cpp
diff --git a/tests/src/BitsTest.cpp b/test/UnitTests/src/BitsTest.cpp
rename from tests/src/BitsTest.cpp
rename to test/UnitTests/src/BitsTest.cpp
diff --git a/tests/src/ContainerTests.cpp b/test/UnitTests/src/ContainerTests.cpp
rename from tests/src/ContainerTests.cpp
rename to test/UnitTests/src/ContainerTests.cpp
diff --git a/tests/src/CssTest.cpp b/test/UnitTests/src/CssTest.cpp
rename from tests/src/CssTest.cpp
rename to test/UnitTests/src/CssTest.cpp
diff --git a/tests/src/JsonTest.cpp b/test/UnitTests/src/JsonTest.cpp
rename from tests/src/JsonTest.cpp
rename to test/UnitTests/src/JsonTest.cpp
diff --git a/tests/src/MatrixTest.cpp b/test/UnitTests/src/MatrixTest.cpp
rename from tests/src/MatrixTest.cpp
rename to test/UnitTests/src/MatrixTest.cpp
diff --git a/tests/src/RangeTest.cpp b/test/UnitTests/src/RangeTest.cpp
rename from tests/src/RangeTest.cpp
rename to test/UnitTests/src/RangeTest.cpp
diff --git a/tests/src/StringClassTests.cpp b/test/UnitTests/src/StringClassTests.cpp
rename from tests/src/StringClassTests.cpp
rename to test/UnitTests/src/StringClassTests.cpp
diff --git a/tests/src/StringPipeTests.cpp b/test/UnitTests/src/StringPipeTests.cpp
rename from tests/src/StringPipeTests.cpp
rename to test/UnitTests/src/StringPipeTests.cpp
diff --git a/tests/src/UnitTests.cpp b/test/UnitTests/src/UnitTests.cpp
rename from tests/src/UnitTests.cpp
rename to test/UnitTests/src/UnitTests.cpp
diff --git a/tests/src/UnitTests.h b/test/UnitTests/src/UnitTests.h
rename from tests/src/UnitTests.h
rename to test/UnitTests/src/UnitTests.h