commit x64 compilation from lulu cause the other branch dont seems to compile properly at home

This commit is contained in:
2026-07-17 16:08:20 +02:00
parent c0f3eeb00d
commit 0efa4ee6f7
625 changed files with 117283 additions and 4426 deletions

View File

@ -0,0 +1,228 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "ascii/ascii_encoder.h"
#include "log/log.h"
//---------------------------------------------------------------------------
#define FEED_OUT(out_c) \
{ \
if (out) \
{ \
if (olen < max) \
out[olen] = (uchar)(out_c); \
else \
break; \
} \
olen++; \
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#define FEED_IN(in_v) \
{ \
if (!len) \
{ \
__LOG_W__ << "input buffer underflow.\n"; \
break; \
} \
(in_v) = (int)*in++; \
len--; \
}
//---------------------------------------------------------------------------
/*
From UUencode wikipedia.
------------------------
(...)
Uuencode repeatedly takes in a group of three bytes, adding trailing zeros
if there are less than three bytes left. These 24 bits are split into four
groups of six which are treated as numbers between 0 and 63.
Decimal 32 is added to each number and they are ouput as ASCII characters
which will lie in the range 32 (space) to 32+63 = 95 (underscore).
ASCII characters greater than 95 may also be used; however, only the six
right-most bits are relevant.
Each group of sixty output characters (corresponding to 45 input bytes) is
output as a separate line preceded by an 'M' (ASCII code 77 = 32+45).
At the end of the input, if there are N output characters left after the
last group of sixty and N>0 then they will be preceded by the character
whose code is 32+N.
(...)
*/
//-----------------------------------------------------------------------------
uint nAsciiEncoder::UUEncode(const uchar *in, size_t len, uchar *out, size_t max)
{
uint olen = 0, n;
while (len)
{
uchar *p = out ? &out[olen] : NULL;
FEED_OUT(0) // Dummy feed.
for (n = 0; (n < 15) && len; n++)
{
uchar a, b, c;
a = *in++;
len--;
if (len) { len--; b = *in++; } else b = 0;
if (len) { len--; c = *in++; } else c = 0;
uint f = (a << 16) + (b << 8) + c;
uchar w, x, y, z;
z = (f & 63) + 32;
y = ((f >> 6) & 63) + 32;
x = ((f >> 12) & 63) + 32;
w = ((f >> 18) & 63) + 32;
FEED_OUT(w);
FEED_OUT(x);
FEED_OUT(y);
FEED_OUT(z);
}
if (p)
p[0] = (uchar)(n * 3 + 32);
FEED_OUT('\n')
}
return olen;
}
uint nAsciiEncoder::UUDecode(const uchar *in, size_t len, uchar *out, size_t max)
{
uint olen = 0, n;
while (len)
{
uint lsize;
FEED_IN(lsize);
lsize = (lsize - 32) / 3;
if (len)
for (n = 0; n < lsize; n++)
{
int x, y, z, w;
FEED_IN(w); w -= 32;
FEED_IN(x); x -= 32;
FEED_IN(y); y -= 32;
FEED_IN(z); z -= 32;
uchar a, b, c;
int f = (w << 18) + (x << 12) + (y << 6) + z;
a = (f >> 16) & 255;
b = (f >> 8) & 255;
c = f & 255;
FEED_OUT(a);
FEED_OUT(b);
FEED_OUT(c);
}
if (len)
FEED_IN(n); // Line jump.
}
return olen;
}
//-----------------------------------------------------------------------------
/*
From yEnc.org (revision 1.3)
----------------------------
A typical encoding process might look something like this:
1. Fetch a character from the input stream.
2. Increment the character's ASCII value by 42, modulo 256
3. If the result is a critical character (as defined in the previous
section), write the escape character to the output stream and increment
character's ASCII value by 64, modulo 256.
4. Output the character to the output stream.
5. Repeat from start.
(...)
Under special circumstances, a single escape character (ASCII 3Dh, "=") is
used to indicate that the following output character is "critical", and
requires special handling.
Critical characters include the following:
ASCII 00h (NULL)
ASCII 0Ah (LF)
ASCII 0Dh (CR)
ASCII 3Dh (=)
> ASCII 09h (TAB) -- removed in version (1.2)
*/
//-----------------------------------------------------------------------------
uint nAsciiEncoder::yEncode(const uchar *in, size_t len, uchar *out, size_t max, uint line_length)
{
if (line_length <= 0)
__ERR__(__LOG_E__ << "invalid line-feed size for yEncoding.\n", 0)
uint olen = 0;
int cchr = (int)line_length;
while (len--)
{
// Line-feed.
if (cchr <= 0)
{
FEED_OUT('\n')
cchr = (int)line_length;
}
// yEnc.
int v = (int(*in++) + 42) % 256;
switch (v)
{
case 0x00:
case 0x0a:
case 0x0d:
case 0x3d:
FEED_OUT(0x3d)
cchr--;
v = (v + 64) % 256;
break;
}
FEED_OUT(v)
cchr--;
}
return olen;
}
uint nAsciiEncoder::yDecode(const uchar *in, size_t len, uchar *out, size_t max)
{
uint olen = 0;
while (len--)
{
int v = (int)*in++;
if (v == 0x0a)
FEED_IN(v)
if ((v == 0x0d) && (in[0] == 0x0a)) // [EJ support for Windows-style EOL]
{
++in;
len--;
FEED_IN(v)
}
if (v == 0x3d)
{
FEED_IN(v)
v = (v - 64) % 256;
}
FEED_OUT((v - 42) % 256)
}
return olen;
}
//-----------------------------------------------------------------------------

View File

@ -0,0 +1,189 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "ascii/parser.h"
#include "ntypes.h"
using namespace GS;
//------------------------------------------------------------------------------
bool AsciiParser::IsUpperCase(const char c)
{ return asbool((c >= 'A') && (c <= 'Z')); }
const char *AsciiParser::RunToEOS(const char *s, const char *e)
{
while (s < e)
{
if (s[0] == '\\')
s += 2; // Jump modifiers.
else if (s[0] == '"')
break;
else s++;
}
return s;
}
bool AsciiParser::IsConstantFloat(const char *s, const char *e)
{
const char *eoc = SkipEntry(s, e);
if (s[0] == '-')
{
s++;
eoc = SkipEntry(s, e);
}
while (s < eoc)
{
if ((s[0] == '.') || (s[0] == 'f'))
return true;
s++;
}
return false;
}
const char *AsciiParser::Find(const char *s, const char *e, char f)
{
for (;;)
{
s = SkipSpace(s, e);
if (s == e)
return NULL;
if (s[0] == '(')
s = RunToEOG(s, e, '(', ')');
else
{
if (s[0] == f)
break;
s++;
}
}
return s;
}
const char *AsciiParser::SkipEntry(const char *s, const char *e, bool skip_minus)
{
{
while (
(s[0] != 0x20) &&
!((s[0] == 0xd) && (s[1] == 0xa)) &&
(s[0] != 0x9) &&
(s[0] != 0xa) &&
(s[0] != 0xd) &&
(s[0] != '/') &&
(s[0] != '*') &&
(s[0] != '+') &&
(s[0] != '=') &&
(s[0] != ';') &&
(s[0] != ':') &&
(s[0] != ',') &&
(s[0] != '<') &&
(s[0] != '>') &&
(s[0] != '(') &&
(s[0] != ')') &&
(s[0] != '\"')
)
{
if (!skip_minus && (s[0] == '-'))
break;
if (s == e)
break;
s++;
}
}
return s;
}
const char *AsciiParser::RunToEOL(const char *s, const char *e)
{
while (
((s[0] != 0xd) || (s[1] != 0xa)) &&
(s[0] != 0xa) &&
(s[0] != 0xd) &&
(s < e)
)
s++;
return s;
}
const char *AsciiParser::SkipEOL(const char *s, const char *e)
{
if ((s[0] == 0xd) && (s[1] == 0xa))
s += 2;
else if ((s[0] == 0xa) || (s[0] == 0xd))
s++;
return s > e ? e : s;
}
const char *AsciiParser::RunToEOG(const char *s, const char *e, char op, char cl)
{
uint pc = 0;
s++;
while (s < e)
{
if (s[0] == op)
pc++;
if (s[0] == cl)
{
if (!pc)
break;
pc--;
}
s++;
}
if (s == e)
return NULL;
return s;
}
const char *AsciiParser::RunToEOC(const char *s, const char *e)
{
s += 2;
while (((s[0] != '*') || (s[1] != '/')) && (s < e))
s += ((s[0] == 0xd) && (s[1] == 0xa)) ? 2 : 1;
return s >= e ? e : s + 2;
}
const char *AsciiParser::RunToEOE(const char *s, const char *e)
{
while (s < e)
{
s = SkipSpace(s, e);
if (s[0] == '(')
s = RunToEOG(s, e, '(', ')');
else
if (s[0] == '\"')
{
s++;
while ((s < e) && (s[0] != '\"'))
s++;
if (s < e)
s++;
}
else
{
if ((s[0] == ',') || (s[0] == ';') )
break;
s++;
}
}
return s;
}
const char *AsciiParser::SkipSpace(const char *s, const char *e)
{
while (s < e)
{
if (s[0] == 0x20) s++;
else if ((s[0] == 0xd) && (s[1] == 0xa)) s += 2;
else if ((s[0] == '/') && (s[1] == '/')) s = RunToEOL(s, e);
else if ((s[0] == '/') && (s[1] == '*')) s = RunToEOC(s, e);
else if (s[0] == 0x9) s++;
else if (s[0] == 0xa) s++;
else if (s[0] == 0xd) s++;
// else if (s[0] == -17 && s[1] == -69 && s[2] == -65) s+=3; // remove the BOM from utf 8 file
else break;
}
return s;
}
const char *AsciiParser::NextEntry(const char *s, const char *e, bool skip_minus)
{
s = SkipEntry(s, e, skip_minus);
s = SkipSpace(s, e);
return s;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,52 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "audio/audio_io.h"
#include "filesystem/filesystem.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
template<> AudioIO *Singleton <AudioIO> ::i = NULL;
//------------------------------------------------------------------------------
void AudioIO::RegisterStreamFactory(IAudioStreamFactory *f)
{ stream_factories.Add(f); }
void AudioIO::RegisterSampleFactory(ISampleFactory *f)
{ sample_factories.Add(f); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
ISample *AudioIO::LoadSample(const char *path, const char *format)
{
String fmt(format);
if (Platform::Get().io->Exists(path))
ListForeachPtr(ISampleFactory *, codec, sample_factories)
if (ISample *sample = codec->Load(path))
{
if (!fmt || (fmt == sample->GetFormat()))
return sample;
_safe_delete(sample);
}
return NULL;
}
IAudioStream *AudioIO::OpenStream(const char *path, const char *format)
{
String fmt(format);
if (Platform::Get().io->Exists(path))
ListForeachPtr(IAudioStreamFactory *, codec, stream_factories)
if (IAudioStream *stream = codec->Open(path))
{
if (!fmt || (fmt == stream->GetFormat()))
return stream;
_safe_delete(stream);
}
return NULL;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,71 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "audio/sample_stream_factory.h"
#include "audio/sample_wav.h"
#include "audio/audio_io.h"
#include "audio/stream_interface.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
ISample *SampleStreamFactory::Load(const char *path)
{
// Open stream...
AutoPtr <IAudioStream> stream(AudioIO::Get().OpenStream(path));
if (stream.IsNull())
return NULL;
#define PCM_OUTPUT_GROW_STEP 16384 // PCM output grows 16k at a time.
Array <char> data, temp(stream->GetPCMBufferSize());
size_t pcm_size = 0;
// ...decode and dump PCM content to buffer.
forever
{
size_t avail = stream->GetPCM(temp.c_ptr());
if (!avail)
{
if (stream->IsEOF())
break;
continue;
}
size_t r_size = pcm_size + avail;
if (r_size > data.GetSize())
{
size_t size = (r_size / PCM_OUTPUT_GROW_STEP + 1) * PCM_OUTPUT_GROW_STEP;
if (!data.Reallocate(size)) // no way to know the PCM output size, this is bad for memory fragmentation...
{
__LOG_W__ << "Failed to append pcm chunk to sample, output will be truncated.\n";
break;
}
}
Memory::Copy(&data[(int)pcm_size], temp.c_ptr(), avail);
pcm_size += avail;
}
if (pcm_size == 0)
return NULL;
// Commit to sample object.
__LOG__ << "OGG '" << path << "' -> PCM data size: " << pcm_size << " bytes.\n";
uint sample_count = pcm_size / (stream->format.channels * stream->format.resolution / 8);
//
AutoPtr <SampleWav> sample(new SampleWav);
if (sample.IsNull())
return NULL;
sample->Set(data, sample_count, stream->format);
return sample.Detach();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,44 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "audio/sample_wav.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
bool SampleWav::GetSampleFormat(SampleFormat &fmt) const
{
fmt = format;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Time SampleWav::GetDuration() const
{ return Time::fromMs(sample_count * 1000 / format.frequency); }
uint SampleWav::GetPCMDataSize() const
{ return format.GetPCMDataSize(sample_count); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
char *SampleWav::AllocAs(uint count, const SampleFormat &fmt)
{
format = fmt;
if (!pcm_data.Allocate(format.GetPCMDataSize(count)))
__ERR__(__LOG_E__ << "Failed to allocate raw PCM sample buffer.\n", NULL)
sample_count = count;
return pcm_data;
}
void SampleWav::Set(Array <char> &pcm, uint count, const SampleFormat &fmt)
{
pcm_data = pcm;
sample_count = count;
format = fmt;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,119 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "audio/sample_wav_factory.h"
#include "audio/sample_wav.h"
#include "filesystem/filesystem.h"
#include "filesystem/io_handle.h"
#include "memory/endian.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
ISample *SampleWavFactory::Load(const char *path)
{
AutoPtr <IO::Handle> h(Platform::Get().io->Open(path));
if (h.IsNull())
return NULL;
// Verify format.
char header[4];
h->Read(header, 4);
if (memcmp(header, "RIFF", 4))
return NULL;
h->Seek(4);
h->Read(header, 4);
if (memcmp(header, "WAVE", 4))
return NULL;
// Create sample.
AutoPtr <SampleWav> sample(new SampleWav);
if (sample.IsNull())
return NULL;
// Parse format.
bool has_format = false, has_data = false;
struct Format
{
short wFormatTag;
unsigned short wChannels;
unsigned long dwSamplesPerSec;
unsigned long dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample;
};
Format format;
Memory::Set(&format, 0, sizeof(Format));
forever
{
// Chunk + size.
if (h->Read(header, 4) != 4)
break;
uint chunk_size = h->Read <uint> ();
// WAV format tag.
if (!memcmp(header, "fmt ", 4))
{
uint cs = chunk_size;
if (cs > sizeof(Format))
{
__LOG_W__ << "Unexpected WAV 'format' chunk size. Found " << cs << ", expected " << (int)sizeof(Format) << ".\n";
cs = sizeof(Format);
}
if (h->Read(&format, cs) != cs)
__ERR__(__LOG_E__ << "Mangled WAV 'format' chunk in '" << path << "'.\n", NULL)
Endian::ToHost(&format.wFormatTag, 2, Endian::Intel);
Endian::ToHost(&format.wChannels, 2, Endian::Intel);
Endian::ToHost(&format.dwSamplesPerSec, 4, Endian::Intel);
Endian::ToHost(&format.dwAvgBytesPerSec, 4, Endian::Intel);
Endian::ToHost(&format.wBlockAlign, 2, Endian::Intel);
Endian::ToHost(&format.wBitsPerSample, 2, Endian::Intel);
has_format = true;
// Finish skipping tag.
if (cs != chunk_size)
h->Seek(chunk_size - cs);
}
// WAV data tag.
else if (!memcmp(header, "data", 4))
{
if (has_format)
{
char *pcm = sample->AllocAs(chunk_size / (format.wBitsPerSample / 8) / format.wChannels, SampleFormat(SampleFormat::Format_PCM, format.wChannels, format.dwSamplesPerSec, (uchar)format.wBitsPerSample));
if (!pcm)
__ERR__(__LOG_E__ << "failed to allocate WAV data chunk for '" << path << "'.\n", NULL)
if (h->Read(pcm, chunk_size) != chunk_size)
__ERR__(__LOG_E__ << "mangled WAV 'data' chunk in '" << path << "'.\n", NULL)
}
else
__ERR__(__LOG_E__ << "WAV data with no format in '" << path << "'.\n", NULL)
has_data = true;
}
else
h->Seek(chunk_size);
}
if (!has_format || !has_data)
return NULL;
SampleFormat sample_format;
if (!sample->GetSampleFormat(sample_format))
return NULL;
__LOG__ << "Sample format: " << sample_format.frequency / 1000 << "KHz@" << sample_format.resolution << "bit, " << sample_format.channels << " channel(s).\n";
return sample.Detach();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,34 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "bih/bih.h"
using namespace GS::BIH;
//------------------------------------------------------------------------------
Node::~Node()
{
if (p)
if (axis != Math::AxisNone)
delete [] ((Node *)p);
p = 0;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Tree::Free()
{
root = NULL;
sarray.Free();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tree::Tree() : min_leaf_vcount(8) {}
Tree::~Tree() { Free(); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,160 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <float.h>
#include "bih/bih.h"
#include "timing/benchmark.h"
#include "log/log.h"
using namespace GS;
using namespace GS::BIH;
//-----------------------------------------------------------------------------
static void HalveMinMax(MinMax &minmax, int n, bool trim_max)
{
if (trim_max)
minmax.mx[n] = (minmax.mn[n] + minmax.mx[n]) * 0.5f;
else minmax.mn[n] = (minmax.mn[n] + minmax.mx[n]) * 0.5f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Tree::MakeNodeLeaf(Node *node, uint count, uint *p_sarray, MinMax * /*varray*/)
{
leaf_count++;
node->axis = Math::AxisNone;
node->p = (void *)p_sarray;
node->count = count;
}
void Tree::DoNodeSplit(MinMax &minmax, uint count, uint *sarray, MinMax *varray, uint &pivot, Node *node, uint &split_axis)
{
// Determine split axis.
Vector4 dt = minmax.mx - minmax.mn;
if ((dt.x > dt.y) && (dt.x > dt.z))
split_axis = 0;
else if ((dt.y > dt.x) && (dt.y > dt.z))
split_axis = 1;
else
split_axis = 2;
float split_coord = (minmax.mn[split_axis] + minmax.mx[split_axis]) * 0.5f;
// Fill split arrays.
float extends[2];
uint high = count;
//--------------------------------------------------------------------------
#define __INDICE_SWAP__(LO, HI) { uint swp = sarray[LO]; sarray[LO] = sarray[HI]; sarray[HI] = swp; }
//--------------------------------------------------------------------------
#define __GET_EXTENDS__(I, S) { extends[0] = varray[sarray[I]].mn[S]; extends[1] = varray[sarray[I]].mx[S]; }
pivot = 0;
while (pivot < high)
{
__GET_EXTENDS__(pivot, split_axis)
if ((extends[1] - split_coord) > (split_coord - extends[0]))
{ // max
__INDICE_SWAP__(pivot, high - 1)
high--;
}
else
{ // min
__INDICE_SWAP__(0, pivot)
pivot++;
}
}
// Node extends.
node->split[0] = -FLT_MAX;
uint n;
for (n = 0; n < pivot; ++n)
{
__GET_EXTENDS__(n, split_axis)
if (extends[1] > node->split[0])
node->split[0] = extends[1] + 0.0001f;
}
node->split[1] = FLT_MAX;
for (; n < count; ++n)
{
__GET_EXTENDS__(n, split_axis)
if (extends[0] < node->split[1])
node->split[1] = extends[0] - 0.0001f;
}
}
bool Tree::Split(MinMax &l_minmax, uint count, uint *p_sarray, MinMax *varray, Node *node, uint dpth)
{
if ((count <= min_leaf_vcount) || (dpth == 64))
{
if (dpth > depth)
depth = dpth;
MakeNodeLeaf(node, count, p_sarray, varray);
}
else
{
// Split node.
uint pivot, split_axis;
DoNodeSplit(l_minmax, count, p_sarray, varray, pivot, node, split_axis);
// Distribute to children.
node_count += 2;
Node *children = new Node[2];
if (!children)
__ERR__(__LOG_E__ << "Failed to allocate BIH node children.\n", false)
node->axis = (char)split_axis;
node->p = (void *)children;
MinMax minmax_child = l_minmax;
HalveMinMax(minmax_child, split_axis, true);
Split(minmax_child, pivot, p_sarray, varray, &children[0], dpth + 1);
minmax_child = l_minmax;
HalveMinMax(minmax_child, split_axis, false);
Split(minmax_child, count - pivot, &p_sarray[pivot], varray, &children[1], dpth + 1);
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool Tree::Build(uint count, MinMax *varray)
{
Benchmark build_bench(true);
if (!count)
return false;
// Initialize split array.
if (!sarray.Allocate(count))
__ERR__(__LOG_E__ << "Failed to allocate BIH indice array.\n", false)
uint n;
for (n = 0; n < count; ++n)
sarray[n] = n;
// Get volume set bounding coordinates.
minmax = varray[0];
for (n = 1; n < count; ++n)
minmax.Grow(varray[n]);
minmax.mn -= 0.0001f;
minmax.mx += 0.0001f;
// Split.
leaf_count = 0;
node_count = 1;
depth = 0;
if (!(root = new Node))
__ERR__(__LOG_E__ << "Failed to allocate BIH root node.\n", false)
bool success = Split(minmax, count, sarray, varray, root, 0);
build_bench.Stop();
// __LOG__ << "Done in " << build_bench.GetLastStepMs() << "ms. " << node_count << " nodes, " << leaf_count << " leaves, depth = " << depth << ".\n";
return success;
}
//-----------------------------------------------------------------------------

View File

@ -0,0 +1,55 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "bih/bih.h"
using namespace GS;
using namespace GS::BIH;
//------------------------------------------------------------------------------
uint Tree::IntersectNode(Node *node, MinMax &mm, uint *iarray, uint max)
{
uint count = 0;
if (node->axis == 3)
{
if (node->count > max)
return 0;
Memory::Copy(iarray, (uint *)node->p, sizeof(uint) * node->count);
return node->count;
}
else
{
if (mm.mx[node->axis] > node->split[1])
{
MinMax sub_mm = mm;
if (node->split[1] > sub_mm.mn[node->axis])
sub_mm.mn[node->axis] = node->split[1];
uint added = IntersectNode(&((Node *)node->p)[1], sub_mm, iarray/* + count*/, max);
max -= added; count += added;
}
if (mm.mn[node->axis] < node->split[0])
{
MinMax sub_mm = mm;
if (node->split[0] < sub_mm.mx[node->axis])
sub_mm.mx[node->axis] = node->split[0];
uint added = IntersectNode(&((Node *)node->p)[0], sub_mm, iarray + count, max);
/*max -= added;*/ count += added;
}
}
return count;
}
uint Tree::Intersect(MinMax &in_mm, uint *iarray, uint max)
{
if (root.IsNull() || !in_mm.TestOverlap(minmax))
return 0;
return IntersectNode(root, in_mm, iarray, max);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,101 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "bih/bih.h"
using namespace GS;
using namespace GS::BIH;
//------------------------------------------------------------------------------
void Tree::Raytrace(Trace &trace, const Vector4 &s, const Vector4 &d, float l, void *parm)
{
trace.has_i = false;
trace.i_t = -1;
trace.node_visited = 0;
trace.stack_pos = 0;
// Intersect BIH bounding volume.
float tmin, tmax;
if (!minmax.IntersectRay(s, d, tmin, tmax))
return;
// Reject if intersection is too far away.
if ((l > 0) && (tmin >= l))
return;
// Initialize trace.
trace.s = s;
trace.d = d;
tmax = ((l > 0) && (tmax > l)) ? l : tmax;
// Iterative trace.
float i_t[2];
for (Node *node = root; node; )
{
if (!trace.has_i || ((tmin < trace.i_t) && trace.want_closest)) // Only bother about rays that could lead to a closer hit.
{
while (node->axis != 3)
{
if (d[node->axis] == 0) // Axis aligned.
{
if (node->split[0] > s[node->axis])
{
if (s[node->axis] > node->split[1])
{
trace.stack[trace.stack_pos].node = &((Node *)node->p)[1];
trace.stack[trace.stack_pos].tmin = tmin;
trace.stack[trace.stack_pos++].tmax = tmax;
}
node = &((Node *)node->p)[0];
}
else if (s[node->axis] > node->split[1])
node = &((Node *)node->p)[1];
else break; // Empty space.
}
else
{
float idn = 1.f / d[node->axis];
i_t[0] = (node->split[0] - s[node->axis]) * idn;
i_t[1] = (node->split[1] - s[node->axis]) * idn;
int min = d[node->axis] > 0 ? 0 : 1, max = 1 - min;
if (i_t[min] > tmin)
{
if (tmax > i_t[max])
{
trace.stack[trace.stack_pos].node = &((Node *)node->p)[max];
trace.stack[trace.stack_pos].tmin = (i_t[max] > tmin) ? i_t[max] : tmin;
trace.stack[trace.stack_pos++].tmax = tmax;
}
node = &((Node *)node->p)[min];
tmax = (i_t[min] < tmax) ? i_t[min] : tmax;
}
else if (tmax > i_t[max])
{
node = &((Node *)node->p)[max];
tmin = (i_t[max] > tmin) ? i_t[max] : tmin;
}
else break; // Empty space.
}
trace.node_visited++;
}
if (node->axis == 3)
TraceLeaf(node, tmin, tmax, trace, parm);
}
if (!trace.stack_pos)
break;
node = trace.stack[--trace.stack_pos].node;
tmin = trace.stack[trace.stack_pos].tmin;
tmax = trace.stack[trace.stack_pos].tmax;
}
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,53 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "color/color.h"
using namespace GS;
Color Color::White(1, 1, 1),
Color::Grey(0.5, 0.5, 0.5),
Color::Black(0, 0, 0),
Color::Red(1, 0, 0),
Color::Green(0, 1, 0),
Color::Blue(0, 0, 1),
Color::Yellow(1, 1, 0),
Color::Purple(1, 0, 1);
//------------------------------------------------------------------------------
uint Color::AsInteger() const
{
uint value;
uchar *pl = (uchar *)&(value);
#if (__PLATFORM_WINDOWS__ || __PLATFORM_LINUX__ || __PLATFORM_NINTENDO_WII__)
float tmp_x = (x * 255.f) + 256.f,
tmp_y = (y * 255.f) + 256.f,
tmp_z = (z * 255.f) + 256.f,
tmp_w = (w * 255.f) + 256.f;
pl[0] = (uchar)((((int &)tmp_x) & 0x7fffff) >> 15);
pl[1] = (uchar)((((int &)tmp_y) & 0x7fffff) >> 15);
pl[2] = (uchar)((((int &)tmp_z) & 0x7fffff) >> 15);
pl[3] = (uchar)((((int &)tmp_w) & 0x7fffff) >> 15);
#else
pl[0] = (uchar)(Types::Clamp(x, 0.f, 1.f) * 255.f);
pl[1] = (uchar)(Types::Clamp(y, 0.f, 1.f) * 255.f);
pl[2] = (uchar)(Types::Clamp(z, 0.f, 1.f) * 255.f);
pl[3] = (uchar)(Types::Clamp(w, 0.f, 1.f) * 255.f);
#endif
return value;
}
void Color::FromInteger(uint value)
{
const uchar *pl = (const uchar *)&(value);
const float i255 = 1.f / 255.f;
x = (float)(pl[0]) * i255;
y = (float)(pl[1]) * i255;
z = (float)(pl[2]) * i255;
w = (float)(pl[3]) * i255;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,280 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include <cstring>
#include "data/nvariant.h"
#include "alloc/ialloc.h"
using namespace GS;
//------------------------------------------------------------------------------
void Variant::Reset()
{
type = VariantNone;
}
void Variant::Free()
{
switch (type)
{
case VariantString:
s_value.Clear();
break;
case VariantBinary:
_safe_delete_array(d_value);
d_size = 0;
break;
default:
break;
}
type = VariantNone;
}
Variant::~Variant()
{ Free(); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant &Variant::operator = (const Variant &v)
{
switch (v.GetType())
{
case VariantBool: *this = v.b_value; break;
case VariantInteger: *this = v.i_value; break;
case VariantFloat: *this = v.f_value; break;
case VariantString: *this = v.s_value; break;
case VariantBinary:
{
Free();
void *v_data; size_t v_size;
if (v.GetBinary(v_data, v_size))
SetBinary(v_data, v_size);
}
break;
default:
Free();
break;
}
return *this;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Variant::operator < (const Variant &b) const
{
switch (GetType())
{
case VariantBool:
if (b.GetType() == VariantBool)
return b_value < b.b_value;
break;
case VariantInteger:
switch (b.GetType())
{
case VariantInteger: return i_value < b.i_value;
case VariantFloat: return i_value < (int)b.f_value;
default: break;
}
break;
case VariantFloat:
switch (b.GetType())
{
case VariantInteger: return f_value < (float)b.i_value;
case VariantFloat: return f_value < b.f_value;
default: break;
}
break;
default: break;
}
return false;
}
bool Variant::operator > (const Variant &b) const
{ return !(*this < b); }
bool Variant::operator == (const Variant &b) const
{
switch (GetType())
{
case VariantBool:
if (b.GetType() == VariantBool)
return b_value == b.b_value;
break;
case VariantInteger:
switch (b.GetType())
{
case VariantInteger: return i_value == b.i_value;
case VariantFloat: return i_value == (int)b.f_value;
default: break;
}
break;
case VariantFloat:
switch (b.GetType())
{
case VariantInteger: return f_value == (float)b.i_value;
case VariantFloat: return f_value == b.f_value;
default: break;
}
break;
case VariantString:
if (b.GetType() == VariantString)
return s_value == b.s_value;
break;
default: break;
}
return false;
}
bool Variant::operator != (const Variant &b) const
{ return !(*this == b); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant &Variant::operator = (const char *v)
{
Free();
s_value.Set(v);
type = VariantString;
return *this;
}
bool Variant::Get(const char * &v) const
{
if (type != VariantString)
return false;
v = s_value.c_str();
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant &Variant::operator = (int v)
{
Free();
type = VariantInteger;
i_value = v;
return *this;
}
bool Variant::Get(int &v) const
{
switch (type)
{
case VariantBool: v = b_value ? 1 : 0; return true;
case VariantInteger: v = i_value; return true;
case VariantFloat: v = (int)f_value; return true;
default: break;
}
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant &Variant::operator = (uint v)
{
Free();
type = VariantInteger;
u_value = v;
return *this;
}
bool Variant::Get(uint &v) const
{
switch (type)
{
case VariantBool: v = b_value ? 1 : 0; return true;
case VariantInteger: v = u_value; return true;
case VariantFloat: v = (int)f_value; return true;
default: break;
}
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant &Variant::operator = (bool v)
{
Free();
type = VariantBool;
b_value = v;
return *this;
}
bool Variant::Get(bool &v) const
{
switch (type)
{
case VariantBool: v = b_value; return true;
case VariantInteger: v = asbool(i_value); return true;
case VariantFloat: v = asbool(f_value); return true;
default: break;
}
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Variant &Variant::operator = (float v)
{
Free();
type = VariantFloat;
f_value = v;
return *this;
}
bool Variant::Get(float &v) const
{
switch (type)
{
case VariantBool: v = b_value ? 1.f : 0.f; return true;
case VariantInteger: v = (float)i_value; return true;
case VariantFloat: v = f_value; return true;
default: break;
}
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Variant::SetBinary(const void *v, size_t size)
{
Free();
d_value = new char[size];
if (d_value)
{
memcpy(d_value, v, size);
d_size = size;
type = VariantBinary;
}
return true;
}
bool Variant::GetBinary(void *&data, size_t &size) const
{
if (type != VariantBinary)
return false;
data = (void *)new char[d_size];
if (data == NULL)
{
size = 0;
return false;
}
memcpy(data, d_value, d_size);
size = d_size;
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,86 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "data/registry.h"
using namespace GS;
using GS::NML::Tag;
//------------------------------------------------------------------------------
Tag *Registry::CreateKey(const char *path, const Variant *value, bool /*recursive*/)
{
StringList tag_path_list;
String(path).TrimChar(';').Split(":", tag_path_list);
Tag *ctag = NULL;
for (uint n = 0; n < tag_path_list.GetCount(); ++n)
{
String &tag_path = tag_path_list.ObjectAt(n);
Tag *ntag = ctag ? ctag->GetTag(tag_path) : GetTag(tag_path);
if (ntag == NULL)
ntag = ctag ? ctag->AddChild(tag_path) : AddRoot(tag_path);
if ((ctag = ntag) == NULL)
return NULL;
}
if (ctag && value)
{
ctag->GetValue() = *value;
RegistryKeyChange msg(path);
BroadcastMessage(RegistryMsg_KeyChange, this, &msg);
}
return ctag;
}
Tag *Registry::CreateKey(const char *path, const Variant &value, bool recursive)
{ return CreateKey(path, &value, recursive); }
bool Registry::DeleteKey(const char *path)
{
StringList tag_path_list;
String(path).Split(":", tag_path_list);
Tag *ctag = NULL, *ptag = NULL;
for (uint n = 0; n < tag_path_list.GetCount(); ++n)
{
ptag = ctag;
String &tag_path = tag_path_list.ObjectAt(n);
Tag *ntag = ctag ? ctag->GetTag(tag_path) : GetTag(tag_path);
if (!ntag)
return false;
ctag = ntag;
}
if (!ctag)
return false;
if (ptag)
ptag->RemoveTag(ctag);
else
tags.Remove(ctag);
_safe_delete(ctag);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
float Registry::GetReal(const char *path, float default_value) const
{
Tag *tag = GetTag(path);
float v;
if (!tag || !tag->GetValue().Get(v))
return default_value;
return v;
}
bool Registry::GetBool(const char *path, bool default_value) const
{
Tag *tag = GetTag(path);
bool v;
if (!tag || !tag->GetValue().Get(v))
return default_value;
return v;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,93 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "font/font_cache.h"
#include "font/font_extended.h"
#include "filesystem/filesystem.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
FontEx *FontCache::GetFont(const char *name) const
{
ListForeachPtr(FontAlias *, alias, font_aliases)
if (alias->font->GetName() == name)
return alias->font;
return NULL;
}
FontAlias *FontCache::GetAlias(const char *alias) const
{
ListForeachPtr(FontAlias *, font, font_aliases)
if (font->alias == alias)
return font;
return NULL;
}
FontEx *FontCache::GetAliasedFont(const char *alias) const
{
FontAlias *fa = GetAlias(alias);
return fa ? fa->font.c_ptr() : NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
FontEx *FontCache::LoadFont(const char *path, const char *alias)
{
// Look for an already loaded instance of the font.
FontEx *font = GetFont(path);
// If font is not available, load it.
if (font == NULL)
{
IFont *base_font = font_factory->LoadFont(path);
if (base_font == NULL)
return NULL;
// Wrap with an extended font.
font = new FontEx(base_font);
}
// Format default alias if none provided.
String _alias(path);
_alias.FileCutPathAndExtension();
if (!alias)
alias = _alias;
// Drop current alias if existing.
FontAlias *font_alias = GetAlias(alias);
if (font_alias)
font_aliases.Remove(font_alias);
// Create the alias.
font_alias = new FontAlias;
font_alias->alias = alias;
font_alias->font = font;
font_aliases.Add(font_alias);
__LOG__ << "Created a new font alias from '" << path << "' to '" << alias << "'.\n";
return font;
}
void FontCache::DeleteAlias(const char *alias)
{
ListForeachPtr(FontAlias *, a, font_aliases)
if (a->alias == alias)
font_aliases.Remove(a);
}
void FontCache::DeleteAllFont()
{
font_aliases.Clear();
}
//------------------------------------------------------------------------------
FontCache::~FontCache()
{
// [EJ] Get rid of the fonts before the factory.
DeleteAllFont();
}

View File

@ -0,0 +1,47 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "font/font_extended.h"
using namespace GS;
//------------------------------------------------------------------------------
bool FontEx::SetPixelSize(int size)
{
pixel_size = size;
return current_glyph_font.IsValid() ? current_glyph_font->SetPixelSize(size) : font->SetPixelSize(size);
}
bool FontEx::HasKerning() const
{ return current_glyph_font.IsValid() ? current_glyph_font->HasKerning() : font->HasKerning(); }
int FontEx::GetKerning(uint previous_codepoint, uint codepoint) const
{ return current_glyph_font.IsValid() ? current_glyph_font->GetKerning(previous_codepoint, codepoint) : font->GetKerning(previous_codepoint, codepoint); }
int FontEx::GetHeight() const
{ return current_glyph_font.IsValid() ? current_glyph_font->GetHeight() : font->GetHeight(); }
int FontEx::GetAdvance() const
{ return current_glyph_font.IsValid() ? current_glyph_font->GetAdvance() : font->GetAdvance(); }
bool FontEx::LoadGlyph(uint codepoint, bool for_render)
{
current_glyph_font = font;
if (font->LoadGlyph(codepoint, for_render))
return true;
// Synchronize and try fallback.
if (fallback.IsNull())
return false;
fallback->SetPixelSize(pixel_size);
bool r = fallback->LoadGlyph(codepoint, for_render);
if (r)
current_glyph_font = fallback;
return r;
}
bool FontEx::RenderCurrentGlyph(Picture &picture, const iPoint &position, const iRect &clip, const Color &color)
{ return current_glyph_font.IsValid() ? current_glyph_font->RenderCurrentGlyph(picture, position, clip, color) : font->RenderCurrentGlyph(picture, position, clip, color); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,377 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cstring>
#include "font/font_renderer.h"
#include "picture/pict.h"
#include "ascii/parser.h"
#include "log/log.h"
using namespace GS;
using namespace GS::AsciiParser;
float FontRenderer::default_text_tracking = 0;
float FontRenderer::default_text_heading = 0;
//
struct GS::SubString
{
const char *entry;
int char_count;
int space_count;
int width; // 26.6
int height;
void Reset(const char *string)
{
entry = string;
char_count = 0;
space_count = 0;
width = 0;
height = 0;
}
};
int substring_count = 0;
SubString substring_array[1024];
//------------------------------------------------------------------------------
const char *FontRenderer::CheckCommand(const char *string, Command &command)
{
command.code = CommandNone;
if (!string[0] || !string[1])
return string;
if ((string[0] == '~') && (string[1] == '~'))
{
//----------------------------------------------------------------------
#define PARSE_COMPONENT(_C_, _M_)\
{\
string = SkipSpace(string + 1, eos);\
command.vector._C_ = (float)String::atoi(string);\
string = NextEntry(string, eos);\
if (string[0] != (_M_))\
{\
command.code = CommandParseError;\
__LOG_E__ << "Error parsing text command components.\n";\
return NULL;\
}\
}
//----------------------------------------------------------------------
const char *eos = string + std::strlen(string);
if (!strncmp("Color(", string + 2, 6) || !strncmp("COLOR(", string + 2, 6)) // [EJ] 1st may: range is [0;255]
{
command.code = CommandColor;
string += 7;
const char *eop = Find(string, eos, ')');
if (!eop)
{
command.code = CommandParseError;
return NULL;
}
PARSE_COMPONENT(x, ',');
PARSE_COMPONENT(y, ',');
PARSE_COMPONENT(z, ',');
PARSE_COMPONENT(w, ')');
string = eop + 1;
}
else if (!strncmp("Size(", string + 2, 5) || !strncmp("SIZE(", string + 2, 5))
{
command.code = CommandSize;
string += 6;
const char *eop = Find(string, eos, ')');
if (!eop)
{
command.code = CommandParseError;
return NULL;
}
PARSE_COMPONENT(x, ')');
string = eop + 1;
}
}
return string;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
const char *FontRenderer::FetchSubString(const char *string, SubString &substring, TextState &state, int max_width, int max_char)
{
if (!string)
return NULL;
// Jump over leading spaces.
forever
{
if (string[0] != ' ')
break;
if (!string[0] || (string[0] == '\n'))
{
substring.Reset(NULL);
return string;
}
string++;
}
// Start sub-string.
substring.Reset(string);
int previous_codepoint = 0;
state.font->SetPixelSize(state.GetSize());
max_width <<= 6;
// Word cut point.
bool rollback_available = false;
SubString rollback_substring;
const char *rollback_string = NULL;
int tracking = int(state.GetTracking() * 64.f);
forever
{
Command command;
if ((string = CheckCommand(string, command)) == 0)
break;
if (command.code == CommandNone)
{
if (string[0] == '\n')
{
string++;
break;
}
if ((string[0] == '\\') && (string[1] == 'n'))
{
string += 2;
break;
}
if (string[0] == 0)
break;
if ((max_char > 0) && (substring.char_count == max_char))
break;
// Get glyph.
uint codepoint;
int codelength = String::Utf8toUtf32((const uchar *)string, &codepoint);
state.font->LoadGlyph(codepoint, false);
// Retrieve glyph formatting informations.
int advance = state.font->GetAdvance();
int kerning = (state.font->HasKerning() && (previous_codepoint != 0)) ? state.font->GetKerning(previous_codepoint, codepoint) : 0;
// Width constraint.
if ((max_width > 0) && ((substring.width + advance) >= max_width))
{
rollback_available = true;
break;
}
substring.width += advance + kerning + tracking;
substring.height = Types::Max(state.font->GetHeight(), substring.height);
// Count space.
if (string[0] == ' ')
{
substring.space_count++;
// Store the word rollback position.
Command dummy_command;
CheckCommand(string, dummy_command);
if ((dummy_command.code == CommandNone) && (string[1] != ' '))
{
rollback_substring = substring;
rollback_string = string;
}
}
substring.char_count++;
// Next glyph.
string += codelength ? codelength : 1;
previous_codepoint = codepoint;
}
else
switch (command.code)
{
case CommandColor: // Irrelevant when not composing.
break;
case CommandSize:
state.SetSize((int)command.vector.x);
state.font->SetPixelSize(state.GetSize());
break;
default: break;
}
}
// Rollback to the last word position.
if (rollback_available && rollback_string)
{
substring = rollback_substring;
string = rollback_string;
}
return string;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void FontRenderer::DrawSubString(SubString &line, Picture &output, TextState &state, const iRect &_out_rect, const iRect &clip_rect, int justification)
{
const char *string = line.entry;
state.font->SetPixelSize(state.GetSize());
iRect out_rect(_out_rect);
uint previous_codepoint = 0;
int tracking = int(state.GetTracking() * 64.f);
for (int n = 0; n < line.char_count; ++n)
{
Command command;
string = CheckCommand(string, command);
if (command.code == CommandNone)
{
// Get glyph.
uint codepoint;
int codelength = String::Utf8toUtf32((const uchar *)string, &codepoint);
state.font->LoadGlyph(codepoint, true);
// Retrieve glyph formatting informations.
int advance = state.font->GetAdvance();
int kerning = (state.font->HasKerning() && (previous_codepoint != 0)) ? state.font->GetKerning(previous_codepoint, codepoint) : 0;
// Render.
int px = out_rect.sx >> 6;
int py = (out_rect.sy + (line.height * 3) / 4) >> 6; // FIXME smells the hack... at best!
state.font->RenderCurrentGlyph(output, iPoint(px, py), clip_rect, state.color);
out_rect.sx += advance + kerning + tracking;
// Next glyph.
string += codelength;
previous_codepoint = codepoint;
}
else
{
switch (command.code)
{
case CommandColor:
state.color = command.vector ;/// 255.f;
break;
case CommandSize:
state.SetSize((int)command.vector.x);
state.font->SetPixelSize(state.GetSize());
break;
default: break;
}
--n;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
iRect FontRenderer::Format(const char *txt, const TextState &in_state, const iRect &out_rect)
{
if (!in_state.font)
return iRect (0, 0, 0, 0);
// Create working state.
TextState state = in_state;
// Setup formatting rules.
int max_width = (state.format == TextState::Line) ? -1 : out_rect.GetWidth(),
max_char = (state.format != TextState::Column) ? -1 : state.column_width;
// Fetch all substrings.
substring_count = 0;
while (txt && txt[0])
txt = FetchSubString(txt, substring_array[substring_count++], state, max_width, max_char);
// Create full text rectangle.
iRect text_rect;
text_rect.Set(0, 0, 0, 0);
int leading = int(state.GetLeading() * 64);
for (int n = 0; n < substring_count; ++n)
{
if (text_rect.ex < substring_array[n].width)
text_rect.ex = substring_array[n].width;
text_rect.ey += substring_array[n].height + leading;
}
if (substring_count > 0)
text_rect.ey -= leading;
text_rect.ex = text_rect.ex / 64;
text_rect.ey = text_rect.ey / 64;
return text_rect;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
iRect FontRenderer::Compose(Picture &output, const char *txt, const TextState &in_state, const iRect &out_rect, const iRect &clip_rect)
{
if (in_state.font.IsNull() || (output.GetPixelFormat().GetBpp() != 32))
return iRect(0, 0, 0, 0);
// Create working state.
TextState state = in_state;
int leading = int(state.GetLeading() * 64);
// Draw substrings.
iRect work_out_rect(out_rect * 64);
for (int n = 0; n < substring_count; ++n)
{
iRect line_rect(work_out_rect);
line_rect.ex = line_rect.sx + substring_array[n].width;
int offset = 0, justification = 0;
switch (state.alignment)
{
case TextState::Left:
offset = out_rect.sx * 64 - line_rect.sx;
break;
case TextState::Center:
offset = (out_rect.GetWidth() * 64 - line_rect.GetWidth()) / 2;
break;
case TextState::Right:
offset = out_rect.ex * 64 - line_rect.ex;
break;
case TextState::Justify:
if (n < (substring_count - 1))
justification = (out_rect.GetWidth() * 64 - line_rect.GetWidth()) / substring_array[n].space_count;
break;
default: break;
}
line_rect.sx += offset;
DrawSubString(substring_array[n], output, state, line_rect, clip_rect, justification);
work_out_rect.sy += substring_array[n].height + leading;
}
return out_rect;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,342 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <float.h>
#include "geometry/bounding_box.h"
#include "metafile/nml.h"
#include "math/matrix4.h"
using namespace GS;
using namespace GS::NML;
//------------------------------------------------------------------------------
void OBB::Transform(const Matrix4 &mtx)
{
Matrix3 rmtx = Matrix3::FromMatrix4(mtx);
bb_rotation = rmtx * bb_rotation;
bb_position = bb_position * rmtx + mtx.GetRow(3);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void OBB::ComputeMinMax(MinMax &minmax)
{
Vector4 xtd(bb_scale * 0.5f);
Vector4 smt[4];
smt[0].Set(xtd.x, xtd.y, xtd.z);
smt[1].Set(-xtd.x, xtd.y, xtd.z);
smt[2].Set(xtd.x, -xtd.y, xtd.z);
smt[3].Set(xtd.x, xtd.y, -xtd.z);
int n;
for (n = 0; n < 4; n++)
smt[n] = (smt[n] * bb_rotation).Abs();
minmax.mx = smt[0];
for (n = 1; n < 4; n++)
{
if (smt[n].x > minmax.mx.x) minmax.mx.x = smt[n].x;
if (smt[n].y > minmax.mx.y) minmax.mx.y = smt[n].y;
if (smt[n].z > minmax.mx.z) minmax.mx.z = smt[n].z;
}
minmax.mn.x = -minmax.mx.x;
minmax.mn.y = -minmax.mx.y;
minmax.mn.z = -minmax.mx.z;
minmax.mn += bb_position;
minmax.mx += bb_position;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool OBB::FromMetaTag(Tag &tag)
{
Tag *t;
List <Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
bb_position.FromMetaTag(*t);
++i;
t = i.ObjectPtr();
if (!t) return false;
bb_scale.FromMetaTag(*t);
++i;
t = i.ObjectPtr();
if (!t) return false;
bb_rotation.FromMetaTag(*t);
return true;
}
Tag *OBB::AsMetaTag()
{
Tag *root = new Tag("OBB");
if (root)
{
root->AddChild(bb_position.AsMetaTag("Position"));
root->AddChild(bb_scale.AsMetaTag("Scale"));
root->AddChild(bb_rotation.AsMetaTag("Matrix"));
}
return root;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool MinMax::IntersectRay(const Vector4 &o, const Vector4 &d, float &tmin, float &tmax)
{
tmin = 0;
tmax = FLT_MAX;
for (uint n = 0; n < 3; ++n)
if (Math::EqualZero(d[n]))
{
if ((o[n] < mn[n]) || (o[n] > mx[n]))
return false;
}
else
{
float ood = 1.f / d[n];
float t0 = (mn[n] - o[n]) * ood;
float t1 = (mx[n] - o[n]) * ood;
if (t0 > t1)
{ float swp = t1; t1 = t0; t0 = swp; }
tmin = tmin < t0 ? t0 : tmin;
tmax = tmax < t1 ? tmax : t1;
if (tmin > tmax)
return false;
}
return true;
}
bool MinMax::ClassifyLine(const Vector4 &p1, const Vector4 &direction, Vector4 &itr, Vector4 *n) const
{
uint oc1, oc2;
oc1 = cc_oc(mn, mx, p1);
if (oc1 == ClipNone)
{
// Point inside bounding box.
if (n)
n->Set(0, 0, 0);
itr = p1;
return true;
}
oc2 = ss_oc(direction);
// Same side.
if ((oc1 & oc2) > ClipNone)
return false;
// Check intersections.
if (oc1 & (ClipRight | ClipLeft))
{
if (oc1 & ClipRight)
{
if (n)
n->Set(1, 0, 0);
itr.x = mx.x;
}
else
{
if (n)
n->Set(-1, 0, 0);
itr.x = mn.x;
}
float x1 = direction.x;
float x2 = itr.x - p1.x;
itr.y = p1.y + x2 * direction.y / x1;
itr.z = p1.z + x2 * direction.z / x1;
if ((itr.y <= mx.y) && (itr.y >= mn.y) && (itr.z <= mx.z) && (itr.z >= mn.z))
return true;
}
if (oc1 & (ClipTop | ClipBottom))
{
if (oc1 & ClipTop)
{
if (n)
n->Set(0, 1, 0);
itr.y = mx.y;
}
else
{
if (n)
n->Set(0, -1, 0);
itr.y = mn.y;
}
float y1 = direction.y;
float y2 = itr.y - p1.y;
itr.x = p1.x + y2 * direction.x / y1;
itr.z = p1.z + y2 * direction.z / y1;
if ((itr.x <= mx.x) && (itr.x >= mn.x) && (itr.z <= mx.z) && (itr.z >= mn.z))
return true;
}
if (oc1 & (ClipFront | ClipBack))
{
if (oc1 & ClipBack)
{
if (n)
n->Set(0, 0, 1);
itr.z = mx.z;
}
else
{
if (n)
n->Set(0, 0, -1);
itr.z = mn.z;
}
float z1 = direction.z;
float z2 = itr.z - p1.z;
itr.x = p1.x + z2 * direction.x / z1;
itr.y = p1.y + z2 * direction.y / z1;
if ((itr.x <= mx.x) && (itr.x >= mn.x) && (itr.y <= mx.y) && (itr.y >= mn.y))
return true;
}
return false;
}
bool MinMax::ClassifySegment(const Vector4 &p1, const Vector4 &p2, Vector4 &itr, Vector4 *n) const
{
uint oc1, oc2;
oc1 = cc_oc(mn, mx, p1);
if (oc1 == ClipNone)
{
// Point inside bounding box.
if (n)
n->Set(0, 0, 0);
itr = p1;
return true;
}
oc2 = cc_oc(mn, mx, p2);
if (oc2 == ClipNone)
{
// point inside bounding box
itr = p2;
return true;
}
// Same side.
if ((oc1 & oc2) > ClipNone)
return false;
// Check intersections.
if (oc1 & (ClipRight | ClipLeft))
{
if (oc1 & ClipRight)
{
if (n)
n->Set(1, 0, 0);
itr.x = mx.x;
}
else
{
if (n)
n->Set(-1, 0, 0);
itr.x = mn.x;
}
float x1 = p2.x - p1.x;
float x2 = itr.x - p1.x;
itr.y = p1.y + x2 * (p2.y - p1.y) / x1;
itr.z = p1.z + x2 * (p2.z - p1.z) / x1;
if ( (itr.y <= mx.y) &&
(itr.y >= mn.y) &&
(itr.z <= mx.z) &&
(itr.z >= mn.z) )
return true;
}
if (oc1 & (ClipTop | ClipBottom))
{
if (oc1 & ClipTop)
{
if (n)
n->Set(0, 1, 0);
itr.y = mx.y;
}
else
{
if (n)
n->Set(0, -1, 0);
itr.y = mn.y;
}
float y1 = p2.y - p1.y;
float y2 = itr.y - p1.y;
itr.x = p1.x + y2 * (p2.x - p1.x) / y1;
itr.z = p1.z + y2 * (p2.z - p1.z) / y1;
if ( (itr.x <= mx.x) &&
(itr.x >= mn.x) &&
(itr.z <= mx.z) &&
(itr.z >= mn.z) )
return true;
}
if (oc1 & (ClipFront | ClipBack))
{
if (oc1 & ClipBack)
{
if (n)
n->Set(0, 0, 1);
itr.z = mx.z;
}
else
{
if (n)
n->Set(0, 0, -1);
itr.z = mn.z;
}
float z1 = p2.z - p1.z;
float z2 = itr.z - p1.z;
itr.x = p1.x + z2 * (p2.x - p1.x) / z1;
itr.y = p1.y + z2 * (p2.y - p1.y) / z1;
if ( (itr.x <= mx.x) &&
(itr.x >= mn.x) &&
(itr.y <= mx.y) &&
(itr.y >= mn.y) )
return true;
}
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool MinMax::FromMetaTag(Tag &tag)
{
Tag *t;
List <Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
mn.FromMetaTag(*t);
++i;
t = i.ObjectPtr();
if (!t) return false;
mx.FromMetaTag(*t);
return true;
}
Tag *MinMax::AsMetaTag()
{
Tag *root = new Tag("MinMax");
if (root)
{
root->AddChild(mn.AsMetaTag("Min"));
root->AddChild(mx.AsMetaTag("Max"));
}
return root;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,549 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "geometry/curve.h"
#include "sort/sort.h"
using namespace GS;
//------------------------------------------------------------------------------
void Curve::Update(const CurvePoint &p, const Time &t_epsilon)
{
for (uint n = 0; n < points.GetCount(); ++n)
{
CurvePoint *point = points[n];
if ((p.t >= (point->t - t_epsilon)) && (p.t <= (point->t + t_epsilon)))
{
point->v = p.v;
return;
}
}
Insert(p);
}
void Curve::Insert(const CurvePoint &k)
{
int idx = GetPointIndex(k.t);
points.Insert(new CurvePoint(k), (idx == -1) ? points.GetCount() : idx);
}
void Curve::Append(const CurvePoint &k)
{
points.Insert(new CurvePoint(k), points.GetCount());
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Curve::Delete(CurvePoint *k)
{
points.Remove(k);
delete k;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int Curve::GetPointIndex(const Time &t, bool t_greater) const
{
if (points.GetCount() == 0)
return -1;
if (t_greater)
{
for (uint n = 0; n < points.GetCount(); ++n)
if (points[n]->t > t)
return n;
}
else
{
for (int n = points.GetCount() - 1; n >= 0; --n)
if (points[n]->t <= t)
return n;
}
return -1;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
TimeRange Curve::GetTimeRange() const
{
return points.GetCount() == 0 ? TimeRange() : TimeRange(points[0]->t, points[points.GetCount() - 1]->t);
}
Range <float> Curve::GetValueRange() const
{
if (points.GetCount() == 0)
return Range <float> ();
Range <float> range(points[0]->v, points[0]->v);
for (uint n = 1; n < points.GetCount(); ++n)
{
range.start = Types::Min(range.start, points[n]->v);
range.end = Types::Max(range.end, points[n]->v);
}
return range;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
uint Curve::Optimize(uint point_count, const CurvePoint *skf, CurvePoint *dkf, float threshold)
{
if (point_count < 3)
return 0;
uint ckf = 0, n;
for (n = 1; n < (point_count - 1); n += 2)
{
float k = (skf[n].t - skf[n - 1].t).toSec() / (skf[n + 1].t - skf[n - 1].t).toSec();
float iv = (skf[n - 1].v * k) + (skf[n + 1].v * (1.f - k));
dkf[ckf++] = skf[n - 1];
if (fabs(skf[n].v - iv) > threshold)
dkf[ckf++] = skf[n];
}
if (n == (point_count - 1))
dkf[ckf++] = skf[point_count - 2];
dkf[ckf++] = skf[point_count - 1];
return point_count - ckf;
}
uint Curve::Optimize(float threshold)
{
if (!points.GetCount())
return 0;
Array <CurvePoint> skf(points.GetCount(), Alloc::Curve), dkf(points.GetCount(), Alloc::Curve);
if (skf.IsNull() || dkf.IsNull())
__ERR__(__LOG__ << "Not enough memory.\n", 0);
// Freeze array.
for (uint n = 0; n < points.GetCount(); ++n)
skf[n] = *points[n];
// Optimize curve.
uint gain = Optimize(points.GetCount(), skf.c_ptr(), dkf.c_ptr(), threshold), out = points.GetCount() - gain;
if (gain)
{
// Send back to curve.
if (!AllocatePoint(out))
__ERR__(__LOG__ << "Failed to reallocate optimized array.\n", 0);
for (uint n = 0; n < points.GetCount(); ++n)
SetPoint(n, dkf[n]);
}
return gain;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Curve::AllocatePoint(uint n)
{
ArrayListDeleteAllPtr(CurvePoint *, points)
while (n--)
if (!points.Add(new CurvePoint))
return false;
return true;
}
void Curve::SetPoint(uint i, const CurvePoint &p) const
{ *points[i] = p; }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Curve::Sort()
{
// Make sure there is work to do.
bool need_sorting = false;
for (uint n = 1; n < points.GetCount(); ++n)
if (points[n - 1]->t > points[n]->t)
{
need_sorting = true;
break;
}
if (!need_sorting)
return;
// Sort keys.
uint count = points.GetCount();
Array <GS::Sort<Time, CurvePoint *>::Entry> entries(count);
for (uint n = 0; n < count; ++n)
{
entries[n].v = points[n]->t;
entries[n].o = points[n];
}
GS::Sort<Time, CurvePoint *>::QuickSort(count, entries);
// Drop current array and rewrite ordered one.
points.Clear(false);
for (uint n = 0; n < count; ++n)
points.Add(entries[n].o);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static float range(float v, float lo, float hi, int *i)
{
float r = hi - lo;
if (r == 0.f)
{
if (i)
*i = 0;
return lo;
}
float v2 = v - lo;
if (v2 >= 0.f)
v2 = lo + v2 - r * floor(v2 / r);
else
v2 = hi + v2 - r * ceil(v2 / r);
if (i)
*i = -(int)((v2 - v) / r + (v2 > v ? 0.5f : -0.5f));
return Types::Clamp(v2, lo, hi);
}
static void hermite(float t, float *h1, float *h2, float *h3, float *h4)
{
float t2 = t * t, t3 = t * t2;
*h2 = 3.f * t2 - t3 - t3;
*h1 = 1.f - *h2;
*h4 = t3 - t2;
*h3 = *h4 - t2 + t;
}
static float bezier(float x0, float x1, float x2, float x3, float t)
{
float a, b, c, t2 = t * t, t3 = t * t2;
c = 3.f * (x1 - x0);
b = 3.f * (x2 - x1) - c;
a = x3 - x0 - c - b;
return a * t3 + b * t2 + c * t + x0;
}
static float bez2_time(float x0, float x1, float x2, float x3, float time, float *t0, float *t1)
{
float t = *t0 + (*t1 - *t0) * 0.5f, v = bezier(x0, x1, x2, x3, t);
if ((fabs(*t1 - *t0) > .0001f) && (fabs(time - v) > .0001f))
{
if (v > time)
*t1 = t;
else
*t0 = t;
return bez2_time(x0, x1, x2, x3, time, t0, t1);
}
return t;
}
static float bez2(const CurvePoint *key0, const CurvePoint *key1, float time)
{
float x, y, t, t0 = 0.f, t1 = 1.f;
if (key0->shape == CurvePoint::Shape_Bezier2)
x = key0->t.toSec() + key0->param[2];
else
x = key0->t.toSec() + (key1->t - key0->t).toSec() / 3.f;
t = bez2_time(key0->t.toSec(), x, key1->t.toSec() + key1->param[0], key1->t.toSec(), time, &t0, &t1);
if (key0->shape == CurvePoint::Shape_Bezier2)
y = key0->v + key0->param[3];
else
y = key0->v + key0->param[1] / 3.f;
return bezier(key0->v, y, key1->param[1] + key1->v, key1->v, t);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
float Curve::Outgoing(const CurvePoint *key0, const CurvePoint *key1, const CurvePoint *keyp) const
{
float a, b, d, t, out;
switch (key1->shape)
{
case CurvePoint::Shape_Linear:
d = key1->v - key0->v;
if (keyp)
{
t = (key1->t - key0->t).toSec() / (key1->t - keyp->t).toSec();
out = t * ((key0->v - keyp->v) + d);
}
else
out = d;
break;
case CurvePoint::Shape_TCB:
a = (1 - key0->tension)
* (1 + key0->continuity)
* (1 + key0->bias);
b = (1 - key0->tension)
* (1 - key0->continuity)
* (1 - key0->bias);
d = key1->v - key0->v;
if (keyp)
{
t = (key1->t - key0->t).toSec() / (key1->t - keyp->t).toSec();
out = t * (a * (key0->v - keyp->v) + b * d);
}
else
out = b * d;
break;
case CurvePoint::Shape_Bezier:
case CurvePoint::Shape_Hermite:
out = key0->param[0];
if (keyp)
out *= (key1->t - key0->t).toSec() / (key1->t - keyp->t).toSec();
break;
case CurvePoint::Shape_Bezier2:
out = key0->param[3] * (key1->t - key0->t).toSec();
if (fabs(key0->param[2]) > 1e-5f)
out /= key0->param[2];
else
out *= 1e5f;
break;
case CurvePoint::Shape_Step:
default:
out = 0;
break;
}
return out;
}
float Curve::Incoming(const CurvePoint *key0, const CurvePoint *key1, const CurvePoint *key2) const
{
float a, b, d, t, in;
switch (key1->shape)
{
case CurvePoint::Shape_Linear:
d = key1->v - key0->v;
if (key2)
{
t = (key1->t - key0->t).toSec() / (key2->t - key0->t).toSec();
in = t * ((key2->v - key1->v) + d);
}
else
in = d;
break;
case CurvePoint::Shape_TCB:
a = (1 - key1->tension)
* (1 - key1->continuity)
* (1 + key1->bias);
b = (1 - key1->tension)
* (1 + key1->continuity)
* (1 - key1->bias);
d = key1->v - key0->v;
if (key2)
{
t = (key1->t - key0->t).toSec() / (key2->t - key0->t).toSec();
in = t * (b * (key2->v - key1->v) + a * d);
}
else
in = a * d;
break;
case CurvePoint::Shape_Bezier:
case CurvePoint::Shape_Hermite:
in = key1->param[0];
if (key2)
in *= (key1->t - key0->t).toSec() / (key2->t - key0->t).toSec();
break;
case CurvePoint::Shape_Bezier2:
in = key1->param[1] * (key1->t - key0->t).toSec();
if (fabs(key1->param[0]) > 1e-5f)
in /= key1->param[0];
else in *= 1e5f;
break;
case CurvePoint::Shape_Step:
default:
in = 0;
break;
}
return in;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Curve::Evaluate(Time t, float *p, LoopMode loop, Time loop_start, Time loop_end) const
{
int point_count = points.GetCount();
if (point_count == 0)
{
*p = 0;
return;
}
if (point_count == 1)
{
*p = points[0]->v;
return;
}
// Loop mode.
CurvePoint *skey = points[0], *ekey = points[point_count - 1];
loop_start = (loop_start == Time::Inf) ? skey->t : Types::Clamp(loop_start, skey->t, ekey->t);
loop_end = (loop_end == Time::Inf) ? ekey->t : Types::Clamp(loop_end, skey->t, ekey->t);
int noff = 0;
float offset = 0;
if (t < loop_start)
{
switch (loop)
{
case Reset:
*p = 0.f;
return;
default:
case Constant:
Evaluate(loop_start, p, loop, loop_start, loop_end);
return;
case Repeat:
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), NULL));
break;
case Oscillate:
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
if (noff % 2)
t = loop_end + loop_start - t;
break;
case OffsetAndRepeat:
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
offset = noff * (ekey->v - skey->v); // Broken on custom loop point.
break;
}
}
else if (t > loop_end)
{
switch (loop)
{
case Reset:
*p = 0.f;
return;
default:
case Constant:
Evaluate(loop_end, p, loop, loop_start, loop_end);
return;
case Repeat:
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), NULL));
break;
case Oscillate:
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
if (noff % 2)
t = loop_end + loop_start - t;
break;
case OffsetAndRepeat:
t.setSec(range(t.toSec(), loop_start.toSec(), loop_end.toSec(), &noff));
offset = noff * (ekey->v - skey->v);
break;
}
}
// Seek to current key.
int ikey0;
#if 1
{
uint lo = 0, hi = points.GetCount() - 1;
forever
{
uint mid = (lo + hi) / 2;
if (points[mid]->t > t)
hi = mid;
else
{
if (lo == mid)
{
ikey0 = lo;
break;
}
else
lo = mid;
}
}
}
#else
ikey0 = 0;
while (((ikey0 + 1) < point_count) && (t > points[ikey0 + 1]->t))
ikey0++;
#endif
CurvePoint *pkey0 = points[ikey0];
if (pkey0 == NULL)
return;
// Sample curve.
CurvePoint *pkeyp = ikey0 > 0 ? points[ikey0 - 1] : NULL;
int ikey1 = ikey0 + 1;
CurvePoint *pkey1 = points[ikey1], *pkey2 = ikey1 < (point_count - 1) ? points[ikey1 + 1] : NULL;
if (t == pkey0->t)
*p = pkey0->v + offset;
else if (t == pkey1->t)
*p = pkey1->v + offset;
else
{
const float k_t = (t - pkey0->t).toSec() / (pkey1->t - pkey0->t).toSec();
switch (pkey0->shape)
{
case CurvePoint::Shape_TCB:
case CurvePoint::Shape_Bezier:
case CurvePoint::Shape_Hermite:
{
float out = Outgoing(pkey0, pkey1, pkeyp), in = Incoming(pkey0, pkey1, pkey2);
float h1, h2, h3, h4;
hermite(k_t, &h1, &h2, &h3, &h4);
*p = h1 * pkey0->v + h2 * pkey1->v + h3 * out + h4 * in + offset;
}
break;
case CurvePoint::Shape_Bezier2:
*p = bez2(pkey0, pkey1, k_t) + offset;
break;
case CurvePoint::Shape_Linear:
*p = pkey0->v + k_t * (pkey1->v - pkey0->v) + offset;
break;
case CurvePoint::Shape_Step:
*p = pkey0->v + offset;
break;
default:
*p = offset;
break;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Curve::Clear()
{
ArrayListDeleteAllPtr(CurvePoint *, points)
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Curve::~Curve()
{ Clear(); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,268 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <stdio.h>
#include "geometry/curve.h"
#include "math/nmath.h"
#include "sort/sort.h"
#include "alloc/ialloc.h"
#include "log/log.h"
using namespace GS;
using namespace GS::NML;
//------------------------------------------------------------------------------
Reflection::Enum::Dict Curve::loop_mode_dict[] =
{
{ Curve::Reset, "Reset" },
{ Curve::Constant, "Constant" },
{ Curve::Repeat, "Repeat" },
{ Curve::Oscillate, "Oscillate" },
{ Curve::OffsetAndRepeat, "OffsetAndRepeat" },
{ 0, 0 }
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// ARM odd address read/write helper functions.
void ARM_unaligned_read(float &out, const char *addr)
{
char *p_out = (char *)&out;
for (int n = 0; n < sizeof(float); ++n)
p_out[n] = addr[n];
}
void ARM_unaligned_write(char *addr, const float &in)
{
const char *p_in = (const char *)&in;
for (int n = 0; n < sizeof(float); ++n)
addr[n] = p_in[n];
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Curve::FromMetaTag(Tag &tag)
{
if (tag.name != "Curve")
__ERR__(__LOG_E__ << "Could not parse curve, incorrect root tag (" << tag.name << ").\n", false)
Clear();
// Parse root tags.
NMLTagForeach(pt, tag)
{
if (pt->name == "BinaryKnot")
{
Tag *count_tag = pt->GetTag("Count"), *data_tag = pt->GetTag("Data");
if (count_tag && data_tag)
{
char *data = (char *)data_tag->GetValue().GetBinaryBuffer(), *p_data = data;
if (data && AllocatePoint(count_tag->GetInteger()))
for (uint n = 0; n < points.GetCount(); ++n)
{
CurvePoint *p = points[n];
p->shape = CurvePoint::Shape(*p_data++);
float t;
ARM_unaligned_read(t, p_data + 0);
p->t.setSec(t);
ARM_unaligned_read(p->v, p_data + 4);
if (p->shape == CurvePoint::Shape_Linear)
p_data += 2 * 4;
else
{
ARM_unaligned_read(p->tension, p_data + 8);
ARM_unaligned_read(p->continuity, p_data + 12);
ARM_unaligned_read(p->bias, p_data + 16);
for (int n = 0; n < 4; ++n)
ARM_unaligned_read(p->param[n], p_data + 20 + n * 4);
p_data += 9 * 4;
}
}
}
}
else if (pt->name == "Knot")
{
Tag *st = pt->GetTags()[0];
if (!st || (st->name != "Count"))
__ERR__(__LOG_E__ << "First sub-tag in <Curve> must be the knot <Count> tag.\n", false)
if (!AllocatePoint((uint)st->GetInteger()))
return false;
static String _count("Count"), _knot("Knot"), _knotex("KnotEx");
uint n = 0;
NMLTagForeach(st, *pt)
{
if (st->name == _count)
{}
// Legacy knot definition.
if (st->name == _knot)
{
if (n == points.GetCount())
{
__LOG_E__ << "Too many knot in <Curve>, " << points.GetCount() << " expected.\n";
break;
}
if (const char *p = st->GetString())
{
points[n]->t = Time::fromSec(String::atof(p));
points[n]->shape = CurvePoint::Shape_Linear;
p = String::strfindchar(p, ':');
points[n]->v = p[0] ? String::atof(p + 1) : 0;
n++;
}
else
__LOG_W__ << "Invalid knot tag while parsing curve.\n";
}
/*
Extended knot definition.
*/
else if (st->name == _knotex)
{
if (n == points.GetCount())
{
__LOG_E__ << "Too many knot in <Knot>, " << points.GetCount() << " specified.\n";
break;
}
if (const char *p = st->GetString())
{
CurvePoint *_knot = points[n];
_knot->t = Time::fromSec(String::atof(p));
// Read shape.
p = String::strfindchar(p, ':');
int shape = p[0] ? String::atoi(p + 1) : 0;
p++;
switch (shape)
{
default:
case 0: _knot->shape = CurvePoint::Shape_None; break;
case 1: _knot->shape = CurvePoint::Shape_Linear; break;
case 2: _knot->shape = CurvePoint::Shape_Bezier; break;
case 3: _knot->shape = CurvePoint::Shape_Bezier2; break;
case 4: _knot->shape = CurvePoint::Shape_Hermite; break;
case 5: _knot->shape = CurvePoint::Shape_TCB; break;
case 6: _knot->shape = CurvePoint::Shape_Step; break;
}
// Read knot parameters.
//--------------------------------------------
#define GetInputKnotParamEx(_PARM_)\
{\
p = String::strfindchar(p, ':');\
(_PARM_) = p[0] ? String::atof(p + 1) : -1;\
p++;\
}
//--------------------------------------------
GetInputKnotParamEx(_knot->v);
GetInputKnotParamEx(_knot->tension);
GetInputKnotParamEx(_knot->continuity);
GetInputKnotParamEx(_knot->bias);
GetInputKnotParamEx(_knot->param[0]);
GetInputKnotParamEx(_knot->param[1]);
GetInputKnotParamEx(_knot->param[2]);
GetInputKnotParamEx(_knot->param[3]);
n++;
}
else
__LOG_W__ << "Invalid extended knot tag while parsing curve.\n";
}
else
__LOG_W__ << "Unsupported knot tag '" << st->name << "'.\n";
}
// Incomplete/erroneous definition.
if (n != points.GetCount())
{
Clear();
__ERR__(__LOG_E__ << "<Curve> is corrupted, discarding.\n", false)
}
}
else __LOG_W__ << "Unknown tag '" << pt->name << "' in <Curve>.\n";
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tag *Curve::AsMetaTag() const
{
Tag *root = new Tag("Curve");
if (!root)
__ERR__(__LOG_E__ << "Could not create curve root tag to serialize.\n", NULL)
// Binary knots.
if (points.GetCount())
if (Tag *binary_knot_tag = root->AddChild("BinaryKnot"))
{
binary_knot_tag->AddChild("Count", points.GetCount());
// Get size.
int size = 0;
for (uint n = 0; n < points.GetCount(); ++n)
{
CurvePoint *_knot = points[n];
// Legacy definition.
if (_knot->shape == CurvePoint::Shape_Linear)
size += 2 * 4; // Knot size.
else size += 9 * 4; // Extended knot size.
}
// Output binary.
Array <char> knot_array(points.GetCount() + size);
char *p_knot = knot_array;
for (uint n = 0; n < points.GetCount(); ++n)
{
CurvePoint *_knot = points[n];
*p_knot++ = uchar(_knot->shape);
float t = _knot->t.toSec();
ARM_unaligned_write(p_knot + 0, t);
ARM_unaligned_write(p_knot + 4, _knot->v);
if (_knot->shape == CurvePoint::Shape_Linear)
p_knot += 2 * 4;
else
{
ARM_unaligned_write(p_knot + 8, _knot->tension);
ARM_unaligned_write(p_knot + 12, _knot->continuity);
ARM_unaligned_write(p_knot + 16, _knot->bias);
for (int n = 0; n < 4; ++n)
ARM_unaligned_write(p_knot + 20 + n * 4, _knot->param[n]);
p_knot += 9 * 4;
}
}
binary_knot_tag->AddChild("Data", knot_array, points.GetCount() + size);
}
return root;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,309 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "geometry/frustum.h"
#include "geometry/bounding_box.h"
#include "geometry/sat.h"
#include "shape/shape.h"
#include "math/matrix4.h"
using namespace GS;
//------------------------------------------------------------------------------
void Frustum::SetPerspective(float fov, float znear, float zfar, const Matrix4 *matrix, float h_ar, float v_ar)
{
fov *= 0.5f;
const float hyp = tan(fov);
const float hfov = atan(hyp / h_ar), vfov = atan(hyp / v_ar);
Vector4 n;
const float sinv = sin(vfov);
const float cosv = cos(vfov);
n.Set(0, cosv, -sinv);
plane[Top].Set(NULL, n, matrix);
n.Set(0, -cosv, -sinv);
plane[Bottom].Set(NULL, n, matrix);
const float sinh = sin(hfov);
const float cosh = cos(hfov);
n.Set(-cosh, 0, -sinh);
plane[Left].Set(NULL, n, matrix);
n.Set(cosh, 0, -sinh);
plane[Right].Set(NULL, n, matrix);
Vector4 s;
s.Set(0, 0, znear);
n.Set(0, 0, -1);
plane[Near].Set(&s, n, matrix);
s.Set(0, 0, zfar);
n.Set(0, 0, 1);
plane[Far].Set(&s, n, matrix);
// Model vertices.
Vector4 bvtx[8];
Vector4 *_vtx = matrix ? bvtx : vtx;
// Compute near plane corners.
float k = znear / -cosv;
_vtx[0].y = -sinv * k;
_vtx[0].z = znear;//-cosv * k;
k = znear / cosh;
_vtx[0].x = -sinh * k;
_vtx[0].w = 1;
_vtx[1].Set(-_vtx[0].x, _vtx[0].y, _vtx[0].z);
_vtx[2].Set(-_vtx[0].x, -_vtx[0].y, _vtx[0].z);
_vtx[3].Set(_vtx[0].x, -_vtx[0].y, _vtx[0].z);
// Compute far plane corners.
k = zfar / -cosv;
_vtx[4].y = -sinv * k;
_vtx[4].z = zfar;//-cosv * k;
k = zfar / cosh;
_vtx[4].x = -sinh * k;
_vtx[4].w = 1;
_vtx[5].Set(-_vtx[4].x, _vtx[4].y, _vtx[4].z);
_vtx[6].Set(-_vtx[4].x, -_vtx[4].y, _vtx[4].z);
_vtx[7].Set(_vtx[4].x, -_vtx[4].y, _vtx[4].z);
if (matrix)
matrix->Apply(vtx, bvtx, 8);
}
void Frustum::SetOrthographic(float width, float height, float znear, float zfar, const Matrix4 *matrix, float h_ar, float v_ar)
{
Vector4 s, n;
width *= h_ar;
height *= v_ar;
s.Set(0, height * 0.5f, 0);
n.Set(0, 1, 0);
plane[Top].Set(&s, n, matrix);
s.Set(0, -height * 0.5f, 0);
n.Set(0, -1, 0);
plane[Bottom].Set(&s, n, matrix);
s.Set(-width * 0.5f, 0, 0);
n.Set(-1, 0, 0);
plane[Left].Set(&s, n, matrix);
s.Set(width * 0.5f, 0, 0);
n.Set(1, 0, 0);
plane[Right].Set(&s, n, matrix);
s.Set(0, 0, znear);
n.Set(0, 0, -1);
plane[Near].Set(&s, n, matrix);
s.Set(0, 0, zfar);
n.Set(0, 0, 1);
plane[Far].Set(&s, n, matrix);
// Model vertices.
Vector4 bvtx[8];
Vector4 *_vtx = matrix ? bvtx : vtx;
// Compute near plane corners.
_vtx[0].Set(-width * 0.5f, height * 0.5f, znear);
_vtx[1].Set( width * 0.5f, height * 0.5f, znear);
_vtx[2].Set( width * 0.5f, -height * 0.5f, znear);
_vtx[3].Set(-width * 0.5f, -height * 0.5f, znear);
_vtx[4].Set(-width * 0.5f, height * 0.5f, zfar);
_vtx[5].Set( width * 0.5f, height * 0.5f, zfar);
_vtx[6].Set( width * 0.5f, -height * 0.5f, zfar);
_vtx[7].Set(-width * 0.5f, -height * 0.5f, zfar);
if (matrix)
matrix->Apply(vtx, bvtx, 8);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Frustum::Visibility Frustum::ClassifyShape(const Shape &s, const Matrix4 *m) const
{
Visibility v = Inside;
Matrix3 rm;
if (m)
rm = Matrix3::FromMatrix4(*m).Transposed();
for (uint n = 0; n < 6; ++n)
{
float d, r;
if (m)
{
d = plane[n].DistanceToPlane(s.GetCenter() * m[0]);
r = s.GetSupportDistance(plane[n].GetNormal() * rm);
}
else
{
d = plane[n].DistanceToPlane(s.GetCenter());
r = s.GetSupportDistance(plane[n].GetNormal());
}
if (d > r)
return Outside;
if (d > -r)
v = Clipped;
}
return v;
}
Frustum::Visibility Frustum::ClassifySphere(const Vector4 &p, float r) const
{
Visibility v = Inside;
for (uint n = 0; n < 6; ++n)
{
if (plane[n].DistanceToPlane(p) > r)
return Outside;
if (plane[n].DistanceToPlane(p) > -r)
v = Clipped;
}
return v;
}
Frustum::Visibility Frustum::ClassifySet(uint count, const Vector4 * const GSRESTRICT set, const float offset) const
{
Visibility v = Inside;
for (uint n = 0; n < 6; ++n)
{
uint out = 0;
for (uint i = 0; i < count; ++i)
if (plane[n].DistanceToPlane(set[i]) > offset)
++out;
if (out == count)
return Outside;
if (out > 0)
v = Clipped;
}
return v;
}
//------------------------------------------------------------------------------
#if (__PLATFORM_NINTENDO_WII__ == 0)
//#define FRUSTUM_TEST_USE_SAT
#endif
//--------------------------------------------
#define SAT_TEST(_N_, _U_, _A_, _V_, _B_)\
{\
SAT::Overlap _v = SAT::TestOverlap(_N_, _U_, _A_, _V_, _B_);\
if (_v == SAT::Outside)\
return Outside;\
if (_v == SAT::Clipped)\
v = Clipped;\
}
//--------------------------------------------
//------------------------------------------------------------------------------
Frustum::Visibility Frustum::ClassifyMinMax(const MinMax &mm, const Matrix4 *matrix) const
{
// TODO Please, use AABB half width and implicit interval projection... will you?
Vector4 s[8], d[8], *p;
s[0].Set(mm.mn.x, mm.mn.y, mm.mn.z);
s[1].Set(mm.mx.x, mm.mn.y, mm.mn.z);
s[2].Set(mm.mx.x, mm.mx.y, mm.mn.z);
s[3].Set(mm.mn.x, mm.mx.y, mm.mn.z);
s[4].Set(mm.mn.x, mm.mn.y, mm.mx.z);
s[5].Set(mm.mx.x, mm.mn.y, mm.mx.z);
s[6].Set(mm.mx.x, mm.mx.y, mm.mx.z);
s[7].Set(mm.mn.x, mm.mx.y, mm.mx.z);
if (matrix)
{
matrix->Apply(d, s, 8);
p = d;
}
else
p = s;
#ifndef FRUSTUM_TEST_USE_SAT
// Faster but much coarser test.
return ClassifySet(8, p);
#else
// Frustum/AABB SAT.
Visibility v = Inside;
// Test face/{face/edge} contact.
SAT_TEST(plane[Top].GetNormal(), 8, vtx, 8, p);
SAT_TEST(plane[Bottom].GetNormal(), 8, vtx, 8, p);
SAT_TEST(plane[Left].GetNormal(), 8, vtx, 8, p);
SAT_TEST(plane[Right].GetNormal(), 8, vtx, 8, p);
SAT_TEST(plane[Near].GetNormal(), 8, vtx, 8, p);
SAT_TEST(plane[Far].GetNormal(), 8, vtx, 8, p);
Vector4 _edge[3];
_edge[0] = matrix ? matrix->GetRow(0) : Vector4(1, 0, 0);
SAT_TEST(_edge[0], 8, vtx, 8, p);
_edge[1] = matrix ? matrix->GetRow(1) : Vector4(0, 1, 0);
SAT_TEST(_edge[1], 8, vtx, 8, p);
_edge[2] = matrix ? matrix->GetRow(2) : Vector4(0, 0, 1);
SAT_TEST(_edge[2], 8, vtx, 8, p);
// Test edge/edge contact.
Vector4 edge[6];
for (uint n = 0; n < 4; ++n)
edge[n] = vtx[n + 4] - vtx[n];
edge[4] = vtx[1] - vtx[0];
edge[5] = vtx[3] - vtx[0];
for (uint n = 0; n < 6; ++n)
for (uint m = 0; m < 3; ++m)
{
Vector4 axis = edge[n].Cross(_edge[m]);
if (Math::EqualZero(axis.Len2()))
continue;
SAT_TEST(axis, 8, vtx, 8, p);
}
return v;
#endif
}
Frustum::Visibility Frustum::ClassifyFrustrum(const Frustum &frustum) const
{
#ifndef FRUSTUM_TEST_USE_SAT
// Faster but much coarser test.
return ClassifySet(8, frustum.vtx);
#else
// Frustum/frustum SAT.
Visibility v = Inside;
// Test face/{face/edge} contact.
SAT_TEST(plane[Top].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(plane[Bottom].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(plane[Left].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(plane[Right].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(plane[Far].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(frustum.plane[Top].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(frustum.plane[Bottom].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(frustum.plane[Left].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(frustum.plane[Right].GetNormal(), 8, vtx, 8, frustum.vtx);
SAT_TEST(frustum.plane[Far].GetNormal(), 8, vtx, 8, frustum.vtx);
// Test edge/edge contact.
Vector4 edge[6], _edge[6];
for (uint n = 0; n < 4; ++n)
{
edge[n] = vtx[n + 4] - vtx[n];
_edge[n] = frustum.vtx[n + 4] - frustum.vtx[n];
}
edge[4] = vtx[1] - vtx[0];
edge[5] = vtx[3] - vtx[0];
_edge[4] = frustum.vtx[1] - frustum.vtx[0];
_edge[5] = frustum.vtx[3] - frustum.vtx[0];
for (uint n = 0; n < 6; ++n)
for (uint m = 0; m < 6; ++m)
{
Vector4 axis = edge[n].Cross(_edge[m]);
if (!Math::EqualZero(axis.Len2()))
SAT_TEST(axis, 8, vtx, 8, frustum.vtx);
}
return v;
#endif
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,122 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "geometry/geometric_tools.h"
namespace GS {
namespace Geometric {
//------------------------------------------------------------------------------
float TriArea2D(float x0, float y0, float x1, float y1, float x2, float y2)
{ return (x0 - x1) * (y1 - y2) - (x1 - x2) * (y0 - y1); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Barycentric(const Vector4 &a, const Vector4 &b, const Vector4 &c, const Vector4 &p, float &u, float &v, float &w)
{
Vector4 m = (b - a).Cross(c - a);
float nu, nv, ood;
float x = fabs(m.x), y = fabs(m.y), z = fabs(m.z);
if (x >= y && x >= z)
{
nu = TriArea2D(p.y, p.z, b.y, b.z, c.y, c.z);
nv = TriArea2D(p.y, p.z, c.y, c.z, a.y, a.z);
ood = 1.f / m.x;
}
else if (y >= x && y >= z)
{
nu = TriArea2D(p.x, p.z, b.x, b.z, c.x, c.z);
nv = TriArea2D(p.x, p.z, c.x, c.z, a.x, a.z);
ood = 1.f / -m.y;
}
else
{
nu = TriArea2D(p.x, p.y, b.x, b.y, c.x, c.y);
nv = TriArea2D(p.x, p.y, c.x, c.y, a.x, a.y);
ood = 1.f / m.z;
}
u = nu * ood;
v = nv * ood;
w = 1.f - u - v;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool LineIntersectPlane(const Vector4 &a, const Vector4 &v, const Vector4 &n, const Vector4 &p, float &t)
{
float k = v.Dot(n);
if (Math::EqualZero(k))
return false;
t = (p.Dot(n) - a.Dot(n)) / k;
return true;
}
bool LineIntersectSphere(const Vector4 &a, const Vector4 &v, const Vector4 &c, float r, float t[2])
{
Vector4 e = c - a;
float k = e.Dot(v);
float d = r * r - (e.Len2() - k * k);
if (d < 0)
return false;
d = Math::Sqrt(d);
if (t)
{
t[0] = k - d;
t[1] = k + d;
}
return true;
}
float LineClosestPoint(const Vector4 &a, const Vector4 &b, const Vector4 &u, Vector4 *p)
{
Vector4 _u = u - a;
Vector4 _v = b - a;
float t = _u.Dot(_v) / _v.Dot(_v);
if (p)
p[0] = _v * t + a;
return t;
}
bool LineClosestPointToLine(const Vector4 &a, const Vector4 &b, const Vector4 &la, const Vector4 &lb, float t[2])
{
Vector4 u = b - a, v = lb - la;
float ul2 = u.Len2(), vl2 = v.Len2();
float d = u.Dot(v), k = ul2 * vl2 - d * d;
if (fabs(k) < 0.00000001f)
return false;
k = 1.f / k;
float uv = d, du = (la - a).Dot(u), dv = (a - la).Dot(v);
t[0] = (vl2 * du + uv * dv) * k;
t[1] = (uv * du + ul2 * dv) * k;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
float SegmentClosestPoint(const Vector4 &a, const Vector4 &b, const Vector4 &u, Vector4 *p)
{
Vector4 _u = u - a, _v = b - a;
float t = Types::Clamp(_u.Dot(_v) / _v.Dot(_v));
if (p)
p[0] = _v * t + a;
return t;
}
//------------------------------------------------------------------------------
} // Geometric
} // GS

View File

@ -0,0 +1,46 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "geometry/plane.h"
#include "math/matrix4.h"
using namespace GS;
//------------------------------------------------------------------------------
void Plane::Set(const Vector4 *_p, const Vector4 &_n, const Matrix4 *mtx)
{
if (mtx)
{
if (_p)
mtx->Apply(&p, _p);
else p = mtx->GetRow(3);
mtx->ApplyRotation(&n, &_n);
}
else
{
if (_p)
p = *_p;
else p.Set(0, 0, 0, 1);
n = _n;
}
d = -p.Dot(n);
}
void Plane::Set(const Vector4 _p[3], const Matrix4 *mtx)
{
Vector4 _n = (_p[1] - _p[0]).Cross(_p[2] - _p[0]);
Set(&_p[0], _n, mtx);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Plane::Plane()
{
d = 0;
p.Set();
n.Set();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,54 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "geometry/rect.h"
#include "metafile/nml.h"
using namespace GS;
//------------------------------------------------------------------------------
template <class T> NML::Tag *Rect<T>::AsMetaTag(const char *id) const
{
NML::Tag *root = new NML::Tag(id ? id : "Rect");
if (root)
{
root->AddChild("SX", sx);
root->AddChild("SY", sy);
root->AddChild("EX", ex);
root->AddChild("EY", ey);
}
return root;
}
template <class T> bool Rect<T>::FromMetaTag(NML::Tag &tag)
{
NML::Tag *t;
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
sx = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
sy = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
ex = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
ey = t->GetReal();
++i;
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,319 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "math/matrix3.h"
#include "math/matrix4.h"
#include "metafile/nml.h"
using namespace GS;
using namespace GS::Math;
Matrix3 Matrix3::static_identity;
//------------------------------------------------------------------------------
bool Matrix3::Inverse(Matrix3 &i) const
{
// Covariants.
i.m[0][0] = m[1][1] * m[2][2] - m[1][2] * m[2][1];
i.m[0][1] = m[0][2] * m[2][1] - m[0][1] * m[2][2];
i.m[0][2] = m[0][1] * m[1][2] - m[0][2] * m[1][1];
i.m[1][0] = m[1][2] * m[2][0] - m[1][0] * m[2][2];
i.m[1][1] = m[0][0] * m[2][2] - m[0][2] * m[2][0];
i.m[1][2] = m[0][2] * m[1][0] - m[0][0] * m[1][2];
i.m[2][0] = m[1][0] * m[2][1] - m[1][1] * m[2][0];
i.m[2][1] = m[0][1] * m[2][0] - m[0][0] * m[2][1];
i.m[2][2] = m[0][0] * m[1][1] - m[0][1] * m[1][0];
float k = m[0][0] * i.m[0][0] + m[0][1] * i.m[1][0] + m[0][2] * i.m[2][0];
if (!k)
return false;
k = 1.f / k;
i.m[0][0] *= k; i.m[0][1] *= k; i.m[0][2] *= k;
i.m[1][0] *= k; i.m[1][1] *= k; i.m[1][2] *= k;
i.m[2][0] *= k; i.m[2][1] *= k; i.m[2][2] *= k;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Matrix3::VectorMatrix(const Vector4 &v)
{ return Matrix3(v.x, 0, 0, v.y, 0, 0, v.z, 0, 0); }
Matrix3 Matrix3::CrossProductMatrix(const Vector4 &v)
{ return Matrix3(0, -v.z, v.y, v.z, 0, -v.x, -v.y, v.x, 0); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Matrix3::Normalized() const
{
Vector4 x(GetRow(0)), y(GetRow(1)), z(GetRow(2));
Matrix3 m;
m.SetRow(0, x.Normalized());
m.SetRow(1, y.Normalized());
m.SetRow(2, z.Normalized());
return m;
}
Matrix3 Matrix3::AsOrthonormalBase() const
{
Vector4 x(GetRow(0)), y(GetRow(1));
Matrix3 m;
x = x.Normalized();
m.SetRow(0, x);
Vector4 z(x.Cross(y).Normalized());
m.SetRow(2, z);
y = z.Cross(x).Normalized();
m.SetRow(1, y);
return m;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector4 Matrix3::AsEuler(rOrder rorder) const
{
Vector4 euler(0, 0, 0);
switch (rorder)
{
case rOrder_ZYX:
euler.y = ASin(-m[2][0]);
euler.z = atan2(m[1][0], m[0][0]);
euler.x = atan2(m[2][1], m[2][2]);
break;
case rOrder_XZY:
euler.z = ASin(-m[0][1]);
euler.x = atan2(m[2][1], m[1][1]);
euler.y = atan2(m[0][2], m[0][0]);
break;
case rOrder_XYZ:
euler.y = ASin(m[0][2]);
euler.x = atan2(-m[1][2], m[2][2]);
euler.z = atan2(-m[0][1], m[0][0]);
break;
case rOrder_YZX:
euler.z = ASin(m[1][0]);
euler.x = atan2(-m[1][2], m[1][1]);
euler.y = atan2(-m[2][0], m[0][0]);
break;
default:
case rOrder_YXZ: // Engine default.
euler.x = ASin(-m[1][2]);
euler.y = atan2(m[0][2], m[2][2]);
euler.z = atan2(m[1][0], m[1][1]);
break;
case rOrder_ZXY: // MAX default.
euler.x = ASin(m[2][1]);
euler.y = atan2(-m[2][0], m[2][2]);
euler.z = atan2(-m[0][1], m[1][1]);
break;
case rOrder_XY:
euler.y = ACos(m[0][0]);
euler.x = ACos(m[1][1]);
euler.z = 0;
break;
}
return euler;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Matrix3::FromEuler(const Vector4 &euler, rOrder rorder)
{ return Matrix3::FromEuler(euler.x, euler.y, euler.z, rorder); }
Matrix3 Matrix3::FromEuler(float x, float y, float z, rOrder rorder)
{
float cx = Cos(x), cy = Cos(y), cz = Cos(z),
sx = Sin(x), sy = Sin(y), sz = Sin(z);
switch (rorder)
{
case rOrder_XZY:
return Matrix3 ( cy * cz, sx * sy + cx * cy * sz, -cx * sy + cy * sx * sz,
-sz, cx * cz, cz * sx,
cz * sy, -cy * sx + cx * sy * sz, cx * cy + sx * sy * sz );
case rOrder_ZYX:
return Matrix3 ( cy * cz, cy * sz, -sy,
cz * sx * sy - cx * sz, cx * cz + sx * sy * sz, cy * sx,
cx *cz * sy + sx * sz, -cz * sx + cx * sy * sz, cx * cy );
case rOrder_XYZ:
return Matrix3 ( cy * cz, cz * sx * sy + cx * sz, -cx * cz * sy + sx * sz,
-cy * sz, cx * cz - sx * sy * sz, cz * sx + cx * sy * sz,
sy, -cy * sx, cx * cy );
case rOrder_ZXY:
return Matrix3 ( cy * cz - sx * sy * sz, cz * sx * sy + cy * sz, -cx * sy,
-cx * sz, cx * cz, sx,
cz * sy + cy * sx * sz, -cy * cz * sx + sy * sz, cx * cy );
case rOrder_YZX:
return Matrix3 ( cy * cz, sz, -cz * sy,
sx * sy - cx * cy * sz, cx * cz, cy * sx + cx * sy * sz,
cx * sy + cy * sx * sz, -cz * sx, cx * cy - sx * sy * sz );
case rOrder_YXZ:
return Matrix3 ( cy * cz + sx * sy * sz, cx * sz, -cz * sy + cy * sx * sz,
cz * sx * sy - cy * sz, cx * cz, cy * cz * sx + sy * sz,
cx * sy, -sx, cx * cy );
case rOrder_XY:
return Matrix3 ( cy, sx * sy, -cx * sy,
0, cx, sx,
sy, -cy * sx, cx * cy );
}
return Matrix3::IdentityMatrix();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Matrix3::TranslationMatrix(const Vector4 &t)
{ return Matrix3(1, 0, 0, 0, 1, 0, t.x, t.y, 1); }
Matrix3 Matrix3::TranslationMatrix(const Vector2 &t)
{ return Matrix3(1, 0, 0, 0, 1, 0, t.x, t.y, 1); }
Matrix3 Matrix3::ScaleMatrix(const Vector4 &s)
{ return Matrix3(s.x, 0, 0, 0, s.y, 0, 0, 0, s.z); }
Matrix3 Matrix3::ScaleMatrix(const Vector2 &s)
{ return Matrix3(s.x, 0, 0, 0, s.y, 0, 0, 0, 1); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Matrix3::RotationMatrixXAxis(float a)
{ return Matrix3(1, 0, 0, 0, Cos(a), Sin(a), 0, -Sin(a), Cos(a)); }
Matrix3 Matrix3::RotationMatrixYAxis(float a)
{ return Matrix3(Cos(a), 0, -Sin(a), 0, 1, 0, Sin(a), 0, Cos(a)); }
Matrix3 Matrix3::RotationMatrixZAxis(float a)
{ return Matrix3(Cos(a), Sin(a), 0, -Sin(a), Cos(a), 0, 0, 0, 1); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Matrix3::SetRow(uint n, const Vector4 &row)
{ m[0][n] = row.x; m[1][n] = row.y; m[2][n] = row.z; }
void Matrix3::SetColumn(uint n, const Vector4 &col)
{ m[n][0] = col.x; m[n][1] = col.y; m[n][2] = col.z; }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Matrix3::FromOrthonormalBasis(const Vector4 &w, const Vector4 *v)
{
Matrix3 mtx;
float l = w.Len();
if (!l)
return Matrix3::IdentityMatrix();
Vector4 wn = w / l, u;
if (!v)
{
if (!EqualZero(wn.x) || !EqualZero(wn.z))
{
u.Set(wn.z, 0, -wn.x); // Cross with up = {0,1,0}.
u = u.Normalized();
}
else
u.Set(-1, 0, 0);
Vector4 c(wn.Cross(u));
mtx.SetRow(1, c);
}
else
{
Vector4 vn(v->Normalized());
mtx.SetRow(1, vn);
u = vn.Cross(wn);
}
mtx.SetRow(0, u);
mtx.SetRow(2, wn);
return mtx;
}
Matrix3 Matrix3::FromMatrix4(const Matrix4 &mtx)
{
return Matrix3(
mtx.m[0][0], mtx.m[1][0], mtx.m[2][0],
mtx.m[0][1], mtx.m[1][1], mtx.m[2][1],
mtx.m[0][2], mtx.m[1][2], mtx.m[2][2]
);
}
//------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Matrix3::Apply(Vector4 *o, const Vector4 *v, uint n) const
//-----------------------------------------------------------------------------
{
for (uint c = 0; c < n; c++)
{
float x = v->x, y = v->y, z = v->z;
o->x = x * m[0][0] + y * m[0][1] + z * m[0][2];
o->y = x * m[1][0] + y * m[1][1] + z * m[1][2];
o->z = x * m[2][0] + y * m[2][1] + z * m[2][2];
o->w = 1;
o++; v++;
}
}
//------------------------------------------------------------------------------
void Matrix3::Set
(
float m00, float m10, float m20,
float m01, float m11, float m21,
float m02, float m12, float m22
)
{
m[0][0] = m00; m[1][0] = m10; m[2][0] = m20;
m[0][1] = m01; m[1][1] = m11; m[2][1] = m21;
m[0][2] = m02; m[1][2] = m12; m[2][2] = m22;
}
void Matrix3::Set(const Vector4 &u, const Vector4 &v, const Vector4 &w)
{
m[0][0] = u.x; m[1][0] = u.y; m[2][0] = u.z;
m[0][1] = v.x; m[1][1] = v.y; m[2][1] = v.z;
m[0][2] = w.x; m[1][2] = w.y; m[2][2] = w.z;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
NML::Tag *Matrix3::AsMetaTag(const char *id) const
{
NML::Tag *root = new NML::Tag(id ? id : "Mtx3");
root->AddChild(GetRow(0).AsMetaTag("R0"));
root->AddChild(GetRow(1).AsMetaTag("R1"));
root->AddChild(GetRow(2).AsMetaTag("R2"));
return root;
}
bool Matrix3::FromMetaTag(NML::Tag &tag)
{
NML::Tag *t;
Vector4 R;
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(0, R);
++i;
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(1, R);
++i;
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(2, R);
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,277 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include "math/matrix4.h"
#include "math/matrix3.h"
#include "math/quaternion.h"
#include "metafile/nml.h"
using namespace GS;
Matrix4 Matrix4::static_identity(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
//------------------------------------------------------------------------------
Matrix4 Matrix4::FromMatrix3(const Matrix3 &m)
{
return Matrix4(
m.m[0][0], m.m[1][0], m.m[2][0], 0,
m.m[0][1], m.m[1][1], m.m[2][1], 0,
m.m[0][2], m.m[1][2], m.m[2][2], 0,
0, 0, 0, 1
);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
const Matrix4 &Matrix4WithInverse::Get() const
{ return matrix; }
const Matrix4 &Matrix4WithInverse::GetInverse() const
{ return imatrix; }
void Matrix4WithInverse::Commit()
{ imatrix = matrix.InversedFast(); }
void Matrix4WithInverse::Set(const Matrix4 &m)
{
matrix = m;
Commit();
}
Vector4 Matrix4WithInverse::GetRow(uint n, bool w_1) const
{ return matrix.GetRow(n, w_1); }
Vector4 Matrix4WithInverse::GetColumn(uint n, bool w_1) const
{ return matrix.GetColumn(n, w_1); }
void Matrix4WithInverse::SetRow(uint n, const Vector4 &row, bool w_1)
{
matrix.SetRow(n, row, w_1);
Commit();
}
void Matrix4WithInverse::SetColumn(uint n, const Vector4 &col, bool w_1)
{
matrix.SetColumn(n, col, w_1);
Commit();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
NML::Tag *Matrix4WithInverse::AsMetaTag(const char *id) const
{ return matrix.AsMetaTag(id); }
bool Matrix4WithInverse::FromMetaTag(NML::Tag &tag)
{
if (!matrix.FromMetaTag(tag))
return false;
Commit();
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix4 Matrix4::TransformationMatrix(const Vector4 &p, const Matrix3 &r, const Vector4 &s, const Vector4 *o)
{
Matrix4 m =
Matrix4::TranslationMatrix(p) *
Matrix4::FromMatrix3(r) *
Matrix4::ScaleMatrix(s);
return o ? m * Matrix4::TranslationMatrix(*o) : m;
}
Matrix4 Matrix4::TransformationMatrix(const Vector4 &p, const Vector4 &r, const Vector4 &s, const Vector4 *o)
{
Matrix4 m =
Matrix4::TranslationMatrix(p) *
Matrix4::FromMatrix3(Matrix3::FromEuler(r.x, r.y, r.z)) *
Matrix4::ScaleMatrix(s);
return o ? m * Matrix4::TranslationMatrix(*o) : m;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix4 Matrix4::LerpAsOrthonormalBase(const Matrix4 &a, const Matrix4 &b, float k, bool fast)
{
if (fast)
{
Matrix4 o;
for (int m = 0; m < 4; ++m)
for (int n = 0; n < 4; ++n)
o.m[m][n] = (b.m[m][n] - a.m[m][n]) * k + a.m[m][n];
return o;
}
Matrix3 a_matrix3, b_matrix3;
Vector4 a_position, b_position, a_scale, b_scale;
a.Decompose(&a_position, &a_scale, &a_matrix3);
b.Decompose(&b_position, &b_scale, &b_matrix3);
Quaternion a_orientation(Quaternion::FromMatrix3(a_matrix3));
Quaternion b_orientation(Quaternion::FromMatrix3(b_matrix3));
return Matrix4::TranslationMatrix((b_position - a_position) * k + a_position) *
Matrix4::FromMatrix3(Quaternion::Slerp(k, a_orientation, b_orientation).AsMatrix3()) *
Matrix4::ScaleMatrix((b_scale - a_scale) * k + a_scale);
}
void Matrix4::Decompose(Vector4 *position, Vector4 *scale, Vector4 *rotation, Math::rOrder order) const
{
Matrix3 m3;
Decompose(position, scale, &m3);
if (rotation)
*rotation = m3.AsEuler(order);
}
void Matrix4::Decompose(Vector4 *position, Vector4 *scale, Matrix3 *rotation) const
{
// Extract position.
if (position)
*position = GetRow(3);
// Extract scale.
Vector4 scl;
scl.Set(GetRow(0).Len(), GetRow(1).Len(), GetRow(2).Len());
// Handle negative scale (permute X to preserve left-handedness).
Vector4 left = GetRow(1).Cross(GetRow(2));
if (left.Dot(GetRow(0)) < 0)
scl.x = -scl.x;
if (scale)
*scale = scl;
// Rotation 3x3 (renormalized).
if (rotation)
{
if (scl.x)
{
scl.x = 1 / scl.x;
rotation->SetRow(0, Vector4(m[0][0] * scl.x, m[1][0] * scl.x, m[2][0] * scl.x));
}
else rotation->SetRow(0, Vector4(1, 0, 0));
if (scl.y)
{
scl.y = 1 / scl.y;
rotation->SetRow(1, Vector4(m[0][1] * scl.y, m[1][1] * scl.y, m[2][1] * scl.y));
}
else rotation->SetRow(1, Vector4(0, 1, 0));
if (scl.z)
{
scl.z = 1 / scl.z;
rotation->SetRow(2, Vector4(m[0][2] * scl.z, m[1][2] * scl.z, m[2][2] * scl.z));
}
else rotation->SetRow(2, Vector4(0, 0, 1));
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix4 Matrix4::InversedFast() const
{
// Extract inverse scale.
Vector4 scl(1.f / GetRow(0).Len(), 1.f / GetRow(1).Len(), 1.f / GetRow(2).Len());
// Inverse rotation 3x3 (renormalized).
Matrix3 irt (
m[0][0] * scl.x, m[0][1] * scl.y, m[0][2] * scl.z,
m[1][0] * scl.x, m[1][1] * scl.y, m[1][2] * scl.z,
m[2][0] * scl.x, m[2][1] * scl.y, m[2][2] * scl.z
);
// Recompose as inverse matrix.
return Matrix4::ScaleMatrix(scl) * (Matrix4::FromMatrix3(irt) * Matrix4::TranslationMatrix(GetRow(3).Reversed()));
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix4 Matrix4::AsOrthonormalBase() const
{
Matrix3 rcp (
m[0][0], m[1][0], m[2][0],
m[0][1], m[1][1], m[2][1],
m[0][2], m[1][2], m[2][2]
);
rcp = rcp.AsOrthonormalBase();
Matrix4 otb(*this);
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
otb.m[i][j] = rcp.m[i][j];
return otb;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix4 Matrix4::TranslationMatrix(const Vector4 &t)
{ return Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1); }
Matrix4 Matrix4::ScaleMatrix(const Vector4 &s)
{ return Matrix4(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
NML::Tag *Matrix4::AsMetaTag(const char *id) const
{
NML::Tag *root = new NML::Tag(id ? id : "Mtx4");
root->AddChild(GetRow(0, false).AsMetaTag("R0", true));
root->AddChild(GetRow(1, false).AsMetaTag("R1", true));
root->AddChild(GetRow(2, false).AsMetaTag("R2", true));
root->AddChild(GetRow(3, false).AsMetaTag("R3", true));
return root;
}
bool Matrix4::FromMetaTag(NML::Tag &tag)
{
NML::Tag *t;
Vector4 R;
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(0, R, false);
++i;
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(1, R, false);
++i;
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(2, R, false);
++i;
t = i.ObjectPtr();
if (!t) return false;
R.FromMetaTag(*t); SetRow(3, R, false);
return true;
}
//------------------------------------------------------------------------------
bool Matrix4::Inverse(Matrix4 &out) const
{
float inv[16], det;
inv[0] = m[1][1] * m[2][2] * m[3][3] - m[1][1] * m[2][3] * m[3][2] - m[2][1] * m[1][2] * m[3][3] + m[2][1] * m[1][3] * m[3][2] + m[3][1] * m[1][2] * m[2][3] - m[3][1] * m[1][3] * m[2][2];
inv[4] = -m[1][0] * m[2][2] * m[3][3] + m[1][0] * m[2][3] * m[3][2] + m[2][0] * m[1][2] * m[3][3] - m[2][0] * m[1][3] * m[3][2] - m[3][0] * m[1][2] * m[2][3] + m[3][0] * m[1][3] * m[2][2];
inv[8] = m[1][0] * m[2][1] * m[3][3] - m[1][0] * m[2][3] * m[3][1] - m[2][0] * m[1][1] * m[3][3] + m[2][0] * m[1][3] * m[3][1] + m[3][0] * m[1][1] * m[2][3] - m[3][0] * m[1][3] * m[2][1];
inv[12] = -m[1][0] * m[2][1] * m[3][2] + m[1][0] * m[2][2] * m[3][1] + m[2][0] * m[1][1] * m[3][2] - m[2][0] * m[1][2] * m[3][1] - m[3][0] * m[1][1] * m[2][2] + m[3][0] * m[1][2] * m[2][1];
inv[1] = -m[0][1] * m[2][2] * m[3][3] + m[0][1] * m[2][3] * m[3][2] + m[2][1] * m[0][2] * m[3][3] - m[2][1] * m[0][3] * m[3][2] - m[3][1] * m[0][2] * m[2][3] + m[3][1] * m[0][3] * m[2][2];
inv[5] = m[0][0] * m[2][2] * m[3][3] - m[0][0] * m[2][3] * m[3][2] - m[2][0] * m[0][2] * m[3][3] + m[2][0] * m[0][3] * m[3][2] + m[3][0] * m[0][2] * m[2][3] - m[3][0] * m[0][3] * m[2][2];
inv[9] = -m[0][0] * m[2][1] * m[3][3] + m[0][0] * m[2][3] * m[3][1] + m[2][0] * m[0][1] * m[3][3] - m[2][0] * m[0][3] * m[3][1] - m[3][0] * m[0][1] * m[2][3] + m[3][0] * m[0][3] * m[2][1];
inv[13] = m[0][0] * m[2][1] * m[3][2] - m[0][0] * m[2][2] * m[3][1] - m[2][0] * m[0][1] * m[3][2] + m[2][0] * m[0][2] * m[3][1] + m[3][0] * m[0][1] * m[2][2] - m[3][0] * m[0][2] * m[2][1];
inv[2] = m[0][1] * m[1][2] * m[3][3] - m[0][1] * m[1][3] * m[3][2] - m[1][1] * m[0][2] * m[3][3] + m[1][1] * m[0][3] * m[3][2] + m[3][1] * m[0][2] * m[1][3] - m[3][1] * m[0][3] * m[1][2];
inv[6] = -m[0][0] * m[1][2] * m[3][3] + m[0][0] * m[1][3] * m[3][2] + m[1][0] * m[0][2] * m[3][3] - m[1][0] * m[0][3] * m[3][2] - m[3][0] * m[0][2] * m[1][3] + m[3][0] * m[0][3] * m[1][2];
inv[10] = m[0][0] * m[1][1] * m[3][3] - m[0][0] * m[1][3] * m[3][1] - m[1][0] * m[0][1] * m[3][3] + m[1][0] * m[0][3] * m[3][1] + m[3][0] * m[0][1] * m[1][3] - m[3][0] * m[0][3] * m[1][1];
inv[14] = -m[0][0] * m[1][1] * m[3][2] + m[0][0] * m[1][2] * m[3][1] + m[1][0] * m[0][1] * m[3][2] - m[1][0] * m[0][2] * m[3][1] - m[3][0] * m[0][1] * m[1][2] + m[3][0] * m[0][2] * m[1][1];
inv[3] = -m[0][1] * m[1][2] * m[2][3] + m[0][1] * m[1][3] * m[2][2] + m[1][1] * m[0][2] * m[2][3] - m[1][1] * m[0][3] * m[2][2] - m[2][1] * m[0][2] * m[1][3] + m[2][1] * m[0][3] * m[1][2];
inv[7] = m[0][0] * m[1][2] * m[2][3] - m[0][0] * m[1][3] * m[2][2] - m[1][0] * m[0][2] * m[2][3] + m[1][0] * m[0][3] * m[2][2] + m[2][0] * m[0][2] * m[1][3] - m[2][0] * m[0][3] * m[1][2];
inv[11] = -m[0][0] * m[1][1] * m[2][3] + m[0][0] * m[1][3] * m[2][1] + m[1][0] * m[0][1] * m[2][3] - m[1][0] * m[0][3] * m[2][1] - m[2][0] * m[0][1] * m[1][3] + m[2][0] * m[0][3] * m[1][1];
inv[15] = m[0][0] * m[1][1] * m[2][2] - m[0][0] * m[1][2] * m[2][1] - m[1][0] * m[0][1] * m[2][2] + m[1][0] * m[0][2] * m[2][1] + m[2][0] * m[0][1] * m[1][2] - m[2][0] * m[0][2] * m[1][1];
det = m[0][0] * inv[0] + m[0][1] * inv[4] + m[0][2] * inv[8] + m[0][3] * inv[12];
if (det == 0)
return false;
det = 1.f / det;
for (int i = 0; i < 16; i++)
((float *)out.m)[i] = inv[i] * det;
return true;
}

View File

@ -0,0 +1,218 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "math/quaternion.h"
#include "math/matrix3.h"
#include "metafile/nml.h"
using namespace GS;
//------------------------------------------------------------------------------
Quaternion Quaternion::Slerp(float t, const Quaternion &a, const Quaternion &b)
{
float norm = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
bool bFlip = false;
if (norm < 0.0f)
{
norm = -norm;
bFlip = true;
}
float inv_d;
if (1.0f - norm < 0.000001f)
inv_d = 1.0f - t;
else
{
float theta = Math::ACos(norm);
float s = 1.f / Math::Sin(theta);
inv_d = Math::Sin((1.0f - t) * theta) * s;
t = Math::Sin(t * theta) * s;
}
if (bFlip)
t = -t;
return Quaternion(inv_d * a.x + t * b.x, inv_d * a.y + t * b.y, inv_d * a.z + t * b.z, inv_d * a.w + t * b.w);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
float Quaternion::Distance(const Quaternion &a, const Quaternion &b)
{
const float dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z, dw = a.w - b.w;
return Math::Sqrt((dx * dx) + (dy * dy) + (dz * dz) + (dw * dw));
}
Quaternion Quaternion::Inverse() const
{
const float norm = w * w + x * x + y * y + z * z;
if (norm > 0)
{
const float inorm = 1.f / norm;
return Quaternion(x * -inorm, y * -inorm, z * -inorm, w * inorm);
}
return *this;
}
Quaternion Quaternion::Normalize() const
{
float d = Math::Sqrt(x * x + y * y + z * z + w * w);
if (!d)
return Quaternion(1, 1, 1, 1);
float k = 1.f / d;
return Quaternion(x * k, y * k, z * k, w * k);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Quaternion Quaternion::LookAt(const Vector4 &at)
{ return Quaternion::FromMatrix3(Matrix3::FromOrthonormalBasis(at)); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Quaternion Quaternion::FromMatrix3(const Matrix3 &m)
{
// From "Quaternion Calculus and Fast Animation".
float x, y, z, w;
float trace = m.m[0][0] + m.m[1][1] + m.m[2][2];
if (trace > 0.0)
{
// |w| > 1/2, may as well choose w > 1/2
float root = Math::Sqrt(trace + 1.0f); // 2w
w = 0.5f * root;
root = 0.5f / root; // 1/(4w)
x = (m.m[2][1] - m.m[1][2]) * root;
y = (m.m[0][2] - m.m[2][0]) * root;
z = (m.m[1][0] - m.m[0][1]) * root;
}
else
{
// |w| <= 1/2
static size_t inext[3] = { 1, 2, 0 };
size_t i = 0;
if (m.m[1][1] > m.m[0][0])
i = 1;
if (m.m[2][2] > m.m[i][i])
i = 2;
size_t j = inext[i];
size_t k = inext[j];
float root = Math::Sqrt(m.m[i][i] - m.m[j][j] - m.m[k][k] + 1.0f);
float *quat[3] = { &x, &y, &z };
*quat[i] = 0.5f * root;
root = 0.5f / root;
w = (m.m[k][j] - m.m[j][k]) * root;
*quat[j] = (m.m[j][i] + m.m[i][j]) * root;
*quat[k] = (m.m[k][i] + m.m[i][k]) * root;
}
return Quaternion(x, y, z, w);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Quaternion Quaternion::FromAxisAngle(float a, float _x, float _y, float _z)
{
float sn = Math::Sin(a * 0.5f), cs = Math::Cos(a * 0.5f);
return Quaternion(_x * sn, _y * sn, _z * sn, cs).Normalize();
}
Quaternion Quaternion::FromEuler(float _x, float _y, float _z, Math::rOrder rorder)
{
Quaternion qx(Quaternion::FromAxisAngle(_x, 1, 0, 0)),
qy(Quaternion::FromAxisAngle(_y, 0, 1, 0)),
qz(Quaternion::FromAxisAngle(_z, 0, 0, 1)),
q;
switch (rorder)
{
case Math::rOrder_ZYX: q = qz * qy * qx; break;
case Math::rOrder_YZX: q = qy * qz * qx; break;
case Math::rOrder_ZXY: q = qz * qx * qy; break;
case Math::rOrder_XZY: q = qx * qz * qy; break;
default:
case Math::rOrder_YXZ: q = qy * qx * qz; break;
case Math::rOrder_XYZ: q = qx * qy * qz; break;
case Math::rOrder_XY: q = qx * qy; break;
}
return q.Normalize();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Matrix3 Quaternion::AsMatrix3() const
{
float sqw = w * w, sqx = x * x, sqy = y * y, sqz = z * z;
Matrix3 m;
float invs = 1.f / (sqx + sqy + sqz + sqw);
m.m[0][0] = ( sqx - sqy - sqz + sqw) * invs; // Since sqw + sqx + sqy + sqz = 1 / invs * invs.
m.m[1][1] = (-sqx + sqy - sqz + sqw) * invs;
m.m[2][2] = (-sqx - sqy + sqz + sqw) * invs;
float tmp1 = x * y;
float tmp2 = z * w;
m.m[1][0] = 2.f * (tmp1 + tmp2) * invs;
m.m[0][1] = 2.f * (tmp1 - tmp2) * invs;
tmp1 = x * z;
tmp2 = y * w;
m.m[2][0] = 2.f * (tmp1 - tmp2) * invs;
m.m[0][2] = 2.f * (tmp1 + tmp2) * invs;
tmp1 = y * z;
tmp2 = x * w;
m.m[2][1] = 2.f * (tmp1 + tmp2) * invs;
m.m[1][2] = 2.f * (tmp1 - tmp2) * invs;
return m;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
NML::Tag *Quaternion::AsMetaTag(const char *id) const
{
NML::Tag *root = new NML::Tag(id ? id : "Quaternion");
if (root)
{
root->AddChild("X", x);
root->AddChild("Y", y);
root->AddChild("Z", z);
root->AddChild("W", w);
}
return root;
}
bool Quaternion::FromMetaTag(NML::Tag &tag)
{
NML::Tag *t;
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
x = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
y = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
z = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
w = t->GetReal();
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,207 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "metafile/nml.h"
#include "math/matrix3.h"
#include "math/matrix4.h"
#include "rand/rand.h"
using namespace GS;
namespace GS {
//------------------------------------------------------------------------------
template <> tVector2 <int> tVector2 <int> ::operator * (const Matrix3 &m) const
{
return tVector2 <int> ( (float(x) * m.m[0][0] + float(y) * m.m[0][1] + m.m[0][2]),
int(float(x) * m.m[1][0] + float(y) * m.m[1][1] + m.m[1][2]) );
}
template <> tVector2 <float> tVector2 <float> ::operator * (const Matrix3 &m) const
{
return tVector2 <float> ( x * m.m[0][0] + y * m.m[0][1] + m.m[0][2],
x * m.m[1][0] + y * m.m[1][1] + m.m[1][2] );
}
//------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
Vector4 Vector4::Floor() const
{ return Vector4(Math::Floor(x), Math::Floor(y), Math::Floor(z), Math::Floor(w)); }
Vector4 Vector4::Ceil() const
{ return Vector4(Math::Ceil(x), Math::Ceil(y), Math::Ceil(z), Math::Ceil(w)); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector4 Vector4::Abs() const
{ return Vector4(Types::Abs(x), Types::Abs(y), Types::Abs(z)); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector4 Vector4::Clamped(float min, float max) const
{
float _x, _y, _z;
if (x < min) _x = min; else if (x > max) _x = max; else _x = x;
if (y < min) _y = min; else if (y > max) _y = max; else _y = y;
if (z < min) _z = min; else if (z > max) _z = max; else _z = z;
return Vector4(_x, _y, _z);
}
Vector4 Vector4::Clamped(const Vector4 &min, const Vector4 &max) const
{
float _x, _y, _z;
if (x < min.x) _x = min.x; else if (x > max.x) _x = max.x; else _x = x;
if (y < min.y) _y = min.y; else if (y > max.y) _y = max.y; else _y = y;
if (z < min.z) _z = min.z; else if (z > max.z) _z = max.z; else _z = z;
return Vector4(_x, _y, _z);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector4 Vector4::ClampedMagnitude(float min, float max) const
{
float l2 = Len2();
if ((l2 >= (min * min)) && (l2 <= (max * max)))
return Vector4(*this);
if (l2 < 0.000001)
return Vector4(*this);
float l = Math::Sqrt((float)l2);
return (*this) * Types::Clamp(l, min, max) / l;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Vector4::operator *= (const Matrix4 &m)
{
float _x = x, _y = y, _z = z;
x = _x * m.m[0][0] + _y * m.m[0][1] + _z * m.m[0][2] + m.m[0][3];
y = _x * m.m[1][0] + _y * m.m[1][1] + _z * m.m[1][2] + m.m[1][3];
z = _x * m.m[2][0] + _y * m.m[2][1] + _z * m.m[2][2] + m.m[2][3];
}
Vector4 Vector4::operator * (const Matrix4 &m) const
{
return Vector4( x * m.m[0][0] + y * m.m[0][1] + z * m.m[0][2] + m.m[0][3],
x * m.m[1][0] + y * m.m[1][1] + z * m.m[1][2] + m.m[1][3],
x * m.m[2][0] + y * m.m[2][1] + z * m.m[2][2] + m.m[2][3] );
}
void Vector4::operator *= (const Matrix3 &m)
{
float _x = x, _y = y, _z = z;
x = _x * m.m[0][0] + _y * m.m[0][1] + _z * m.m[0][2];
y = _x * m.m[1][0] + _y * m.m[1][1] + _z * m.m[1][2];
z = _x * m.m[2][0] + _y * m.m[2][1] + _z * m.m[2][2];
}
Vector4 Vector4::operator * (const Matrix3 &m) const
{
return Vector4( x * m.m[0][0] + y * m.m[0][1] + z * m.m[0][2],
x * m.m[1][0] + y * m.m[1][1] + z * m.m[1][2],
x * m.m[2][0] + y * m.m[2][1] + z * m.m[2][2] );
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector4 Vector4::FaceForward(Vector4 &dir)
{
if (Dot(dir) >= 0)
return Reversed();
return *this;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int Vector4::Hash() const
{
int a = (int)(x * 10.f), b = (int)(y * 10.f), c = (int)(z * 10.f);
// From Christer Ericson's Realtime Collision Detection.
return a * 0x8da6b343 + b * 0xd8163841 + c * 0xcb1ab31f;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Vector4::BaseToEuler(Vector4 &euler, Vector4 &u, Vector4 *v)
{
float _v = Math::Sqrt(u.x * u.x + u.y * u.y + u.z * u.z);
euler.x = -Math::ASin(u.y / _v);
_v = Math::Sqrt(u.x * u.x + u.z * u.z);
if (_v > 0.00001f)
euler.y = Math::ASin(u.x / _v);
else euler.y = 0;
if (u.z < 0.f)
{
if (euler.y < 0.f)
euler.y = - (Math::Pi + euler.y);
else euler.y = Math::Pi - euler.y;
}
euler.z = 0;
if (v)
{
Matrix3 mx(Matrix3::RotationMatrixXAxis(Units::Rad(euler.x)));
Matrix3 my(Matrix3::RotationMatrixYAxis(Units::Rad(euler.y)));
Vector4 bv(Vector4(1,0,0) * my * mx), vn(v->Normalized());
const float vc = vn.Dot(bv);
if (vc >= 1.f)
euler.z = 0.f;
else if (vc <= -1.f)
euler.z = Math::Pi;
else euler.z = Math::ACos(vc);
if ((bv.Cross(vn)).Dot(u) <= 0.f)
euler.z = (Math::Pi + Math::Pi) - euler.z;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Vector4 Vector4::Random(float min, float max)
{ return Vector4(Random::FRRand(min, max), Random::FRRand(min, max), Random::FRRand(min, max)); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
NML::Tag *Vector4::AsMetaTag(const char *id, bool fulldump) const
{
NML::Tag *root = new NML::Tag(id ? id : "Vector");
if (root)
{
root->AddChild("X", x);
root->AddChild("Y", y);
root->AddChild("Z", z);
if (fulldump)
root->AddChild("W", w);
}
return root;
}
bool Vector4::FromMetaTag(NML::Tag &tag)
{
NML::Tag *t;
List <NML::Tag *> ::Iterator i(tag.GetTags().GetRoot());
t = i.ObjectPtr();
if (!t) return false;
x = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
y = t->GetReal();
++i;
t = i.ObjectPtr();
if (!t) return false;
z = t->GetReal();
++i;
t = i.ObjectPtr();
if (t)
w = t->GetReal();
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,111 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cstring>
#include "metafile/nml.h"
#include "filesystem/io_handle.h"
#include "filesystem/filesystem.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
using namespace GS::NML;
//------------------------------------------------------------------------------
bool Parser::SaveBinaryTag(IO::Handle &out, const Tag &tag, File::Binary method)
{
const Variant &v = tag.GetValue();
if (v.GetType() != Variant::VariantNone)
{
out.Write <ushort> ((ushort)tag.name.Len());
out.Write((void *)tag.name.c_str(), tag.name.Len());
}
else
__ERR__(__LOG_W__ << "Ignoring invalid metatag '" << tag.name << "'.\n", true)
// Store tag start size/type.
out.Write <uchar> ((uchar)v.GetType());
size_t tag_length_pos = out.Tell();
switch (v.GetType())
{
case Variant::VariantNone:
case Variant::VariantBinary:
case Variant::VariantString:
out.Write <int> (-1);
break;
default: break;
}
switch (v.GetType())
{
case Variant::VariantNone:
NMLTagForeach(child, tag)
if (!SaveBinaryTag(out, *child, method))
return false;
break;
case Variant::VariantBinary:
out.Write(tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
break;
case Variant::VariantInteger:
out.Write <int> (tag.GetInteger());
break;
case Variant::VariantFloat:
out.Write <float> (tag.GetReal());
break;
case Variant::VariantString:
out.Write(tag.GetString(), std::strlen(tag.GetString()));
break;
default:
__ERR__(__LOG_E__ << "No method to output tag '" << tag.name << "' type.\n", false)
}
switch (v.GetType())
{
case Variant::VariantNone:
case Variant::VariantBinary:
case Variant::VariantString:
{
size_t tag_end_pos = out.Tell();
out.Seek(tag_length_pos, IO::Base::SeekStart);
out.Write <int> (tag_end_pos - tag_length_pos);
out.Seek(tag_end_pos, IO::Base::SeekStart);
}
break;
default: break;
}
return true;
}
bool Parser::SaveBinary(IO::Handle &h, const File &file)
{
h.Write((const void *)"<BML=1.0>\n", 10);
NMLFileForeach(tag, file)
if (!SaveBinaryTag(h, *tag, file.GetBinaryMethod()))
return false;
return true;
}
bool Parser::SaveBinary(const char *uri, const File &file)
{
if (!uri)
return false;
AutoPtr <IO::Handle> handle;
if (!(handle = Platform::Get().io->Open(uri, IO::ModeWrite)))
__ERR__(__LOG_E__ << "Failed to open metafile output '" << uri << "'.\n", false)
return SaveBinary(*handle, file);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,83 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "metafile/nml.h"
#include "alloc/ialloc.h"
using namespace GS::NML;
//------------------------------------------------------------------------------
bool File::GetBool(const char *path, bool dflt, bool verbose) const {
Tag *t = GetTypedTag(path, Variant::VariantBool, verbose);
return t ? t->GetBool() : dflt;
}
int File::GetInteger(const char *path, int dflt, bool verbose) const {
Tag *t = GetTypedTag(path, Variant::VariantInteger, verbose);
return t ? t->GetInteger() : dflt;
}
float File::GetReal(const char *path, float dflt, bool verbose) const {
Tag *t = GetTypedTag(path, Variant::VariantFloat, verbose);
return t ? t->GetReal() : dflt;
}
const char *File::GetString(const char *path, const char *dflt, bool verbose) const {
Tag *t = GetTypedTag(path, Variant::VariantString, verbose);
return t ? t->GetString() : dflt;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void File::Import(const File &src, bool clear_before_import) {
if (clear_before_import)
Clear();
ListForeachPtr(Tag *, tag, src.GetTags())
AddRoot(tag->Clone());
}
File *File::Clone() const {
File *clone = new File;
if (!clone)
return NULL;
if (!name.IsEmpty())
clone->name = name.c_str();
clone->Import(*this);
return clone;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tag *File::AddRoot(Tag *t) {
if (!t)
return NULL;
if (!tags.Add(t))
return NULL;
return t;
}
Tag *File::AddRoot(const char *name) { return AddRoot(new Tag(name)); }
Tag *File::AddRoot(const char *name, bool v) { return AddRoot(new Tag(name, v)); }
Tag *File::AddRoot(const char *name, int v) { return AddRoot(new Tag(name, v)); }
Tag *File::AddRoot(const char *name, float v) { return AddRoot(new Tag(name, v)); }
Tag *File::AddRoot(const char *name, const char *s) { return AddRoot(new Tag(name, s)); }
Tag *File::AddRoot(const char *name, void *d, size_t s) { return AddRoot(new Tag(name, d, s)); }
bool File::UnlinkRoot(Tag *t) { return tags.Remove(t); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void File::Free() {
ListDeleteAllPtr(Tag *, tags);
name.Clear();
}
File::~File() { Free(); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,122 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "metafile/nml.h"
#include "reflection/c_refl.h"
#include "memory/nauto_ptr.h"
#include "assert/nassert.h"
namespace GS {
namespace NML {
using namespace Reflection;
//------------------------------------------------------------------------------
bool GenericObjectFromMetaTag(Tag &t, void *o, Property *o_prop)
{
NMLTagForeach(pt, t)
for (int n = 0; o_prop[n].name; ++n)
if (pt->name == o_prop[n].name)
{
size_t p_prop = (size_t)o + o_prop[n].offset_of;
switch (o_prop[n].type)
{
case Property::BoolProp:
*(bool *)p_prop = pt->GetBool();
break;
case Property::CharProp:
*(char *)p_prop = (char)pt->GetInteger();
break;
case Property::ShortProp:
*(short *)p_prop = (short)pt->GetInteger();
break;
case Property::IntProp:
*(int *)p_prop = pt->GetInteger();
break;
case Property::FloatProp:
*(float *)p_prop = pt->GetReal();
break;
case Property::StringProp:
*(GS::String *)p_prop = pt->GetString();
break;
case Property::EnumProp:
__ASSERT__(o_prop[n].enum_dict);
*(int *)p_prop = Enum::fromString(pt->GetString(), o_prop[n].enum_dict);
break;
default:
__ASSERT_ALWAYS__;
break;
}
}
return true;
}
Tag *GenericObjectToMetaTag(Tag *t, const void *o, Property *o_prop)
{
if (t)
for (int n = 0; o_prop[n].name; ++n)
{
size_t p_prop = (size_t)o + o_prop[n].offset_of;
switch (o_prop[n].type)
{
case Property::BoolProp:
t->AddChild(o_prop[n].name, *(bool *)p_prop);
break;
case Property::CharProp:
t->AddChild(o_prop[n].name, (int)*(char *)p_prop);
break;
case Property::ShortProp:
t->AddChild(o_prop[n].name, (int)*(short *)p_prop);
break;
case Property::IntProp:
t->AddChild(o_prop[n].name, *(int *)p_prop);
break;
case Property::FloatProp:
t->AddChild(o_prop[n].name, *(float *)p_prop);
break;
case Property::StringProp:
t->AddChild(o_prop[n].name, ((GS::String *)p_prop)->c_str());
break;
case Property::EnumProp:
__ASSERT__(o_prop[n].enum_dict);
t->AddChild(o_prop[n].name, Enum::toString(*(int *)p_prop, o_prop[n].enum_dict));
break;
default:
__ASSERT_ALWAYS__;
break;
}
}
return t;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool GenericObjectFromMetaFile(const char *uri, void *obj, Property *obj_prop, const char *root_name)
{
File file;
if (!Parser::Load(uri, file))
return false;
if (Tag *root = file.GetTag(root_name))
if (!GenericObjectFromMetaTag(*root, obj, obj_prop))
return false;
return true;
}
bool GenericObjectToMetaFile(const char *uri, const void *obj, Property *obj_prop, const char *root_name)
{
File file;
file.AddRoot(GenericObjectToMetaTag(new Tag(root_name), obj, obj_prop));
return Parser::Save(uri, file);
}
//------------------------------------------------------------------------------
} //NML
} // GS

View File

@ -0,0 +1,380 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cstring>
#include "metafile/nml.h"
#include "ascii/parser.h"
#include "ascii/ascii_encoder.h"
#include "filesystem/io_handle.h"
#include "filesystem/filesystem.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
using namespace GS::NML;
using namespace GS::AsciiParser;
//------------------------------------------------------------------------------
bool Parser::IsMetafile(const char *name)
{
AutoPtr <IO::Handle> h(Platform::Get().io->Open(name));
if (h.IsNull())
return false;
char header[9];
h->Read(header, 9);
if (Memory::Compare(header, "<Version=", 9) && Memory::Compare(header, "<NML=", 5))
return false;
return true;
}
const char *Parser::ParseTagPreprocessorDirective(Tag &tag, const char *s, const char *e)
{
s++;
// Catch preprocessor directive.
if (!strncmp(s, "include", 7))
{
// Ensure base coherency.
if (tag.GetValue().GetType() != Variant::VariantNone)
__ERR__(__LOG_E__ << "Incoherent type in tag '" << tag.name << "' declaration.\n", NULL)
// Parse directive.
s += SkipSpace(s + 7, e) - s;
if (s[0] != '(')
__ERR__(__LOG_E__ << "Mangled #include directive.\n", NULL);
// Fetch arguments.
String include_path;
forever
{
if (s[0] == ')')
break;
s += SkipSpace(s + 1, e) - s;
// Path.
if (s[0] == '"')
{
s++;
uint len = (uint)(RunToEOS(s, e) - s);
include_path.Set(s, s + len);
s += SkipSpace(s + len + 1, e) - s;
}
else
__ERR__(__LOG_E__ << "Unexpected character in #include directive.\n", NULL);
}
s++;
// Load external metafile.
File tmp_file;
if (Parser::Load(include_path, tmp_file, true))
{
// Then transfer all tags to our current file.
NMLFileForeach(t, tmp_file)
{
if (tmp_file.UnlinkRoot(t))
tag.AddChild(t);
else
__LOG_E__ << "Could not relink root tag from included metafile.\n";
}
}
else
__LOG_E__ << "Failed to include external metafile '" << include_path << "'.\n";
}
else if (!strncmp(s, "append", 6))
__LOG_E__ << "Preprocessor directive #append deprecated.\n";
else
__ERR__(__LOG_E__ << "Unknown preprocessor directive, metatag '" << tag.name << "'.\n", NULL)
return s;
}
bool Parser::ParseTag(Tag &tag, const char *s, const char *e, const char **es)
{
// Safety net.
s += SkipSpace(s, e) - s;
if (s[0] != '<')
return false;
// Get the tag id.
#define MLTAG_ERROR(c) { __LOG_E__ << c; return false; }
const char *etn = s + 1;
etn += SkipEntry(etn, e) - etn;
if (String::strfindchar(s + 1, ':', etn) != etn)
MLTAG_ERROR("':' is a path character and cannot be used in tag name.\n")
if (String::strfindchar(s + 1, ';', etn) != etn)
MLTAG_ERROR("';' is a path character and cannot be used in tag name.\n")
tag.name.Set(s + 1, etn);
s = etn;
// Get tag type.
s += SkipSpace(s, e) - s;
if (s == e)
MLTAG_ERROR("Mangled definition, metatag '" << tag.name << "'.\n")
switch (s[0])
{
case '>':
s++;
break;
// Node/real/integer/string.
case '=':
{
s++;
forever
{
s += SkipSpace(s, e) - s;
if (s == e)
MLTAG_ERROR("Mangled definition, metatag '" << tag.name << "'.\n")
// End of tag.
if (s[0] == '>')
{
s++;
break;
}
// Preprocessor directive.
if (s[0] == '#')
s = ParseTagPreprocessorDirective(tag, s + 1, e);
// Node.
else if (s[0] == '<')
{
if (tag.GetValue().GetType() != Variant::VariantNone)
MLTAG_ERROR("Incoherent type in tag '" << tag.name << "' declaration.\n")
Tag *stag = tag.tags.Add(new Tag)->Object();
if (!ParseTag(*stag, s, e, &s))
MLTAG_ERROR("")
s += SkipSpace(s, e) - s;
}
// Constant.
else
{
if (tag.GetValue().GetType() != Variant::VariantNone)
MLTAG_ERROR("Incoherent type in tag '" << tag.name << "' declaration.\n")
// Binary.
if (s[0] == '=')
{
s++;
if (!(s[0] >= '0' && s[0] <= '9'))
MLTAG_ERROR("Expected encoded size in binary tag '" << tag.name << "' declaration.\n")
const char *ye = s;
while (ye[0] >= '0' && ye[0] <= '9')
ye++;
if (ye[0] != ':')
MLTAG_ERROR("Expected size delimiter in binary tag '" << tag.name << "' declaration.\n")
uint asize = String(s, ye).Integer();
s = ye + 1;
if (!(s[0] >= '0' && s[0] <= '9'))
MLTAG_ERROR("Expected binary size in binary tag '" << tag.name << "' declaration.\n")
ye = s;
while (ye[0] >= '0' && ye[0] <= '9')
ye++;
// Trailing @ means yEnc binary.
File::Binary encoding = File::Binary_UU;
if (ye[0] == '@')
{
encoding = File::Binary_yEnc;
ye++;
}
// Detect EOL
if ((ye[0] != 0x0a) && ((ye[0] != 0x0d) && (ye[1] != 0x0a)))
MLTAG_ERROR("Expected EOL following binary size in binary tag '" << tag.name << "' declaration.\n")
size_t eol_size = (ye[0] == 0x0a) ? 1 : 2;
uint bsize = String(s, ye).Integer();
uchar *astart = (uchar *)(ye + eol_size);
// [EJ] Adjust asize to account for Windows EOL (historically NML only specifies Unix ascii size).
if (eol_size > 1)
{
__LOG_V__ << "CRLF reduces NML binary load performance.\n";
size_t a_size_in = asize;
asize = 0;
for (; a_size_in > 0; --a_size_in)
if ((astart[asize] == 0x0d) && (astart[asize + 1] == 0x0a))
asize += 2;
else
++asize;
}
// Load ASCII encoded data.
Array <uchar> aenc(asize, Alloc::Metatag);
if (!aenc)
MLTAG_ERROR("Failed to allocate binary buffer in binary tag '" << tag.name << "'.\n")
memcpy(&aenc[0], astart, asize);
s = ye + eol_size + asize;
if (s[0] != '>')
MLTAG_ERROR("Expected closing tag in tag '" << tag.name << "'.\n")
Array <uchar> data(bsize, Alloc::Metatag);
if (data)
{
switch (encoding)
{
case File::Binary_UU: nAsciiEncoder::UUDecode(&aenc[0], asize, &data[0], bsize); break;
case File::Binary_yEnc: nAsciiEncoder::yDecode(&aenc[0], asize, &data[0], bsize); break;
}
tag.GetValue().SetBinary(&data[0], bsize);
}
}
// Real/Integer.
else if ((s[0] >= '0' && s[0] <= '9') || (s[0] == '.') || (s[0] == '-'))
{
if (IsConstantFloat(s, e))
tag.GetValue() = String::atof(s, e, true);
else tag.GetValue() = String::atoi(s);
if (s[0] == '-')
s++;
s += SkipEntry(s, e) - s;
if (s[0] != '>')
MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n")
}
// String.
else if (s[0] == '\"')
{
s++;
ptrdiff_t len = RunToEOS(s, e) - s;
if ((s + len) == e)
MLTAG_ERROR("Mangled string declaration, metatag '" << tag.name << "'.\n")
// Copy string.
tag.GetValue() = String(s, s + len);
s += SkipSpace(s + len + 1, e) - s; // Jump over string.
if (s == e)
MLTAG_ERROR("Unexpected EOF after string declaration, metatag '" << tag.name << "'.\n")
if (s[0] != '>')
MLTAG_ERROR("Unexpected trailing expression following string object, metatag '" << tag.name << "'.\n")
tag.GetValue().s_value.ReplaceAll("\\n", "\n"); // convert CF
}
// Boolean.
else if (!strncmp(s, "True", 4))
{
tag.GetValue() = true;
s += SkipSpace(s + 4, e) - s;
if (s[0] != '>')
MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n")
}
else if (!strncmp(s, "False", 5))
{
tag.GetValue() = false;
s += SkipSpace(s + 5, e) - s;
if (s[0] != '>')
MLTAG_ERROR("Unexpected trailing expression following value, metatag '" << tag.name << "'.\n")
}
else
MLTAG_ERROR("Unexpected '" << s[0] << "' in assignation, metatag '" << tag.name << "'.\n")
}
}
}
break;
default:
MLTAG_ERROR("Unexpected trailing expression after metatag '" << tag.name << "' name declaration.\n")
}
if (es)
es[0] = s;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Parser::LoadFromMemory(const char *data, size_t size, File &mfl)
{
mfl.Free();
if (!data)
return false;
// Read header tag, expected to be NML version.
const char *pof = data, *eof = data + size;
#define MEM_MLP_ERROR(c) { (c); return false; }
Tag header_tag;
if (!ParseTag(header_tag, pof, eof, &pof))
MEM_MLP_ERROR(__LOG_E__ << "Invalid metafile.\n")
if ((header_tag.name != "Version") && (header_tag.name != "NML"))
MEM_MLP_ERROR(__LOG_E__ << "Unknown metafile variant.\n")
switch (header_tag.GetValue().GetType())
{
case Variant::VariantInteger:
if (header_tag.GetInteger() > version)
__LOG_W__ << "Newer version NML header found (" << header_tag.GetInteger() << ">" << version << ").\n";
break;
case Variant::VariantFloat:
if (header_tag.GetReal() > version)
__LOG_W__ << "Newer version NML header found (" << header_tag.GetReal() << ">" << version << ").\n";
break;
default:
__LOG_W__ << "Unknown NML header version identification method.\n";
break;
}
// Read all root tags.
while (pof < eof)
{
Tag *tag = mfl.tags.Add(new Tag)->Object();
if (!ParseTag(*tag, pof, eof, &pof))
return false;
pof += SkipSpace(pof, eof) - pof;
}
return true;
}
bool Parser::Load(const char *path, File &file, bool verbose)
{
if (!path)
return false;
Array <char> data;
if (!Platform::Get().io->FileLoad(path, data, verbose))
return false;
try
{
if (!LoadFromMemory(data.c_ptr(), data.GetSize(), file))
return false;
}
catch (char *e)
{
__LOG_E__ << "Failed to load file LoadFromMemory. " << path << "\n";
return false;
}
file.name = path;
return true;
}
File *Parser::Load(const char *metafile, bool verbose)
{
AutoPtr <File> mfl(new File);
if (mfl.IsNull())
__ERR__(__LOG_E__ << "Failed to allocate file.\n", NULL)
return Load(metafile, *mfl, verbose) ? mfl.Detach() : NULL;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,97 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "metafile/nml.h"
#include "log/log.h"
using namespace GS;
using namespace GS::NML;
static bool MetatagNameCompare(const Tag *o, const String &name) { return o->name == name; }
//------------------------------------------------------------------------------
Tag *Tag::GetTagEx(const List <Tag *> &tg, const char *s, const File *, bool verbose)
{
if (!s)
return NULL;
const char *path = s;
const List <Tag *> *list = &tg;
Tag *tag = NULL;
while (s[0])
{
while (s[0] == ':')
s++;
if (s[0] == ';')
break;
const char *t = s;
while ((t[0] != ';') && (t[0] != ':') && t[0])
t++;
if (!t[0] && verbose)
{
if (t > s)
__LOG_W__ << "incomplete path '" << path << "' (missing ';').\n";
else __LOG_E__ << "unexpected end of path'" << path << "'.\n";
}
// No more node to search.
if (!list)
{
if (verbose)
__LOG_W__ << "'" << path << "' is deeper than lowest tree node.\n";
return NULL;
}
String node_name(s, t);
s = t;
tag = ListFindEx(*list, MetatagNameCompare, node_name);
if (tag == NULL)
{
if (verbose)
__LOG__ << "!! Error '" << node_name << "' in '" << path << "' not found.\n";
return NULL;
}
switch (tag->GetValue().GetType())
{
case Variant::VariantNone:
list = &tag->tags;
break;
default:
list = NULL;
break;
}
}
return tag;
}
Tag *Tag::GetTag(const char *path, const File *root, bool verbose) const
{
return GetTagEx(tags, path, root, verbose);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tag *Tag::GetTypedTag(const char *path, Variant::Type type, const File *root, bool verbose) const
{
Tag *t = Tag::GetTagEx(tags, path, root, verbose);
if ((!t) || (t->GetValue().GetType() != type))
return NULL;
return t;
}
Tag *File::GetTypedTag(const char *path, Variant::Type type, bool verbose) const
{
Tag *t = Tag::GetTagEx(tags, path, this, verbose);
if ((!t) || (t->GetValue().GetType() != type))
return NULL;
return t;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,152 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cstdio>
#include <cstring>
#include "metafile/nml.h"
#include "ascii/ascii_encoder.h"
#include "filesystem/filesystem.h"
#include "filesystem/io_handle.h"
#include "memory/nauto_ptr.h"
#include "platform_config.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
using namespace GS::NML;
//------------------------------------------------------------------------------
bool Parser::SaveTag(IO::Handle &out, const Tag &tag, File::Binary method, uint idt)
{
//---------------------------------------------------------------------------
#define OUTPUT_INDENT { for (uint n = 0; n < idt; n++) out << "\t"; }
//---------------------------------------------------------------------------
if (tag.name.IsEmpty() && !tag.GetChildCount())
return true; // silently skip this tag
OUTPUT_INDENT;
out << "<" << tag.name.c_str();
switch (tag.GetValue().GetType())
{
case Variant::VariantNone:
if (tag.GetChildCount())
{
out << "=\n";
NMLTagForeach(child, tag)
if (!SaveTag(out, *child, method, idt + 1))
return false;
OUTPUT_INDENT;
}
break;
case Variant::VariantBinary:
{
uint olen = (uint)~0;
switch (method)
{
case File::Binary_UU:
olen = nAsciiEncoder::UUEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
break;
case File::Binary_yEnc:
olen = nAsciiEncoder::yEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize());
break;
}
if (olen)
{
Array <uchar> aenc(olen, Alloc::Metatag);
if (aenc.IsValid())
{
uint asize = 0;
switch (method)
{
case File::Binary_UU:
asize = nAsciiEncoder::UUEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize(), &aenc[0], olen);
break;
case File::Binary_yEnc:
asize = nAsciiEncoder::yEncode((uchar *)tag.GetValue().GetBinaryBuffer(), tag.GetValue().GetBinarySize(), &aenc[0], olen);
break;
}
if (asize != olen)
__LOG_W__ << "Internal ASCII encoding inconsistency detected while processing tag '" << tag.name << "'.\n";
char str[256];
_snprintf(str, 255, "==%d:%d", asize, tag.GetValue().GetBinarySize()); // ==[encoded size:decoded size] is encoded binary.
out << str;
if (method == File::Binary_yEnc) // @ marker select yEncoding.
out << "@";
out << "\n";
out.Write(aenc, asize);
}
else
__LOG_E__ << "Tag '" << tag.name << "' failed to allocate internal binary buffer.\n";
}
// else __LOG_W__ << "NULL size ASCII encoded binary tag '" << tag.id << "'.\n";
}
break;
case Variant::VariantInteger:
{
char str[256];
_snprintf(str, 255, "=%d", tag.GetInteger());
out << str;
}
break;
case Variant::VariantFloat:
{
char str[256];
_snprintf(str, 255, "=%f", tag.GetReal());
out << str;
}
break;
case Variant::VariantString:
out << "=\"" << tag.GetString() << "\"";
break;
case Variant::VariantBool:
out << "=" << (tag.GetBool() ? "True" : "False");
break;
default:
__ERR__(__LOG_E__ << "No method to output tag '" << tag.name << "' type.\n", false)
}
out << ">\n";
return true;
}
bool Parser::Save(IO::Handle &h, const File &file)
{
h << "<Version=1.0>\n";
NMLFileForeach(tag, file)
if (!SaveTag(h, *tag, file.GetBinaryMethod(), 0))
return false;
return true;
}
bool Parser::Save(const char *uri, const File &file)
{
if (!uri)
return false;
AutoPtr <IO::Handle> h(Platform::Get().io->Open(uri, IO::ModeWrite));
if (h.IsNull())
__ERR__(__LOG_E__ << "Failed to open nml output '" << uri << "'.\n", false)
return Save(*h, file);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,65 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "metafile/nml_string.h"
#include "metafile/nml.h"
#include "filesystem/io_memory.h"
#include "memory/nauto_ptr.h"
#include "nstring/nstring.h"
#include "log/log.h"
namespace GS {
namespace NML {
//-----------------------------------------------------------------------------
bool TagToString(const Tag &tag, String &str)
{
IO::Memory memory_fs;
AutoPtr <IO::Handle> h(memory_fs.Open("file", IO::ModeWrite));
if (h.IsNull() || !Parser::SaveTag(*h, tag))
return false;
h = NULL;
Array <char> data;
if (!memory_fs.FileLoad("file", data))
return false;
str.Set(data.Start(), data.End());
return true;
}
bool TagFromString(const String &str, Tag &tag)
{
return Parser::ParseTag(tag, str.c_str(), &str.c_str()[str.Len()]);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool FileToString(const File &file, String &str)
{
IO::Memory memory_fs;
AutoPtr <IO::Handle> h(memory_fs.Open("file", IO::ModeWrite));
if (h.IsNull() || !Parser::Save(*h, file))
return false;
h = NULL;
Array <char> data;
if (!memory_fs.FileLoad("file", data))
return false;
str.Set(data.Start(), data.End());
return true;
}
bool FileFromString(const String &str, File &file)
{
return Parser::LoadFromMemory(str.c_str(), str.Len(), file);
}
//-----------------------------------------------------------------------------
} // NML
} // GS

View File

@ -0,0 +1,121 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "metafile/nml.h"
#include "filesystem/io_handle.h"
#include "alloc/ialloc.h"
#include "log/log.h"
using namespace GS::NML;
//------------------------------------------------------------------------------
Tag *Tag::GetParent(Tag *root) const
{
ListForeachPtr(Tag *, child, root->GetTags())
if (child == this)
return root;
Tag *parent = NULL;
ListForeachPtr(Tag *, child, root->GetTags())
if ((parent = GetParent(child)) != NULL)
break;
return parent;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tag *Tag::AddChild(Tag *t)
{
if (!t)
return NULL;
if (value.type != Variant::VariantNone)
__ERR__(__LOG_E__ << "Cannot add child to tag '" << name << "' as it is neither a pure tag or a node.\n", NULL)
if (!tags.Add(t))
return NULL;
return t;
}
Tag *Tag::AddChild(const char *name)
{ return AddChild(new Tag(name)); }
Tag *Tag::AddChild(const char *name, bool v)
{ return AddChild(new Tag(name, v)); }
Tag *Tag::AddChild(const char *name, int v)
{ return AddChild(new Tag(name, v)); }
Tag *Tag::AddChild(const char *name, uint v)
{ return AddChild(new Tag(name, v)); }
Tag *Tag::AddChild(const char *name, float v)
{ return AddChild(new Tag(name, v)); }
Tag *Tag::AddChild(const char *name, const char *s)
{ return AddChild(new Tag(name, s)); }
Tag *Tag::AddChild(const char *name, void *buffer, size_t size)
{ return AddChild(new Tag(name, buffer, size)); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Tag::Clone(const Tag &src, bool recursive)
{
Free();
value = src.GetValue();
if (recursive)
ListForeachPtr(Tag *, ct, src.GetTags())
AddChild(ct->Clone(true));
return true;
}
Tag *Tag::Clone(bool recursive) const
{
Tag *clone = new Tag(name);
if (!clone)
return NULL;
// Copy tag content.
clone->GetValue() = value;
// Clone children.
if (recursive)
ListForeachPtr(Tag *, ct, tags)
clone->AddChild(ct->Clone(true));
return clone;
}
uint Tag::DeleteChildren(const char *filter)
{
uint count = 0;
if (filter)
{
String _filter(filter);
ListForeachPtr(Tag *, t, tags)
if (t->name == _filter)
{
tags.Remove(t);
_safe_delete(t);
count++;
}
}
else
ListDeleteAllPtr(Tag *, tags)
return count;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Tag::Free()
{
value.Free();
DeleteChildren();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Tag::~Tag()
{ Free(); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,205 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cstring>
#include <iostream>
#include "picture/pict.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
bool Picture::HasAlpha() const
{
uchar *pdata = GetData();
if (!pdata || !width || !height)
return false;
for (uint y = 0; y < height; y++)
for (uint x = 0; x < width; x++)
{
union
{
uint packed;
uchar ppack[4];
};
switch (pxformat.GetBpp())
{
case 8: ppack[0] = pdata[0]; pdata++; break;
case 16: ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2; break;
case 24: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3; break;
case 32: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4; break;
}
int a = (int)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount));
if (a < ((1 << pxformat.acount) - 1))
return true;
}
return false;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
uint Picture::ColorBlend(uint u, uint v, float opacity)
{
int fk = (int)(opacity * 65536),
ik = 65536 - fk;
uint ta = ((u >> 24) & 0xff) * ik + ((v >> 24) & 0xff) * fk,
tr = ((u >> 16) & 0xff) * ik + ((v >> 16) & 0xff) * fk,
tg = ((u >> 8) & 0xff) * ik + ((v >> 8) & 0xff) * fk,
tb = (u & 0xff) * ik + (v & 0xff) * fk;
return ((ta << 8) & 0xff000000) + (tr & 0xff0000) + ((tg >> 8) & 0xff00) + (tb >> 16);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::Compare(const Picture &picture) const
{
if (pxformat != picture.GetPixelFormat().GetDesc())
return false;
// Binary comparison.
return !Memory::Compare(data, picture.GetData(), width * height * (pxformat.GetBpp() >> 3));
}
bool Picture::ComputeHash()
{
uchar *pdata = GetData();
if (!pdata || !width || !height)
return false;
hash = 0;
for (uint y = 0; y < height; y++)
for (uint x = 0; x < width; x++)
{
union
{
uint packed;
uchar ppack[4];
};
switch (pxformat.GetBpp())
{
case 8: ppack[0] = pdata[0]; pdata++; break;
case 16: ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2; break;
case 24: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3; break;
case 32: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4; break;
}
int a = (int)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount));
int r = (int)(((packed & pxformat.desc.rmask) >> pxformat.rshift) << (8 - pxformat.rcount));
int g = (int)(((packed & pxformat.desc.gmask) >> pxformat.gshift) << (8 - pxformat.gcount));
int b = (int)(((packed & pxformat.desc.bmask) >> pxformat.bshift) << (8 - pxformat.bcount));
hash += (a << 24) + (r << 16) + (g << 8) + b;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::SetData(void *ud, uint w, uint h, const PixelFormatDescription &fmt, bool take_ownership)
{
if (!ud)
return;
FreeData();
hash = 0;
data = (uchar *)ud;
if (!take_ownership)
pic_flag.Set(HasForeignData);
width = w;
height = h;
pxformat.Set(fmt);
}
bool Picture::AllocAs(uint w, uint h, const PixelFormatDescription &fmt)
{
if (!pic_flag.IsSet(HasForeignData) && data && (width == w) && (height == h) && (pxformat.GetBpp() == fmt.bpp))
{
pxformat.Set(fmt);
return true;
}
FreeData();
if (w && h && fmt.bpp)
{
size_t count = w * h * (fmt.bpp / 8);
data = AllocMemory(count);
if (data)
memset(data, 0, sizeof(uchar) * count);
else
__ERR__(__LOG_E__ << "Failed to allocate picture buffer (" << w << "x" << h << "@" << fmt.bpp << "bpp).\n", false);
width = w;
height = h;
pxformat.Set(fmt);
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::Clone(const Picture &src, bool clone_data)
{
Free();
if (src.IsStub())
Stub(src.GetWidth(), src.GetHeight());
else
{
if (clone_data)
{
if (AllocAs(src.GetWidth(), src.GetHeight(), src.pxformat.GetDesc()))
Memory::Copy((char *)data, (char *)src.GetData(), width * height * (pxformat.GetBpp() / 8));
}
else
SetData(src.GetData(), src.GetWidth(), src.GetHeight(), src.pxformat.GetDesc(), false);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::Stub(uint w, uint h)
{
Free();
protected_flag.Set(PictureIsStub);
width = w;
height = h;
}
void Picture::Zeroify()
{
data = NULL;
hash = 0;
width = height = 0;
pxformat.Set(PixelFormat::NONE);
pic_flag = 0;
protected_flag = 0;
}
void Picture::FreeData()
{
if (!pic_flag.IsSet(HasForeignData))
FreeMemory(data);
data = NULL;
pic_flag.Remove(HasForeignData);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
uchar *Picture::AllocMemory(size_t size)
{ return (uchar *)Alloc::DefaultAllocator::Alloc(size, Alloc::Picture); }
void Picture::FreeMemory(uchar *data)
{ Alloc::DefaultAllocator::Delete(data, Alloc::Picture); }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
Picture::~Picture() { Free(); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,420 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include <cstring>
#include "picture/pict.h"
#include "color/color.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
bool Picture::Reframe(int offset_sx, int offset_sy, int offset_ex, int offset_ey, const Color *fill)
{
if (!GetData() || (GetPixelFormat().GetBpp() != 32))
return false;
int _width = GetWidth() - offset_sx + offset_ex,
_height = GetHeight() - offset_sy + offset_ey;
uint *new_data = (uint *)AllocMemory(sizeof(uint) * _width * _height), *_d = new_data;
if (!new_data)
__ERR__(__LOG_E__ << "Failed to allocate destination buffer.\n", false)
// Fill color.
uint _fill = fill ? GetPixelFormat().Format(fill->x, fill->y, fill->z, fill->w) : GetPixelFormat().Format(0, 0, 0);
// Top framing.
for (int y = offset_sy; y < 0; ++y)
for (int x = 0; x < _width; ++x)
*_d++ = _fill;
// Blit + left/right framing.
int blit_ex = offset_ex > 0 ? GetWidth() : GetWidth() + offset_ex,
blit_ey = offset_ey > 0 ? GetHeight() : GetHeight() + offset_ey;
uint *s = (uint *)GetData();
if (offset_sy > 0)
s += GetWidth() * offset_sy;
for (int y = offset_sy > 0 ? offset_sy : 0; y < blit_ey; ++y)
{
uint *_s = s;
for (int x = offset_sx; x < 0; ++x)
*_d++ = _fill; // Left framing
for (int x = offset_sx > 0 ? offset_sx : 0; x < blit_ex; ++x)
*_d++ = _s[x]; // Blit
for (int x = 0; x < offset_ex; ++x)
*_d++ = _fill; // Right framing
s += GetWidth();
}
// Bottom framing.
for (int y = 0; y < offset_ey; ++y)
for (int x = 0; x < _width; ++x)
*_d++ = _fill;
SetData(new_data, _width, _height, GetPixelFormat().GetDesc(), true);
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::Flip(bool flip_h, bool flip_v)
{
if (!GetData() || (GetPixelFormat().GetBpp() != 32))
return false;
uint *s = (uint *)GetData(),
*d = s, t;
if (flip_h)
{
if (flip_v)
{
d += GetWidth() * GetHeight() - 1;
while (d > s)
{
t = *s;
*s++ = *d;
*d-- = t;
}
}
else
{
for (uint n = 0; n < GetHeight(); ++n)
{
uint *_s = s, *_d = d + GetWidth() - 1;
while (_d > _s)
{
t = *_s;
*_s++ = *_d;
*_d-- = t;
}
s += GetWidth();
d += GetWidth();
}
}
}
else
if (flip_v)
{
d += GetWidth() * (GetHeight() - 1);
while (d > s)
{
for (uint n = 0; n < GetWidth(); ++n)
{
t = s[n];
s[n] = d[n];
d[n] = t;
}
s += GetWidth();
d -= GetWidth();
}
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static void WindowClip(const Rect <int> *a, Rect <int> *fa, const Rect <int> *b, Rect <int> *fb)
{
Rect <int> _b(*b);
_b.ex = _b.sx + a->GetWidth();
_b.ey = _b.sy + a->GetHeight();
// Clip source rectangle and correct destination rectangle.
*fa = fa->Intersection(*a);
_b.sx += fa->sx - a->sx;
_b.sy += fa->sy - a->sy;
_b.ex += fa->ex - a->ex;
_b.ey += fa->ey - a->ey;
// Clip destination rectangle and correct source rectangle.
*fb = fb->Intersection(_b);
fa->sx += fb->sx - _b.sx;
fa->sy += fb->sy - _b.sy;
fa->ex += fb->ex - _b.ex;
fa->ey += fb->ey - _b.ey;
}
static void WindowStretch(Rect <float> *a, Rect <float> *fa, Rect <float> *b, Rect <float> *fb)
{
float ku = (float)b->GetWidth() / (float)a->GetWidth(),
kv = (float)b->GetHeight() / (float)a->GetHeight();
// Clip source rectangle and correct destination rectangle.
fa[0] = fa->Intersection(a[0]);
b->sx += (fa->sx - a->sx) * ku;
b->sy += (fa->sy - a->sy) * kv;
b->ex += (fa->ex - a->ex) * ku;
b->ey += (fa->ey - a->ey) * kv;
// Clip destination rectangle and correct source rectangle.
fb[0] = fb->Intersection(b[0]);
ku = 1 / ku;
kv = 1 / kv;
fa->sx += (fb->sx - b->sx) * ku;
fa->sy += (fb->sy - b->sy) * kv;
fa->ex += (fb->ex - b->ex) * ku;
fa->ey += (fb->ey - b->ey) * kv;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::BlitMask(const Picture &src, Picture &dst, Picture &msk, const Rect <int> *src_rect, const Rect <int> *dst_rect)
{
if (!src.GetData() || !dst.GetData())
return false;
// Default blit rectangles.
Rect <int> default_a(src.GetRect()), default_b(dst.GetRect());
if (!src_rect)
src_rect = &default_a;
if (!dst_rect)
dst_rect = &default_b;
// Compute blit window.
Rect <int> frame_a(src.GetRect()), frame_b(dst.GetRect());
WindowClip(src_rect, &frame_a, dst_rect, &frame_b);
// Limit to mask dimensions.
if (frame_b.GetWidth() > (int)msk.GetWidth())
frame_b.SetWidth(msk.GetWidth());
if (frame_b.GetHeight() > (int)msk.GetHeight())
frame_b.SetHeight(msk.GetHeight());
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
return false;
PixelFormatDescription initial = dst.GetPixelFormat().GetDesc();
dst.Convert(src.GetPixelFormat().GetDesc());
// Perform blit.
uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy),
*pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy),
*pmsk = msk.GetData();
for (int n = 0; n < frame_b.GetHeight(); n++)
{
uchar *_pdst = pdst, *_psrc = psrc, *_pmsk = pmsk;
for (int x = frame_b.GetWidth(); x--; )
{
uchar alpha = _pmsk[3];
uchar a_blend = AlphaCompositeAlpha(_pdst[3], alpha);
_pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], alpha, a_blend);
_pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], alpha, a_blend);
_pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], alpha, a_blend);
_pdst[3] = a_blend;
_psrc += 4;
_pdst += 4;
_pmsk += 4;
}
psrc += src.GetPitch();
pdst += dst.GetPitch();
pmsk += msk.GetPitch();
}
dst.Convert(initial);
return true;
}
bool Picture::Blit(const Picture &src, Picture &dst, const Rect <int> *src_rect, const Rect <int> *dst_rect, BlendMode mode)
{
if (!src.GetData() || !dst.GetData())
return false;
// Default blit rectangles.
Rect <int> default_a(src.GetRect()), default_b(dst.GetRect());
if (!src_rect)
src_rect = &default_a;
if (!dst_rect)
dst_rect = &default_b;
// Compute blit window.
Rect <int> frame_a(src.GetRect()), frame_b(dst.GetRect());
WindowClip(src_rect, &frame_a, dst_rect, &frame_b);
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
return false;
PixelFormatDescription initial = dst.GetPixelFormat().GetDesc();
dst.Convert(src.GetPixelFormat().GetDesc());
// Perform blit.
uchar *psrc = src.GetDataOffset(frame_a.sx, frame_a.sy),
*pdst = dst.GetDataOffset(frame_b.sx, frame_b.sy);
for (int n = 0; n < frame_b.GetHeight(); n++)
{
uchar *_pdst = pdst, *_psrc = psrc;
switch (mode)
{
case RgbToAlpha:
for (int x = frame_b.GetWidth(); x--; )
{
_pdst[3] = (_psrc[0] + _psrc[1] + _psrc[2]) / 3;
_psrc += 4;
_pdst += 4;
}
break;
case BlendReplace:
memmove(pdst, psrc, frame_b.GetWidth() * dst.GetBpp() / 8);
break;
case BlendComposeFast:
for (int x = frame_b.GetWidth(); x--; )
{
int a = _psrc[3], ia = 255 - a;
_pdst[0] = uchar((_psrc[0] * a + _pdst[0] * ia) >> 8);
_pdst[1] = uchar((_psrc[1] * a + _pdst[1] * ia) >> 8);
_pdst[2] = uchar((_psrc[2] * a + _pdst[2] * ia) >> 8);
_pdst[3] = uchar(Types::Max <int> (_pdst[3], a));
_psrc += 4;
_pdst += 4;
}
break;
case BlendCompose:
for (int x = frame_b.GetWidth(); x--; )
{
uchar a_blend = AlphaCompositeAlpha(_pdst[3], _psrc[3]);
_pdst[0] = AlphaCompositeColor(_pdst[0], _psrc[0], _pdst[3], _psrc[3], a_blend);
_pdst[1] = AlphaCompositeColor(_pdst[1], _psrc[1], _pdst[3], _psrc[3], a_blend);
_pdst[2] = AlphaCompositeColor(_pdst[2], _psrc[2], _pdst[3], _psrc[3], a_blend);
_pdst[3] = a_blend;
_psrc += 4;
_pdst += 4;
}
break;
default: break;
}
psrc += src.GetPitch();
pdst += dst.GetPitch();
}
dst.Convert(initial);
return true;
}
bool Picture::ScaleBlit(Picture &src, Picture &dst, Rect <float> *src_rect, Rect <float> *dst_rect)
{
if (!src.GetData() || !dst.GetData())
return false;
// Default blitting rectangles.
Rect <float> default_a(src.GetRect().AsFloat()), default_b(dst.GetRect().AsFloat());
if (!src_rect)
src_rect = &default_a;
if (!dst_rect)
dst_rect = &default_b;
Rect <float> frame_a(src.GetRect().AsFloat()), frame_b(dst.GetRect().AsFloat());
WindowStretch(src_rect, &frame_a, dst_rect, &frame_b);
if ((frame_b.GetWidth() <= 0) || (frame_b.GetHeight() <= 0))
return false;
// Apply sub-pixel/sub-texel correction.
float ku = frame_a.GetWidth() / frame_b.GetWidth(),
kv = frame_a.GetHeight() / frame_b.GetHeight();
float fb_isx = Math::Floor(frame_b.sx),
fb_isy = Math::Floor(frame_b.sy),
fb_iex = Math::Floor(frame_b.ex),
fb_iey = Math::Floor(frame_b.ey);
float dt_su = (frame_b.sx - fb_isx),
dt_sv = (frame_b.sy - fb_isy),
dt_eu = (frame_b.ex - fb_iex),
dt_ev = (frame_b.ey - fb_iey);
frame_a.sx -= dt_su * ku;
frame_a.sy -= dt_sv * kv;
frame_a.ex -= dt_eu * ku;
frame_a.ey -= dt_ev * kv;
frame_b.sx = fb_isx;
frame_b.sy = fb_isy;
frame_b.ex = fb_iex;
frame_b.ey = fb_iey;
// Convert sub pixel deltas to blending coefficients.
dt_su = 1 - dt_su;
dt_sv = 1 - dt_sv;
uint *pdst = (uint *)dst.GetDataOffset((uint)frame_b.sx, (uint)frame_b.sy);
int src_width = (int)frame_b.GetWidth(),
src_height = (int)frame_b.GetHeight();
float v = frame_a.sy;
for (int y = 0; y < src_height; y++)
{
float u = frame_a.sx;
// Blended or opaque scanline.
if ((!y) || (y == (src_height - 1)))
{
float kfrst, kscan, klast;
// Set blend coefficients.
if (!y)
{
kfrst = dt_su * dt_sv; // top-left
kscan = dt_sv; // top
klast = dt_eu * dt_sv; // top-right
}
else
{
kfrst = dt_su * dt_ev; // bottom-left
kscan = dt_ev; // bottom
klast = dt_eu * dt_ev; // bottom-right
}
// 1st pixel, scanline, last pixel.
pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), kfrst);
u += ku;
int x;
for (x = 1; x < (src_width - 1); x++)
{
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), kscan);
u += ku;
}
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), klast);
}
else
{
pdst[0] = ColorBlend(pdst[0], src.SampleInteger(u, v), dt_su);
u += ku;
int x;
for (x = 1; x < (src_width - 1); x++)
{
pdst[x] = src.SampleInteger(u, v);
u += ku;
}
pdst[x] = ColorBlend(pdst[x], src.SampleInteger(u, v), dt_eu);
}
pdst += dst.GetWidth();
v += kv;
}
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,137 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include "picture/pict.h"
#include "picture/pict_io.h"
#include "log/log.h"
using namespace GS;
#define IMGBMP_BI_RGB 0
#define IMGBMP_BI_RLE8 1
#define IMGBMP_BI_RLE4 2
//-----------------------------------------------------------------------------
bool PictureIO::BmpLoad(Picture &picture, IO::Handle &handle)
{
handle.Rewind();
size_t size = handle.GetSize();
if (size < 10)
return false;
// Magic number
if ((handle.Read <char> () != 'B') || (handle.Read <char> () != 'M'))
return false;
handle.Seek(8);
uint OffBits = handle.Read <uint> (), width, height;
ushort bpp;
if (handle.Read <uint> () == 40) // we met a BITMAPCOREHEADER
{
width = handle.Read <uint> ();
height = handle.Read <uint> ();
}
else // we met a BITMAPINFOHEADER
{
width = handle.Read <ushort> ();
height = handle.Read <ushort> ();
}
handle.Seek(2);
bpp = handle.Read <ushort> ();
if ((bpp != 8) && (bpp != 24) && (bpp != 32))
__ERR__(__LOG_E__ << "BMP format unhandled (neither RGB8, RGB24 or BGR8).\n", false)
// Decode bitmap data.
if (!picture.AllocAs(width, height, PixelFormat::BGR8))
__ERR__(__LOG_E__ << "Failed to allocate output buffer.\n", false)
uint *rgb = (uint *)picture.GetData();
Array <uchar> bmp(size - OffBits);
if (!bmp)
__ERR__(__LOG_E__ << "Failed to allocate input framebuffer.\n", false)
uchar *_bmp = &bmp[0];
handle.Seek(OffBits, GS::IO::Base::SeekStart);
handle.Read((void *)_bmp, OffBits);
rgb += (picture.GetHeight() - 1) * picture.GetWidth();
// Load palette
Array <uint> palette;
if (bpp < 16)
{
__LOG__ << "Picture is palletized.\n";
if (palette.Allocate(1 << bpp))
{
char cbuf[4];
handle.Seek(54, GS::IO::Base::SeekStart);
for (int n = 0; n < (1 << bpp); ++n)
{
handle.Read(cbuf, 4);
palette[n] = (cbuf[3] << 24) + (cbuf[2] << 16) + (cbuf[1] << 8) + cbuf[0];
}
}
else
__ERR__(__LOG_E__ << "Failed to allocate palette.\n", false)
}
// Even width
switch (bpp)
{
case 8:
{
// 32 bit 0 padding.
uint pad = 0;
if (picture.GetWidth() & 3)
pad = 4 - (picture.GetWidth() & 3);
for (uint c2 = 0; c2 < picture.GetHeight(); c2++)
{
for (uint c = 0; c < picture.GetWidth(); c++)
rgb[c] = palette[*_bmp++];
_bmp += pad;
rgb -= picture.GetWidth();
}
}
break;
case 16:
break;
case 24:
for (uint c2 = 0; c2 < picture.GetHeight(); c2++)
{
for (uint c = 0; c < picture.GetWidth(); c++)
{
rgb[c] = (_bmp[2] << 16) + (_bmp[1] << 8) + _bmp[0];
_bmp += 3;
}
uint dt = 3 * picture.GetWidth();
if (dt & 3)
_bmp += (4 - (dt & 3));
rgb -= picture.GetWidth();
}
break;
case 32:
for (uint c2 = 0; c2 < picture.GetHeight(); c2++)
{
for (uint c = 0; c < picture.GetWidth(); c++)
{
rgb[c] = (_bmp[2] << 16) + (_bmp[1] << 8) + _bmp[0];
_bmp += 4;
}
rgb -= picture.GetWidth();
}
break;
}
return true;
}
//-----------------------------------------------------------------------------

View File

@ -0,0 +1,333 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict_color_format.h"
#include "picture/pict.h"
#include "math/nmath.h"
#include "sort/sort.h"
#include "memory/endian.h"
#include "log/log.h"
using namespace GS;
PixelFormatDescription PixelFormat::NONE
= {PixelColorSpace_NULL, 0, 0, 0, 0, 0, false};
PixelFormatDescription PixelFormat::BGRA8
= {PixelColorSpace_RGB, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff, 32, false};
PixelFormatDescription PixelFormat::RGBA8
= {PixelColorSpace_RGB, 0xff000000, 0x000000ff, 0x0000ff00, 0x00ff0000, 32, false};
PixelFormatDescription PixelFormat::ARGB8
= {PixelColorSpace_RGB, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, 32, false};
PixelFormatDescription PixelFormat::BGR8
= {PixelColorSpace_RGB, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 24, false};
PixelFormatDescription PixelFormat::RGB8
= {PixelColorSpace_RGB, 0, 0x000000ff, 0x0000ff00,0x00ff0000, 24, false};
PixelFormatDescription PixelFormat::RGB555
= {PixelColorSpace_RGB, 0, 0x0000001f, 0x000003e0, 0x00007c00, 16, false};
PixelFormatDescription PixelFormat::RGB565
= {PixelColorSpace_RGB, 0, 0x0000001f, 0x000007e0, 0x0000f800, 16, false};
PixelFormatDescription PixelFormat::RGBA4444
= {PixelColorSpace_RGB, 0x0000f000, 0x0000000f, 0x000000f0, 0x00000f00, 16, false};
PixelFormatDescription PixelFormat::RGBF
= { PixelColorSpace_RGB, 0, 0, 1, 2, sizeof(float) * 3 * 8, true };
PixelFormatDescription PixelFormat::RGBAF
= { PixelColorSpace_RGB, 0xff000000, 0x000000ff, 0x0000ff00, 0x00ff0000, 4 * 16, true };
//------------------------------------------------------------------------------
String PixelFormat::GetName() const
{
// Sort components.
uint count[4] = { Memory::GetBitCount(desc.rmask), Memory::GetBitCount(desc.gmask), Memory::GetBitCount(desc.bmask), Memory::GetBitCount(desc.amask) },
shift[4] = { Memory::GetShiftCount(desc.rmask), Memory::GetShiftCount(desc.gmask), Memory::GetShiftCount(desc.bmask), Memory::GetShiftCount(desc.amask) };
Sort<uint, uint>::Entry comp_sort[4];
for (uint n = 0; n < 4; ++n)
{
comp_sort[n].v = shift[n];
comp_sort[n].o = n;
}
Sort<uint, uint>::QuickSort(4, comp_sort);
// Build name.
static const char *comp_name[4] = {"R", "G", "B", "A"};
String name;
switch (desc.space)
{
case PixelColorSpace_RGB:
{
for (uint n = 0; n < 4; ++n)
{
int i = comp_sort[n].o;
if (count[i] != 0)
{
name += String::Format("%s%d", comp_name[i], count[i]);
if (desc.real)
name += "F";
}
}
}
break;
default:
name = "NONE";
break;
}
return name;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
uint PixelFormat::Format(float r, float g, float b, float a) const
{
switch (GetBpp())
{
case 32:
return Endian::ToHost((int(r * 255) << rshift) + (int(g * 255) << gshift) + (int(b * 255) << bshift) + (int(a * 255) << ashift), Endian::Intel);
default:
__LOG_W__ << "Unimplemented formatting.\n";
}
return 0;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::Negative(bool r, bool g, bool b, bool a)
{
if ((GetBpp() != 32) || (!r && !g && !b && !a) || !GetData())
return;
int iia = pxformat.ashift >> 3,
iir = pxformat.rshift >> 3,
iig = pxformat.gshift >> 3,
iib = pxformat.bshift >> 3;
unsigned char *p = GetData();
for (uint v = 0; v < height; ++v)
for (uint u = 0; u < width; ++u)
{
if (r) p[iir] = 255 - p[iir];
if (g) p[iig] = 255 - p[iig];
if (b) p[iib] = 255 - p[iib];
if (a) p[iia] = 255 - p[iia];
p += 4;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::Fast8888Conversion(const PixelFormat &dsformat)
{
int iia = pxformat.ashift >> 3,
iir = pxformat.rshift >> 3,
iig = pxformat.gshift >> 3,
iib = pxformat.bshift >> 3,
dia = dsformat.ashift >> 3,
dir = dsformat.rshift >> 3,
dig = dsformat.gshift >> 3,
dib = dsformat.bshift >> 3;
unsigned char *p = GetData();
if (!p)
return false;
// Optimized for the most common reorganizing done on 8888 ARGB data.
unsigned char r, g, b, a;
if (iia == dia)
{
if (iig == dig) // Fixed green & alpha.
for (uint v = 0; v < GetHeight(); ++v)
for (uint u = 0; u < GetWidth(); ++u)
{
r = p[iir]; b = p[iib];
p[dir] = r; p[dib] = b;
p += 4;
}
else // Fixed alpha.
for (uint v = 0; v < GetHeight(); ++v)
for (uint u = 0; u < GetWidth(); ++u)
{
r = p[iir]; g = p[iig]; b = p[iib];
p[dir] = r; p[dig] = g; p[dib] = b;
p += 4;
}
}
else // Fully generic.
for (uint v = 0; v < GetHeight(); ++v)
for (uint u = 0; u < GetWidth(); ++u)
{
a = p[iia]; r = p[iir]; g = p[iig]; b = p[iib];
p[dia] = a; p[dir] = r; p[dig] = g; p[dib] = b;
p += 4;
}
pxformat = dsformat;
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::RealToIntegerConversion(const PixelFormat &dstfmt)
{
uchar *new_data = new uchar[width * height * (dstfmt.GetBpp() / 8)];
if (!new_data)
return false;
uchar *out = new_data;
float *pdata = (float *)GetData();
// Convert.
for (uint y = 0; y < GetHeight(); ++y)
for (uint x = 0; x < GetWidth(); ++x)
{
uchar r = (uchar)Types::Min(pdata[2] * 255.f, 255.f), g = (uchar)Types::Min(pdata[1] * 255.f, 255.f), b = (uchar)Types::Min(pdata[0] * 255.f, 255.f), a = 255;
pdata += 3;
// Convert components.
a >>= (8 - dstfmt.acount);
r >>= (8 - dstfmt.rcount);
g >>= (8 - dstfmt.gcount);
b >>= (8 - dstfmt.bcount);
// Repack components and output.
uint packed;
uchar *ppack = (uchar *)&packed;
packed = (a << dstfmt.ashift) + (r << dstfmt.rshift) + (g << dstfmt.gshift) + (b << dstfmt.bshift);
switch (dstfmt.GetBpp())
{
case 8: out[0] = ppack[0]; out++; break;
case 16: out[0] = ppack[0]; out[1] = ppack[1]; out += 2; break;
case 24: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out += 3; break;
case 32: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out[3] = ppack[3]; out += 4; break;
}
}
// Replace data.
SetData(new_data, width, height, dstfmt.GetDesc());
return true;
}
bool Picture::IntegerToIntegerConversion(const PixelFormat &dsformat)
{
switch (pxformat.GetBpp())
{
case 8: case 16: case 24: case 32: break;
default: return false; // Unsupported source mode.
}
// Allocate destination buffer.
uchar *new_data = new uchar[width * height * (dsformat.GetBpp() / 8)];
if (!new_data)
return false;
uchar *out = new_data;
uchar *pdata = GetData();
// Convert.
for (uint y = 0; y < GetHeight(); ++y)
{
uint packed;
uchar *ppack = (uchar *)&packed;
for (uint x = 0; x < GetWidth(); ++x)
{
// Extract packed color.
switch (pxformat.GetBpp())
{
case 8: ppack[0] = pdata[0]; pdata++; break;
case 16: ppack[0] = pdata[0]; ppack[1] = pdata[1]; pdata += 2; break;
case 24: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; pdata += 3; break;
case 32: ppack[0] = pdata[0]; ppack[1] = pdata[1]; ppack[2] = pdata[2]; ppack[3] = pdata[3]; pdata += 4; break;
}
// Extract components.
uint a = (uchar)(((packed & pxformat.desc.amask) >> pxformat.ashift) << (8 - pxformat.acount)),
r = (uchar)(((packed & pxformat.desc.rmask) >> pxformat.rshift) << (8 - pxformat.rcount)),
g = (uchar)(((packed & pxformat.desc.gmask) >> pxformat.gshift) << (8 - pxformat.gcount)),
b = (uchar)(((packed & pxformat.desc.bmask) >> pxformat.bshift) << (8 - pxformat.bcount));
// Convert components.
a >>= (8 - dsformat.acount);
r >>= (8 - dsformat.rcount);
g >>= (8 - dsformat.gcount);
b >>= (8 - dsformat.bcount);
// Repack components and output.
packed = Endian::ToHost((a << dsformat.ashift) + (r << dsformat.rshift) + (g << dsformat.gshift) + (b << dsformat.bshift), Endian::Intel);
switch (dsformat.GetBpp())
{
case 8: out[0] = ppack[0]; out++; break;
case 16: out[0] = ppack[0]; out[1] = ppack[1]; out += 2; break;
case 24: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out += 3; break;
case 32: out[0] = ppack[0]; out[1] = ppack[1]; out[2] = ppack[2]; out[3] = ppack[3]; out += 4; break;
}
}
}
// Replace data.
SetData(new_data, width, height, dsformat.GetDesc(), true);
return true;
}
bool Picture::Convert(const PixelFormatDescription &dsc)
{
if (pxformat == dsc)
return true;
if (!dsc.bpp)
{
Free();
return true;
}
PixelFormat dsformat(dsc);
// Real to integer.
if (pxformat.IsReal())
return RealToIntegerConversion(dsformat);
// Fast 8888 conversion.
if (
(pxformat.acount == 8) && (pxformat.rcount == 8) && (pxformat.gcount == 8) && (pxformat.bcount == 8) &&
(dsformat.acount == 8) && (dsformat.rcount == 8) && (dsformat.gcount == 8) && (dsformat.bcount == 8)
)
return Fast8888Conversion(dsformat);
// Slower generic integer->integer conversion.
return IntegerToIntegerConversion(dsformat);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::Swizzle(uchar r, uchar g, uchar b, uchar a)
{
// Swizzle output format...
PixelFormatDescription out_format = GetPixelFormat().GetDesc();
const uint in_mask[4] = { out_format.rmask, out_format.gmask, out_format.bmask, out_format.amask };
out_format.rmask = in_mask[r];
out_format.gmask = in_mask[g];
out_format.bmask = in_mask[b];
out_format.amask = in_mask[a];
// ...and convert picture.
return Convert(out_format);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::SetFormat(const PixelFormatDescription &dsc)
{
if (dsc.bpp != pxformat.GetDesc().bpp)
__ERR__(__LOG_E__ << "Target format requires a data conversion, see Convert().\n", false)
pxformat.Set(dsc);
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,94 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#include "color/color.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
bool Picture::ApplyConvolution(uint k_width, uint k_height, const int *p_w, int weight, int pass, const Rect <int> *clip_rect)
{
if (GetBpp() != 32)
__ERR__(__LOG_E__ << "Convolution filter only supported on 32bpp picture.\n", false);
if (!k_width || !k_height)
__ERR__(__LOG_E__ << "Invalid kernel size (" << k_width << "x" << k_height << ").\n", false);
if (pass <= 0)
return true;
// Clipping rect.
Rect <int> rect = GetRect();
if (!clip_rect)
clip_rect = &rect;
// Setup flip chain.
Picture tmp(*this), *src, *dst;
if (pass & 1)
{ src = &tmp; dst = this; }
else { src = this; dst = &tmp; }
// Perform convolution.
for (int p = 0; p < pass; ++p)
{
uchar *pdata = dst->GetDataOffset(clip_rect->sx, clip_rect->sy);
for (int y = 0; y < clip_rect->GetHeight(); ++y)
{
uchar *pscan = pdata;
for (int x = 0; x < rect.GetWidth(); ++x)
{
int sx = x - k_width / 2,
sy = y - k_height / 2;
int k_sx = Types::Max <int> (sx, clip_rect->sx),
k_sy = Types::Max <int> (sy, clip_rect->sy);
int k_ex = Types::Min <int> (k_width + x - k_width / 2, clip_rect->ex),
k_ey = Types::Min <int> (k_height + y - k_height / 2, clip_rect->ey);
const int *w = p_w + Types::Max(0, clip_rect->sx - sx)
+ Types::Max(0, clip_rect->sy - sy) * k_width;
int k = 0;
int accu[4] = { 0, 0, 0, 0 };
for (int ky = k_sy; ky < k_ey; ++ky)
{
const int *sw = w;
for (int kx = k_sx; kx < k_ex; ++kx)
{
uchar *psrc = src->GetDataOffset(kx, ky);
accu[0] += psrc[0] * *sw;
accu[1] += psrc[1] * *sw;
accu[2] += psrc[2] * *sw;
accu[3] += psrc[3] * *sw;
k += *sw++;
}
w += k_width;
}
k = k ? (weight << 6) / k : (weight << 6);
pscan[0] = (uchar)Types::Clamp((accu[0] * k) >> 14, 0, 255);
pscan[1] = (uchar)Types::Clamp((accu[1] * k) >> 14, 0, 255);
pscan[2] = (uchar)Types::Clamp((accu[2] * k) >> 14, 0, 255);
pscan[3] = (uchar)Types::Clamp((accu[3] * k) >> 14, 0, 255);
pscan += 4;
}
pdata += dst->GetPitch();
}
Picture *swp = dst; dst = src; src = swp;
}
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,25 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
using namespace GS;
//------------------------------------------------------------------------------
uchar Picture::AlphaCompositeAlpha(uchar a, uchar b)
{ return (uchar)Types::Clamp <int> (a + b - ((a * b) >> 8), 0, 255); }
uchar Picture::AlphaCompositeColor(uchar u, uchar v, uchar a, uchar b, uchar k)
{ return (uchar)Types::Clamp <int> (k ? (u * a + v * b - ((u * b * a) >> 8)) / k : 0, 0, 255); }
void Picture::AlphaCompositePixel(uchar *data, uchar r, uchar g, uchar b, uchar a)
{
uchar a_blend = AlphaCompositeAlpha(data[3], a);
data[0] = AlphaCompositeColor(data[0], r, data[3], a, a_blend);
data[1] = AlphaCompositeColor(data[1], g, data[3], a, a_blend);
data[2] = AlphaCompositeColor(data[2], b, data[3], a, a_blend);
data[3] = a_blend;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,166 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#include "math/nmath.h"
using namespace GS;
//------------------------------------------------------------------------------
void Picture::LowLevelDrawLine(bool hq, float sx, float sy, float ex, float ey, float r, float g, float b, float a, const Rect <float> *clip_rect)
{
// Output validity.
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
return;
// Clip primitive.
float dx = ex - sx, dy = ey - sy;
// Sub pixel correction.
if (Types::Abs(dy) > Types::Abs(dx))
{
float fsy = Math::Floor(sy);
if (dy)
sx -= dx * (sy - fsy) / dy; // Sub-pixel correction.
sy = fsy;
}
else
{
float fsx = Math::Floor(sx);
if (dx)
sy -= dy * (sx - fsx) / dx; // Sub-pixel correction.
sx = fsx;
}
// Clipping.
if (clip_rect)
{
//---------------------------------------------------------------
#define __SwapFloat(A, B) { float swp = A; A = B; B = swp; }
//---------------------------------------------------------------
if (dx)
{
// Flip line if it is going backward.
if (dx < 0)
{
__SwapFloat(sx, ex)
__SwapFloat(sy, ey)
dx = ex - sx; dy = ey - sy;
}
float idx = 1 / dx;
// Clip on X axis.
if (ex < clip_rect->sx)
return;
float kee = (clip_rect->ex - sx) * idx;
if (kee < 1)
{
ex = clip_rect->ex;
ey = dy * kee + sy;
}
if (sx > clip_rect->ex)
return;
float kss = (clip_rect->sx - sx) * idx;
if (kss > 0)
{
sx = clip_rect->sx;
sy += dy * kss;
}
dx = ex - sx; dy = ey - sy;
}
else
if ((sx < clip_rect->sx) || (sx > clip_rect->ex))
return;
if (dy)
{
// Flip line if it is going backward.
if (dy < 0)
{
__SwapFloat(sx, ex)
__SwapFloat(sy, ey)
dx = ex - sx; dy = ey - sy;
}
float idy = 1 / dy;
// Clip on Y axis.
if (ey < clip_rect->sy)
return;
float kee = (clip_rect->ey - sy) * idy;
if (kee < 1)
{
ey = clip_rect->ey;
ex = dx * kee + sx;
}
if (sy > clip_rect->ey)
return;
float kss = (clip_rect->sy - sy) * idy;
if (kss > 0)
{
sy = clip_rect->sy;
sx += dx * kss;
}
dx = ex - sx; dy = ey - sy;
}
else
if ((sy < clip_rect->sy) || (sy > clip_rect->ey))
return;
}
// Draw line.
if (Types::Abs(dy) > Types::Abs(dx))
{
if (dy < 0)
{
dy = -dy; dx = -dx;
float swp = ey;
ey = sy; sy = swp; sx = ex;
}
float slope = dy ? dx / dy : 0.f;
for (; sy < ey; sy += 1.f)
{
if (hq)
DrawPlotHQ(sx, sy, r, g, b, a);
else DrawPlot(sx, sy, r, g, b, a);
sx += slope;
}
}
else
{
if (dx < 0)
{
dx = -dx; dy = -dy;
float swp = ex;
ex = sx; sx = swp; sy = ey;
}
float slope = dx ? dy / dx : 0.f;
for (; sx < ex; sx += 1.f)
{
if (hq)
DrawPlotHQ(sx, sy, r, g, b, a);
else DrawPlot(sx, sy, r, g, b, a);
sy += slope;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::DrawLine(float sx, float sy, float ex, float ey, float r, float g, float b, float a, const Rect <float> *clip_rect)
{ LowLevelDrawLine(false, sx, sy, ex, ey, r, g, b, a, clip_rect); }
void Picture::DrawLineHQ(float sx, float sy, float ex, float ey, float r, float g, float b, float a, const Rect <float> *clip_rect)
{ LowLevelDrawLine(true, sx, sy, ex, ey, r, g, b, a, clip_rect); }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,60 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#include "math/nmath.h"
using namespace GS;
//------------------------------------------------------------------------------
void Picture::DrawPlot(float x, float y, float r, float g, float b, float a, const Rect <float> *clip_rect)
{
// Output validity.
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
return;
// Clip primitive.
if (clip_rect && ((x < clip_rect->sx) || (x >= clip_rect->ex) || (y < clip_rect->sy) || (y >= clip_rect->ey)))
return;
// Draw.
AlphaCompositePixel(GetDataOffset((uint)x, (uint)y), uchar(r * 255.f), uchar(g * 255.f), uchar(b * 255.f), uchar(a * 255.f));
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::DrawPlotHQ(float x, float y, float r, float g, float b, float a, const Rect <float> *clip_rect)
{
// Output validity.
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
return;
// Clip primitive.
if (clip_rect && ((x < clip_rect->sx) || (x >= (clip_rect->ex - 1)) || (y < clip_rect->sy) || (y >= (clip_rect->ey - 1))))
return;
// Draw.
uchar ir = uchar(r * 255.f),
ig = uchar(g * 255.f),
ib = uchar(b * 255.f),
ia = uchar(a * 255.f);
float xm = Math::Floor(x),
ym = Math::Floor(y);
float a0 = (xm + 1 - x) * (ym + 1 - y),
a1 = (x - xm) * (ym + 1 - y),
a2 = (xm + 1 - x) * (y - ym),
a3 = (x - xm) * (y - ym);
uchar *output = GetDataOffset((uint)x, (uint)y);
AlphaCompositePixel(output, ir, ig, ib, uchar(ia * a0));
AlphaCompositePixel(output + 4, ir, ig, ib, uchar(ia * a1));
AlphaCompositePixel(output + width * 4, ir, ig, ib, uchar(ia * a2));
AlphaCompositePixel(output + (width + 1) * 4, ir, ig, ib, uchar(ia * a3));
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,159 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#include "math/nmath.h"
#include "log/log.h"
using namespace GS;
//-------------------------------------------------
static bool ClipValueTestLess(float v, float c)
{ return v < c; }
static bool ClipValueTestGreater(float v, float c)
{ return v >= c; }
//-------------------------------------------------
//------------------------------------------------------------------------------
static uint ClipPolygonAxis(float clip, uint axis, bool (*ClipValueTest)(float v, float c), uint count_in, Point <float> *p_in, Point <float> *p_out)
{
uint p_current = 0, count_out = 0;
//--------------------------------------------------------------------------------------------------------
#define __ClipSegment\
{\
float k = (clip - p_in[p_current][axis]) / (p_in[p_next][axis] - p_in[p_current][axis]);\
if (!axis)\
p_out[count_out++].Set (clip, (p_in[p_next].y - p_in[p_current].y) * k + p_in[p_current].y);\
else p_out[count_out++].Set ((p_in[p_next].x - p_in[p_current].x) * k + p_in[p_current].x, clip);\
}
//--------------------------------------------------------------------------------------------------------
while (p_current < count_in)
{
uint p_next = p_current + 1;
if (p_next == count_in)
p_next = 0;
if (ClipValueTest(p_in[p_current][axis], clip)) // Inside.
{
p_out[count_out++] = p_in[p_current];
if (!ClipValueTest(p_in[p_next][axis], clip))
__ClipSegment
}
else // Outside.
{
if (ClipValueTest(p_in[p_next][axis], clip))
__ClipSegment
}
p_current++;
}
return count_out;
}
void Picture::DrawPolygon(uint point_count, Point <float> *point, float r, float g, float b, float a, const Rect <float> *clip_rect)
{
// Output validity.
if (!data || (pxformat.GetBpp() != 32) || pxformat.IsReal())
return;
// Clip primitive.
Point <float> *_point = point;
if (clip_rect)
{
_point = new Point <float> [128];
if (!_point)
__ERRRAW__(__LOG_E__ << "failed to allocate polygon clipping array.\n")
// Old boring clipping code...
Point <float> *_point_ = _point + 64;
point_count = ClipPolygonAxis(clip_rect->sx, 0, ClipValueTestGreater, point_count, point, _point_);
point_count = ClipPolygonAxis(clip_rect->ex, 0, ClipValueTestLess, point_count, _point_, _point);
point_count = ClipPolygonAxis(clip_rect->sy, 1, ClipValueTestGreater, point_count, _point, _point_);
point_count = ClipPolygonAxis(clip_rect->ey, 1, ClipValueTestLess, point_count, _point_, _point);
if (point_count < 3)
{
_safe_delete_array(_point);
return;
}
}
// Determine entry vertex.
float y_scan = _point[0].y,
y_max = _point[0].y;
int p_int = 0,
p_ext = 0;
for (uint n = 1; n < point_count; ++n)
{
if (_point[n].y < y_scan)
{
y_scan = _point[n].y;
p_int = p_ext = n;
}
if (_point[n].y > y_max)
y_max = _point[n].y;
}
// Render scanline.
float d_int = 0,
d_ext = 0;
float x_int = _point[p_int].x,
x_ext = _point[p_int].x;
y_scan = Math::Ceil(y_scan);
while (y_scan < y_max)
{
// Update interior pointer.
while (y_scan >= _point[p_int].y)
{
uint p_next = p_int - 1;
if (p_next == -1)
p_next = point_count - 1;
// Update delta.
d_int = (_point[p_next].x - _point[p_int].x) / (_point[p_next].y - _point[p_int].y);
x_int = d_int * (y_scan - _point[p_int].y) + _point[p_int].x;
p_int = p_next;
}
// Update exterior pointer.
while (y_scan >= _point[p_ext].y)
{
uint p_next = p_ext + 1;
if (p_next == point_count)
p_next = 0;
// Update delta.
d_ext = (_point[p_next].x - _point[p_ext].x) / (_point[p_next].y - _point[p_ext].y);
x_ext = d_ext * (y_scan - _point[p_ext].y) + _point[p_ext].x;
p_ext = p_next;
}
// Draw scanline.
{
float sx, ex;
if (x_int > x_ext)
{ sx = x_ext; ex = x_int; }
else { sx = x_int; ex = x_ext; }
for (int x = int(sx); x < int(ex); ++x)
DrawPlot((float)x, y_scan, r, g, b, a);
}
// Step scanline boundaries.
y_scan += 1;
x_int += d_int;
x_ext += d_ext;
}
// Release clipping point array.
if (clip_rect)
_safe_delete_array(_point);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,105 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include <cmath>
#include "picture/pict.h"
//------------------------------------------------------------------------------
void FFT(int size, bool inverse, float *inReal, float *inIm, float *outReal, float *outIm)
{
// Calculate m = log_2(n).
int m = 0, p = 1;
for (; p < size; ++m)
p *= 2;
// Bit reversal.
outReal[size - 1] = inReal[size - 1];
outIm[size - 1] = inIm[size - 1];
int j = 0;
for (int i = 0; i < size - 1; ++i)
{
outReal[i] = inReal[j];
outIm[i] = inIm[j];
int k = size / 2;
while (k <= j)
{
j -= k;
k /= 2;
}
j += k;
}
// Calculate the FFT.
float ca = -1.0, sa = 0.0;
int l1 = 1, l2 = 1;
for (int l = 0; l < m; ++l)
{
l1 = l2;
l2 *= 2;
float u1 = 1.0, u2 = 0.0;
for(int j = 0; j < l1; j++)
{
for(int i = j; i < size; i += l2)
{
int i1 = i + l1;
float t1 = u1 * outReal[i1] - u2 * outIm[i1],
t2 = u1 * outIm[i1] + u2 * outReal[i1];
outReal[i1] = outReal[i] - t1;
outIm[i1] = outIm[i] - t2;
outReal[i] += t1;
outIm[i] += t2;
}
double z = u1 * ca - u2 * sa;
u2 = u1 * sa + u2 * ca;
u1 = (float)z;
}
sa = (float)sqrt((1.f - ca) / 2.f);
if (!inverse)
sa = -sa;
ca = (float)sqrt((1.f + ca) / 2.f);
}
// Divide through n if it isn't the IDFT.
if (!inverse)
for (int i = 0; i < size; ++i)
{
outReal[i] /= size;
outIm[i] /= size;
}
}
void testFFT()
{
float inR[8], inI[8], outR[8], outI[8];
for (int n = 0; n < 8; ++n)
{
inR[n] = 5;
inI[n] = 2;
}
FFT(8, false, inR, inI, outR, outI);
for (int n = 0; n < 8; ++n)
{
inR[n] = 0;
inI[n] = 0;
}
FFT(8, true, outR, outI, inR, inI);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,184 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict_gradient.h"
#include "picture/pict.h"
#include "metafile/nml.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
static uchar ClampChannel(int v)
{
if (v < 0)
return 0;
if (v > 255)
return 255;
return (uchar)v;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::DrawGradient(const Gradient &gradient, const Rect <int> *clip_rect)
{
if (!gradient.GetControlPointCount())
return;
Rect <int> rect = GetRect();
if (!clip_rect)
clip_rect = &rect;
uint current_control_point = 0,
next_control_point = 1;
if (next_control_point == gradient.GetControlPointCount())
next_control_point = current_control_point;
uchar *pdata = GetDataOffset(clip_rect->sx, clip_rect->sy);
for (int y = 0; y < clip_rect->GetHeight(); ++y)
{
float c_k = (float)y / clip_rect->GetHeight();
// Check gradient interval change.
if ((c_k > gradient.k[next_control_point]) && (current_control_point + 1 < gradient.GetControlPointCount()))
{
current_control_point++;
next_control_point++;
if (next_control_point == gradient.GetControlPointCount())
next_control_point = current_control_point;
}
// Blend.
Color blend_color = gradient.color[current_control_point];
if (current_control_point != next_control_point)
blend_color = (gradient.color[next_control_point] - gradient.color[current_control_point]) *
(c_k - gradient.k[current_control_point]) / (gradient.k[next_control_point] - gradient.k[current_control_point]) +
gradient.color[current_control_point];
uchar *pscan = pdata,
r = (uchar)(blend_color.x * 255),
g = (uchar)(blend_color.y * 255),
b = (uchar)(blend_color.z * 255),
a = (uchar)(blend_color.w * 255);
switch (gradient.GetOperator())
{
case Picture::BlendReplace:
for (int x = 0; x < rect.GetWidth(); ++x)
{
pscan[0] = r;
pscan[1] = g;
pscan[2] = b;
pscan[3] = a;
pscan += 4;
}
break;
case Picture::BlendCompose:
for (int x = 0; x < rect.GetWidth(); ++x)
{
uchar a_blend = AlphaCompositeAlpha(pscan[3], a);
pscan[0] = AlphaCompositeColor(pscan[0], r, pscan[3], a, a_blend);
pscan[1] = AlphaCompositeColor(pscan[1], g, pscan[3], a, a_blend);
pscan[2] = AlphaCompositeColor(pscan[2], b, pscan[3], a, a_blend);
pscan[3] = a_blend;
pscan += 4;
}
break;
case Picture::BlendMultiply:
for (int x = 0; x < rect.GetWidth(); ++x)
{
pscan[0] = (pscan[0] * r) >> 8;
pscan[1] = (pscan[1] * g) >> 8;
pscan[2] = (pscan[2] * b) >> 8;
pscan[3] = (pscan[3] * a) >> 8;
pscan += 4;
}
break;
case Picture::BlendMultiply2x:
for (int x = 0; x < rect.GetWidth(); ++x)
{
pscan[0] = ClampChannel((pscan[0] * r) >> 7);
pscan[1] = ClampChannel((pscan[1] * g) >> 7);
pscan[2] = ClampChannel((pscan[2] * b) >> 7);
pscan[3] = (pscan[3] * a) >> 8;
pscan += 4;
}
break;
case Picture::BlendAdd:
for (int x = 0; x < rect.GetWidth(); ++x)
{
pscan[0] = ClampChannel(pscan[0] + r);
pscan[1] = ClampChannel(pscan[1] + g);
pscan[2] = ClampChannel(pscan[2] + b);
pscan[3] = (pscan[3] * a) >> 8;
pscan += 4;
}
break;
default: break;
}
pdata += GetPitch();
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Gradient::FromMetaTag(NML::Tag *tag)
{
control_point_count = 0;
op = Picture::BlendMultiply;
NMLTagForeach(ctag, *tag)
{
if (ctag->name == "Operator")
{
String op_string(ctag->GetString());
if (op_string == "Replace")
op = Picture::BlendReplace;
else if (op_string == "Multiply")
op = Picture::BlendMultiply;
else if (op_string == "Multiply2x")
op = Picture::BlendMultiply2x;
else if (op_string == "Add")
op = Picture::BlendAdd;
else if (op_string == "Compose")
op = Picture::BlendCompose;
else __LOG_W__ << "Unknown gradient operator.\n";
}
else if (ctag->name == "Control")
{
// Control point count safety.
if (control_point_count == 8)
__ERR__(__LOG_E__ << "Too many control points in gradient definition.\n", false);
// Coordinate.
NML::Tag *attr_tag = ctag->GetTypedTag("K", Variant::VariantFloat);
if (!attr_tag)
__ERR__(__LOG_E__ << "Missing tag (control point coordinate) in gradient definition.\n", false);
k[control_point_count] = attr_tag->GetReal();
// Color.
attr_tag = ctag->GetTypedTag("Color", Variant::VariantNone);
if (!attr_tag)
__ERR__(__LOG_E__ << "Missing tag (control point color) in gradient definition.\n", false);
color[control_point_count].Set();
if (!color[control_point_count].FromMetaTag(*attr_tag))
__ERR__(__LOG_E__ << "Erroneous control point color in gradient definition.\n", false);
control_point_count++;
}
else __ERR__(__LOG_E__ << "Unexpected tag in gradient definition.\n", false);
}
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,70 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict_io.h"
#include "picture/pict.h"
#include "filesystem/io_handle.h"
#include "filesystem/filesystem.h"
#include "memory/nauto_ptr.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
using namespace GS::IO;
template<> PictureIO *Singleton <PictureIO> ::i = NULL;
//------------------------------------------------------------------------------
PictureCodec *PictureIO::Codec(const char *codec_name)
{
ListForeachPtr(PictureCodec *, codec, codec_list)
if (GS::String(codec->GetName()) == GS::String(codec_name))
return codec;
return NULL;
}
bool PictureIO::RegisterCodec(PictureCodec *codec, bool verbose)
{
if (Codec(codec->GetName()))
return false;
codec_list.Add(codec);
if (verbose)
__LOG__ << "Codec '" << codec->GetName() << "' registered successfully.\n";
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool PictureIO::Save(const Picture &picture, const char *uri, const char *codec_name)
{
if (PictureCodec *c = Codec(codec_name))
{
AutoPtr <Handle> h(Platform::Get().io->Open(uri, ModeWrite));
if (h.IsNull())
return false;
if (!c->Save(*h, picture))
return false;
}
else
return false;
return true;
}
bool PictureIO::Load(Picture &picture, const char *uri)
{
AutoPtr <Handle> h(Platform::Get().io->Open(uri));
if (h.IsNull())
return false;
picture.name = uri;
ListForeachPtr(PictureCodec *, codec, codec_list)
if (codec->Load(*h, picture))
return true;
return false;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,183 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#include "color/color.h"
using namespace GS;
//------------------------------------------------------------------------------
void Picture::Sample(float u, float v, Color &out, uint _w, uint _h) const
{
if (!GetData())
{
out.Set(1, 0, 1);
return;
}
if (!GetPixelFormat().IsReal())
{
uint _out;
Sample(u, v, _out, _w, _h);
out.FromInteger(_out);
}
else
{
if (!_w) _w = GetWidth();
if (!_h) _h = GetHeight();
u *= _w; v *= _h;
if (u < 0) u = 0;
if (v < 0) v = 0;
if (u >= _w) u = (float)(_w - 1);
if (v >= _h) v = (float)(_h - 1);
uint iu = (uint)u, iv = (uint)v;
Color sample[4];
float *pdata = (float *)GetData();
pdata += (iu + iv * _w) * 3;
//---------------------------------------
#define EXTRACT_FSAMPLE(_S_, _P_) \
{ \
(_S_).z = (_P_)[2]; \
(_S_).y = (_P_)[1]; \
(_S_).x = (_P_)[0]; \
}
//---------------------------------------
EXTRACT_FSAMPLE(sample[0], pdata + 0);
if (iu < (_w - 1))
EXTRACT_FSAMPLE(sample[1], pdata + 3)
else sample[1] = sample[0];
if (iv < (_h - 1))
EXTRACT_FSAMPLE(sample[2], pdata + _w * 3)
else sample[2] = sample[0];
if ((iu < (_w - 1)) && (iv < (_h - 1)))
EXTRACT_FSAMPLE(sample[3], pdata + (_w + 1) * 3)
else sample[3] = sample[0];
// Bilinear sample.
float k[4], uf = u - (float)iu, vf = v - (float)iv;
k[0] = (1.f - uf) * (1.f - vf);
k[1] = uf * (1.f - vf);
k[2] = (1.f - uf) * vf;
k[3] = uf * vf;
out = sample[0] * k[0] + sample[1] * k[1] + sample[2] * k[2] + sample[3] * k[3];
out.w = sample[0].w * k[0] + sample[1].w * k[1] + sample[2].w * k[2] + sample[3].w * k[3];
}
}
void Picture::Sample(float u, float v, uint &out, uint _w, uint _h) const
{
if (!GetData())
{
out = 0xffff00ff;
return;
}
if (GetPixelFormat().IsReal())
{
Color _out;
Sample(u, v, _out, _w, _h);
out = _out.AsInteger();
}
else
{
if (!_w) _w = GetWidth();
if (!_h) _h = GetHeight();
u *= _w; v *= _h;
if (u < 0) u = 0;
if (v < 0) v = 0;
if (u >= _w) u = (float)(_w - 1);
if (v >= _h) v = (float)(_h - 1);
uint iu = (uint)u, iv = (uint)v;
struct iVector
{
int x, y, z, w;
iVector operator + (const iVector &b) const
{ return iVector(x + b.x, y + b.y, z + b.z, w + b.w); }
iVector operator * (const int v) const
{ return iVector(x * v, y * v, z * v, w * v); }
iVector operator >> (const int v) const
{ return iVector(x >> v, y >> v, z >> v, w >> v); }
iVector(int _x, int _y, int _z, int _w = 255)
{ x = _x; y = _y; z = _z; w = _w; }
iVector()
{}
};
iVector sample[4];
uint *pdata = (uint *)GetData();
pdata += iu + iv * _w;
//----------------------------------------------------------------------
#define EXTRACT_SAMPLE(_S_, _P_) \
{ \
const uchar *_t_p = (const uchar *)&(_P_); \
(_S_).x = _t_p[0]; \
(_S_).y = _t_p[1]; \
(_S_).z = _t_p[2]; \
(_S_).w = _t_p[3]; \
}
//----------------------------------------------------------------------
EXTRACT_SAMPLE(sample[0], pdata[0]);
if (iu < (_w - 1))
EXTRACT_SAMPLE(sample[1], pdata[1])
else sample[1] = sample[0];
if (iv < (_h - 1))
EXTRACT_SAMPLE(sample[2], pdata[_w])
else sample[2] = sample[0];
if ((iu < (_w - 1)) && (iv < (_h - 1)))
EXTRACT_SAMPLE(sample[3], pdata[_w + 1])
else sample[3] = sample[0];
int k[4], uf = int((u - (float)iu) * 256), vf = int((v - (float)iv) * 256);
k[0] = (256 - uf) * (256 - vf); // 16bit fixed point.
k[1] = uf * (256 - vf);
k[2] = (256 - uf) * vf;
k[3] = uf * vf;
iVector r = (sample[0] * k[0] + sample[1] * k[1] + sample[2] * k[2] + sample[3] * k[3]) >> 16;
uchar *_out = (uchar *)&out;
_out[0] = (uchar)r.x;
_out[1] = (uchar)r.y;
_out[2] = (uchar)r.z;
_out[3] = (uchar)r.w;
}
}
void Picture::SampleRGBA(float u, float v, Color &o, uint _w, uint _h) const
{
Color s;
Sample(u, v, s, _w, _h);
o[0] = s[pxformat.rshift >> 3];
o[1] = s[pxformat.gshift >> 3];
o[2] = s[pxformat.bshift >> 3];
o[3] = s[pxformat.ashift >> 3];
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
uint Picture::SampleInteger(float u, float v, uint _w, uint _h) const
{ uint s; Sample(u, v, s, _w, _h); return s; }
Color Picture::SampleColor(float u, float v, uint _w, uint _h) const
{ Color s; Sample(u, v, s, _w, _h); return s; }
Color Picture::SampleRGBAColor(float u, float v, uint _w, uint _h) const
{ Color s; SampleRGBA(u, v, s, _w, _h); return s; }
//------------------------------------------------------------------------------

View File

@ -0,0 +1,206 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#include "color/color.h"
using namespace GS;
//------------------------------------------------------------------------------
bool Picture::Resize(uint nwidth, uint nheight)
{
if (!nwidth && !nheight)
return false;
if (!nwidth)
nwidth = (GetWidth() * nheight) / GetHeight();
if (!nheight)
nheight = (GetHeight() * nwidth) / GetWidth();
if ((nwidth == GetWidth()) && (nheight == GetHeight()))
return true;
if (pxformat.IsReal())
{
float *new_data = (float *)AllocMemory(sizeof(float) * nwidth * nheight * 3);
if (!new_data)
return false;
float *pdst = (float *)new_data;
float ku = 1.f / (float)nwidth,
kv = 1.f / (float)nheight;
Color out;
float v = kv * 0.5f - 0.5f / height;
if (data)
for (uint y = 0; y < nheight; ++y)
{
float u = ku * 0.5f - 0.5f / width;
for (uint x = 0; x < nwidth; ++x)
{
Sample(u, v, out);
pdst[0] = out.x;
pdst[1] = out.y;
pdst[2] = out.z;
pdst += 3;
u += ku;
}
v += kv;
}
SetData(new_data, nwidth, nheight, PixelFormat::RGBAF, true);
//SetData(new_data, nwidth, nheight, PixelFormat::RGBF, true);
}
else if (GetBpp() == 32)
{
uchar *new_data = (uchar *)AllocMemory(sizeof(uchar) * nwidth * nheight * 4);
if (!new_data)
return false;
uint *pdst = (uint*)new_data;
float ku = 1.f / (float)nwidth,
kv = 1.f / (float)nheight;
float v = kv * 0.5f - 0.5f / height;
if (data)
for (uint y = 0; y < nheight; ++y)
{
float u = ku * 0.5f - 0.5f / width;
for (uint x = 0; x < nwidth; ++x)
{
Sample(u, v, *pdst++);
u += ku;
}
v += kv;
}
SetData(new_data, nwidth, nheight, GetPixelFormat().GetDesc(), true);
}
else
return false;
return true;
}
bool Picture::Downscale(uint nwidth, uint nheight)
{
if (!nwidth && !nheight)
return false;
if (!nwidth)
nwidth = (GetWidth() * nheight) / GetHeight();
if (!nheight)
nheight = (GetHeight() * nwidth) / GetWidth();
if ((nwidth > GetWidth()) || (nheight > GetHeight()))
return Resize(nwidth, nheight);
if (GetBpp() == 32)
{
uchar *new_data = (uchar *)AllocMemory(sizeof(uchar) * nwidth * nheight * 4);
if (!new_data)
return false;
uchar *pdst = new_data;
Array <float> accu(nwidth * 4);
if (accu)
{
float ku = (float)GetWidth() / (float)nwidth,
kv = (float)GetHeight() / (float)nheight;
float u, v;
//
v = 0.f;
// Accumulate scan lines.
uchar *psrc = GetData();
for (uint y = 0; y < nheight; y++)
{
uint n;
for (n = 0; n < (nwidth * 4); n++)
accu[n] = 0.f;
float tv = kv, nv = kv;
for (;;)
{
float k = GS::Math::Ceil(v) - v;
tv -= k;
if (tv < 0.f)
k += tv; // readjust
v += k;
if (v >= GetHeight())
{
nv -= k;
break;
}
//
u = 0.f;
// Accumulate texels.
uchar *lsrc = psrc;
float *paccu = accu;
float texel[4];
for (uint x = 0; x < nwidth; x++)
{
for (n = 0; n < 4; n++)
texel[n] = 0.f;
float tu = ku, nu = ku;
for (;;)
{
float k = GS::Math::Ceil(u) - u;
tu -= k;
if (tu < 0.f)
k += tu; // readjust
u += k;
if (u >= GetWidth())
{
nu -= k;
break;
}
for (n = 0; n < 4; n++)
texel[n] += (float)(lsrc[n]) * k;
if (tu < 0.f)
break;
lsrc += 4;
}
tu = k / nu;
for (n = 0; n < 4; n++)
paccu[n] += texel[n] * tu;
paccu += 4;
}
if (tv < 0.f)
break;
psrc += GetWidth() * 4;
}
tv = 1.f / nv;
for (n = 0; n < (nwidth * 4); n++)
pdst[n] = (uchar)(accu[n] * tv);
pdst += nwidth * 4;
}
SetData(new_data, nwidth, nheight, GetPixelFormat().GetDesc(), true);
return true;
}
}
return false;
}
//------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/
#include "picture/pict_tools.h"
#include "picture/pict.h"
using namespace GS;
namespace GS {
namespace PictureTools {
bool Compare(const Picture &a, const Picture &b, float threshold)
{
if ((a.GetWidth() != b.GetWidth()) || (a.GetHeight() != b.GetHeight()))
return false;
float dt = 0.f;
for (uint y = 0; y < a.GetHeight(); ++y)
for (uint x = 0; x < a.GetWidth(); ++x)
for (int c = 0; c < 4; ++c)
dt += (a.GetDataOffset(x, y)[c] - b.GetDataOffset(x, y)[c]) / 255.f;
return asbool(dt <= threshold);
}
} // PictureTools
} // GS
//------------------------------------------------------------------------------
bool Picture::Fill(float r, float g, float b, float a, const iRect *rect, bool lock_alpha)
{
uint *data = (uint *)GetData();
if (GetBpp() != 32)
return false;
if (!data)
return false;
// Convert color.
uint fill = GetPixelFormat().Format(r, g, b, a);
uchar ur = uchar(r * 255),
ug = uchar(g * 255),
ub = uchar(b * 255)/*,
ua = uchar(a * 255)*/;
if (rect)
{
Rect <int> _rect = rect->Intersection(GetRect());
if ((_rect.sy >= _rect.ey) || (_rect.sx >= _rect.ex))
return false;
data += _rect.sx + _rect.sy * width;
for (int y = 0; y < _rect.GetHeight(); ++y)
{
if (lock_alpha)
{
uchar *scan = (uchar *)data;
for (int x = 0; x < _rect.GetWidth(); ++x)
{
scan[GetPixelFormat().rshift >> 3] = ur;
scan[GetPixelFormat().gshift >> 3] = ug;
scan[GetPixelFormat().bshift >> 3] = ub;
// scan[GetPixelFormat().ashift >> 3] = ua;
scan += 4;
}
}
else
{
uint *scan = data;
for (int x = 0; x < _rect.GetWidth(); ++x)
*scan++ = fill;
}
data += width;
}
}
else
{
if (lock_alpha)
{
uchar *scan = (uchar *)data;
for (uint n = 0; n < GetWidth() * GetHeight(); n++)
{
scan[GetPixelFormat().rshift >> 3] = ur;
scan[GetPixelFormat().gshift >> 3] = ug;
scan[GetPixelFormat().bshift >> 3] = ub;
// scan[GetPixelFormat().ashift >> 3] = ua;
scan += 4;
}
}
else
for (uint n = 0; n < GetWidth() * GetHeight(); n++)
data[n] = fill;
}
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool Picture::ToGrayscale()
{
if (!(GetWidth() && GetHeight()))
return false;
if (GetBpp() != 32)
return false;
uchar *ptr = GetData();
for (uint y = 0; y < GetHeight(); y++)
for (uint x = 0; x < GetWidth(); x++)
{
int v = (ptr[0] + ptr[1] + ptr[2]) / 3;
ptr[0] = ptr[1] = ptr[2] = (uchar)v;
ptr += 4;
}
return true;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,206 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "picture/pict.h"
#if __PLATFORM_WINDOWS__ && __MMX__
#include "mmintrin.h"
#endif
using namespace GS;
//------------------------------------------------------------------------------
static void mmx_yuv2rgb (uchar *py, uchar *pu, uchar *pv, uchar *image)
{
#if __PLATFORM_WINDOWS__ && __MMX__
static __m64 mmx_80w = {0x0080008000800080LL};
static __m64 mmx_U_green = {0xf37df37df37df37dLL};
static __m64 mmx_U_blue = {0x4093409340934093LL};
static __m64 mmx_V_red = {0x3312331233123312LL};
static __m64 mmx_V_green = {0xe5fce5fce5fce5fcLL};
static __m64 mmx_10w = {0x1010101010101010LL};
static __m64 mmx_00ffw = {0x00ff00ff00ff00ffLL};
static __m64 mmx_Y_coeff = {0x253f253f253f253fLL};
static __m64 mmx_A_unpack = {0xffffffffffffffffLL};
__asm
{
push esi
pxor mm4, mm4 ; mm4 = 0
mov esi, pu
movd mm0, [esi] ; mm0 = 00 00 00 00 u3 u2 u1 u0
mov esi, pv
movd mm1, [esi] ; mm1 = 00 00 00 00 v3 v2 v1 v0
mov esi, py
movq mm6, [esi] ; mm6 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
; Multiply part of the conversion.
punpcklbw mm0, mm4 ; mm0 = u3 u2 u1 u0
punpcklbw mm1, mm4 ; mm1 = v3 v2 v1 v0
psubsw mm0, mmx_80w ; u -= 128
psubsw mm1, mmx_80w ; v -= 128
psllw mm0, 3 ; promote precision
psllw mm1, 3 ; promote precision
movq mm2, mm0 ; mm2 = u3 u2 u1 u0
movq mm3, mm1 ; mm3 = v3 v2 v1 v0
pmulhw mm2, mmx_U_green ; mm2 = u * u_green
pmulhw mm3, mmx_V_green ; mm3 = v * v_green
pmulhw mm0, mmx_U_blue ; mm0 = chroma_b
pmulhw mm1, mmx_V_red ; mm1 = chroma_r
paddsw mm2, mm3 ; mm2 = chroma_g
psubusb mm6, mmx_10w ; Y -= 16
movq mm7, mm6 ; mm7 = Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
pand mm6, mmx_00ffw ; mm6 = Y6 Y4 Y2 Y0
psrlw mm7, 8 ; mm7 = Y7 Y5 Y3 Y1
psllw mm6, 3 ; promote precision
psllw mm7, 3 ; promote precision
pmulhw mm6, mmx_Y_coeff ; mm6 = luma_rgb even
pmulhw mm7, mmx_Y_coeff ; mm7 = luma_rgb odd
; Addition part of the conversion for even and odd pixels.
movq mm3, mm0 ; mm3 = chroma_b
movq mm4, mm1 ; mm4 = chroma_r
movq mm5, mm2 ; mm5 = chroma_g
paddsw mm0, mm6 ; mm0 = B6 B4 B2 B0
paddsw mm3, mm7 ; mm3 = B7 B5 B3 B1
paddsw mm1, mm6 ; mm1 = R6 R4 R2 R0
paddsw mm4, mm7 ; mm4 = R7 R5 R3 R1
paddsw mm2, mm6 ; mm2 = G6 G4 G2 G0
paddsw mm5, mm7 ; mm5 = G7 G5 G3 G1
packuswb mm0, mm0 ; saturate to 0-255
packuswb mm1, mm1 ; saturate to 0-255
packuswb mm2, mm2 ; saturate to 0-255
packuswb mm3, mm3 ; saturate to 0-255
packuswb mm4, mm4 ; saturate to 0-255
packuswb mm5, mm5 ; saturate to 0-255
punpcklbw mm0, mm3 ; mm0 = B7 B6 B5 B4 B3 B2 B1 B0
punpcklbw mm1, mm4 ; mm1 = R7 R6 R5 R4 R3 R2 R1 R0
punpcklbw mm2, mm5 ; mm2 = G7 G6 G5 G4 G3 G2 G1 G0
mov esi, image
;pxor mm3, mm3
movq mm3, mmx_A_unpack
movq mm6, mm0
movq mm7, mm1
movq mm4, mm0
movq mm5, mm1
punpcklbw mm6, mm2
punpcklbw mm7, mm3
punpcklwd mm6, mm7
movq [esi], mm6
movq mm6, mm0
punpcklbw mm6, mm2
punpckhwd mm6, mm7
movq [esi + 8], mm6
punpckhbw mm4, mm2
punpckhbw mm5, mm3
punpcklwd mm4, mm5
movq [esi + 16], mm4
movq mm4, mm0
punpckhbw mm4, mm2
punpckhwd mm4, mm5
movq [esi + 24], mm4
pop esi
emms
}
#endif
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Picture::YUV422toRGB32(uchar * const yuv_plane[3], uchar *rgb32, int width, int height, int dst_pitch, int dst_height)
{
// Default pitch if none specified.
if (!dst_pitch)
dst_pitch = width * 4;
if (!dst_height)
dst_height = height;
// Target resolution.
int c_width = dst_pitch / 4;
if (width < c_width)
c_width = width;
if (height < dst_height)
dst_height = height;
#pragma omp parallel for
for (int y = 0; y < dst_height; ++y)
{
uchar *p_out = rgb32 + dst_pitch * y,
*p_yuv[3] = {
yuv_plane[0] + width * y,
yuv_plane[1] + (width / 2) * (y / 2),
yuv_plane[2] + (width / 2) * (y / 2)
};
// Convert scan line.
for (int x = 0; x < c_width; x += 8)
{
mmx_yuv2rgb(p_yuv[0], p_yuv[1], p_yuv[2], p_out);
p_yuv[0] += 8;
p_yuv[1] += 4;
p_yuv[2] += 4;
p_out += 32;
}
}
}
void Picture::UnpackYCbCr(uchar *y, uchar *cb, uchar *cr)
{
if (!y || !cb || !cr || (GetBpp() != 32))
return;
uchar *pdata = GetData();
for (uint n = 0; n < (GetHeight() * GetWidth()); ++n)
{
float r = (float)pdata[0], g = (float)pdata[1], b = (float)pdata[2];
*y++ = (uchar)( 0.2990f * r + 0.5870f * g + 0.1140f * b + 0.5f);
*cb++ = (uchar)(-0.1687f * r - 0.3313f * g + 0.5000f * b + 128.f + 0.5f);
*cr++ = (uchar)( 0.5000f * r - 0.4187f * g - 0.0813f * b + 128.f + 0.5f);
pdata += 4;
}
}
void Picture::PackYCbCr(uchar *y, uchar *cb, uchar *cr)
{
if (!y || !cb || !cr || (GetBpp() != 32))
return;
uchar *pdata = GetData();
for (uint n = 0; n < (GetHeight() * GetWidth()); ++n)
{
float _y = (float)*y++, _cb = ((float)*cb++) - 128.f, _cr = ((float)*cr++) - 128.f;
float r = _y + 1.402f * _cr;
float g = _y - 0.34414f * _cb - 0.71414f * _cr;
float b = _y + 1.77200f * _cb;
if (r < 0)
pdata[0] = 0;
else if (r > 255.f)
pdata[0] = 255;
else pdata[0] = (uchar)r;
if (g < 0)
pdata[1] = 0;
else if (g > 255.f)
pdata[1] = 255;
else pdata[1] = (uchar)g;
if (b < 0)
pdata[2] = 0;
else if (b > 255.f)
pdata[2] = 255;
else pdata[2] = (uchar)b;
pdata += 4;
}
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,31 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "plugin/shared_systems.h"
#include "picture/pict_io.h"
#include "audio/audio_io.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
void SharedSystems::Get()
{
platform = &Platform::Get();
log_system = &LogSystem::Get();
picture_io = &PictureIO::Get();
audio_io = &AudioIO::Get();
}
void SharedSystems::Set()
{
Platform::Set(platform);
LogSystem::Set(log_system);
PictureIO::Set(picture_io);
AudioIO::Set(audio_io);
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,7 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "sort/sort.h"

View File

@ -0,0 +1,39 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "timing/benchmark.h"
#include "sort/sort.h"
#include "platform.h"
using namespace GS;
//------------------------------------------------------------------------------
void Benchmark::Start()
{
r_clock = Platform::Get().GetTime();
}
void Benchmark::Stop()
{
Time c_clock = Platform::Get().GetTime();
t_clock += c_clock - r_clock;
r_clock = c_clock;
}
float Benchmark::GetMs() const
{ return avg.GetMedian(); }
void Benchmark::Reset()
{
avg.LogValue(t_clock.toMs());
t_clock.setSec(0);
}
Benchmark::Benchmark(bool start)
{
if (start)
Start();
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,53 @@
/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "timing/loop_benchmark.h"
#include "platform.h"
#include "log/log.h"
using namespace GS;
//------------------------------------------------------------------------------
void LoopBenchmark::MarkLoop()
{
++loop_count;
Time c_time = Platform::Get().GetTime();
t_time += c_time - r_time;
r_time = c_time;
float t_ms = t_time.toMs();
if (t_ms > 1000.f)
{
ms = t_ms / loop_count;
loop_count = 0;
t_time.setSec(0);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
float LoopBenchmark::GetMs() const
{ return ms; }
float LoopBenchmark::GetFps() const
{ return ms ? 1000.f / ms : 0.f; }
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void LoopBenchmark::Reset()
{
ms = 0.f;
loop_count = 0;
r_time = Platform::Get().GetTime();
t_time.setSec(0);
}
//------------------------------------------------------------------------------