initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
2599
thirdparty/misc/FastNoiseLite.h
vendored
Normal file
2599
thirdparty/misc/FastNoiseLite.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1345
thirdparty/misc/bcdec.h
vendored
Normal file
1345
thirdparty/misc/bcdec.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
28
thirdparty/misc/cubemap_coeffs.h
vendored
Normal file
28
thirdparty/misc/cubemap_coeffs.h
vendored
Normal file
File diff suppressed because one or more lines are too long
591
thirdparty/misc/fastlz.c
vendored
Normal file
591
thirdparty/misc/fastlz.c
vendored
Normal file
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
FastLZ - Byte-aligned LZ77 compression library
|
||||
Copyright (C) 2005-2020 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "fastlz.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Always check for bound when decompressing.
|
||||
* Generally it is best to leave it defined.
|
||||
*/
|
||||
#define FASTLZ_SAFE
|
||||
#if defined(FASTLZ_USE_SAFE_DECOMPRESSOR) && (FASTLZ_USE_SAFE_DECOMPRESSOR == 0)
|
||||
#undef FASTLZ_SAFE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Give hints to the compiler for branch prediction optimization.
|
||||
*/
|
||||
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 2))
|
||||
#define FASTLZ_LIKELY(c) (__builtin_expect(!!(c), 1))
|
||||
#define FASTLZ_UNLIKELY(c) (__builtin_expect(!!(c), 0))
|
||||
#else
|
||||
#define FASTLZ_LIKELY(c) (c)
|
||||
#define FASTLZ_UNLIKELY(c) (c)
|
||||
#endif
|
||||
|
||||
#if defined(FASTLZ_SAFE)
|
||||
#define FASTLZ_BOUND_CHECK(cond) \
|
||||
if (FASTLZ_UNLIKELY(!(cond))) return 0;
|
||||
#else
|
||||
#define FASTLZ_BOUND_CHECK(cond) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define MAX_COPY 32
|
||||
#define MAX_LEN 264 /* 256 + 8 */
|
||||
#define MAX_L1_DISTANCE 8192
|
||||
#define MAX_L2_DISTANCE 8191
|
||||
#define MAX_FARDISTANCE (65535 + MAX_L2_DISTANCE - 1)
|
||||
|
||||
#define FASTLZ_READU16(p) ((p)[0] | (p)[1] << 8)
|
||||
|
||||
#define HASH_LOG 13
|
||||
#define HASH_SIZE (1 << HASH_LOG)
|
||||
#define HASH_MASK (HASH_SIZE - 1)
|
||||
#define HASH_FUNCTION(v, p) \
|
||||
{ \
|
||||
v = FASTLZ_READU16(p); \
|
||||
v ^= FASTLZ_READU16(p + 1) ^ (v >> (16 - HASH_LOG)); \
|
||||
v &= HASH_MASK; \
|
||||
}
|
||||
|
||||
int fastlz1_compress(const void* input, int length, void* output) {
|
||||
const uint8_t* ip = (const uint8_t*)input;
|
||||
const uint8_t* ip_bound = ip + length - 2;
|
||||
const uint8_t* ip_limit = ip + length - 12 - 1;
|
||||
uint8_t* op = (uint8_t*)output;
|
||||
|
||||
const uint8_t* htab[HASH_SIZE];
|
||||
uint32_t hval;
|
||||
|
||||
uint32_t copy;
|
||||
|
||||
/* sanity check */
|
||||
if (FASTLZ_UNLIKELY(length < 4)) {
|
||||
if (length) {
|
||||
/* create literal copy only */
|
||||
*op++ = length - 1;
|
||||
ip_bound++;
|
||||
while (ip <= ip_bound) *op++ = *ip++;
|
||||
return length + 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initializes hash table */
|
||||
for (hval = 0; hval < HASH_SIZE; ++hval) htab[hval] = ip;
|
||||
|
||||
/* we start with literal copy */
|
||||
copy = 2;
|
||||
*op++ = MAX_COPY - 1;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
|
||||
/* main loop */
|
||||
while (FASTLZ_LIKELY(ip < ip_limit)) {
|
||||
const uint8_t* ref;
|
||||
uint32_t distance;
|
||||
|
||||
/* minimum match length */
|
||||
uint32_t len = 3;
|
||||
|
||||
/* comparison starting-point */
|
||||
const uint8_t* anchor = ip;
|
||||
|
||||
/* find potential match */
|
||||
HASH_FUNCTION(hval, ip);
|
||||
ref = htab[hval];
|
||||
|
||||
/* update hash table */
|
||||
htab[hval] = anchor;
|
||||
|
||||
/* calculate distance to the match */
|
||||
distance = anchor - ref;
|
||||
|
||||
/* is this a match? check the first 3 bytes */
|
||||
if (distance == 0 || (distance >= MAX_L1_DISTANCE) || *ref++ != *ip++ ||
|
||||
*ref++ != *ip++ || *ref++ != *ip++)
|
||||
goto literal;
|
||||
|
||||
/* last matched byte */
|
||||
ip = anchor + len;
|
||||
|
||||
/* distance is biased */
|
||||
distance--;
|
||||
|
||||
if (!distance) {
|
||||
/* zero distance means a run */
|
||||
uint8_t x = ip[-1];
|
||||
while (ip < ip_bound)
|
||||
if (*ref++ != x)
|
||||
break;
|
||||
else
|
||||
ip++;
|
||||
} else
|
||||
for (;;) {
|
||||
/* safe because the outer check against ip limit */
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
while (ip < ip_bound)
|
||||
if (*ref++ != *ip++) break;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy count */
|
||||
if (copy) /* copy is biased, '0' means 1 byte copy */
|
||||
*(op - copy - 1) = copy - 1;
|
||||
else
|
||||
/* back, to overwrite the copy count */
|
||||
op--;
|
||||
|
||||
/* reset literal counter */
|
||||
copy = 0;
|
||||
|
||||
/* length is biased, '1' means a match of 3 bytes */
|
||||
ip -= 3;
|
||||
len = ip - anchor;
|
||||
|
||||
/* encode the match */
|
||||
if (FASTLZ_UNLIKELY(len > MAX_LEN - 2))
|
||||
while (len > MAX_LEN - 2) {
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = MAX_LEN - 2 - 7 - 2;
|
||||
*op++ = (distance & 255);
|
||||
len -= MAX_LEN - 2;
|
||||
}
|
||||
|
||||
if (len < 7) {
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
} else {
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = len - 7;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
|
||||
/* update the hash at match boundary */
|
||||
HASH_FUNCTION(hval, ip);
|
||||
htab[hval] = ip++;
|
||||
HASH_FUNCTION(hval, ip);
|
||||
htab[hval] = ip++;
|
||||
|
||||
/* assuming literal copy */
|
||||
*op++ = MAX_COPY - 1;
|
||||
|
||||
continue;
|
||||
|
||||
literal:
|
||||
*op++ = *anchor++;
|
||||
ip = anchor;
|
||||
copy++;
|
||||
if (FASTLZ_UNLIKELY(copy == MAX_COPY)) {
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* left-over as literal copy */
|
||||
ip_bound++;
|
||||
while (ip <= ip_bound) {
|
||||
*op++ = *ip++;
|
||||
copy++;
|
||||
if (copy == MAX_COPY) {
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy length */
|
||||
if (copy)
|
||||
*(op - copy - 1) = copy - 1;
|
||||
else
|
||||
op--;
|
||||
|
||||
return op - (uint8_t*)output;
|
||||
}
|
||||
|
||||
#if defined(FASTLZ_USE_MEMMOVE) && (FASTLZ_USE_MEMMOVE == 0)
|
||||
|
||||
static void fastlz_memmove(uint8_t* dest, const uint8_t* src, uint32_t count) {
|
||||
do {
|
||||
*dest++ = *src++;
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
static void fastlz_memcpy(uint8_t* dest, const uint8_t* src, uint32_t count) {
|
||||
return fastlz_memmove(dest, src, count);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void fastlz_memmove(uint8_t* dest, const uint8_t* src, uint32_t count) {
|
||||
if ((count > 4) && (dest >= src + count)) {
|
||||
memmove(dest, src, count);
|
||||
} else {
|
||||
switch (count) {
|
||||
default:
|
||||
do {
|
||||
*dest++ = *src++;
|
||||
} while (--count);
|
||||
break;
|
||||
case 3:
|
||||
*dest++ = *src++;
|
||||
case 2:
|
||||
*dest++ = *src++;
|
||||
case 1:
|
||||
*dest++ = *src++;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fastlz_memcpy(uint8_t* dest, const uint8_t* src, uint32_t count) {
|
||||
memcpy(dest, src, count);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int fastlz1_decompress(const void* input, int length, void* output,
|
||||
int maxout) {
|
||||
const uint8_t* ip = (const uint8_t*)input;
|
||||
const uint8_t* ip_limit = ip + length;
|
||||
const uint8_t* ip_bound = ip_limit - 2;
|
||||
uint8_t* op = (uint8_t*)output;
|
||||
uint8_t* op_limit = op + maxout;
|
||||
uint32_t ctrl = (*ip++) & 31;
|
||||
|
||||
while (1) {
|
||||
if (ctrl >= 32) {
|
||||
uint32_t len = (ctrl >> 5) - 1;
|
||||
uint32_t ofs = (ctrl & 31) << 8;
|
||||
const uint8_t* ref = op - ofs - 1;
|
||||
if (len == 7 - 1) {
|
||||
FASTLZ_BOUND_CHECK(ip <= ip_bound);
|
||||
len += *ip++;
|
||||
}
|
||||
ref -= *ip++;
|
||||
len += 3;
|
||||
FASTLZ_BOUND_CHECK(op + len <= op_limit);
|
||||
FASTLZ_BOUND_CHECK(ref >= (uint8_t*)output);
|
||||
fastlz_memmove(op, ref, len);
|
||||
op += len;
|
||||
} else {
|
||||
ctrl++;
|
||||
FASTLZ_BOUND_CHECK(op + ctrl <= op_limit);
|
||||
FASTLZ_BOUND_CHECK(ip + ctrl <= ip_limit);
|
||||
fastlz_memcpy(op, ip, ctrl);
|
||||
ip += ctrl;
|
||||
op += ctrl;
|
||||
}
|
||||
|
||||
if (FASTLZ_UNLIKELY(ip > ip_bound)) break;
|
||||
ctrl = *ip++;
|
||||
}
|
||||
|
||||
return op - (uint8_t*)output;
|
||||
}
|
||||
|
||||
int fastlz2_compress(const void* input, int length, void* output) {
|
||||
const uint8_t* ip = (const uint8_t*)input;
|
||||
const uint8_t* ip_bound = ip + length - 2;
|
||||
const uint8_t* ip_limit = ip + length - 12 - 1;
|
||||
uint8_t* op = (uint8_t*)output;
|
||||
|
||||
const uint8_t* htab[HASH_SIZE];
|
||||
uint32_t hval;
|
||||
|
||||
uint32_t copy;
|
||||
|
||||
/* sanity check */
|
||||
if (FASTLZ_UNLIKELY(length < 4)) {
|
||||
if (length) {
|
||||
/* create literal copy only */
|
||||
*op++ = length - 1;
|
||||
ip_bound++;
|
||||
while (ip <= ip_bound) *op++ = *ip++;
|
||||
return length + 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initializes hash table */
|
||||
for (hval = 0; hval < HASH_SIZE; ++hval) htab[hval] = ip;
|
||||
|
||||
/* we start with literal copy */
|
||||
copy = 2;
|
||||
*op++ = MAX_COPY - 1;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
|
||||
/* main loop */
|
||||
while (FASTLZ_LIKELY(ip < ip_limit)) {
|
||||
const uint8_t* ref;
|
||||
uint32_t distance;
|
||||
|
||||
/* minimum match length */
|
||||
uint32_t len = 3;
|
||||
|
||||
/* comparison starting-point */
|
||||
const uint8_t* anchor = ip;
|
||||
|
||||
/* check for a run */
|
||||
if (ip[0] == ip[-1] && ip[0] == ip[1] && ip[1] == ip[2]) {
|
||||
distance = 1;
|
||||
ip += 3;
|
||||
ref = anchor - 1 + 3;
|
||||
goto match;
|
||||
}
|
||||
|
||||
/* find potential match */
|
||||
HASH_FUNCTION(hval, ip);
|
||||
ref = htab[hval];
|
||||
|
||||
/* update hash table */
|
||||
htab[hval] = anchor;
|
||||
|
||||
/* calculate distance to the match */
|
||||
distance = anchor - ref;
|
||||
|
||||
/* is this a match? check the first 3 bytes */
|
||||
if (distance == 0 || (distance >= MAX_FARDISTANCE) || *ref++ != *ip++ ||
|
||||
*ref++ != *ip++ || *ref++ != *ip++)
|
||||
goto literal;
|
||||
|
||||
/* far, needs at least 5-byte match */
|
||||
if (distance >= MAX_L2_DISTANCE) {
|
||||
if (*ip++ != *ref++ || *ip++ != *ref++) goto literal;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
match:
|
||||
|
||||
/* last matched byte */
|
||||
ip = anchor + len;
|
||||
|
||||
/* distance is biased */
|
||||
distance--;
|
||||
|
||||
if (!distance) {
|
||||
/* zero distance means a run */
|
||||
uint8_t x = ip[-1];
|
||||
while (ip < ip_bound)
|
||||
if (*ref++ != x)
|
||||
break;
|
||||
else
|
||||
ip++;
|
||||
} else
|
||||
for (;;) {
|
||||
/* safe because the outer check against ip limit */
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
if (*ref++ != *ip++) break;
|
||||
while (ip < ip_bound)
|
||||
if (*ref++ != *ip++) break;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy count */
|
||||
if (copy) /* copy is biased, '0' means 1 byte copy */
|
||||
*(op - copy - 1) = copy - 1;
|
||||
else
|
||||
/* back, to overwrite the copy count */
|
||||
op--;
|
||||
|
||||
/* reset literal counter */
|
||||
copy = 0;
|
||||
|
||||
/* length is biased, '1' means a match of 3 bytes */
|
||||
ip -= 3;
|
||||
len = ip - anchor;
|
||||
|
||||
/* encode the match */
|
||||
if (distance < MAX_L2_DISTANCE) {
|
||||
if (len < 7) {
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
} else {
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
for (len -= 7; len >= 255; len -= 255) *op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
} else {
|
||||
/* far away, but not yet in the another galaxy... */
|
||||
if (len < 7) {
|
||||
distance -= MAX_L2_DISTANCE;
|
||||
*op++ = (len << 5) + 31;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
} else {
|
||||
distance -= MAX_L2_DISTANCE;
|
||||
*op++ = (7 << 5) + 31;
|
||||
for (len -= 7; len >= 255; len -= 255) *op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
}
|
||||
}
|
||||
|
||||
/* update the hash at match boundary */
|
||||
HASH_FUNCTION(hval, ip);
|
||||
htab[hval] = ip++;
|
||||
HASH_FUNCTION(hval, ip);
|
||||
htab[hval] = ip++;
|
||||
|
||||
/* assuming literal copy */
|
||||
*op++ = MAX_COPY - 1;
|
||||
|
||||
continue;
|
||||
|
||||
literal:
|
||||
*op++ = *anchor++;
|
||||
ip = anchor;
|
||||
copy++;
|
||||
if (FASTLZ_UNLIKELY(copy == MAX_COPY)) {
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* left-over as literal copy */
|
||||
ip_bound++;
|
||||
while (ip <= ip_bound) {
|
||||
*op++ = *ip++;
|
||||
copy++;
|
||||
if (copy == MAX_COPY) {
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy length */
|
||||
if (copy)
|
||||
*(op - copy - 1) = copy - 1;
|
||||
else
|
||||
op--;
|
||||
|
||||
/* marker for fastlz2 */
|
||||
*(uint8_t*)output |= (1 << 5);
|
||||
|
||||
return op - (uint8_t*)output;
|
||||
}
|
||||
|
||||
int fastlz2_decompress(const void* input, int length, void* output,
|
||||
int maxout) {
|
||||
const uint8_t* ip = (const uint8_t*)input;
|
||||
const uint8_t* ip_limit = ip + length;
|
||||
const uint8_t* ip_bound = ip_limit - 2;
|
||||
uint8_t* op = (uint8_t*)output;
|
||||
uint8_t* op_limit = op + maxout;
|
||||
uint32_t ctrl = (*ip++) & 31;
|
||||
|
||||
while (1) {
|
||||
if (ctrl >= 32) {
|
||||
uint32_t len = (ctrl >> 5) - 1;
|
||||
uint32_t ofs = (ctrl & 31) << 8;
|
||||
const uint8_t* ref = op - ofs - 1;
|
||||
|
||||
uint8_t code;
|
||||
if (len == 7 - 1) do {
|
||||
FASTLZ_BOUND_CHECK(ip <= ip_bound);
|
||||
code = *ip++;
|
||||
len += code;
|
||||
} while (code == 255);
|
||||
code = *ip++;
|
||||
ref -= code;
|
||||
len += 3;
|
||||
|
||||
/* match from 16-bit distance */
|
||||
if (FASTLZ_UNLIKELY(code == 255))
|
||||
if (FASTLZ_LIKELY(ofs == (31 << 8))) {
|
||||
FASTLZ_BOUND_CHECK(ip < ip_bound);
|
||||
ofs = (*ip++) << 8;
|
||||
ofs += *ip++;
|
||||
ref = op - ofs - MAX_L2_DISTANCE - 1;
|
||||
}
|
||||
|
||||
FASTLZ_BOUND_CHECK(op + len <= op_limit);
|
||||
FASTLZ_BOUND_CHECK(ref >= (uint8_t*)output);
|
||||
fastlz_memmove(op, ref, len);
|
||||
op += len;
|
||||
} else {
|
||||
ctrl++;
|
||||
FASTLZ_BOUND_CHECK(op + ctrl <= op_limit);
|
||||
FASTLZ_BOUND_CHECK(ip + ctrl <= ip_limit);
|
||||
fastlz_memcpy(op, ip, ctrl);
|
||||
ip += ctrl;
|
||||
op += ctrl;
|
||||
}
|
||||
|
||||
if (FASTLZ_UNLIKELY(ip >= ip_limit)) break;
|
||||
ctrl = *ip++;
|
||||
}
|
||||
|
||||
return op - (uint8_t*)output;
|
||||
}
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output) {
|
||||
/* for short block, choose fastlz1 */
|
||||
if (length < 65536) return fastlz1_compress(input, length, output);
|
||||
|
||||
/* else... */
|
||||
return fastlz2_compress(input, length, output);
|
||||
}
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout) {
|
||||
/* magic identifier for compression level */
|
||||
int level = ((*(const uint8_t*)input) >> 5) + 1;
|
||||
|
||||
if (level == 1) return fastlz1_decompress(input, length, output, maxout);
|
||||
if (level == 2) return fastlz2_decompress(input, length, output, maxout);
|
||||
|
||||
/* unknown level, trigger error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length,
|
||||
void* output) {
|
||||
if (level == 1) return fastlz1_compress(input, length, output);
|
||||
if (level == 2) return fastlz2_compress(input, length, output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
98
thirdparty/misc/fastlz.h
vendored
Normal file
98
thirdparty/misc/fastlz.h
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
FastLZ - Byte-aligned LZ77 compression library
|
||||
Copyright (C) 2005-2020 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FASTLZ_H
|
||||
#define FASTLZ_H
|
||||
|
||||
#define FASTLZ_VERSION 0x000500
|
||||
|
||||
#define FASTLZ_VERSION_MAJOR 0
|
||||
#define FASTLZ_VERSION_MINOR 5
|
||||
#define FASTLZ_VERSION_REVISION 0
|
||||
|
||||
#define FASTLZ_VERSION_STRING "0.5.0"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Compression level can be specified in parameter level. At the moment,
|
||||
only level 1 and level 2 are supported.
|
||||
Level 1 is the fastest compression and generally useful for short data.
|
||||
Level 2 is slightly slower but it gives better compression ratio.
|
||||
|
||||
Note that the compressed data, regardless of the level, can always be
|
||||
decompressed using the function fastlz_decompress below.
|
||||
*/
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length,
|
||||
void* output);
|
||||
|
||||
/**
|
||||
Decompress a block of compressed data and returns the size of the
|
||||
decompressed block. If error occurs, e.g. the compressed data is
|
||||
corrupted or the output buffer is not large enough, then 0 (zero)
|
||||
will be returned instead.
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Decompression is memory safe and guaranteed not to write the output buffer
|
||||
more than what is specified in maxout.
|
||||
|
||||
Note that the decompression will always work, regardless of the
|
||||
compression level specified in fastlz_compress_level above (when
|
||||
producing the compressed block).
|
||||
*/
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||
|
||||
/**
|
||||
DEPRECATED.
|
||||
|
||||
This is similar to fastlz_compress_level above, but with the level
|
||||
automatically chosen.
|
||||
|
||||
This function is deprecated and it will be completely removed in some future
|
||||
version.
|
||||
*/
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FASTLZ_H */
|
||||
221
thirdparty/misc/ifaddrs-android.cc
vendored
Normal file
221
thirdparty/misc/ifaddrs-android.cc
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2012, Google Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "ifaddrs-android.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
struct netlinkrequest {
|
||||
nlmsghdr header;
|
||||
ifaddrmsg msg;
|
||||
};
|
||||
namespace {
|
||||
const int kMaxReadSize = 4096;
|
||||
};
|
||||
int set_ifname(struct ifaddrs* ifaddr, int interface) {
|
||||
char buf[IFNAMSIZ] = {0};
|
||||
char* name = if_indextoname(interface, buf);
|
||||
if (name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
ifaddr->ifa_name = new char[strlen(name) + 1];
|
||||
strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
|
||||
return 0;
|
||||
}
|
||||
int set_flags(struct ifaddrs* ifaddr) {
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1);
|
||||
int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
|
||||
close(fd);
|
||||
if (rc == -1) {
|
||||
return -1;
|
||||
}
|
||||
ifaddr->ifa_flags = ifr.ifr_flags;
|
||||
return 0;
|
||||
}
|
||||
int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data,
|
||||
size_t len) {
|
||||
if (msg->ifa_family == AF_INET) {
|
||||
sockaddr_in* sa = new sockaddr_in;
|
||||
sa->sin_family = AF_INET;
|
||||
memcpy(&sa->sin_addr, data, len);
|
||||
ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
|
||||
} else if (msg->ifa_family == AF_INET6) {
|
||||
sockaddr_in6* sa = new sockaddr_in6;
|
||||
sa->sin6_family = AF_INET6;
|
||||
sa->sin6_scope_id = msg->ifa_index;
|
||||
memcpy(&sa->sin6_addr, data, len);
|
||||
ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) {
|
||||
char* prefix = NULL;
|
||||
if (family == AF_INET) {
|
||||
sockaddr_in* mask = new sockaddr_in;
|
||||
mask->sin_family = AF_INET;
|
||||
memset(&mask->sin_addr, 0, sizeof(in_addr));
|
||||
ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
|
||||
if (prefixlen > 32) {
|
||||
prefixlen = 32;
|
||||
}
|
||||
prefix = reinterpret_cast<char*>(&mask->sin_addr);
|
||||
} else if (family == AF_INET6) {
|
||||
sockaddr_in6* mask = new sockaddr_in6;
|
||||
mask->sin6_family = AF_INET6;
|
||||
memset(&mask->sin6_addr, 0, sizeof(in6_addr));
|
||||
ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
|
||||
if (prefixlen > 128) {
|
||||
prefixlen = 128;
|
||||
}
|
||||
prefix = reinterpret_cast<char*>(&mask->sin6_addr);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < (prefixlen / 8); i++) {
|
||||
*prefix++ = 0xFF;
|
||||
}
|
||||
char remainder = 0xff;
|
||||
remainder <<= (8 - prefixlen % 8);
|
||||
*prefix = remainder;
|
||||
return 0;
|
||||
}
|
||||
int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes,
|
||||
size_t len) {
|
||||
if (set_ifname(ifaddr, msg->ifa_index) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (set_flags(ifaddr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (set_addresses(ifaddr, msg, bytes, len) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int getifaddrs(struct ifaddrs** result) {
|
||||
int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
netlinkrequest ifaddr_request;
|
||||
memset(&ifaddr_request, 0, sizeof(ifaddr_request));
|
||||
ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
|
||||
ifaddr_request.header.nlmsg_type = RTM_GETADDR;
|
||||
ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg));
|
||||
ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
|
||||
if (static_cast<size_t>(count) != ifaddr_request.header.nlmsg_len) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
struct ifaddrs* start = NULL;
|
||||
struct ifaddrs* current = NULL;
|
||||
char buf[kMaxReadSize];
|
||||
ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
|
||||
while (amount_read > 0) {
|
||||
nlmsghdr* header = reinterpret_cast<nlmsghdr*>(&buf[0]);
|
||||
size_t header_size = static_cast<size_t>(amount_read);
|
||||
for ( ; NLMSG_OK(header, header_size);
|
||||
header = NLMSG_NEXT(header, header_size)) {
|
||||
switch (header->nlmsg_type) {
|
||||
case NLMSG_DONE:
|
||||
// Success. Return.
|
||||
*result = start;
|
||||
close(fd);
|
||||
return 0;
|
||||
case NLMSG_ERROR:
|
||||
close(fd);
|
||||
freeifaddrs(start);
|
||||
return -1;
|
||||
case RTM_NEWADDR: {
|
||||
ifaddrmsg* address_msg =
|
||||
reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(header));
|
||||
rtattr* rta = IFA_RTA(address_msg);
|
||||
ssize_t payload_len = IFA_PAYLOAD(header);
|
||||
while (RTA_OK(rta, payload_len)) {
|
||||
if (rta->rta_type == IFA_ADDRESS) {
|
||||
int family = address_msg->ifa_family;
|
||||
if (family == AF_INET || family == AF_INET6) {
|
||||
ifaddrs* newest = new ifaddrs;
|
||||
memset(newest, 0, sizeof(ifaddrs));
|
||||
if (current) {
|
||||
current->ifa_next = newest;
|
||||
} else {
|
||||
start = newest;
|
||||
}
|
||||
if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
|
||||
RTA_PAYLOAD(rta)) != 0) {
|
||||
freeifaddrs(start);
|
||||
*result = NULL;
|
||||
return -1;
|
||||
}
|
||||
current = newest;
|
||||
}
|
||||
}
|
||||
rta = RTA_NEXT(rta, payload_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
amount_read = recv(fd, &buf, kMaxReadSize, 0);
|
||||
}
|
||||
close(fd);
|
||||
freeifaddrs(start);
|
||||
return -1;
|
||||
}
|
||||
void freeifaddrs(struct ifaddrs* addrs) {
|
||||
struct ifaddrs* last = NULL;
|
||||
struct ifaddrs* cursor = addrs;
|
||||
while (cursor) {
|
||||
delete[] cursor->ifa_name;
|
||||
delete cursor->ifa_addr;
|
||||
delete cursor->ifa_netmask;
|
||||
last = cursor;
|
||||
cursor = cursor->ifa_next;
|
||||
delete last;
|
||||
}
|
||||
}
|
||||
63
thirdparty/misc/ifaddrs-android.h
vendored
Normal file
63
thirdparty/misc/ifaddrs-android.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* libjingle
|
||||
* Copyright 2013, Google Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TALK_BASE_IFADDRS_ANDROID_H_
|
||||
#define TALK_BASE_IFADDRS_ANDROID_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
// Implementation of getifaddrs for Android.
|
||||
// Fills out a list of ifaddr structs (see below) which contain information
|
||||
// about every network interface available on the host.
|
||||
// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function).
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct ifaddrs {
|
||||
struct ifaddrs* ifa_next;
|
||||
char* ifa_name;
|
||||
unsigned int ifa_flags;
|
||||
struct sockaddr* ifa_addr;
|
||||
struct sockaddr* ifa_netmask;
|
||||
// Real ifaddrs has broadcast, point to point and data members.
|
||||
// We don't need them (yet?).
|
||||
// We never initialize the following members. We only define them to match the ifaddrs struct.
|
||||
union
|
||||
{
|
||||
struct sockaddr *ifu_broadaddr;
|
||||
struct sockaddr *ifu_dstaddr;
|
||||
} ifa_ifu;
|
||||
void *ifa_data;
|
||||
};
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
int getifaddrs(struct ifaddrs** result);
|
||||
void freeifaddrs(struct ifaddrs* addrs);
|
||||
|
||||
#endif // TALK_BASE_IFADDRS_ANDROID_H_
|
||||
1890
thirdparty/misc/mikktspace.c
vendored
Normal file
1890
thirdparty/misc/mikktspace.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
145
thirdparty/misc/mikktspace.h
vendored
Normal file
145
thirdparty/misc/mikktspace.h
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
/** \file mikktspace/mikktspace.h
|
||||
* \ingroup mikktspace
|
||||
*/
|
||||
/**
|
||||
* Copyright (C) 2011 by Morten S. Mikkelsen
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef __MIKKTSPACE_H__
|
||||
#define __MIKKTSPACE_H__
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Author: Morten S. Mikkelsen
|
||||
* Version: 1.0
|
||||
*
|
||||
* The files mikktspace.h and mikktspace.c are designed to be
|
||||
* stand-alone files and it is important that they are kept this way.
|
||||
* Not having dependencies on structures/classes/libraries specific
|
||||
* to the program, in which they are used, allows them to be copied
|
||||
* and used as is into any tool, program or plugin.
|
||||
* The code is designed to consistently generate the same
|
||||
* tangent spaces, for a given mesh, in any tool in which it is used.
|
||||
* This is done by performing an internal welding step and subsequently an order-independent evaluation
|
||||
* of tangent space for meshes consisting of triangles and quads.
|
||||
* This means faces can be received in any order and the same is true for
|
||||
* the order of vertices of each face. The generated result will not be affected
|
||||
* by such reordering. Additionally, whether degenerate (vertices or texture coordinates)
|
||||
* primitives are present or not will not affect the generated results either.
|
||||
* Once tangent space calculation is done the vertices of degenerate primitives will simply
|
||||
* inherit tangent space from neighboring non degenerate primitives.
|
||||
* The analysis behind this implementation can be found in my master's thesis
|
||||
* which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
|
||||
* Note that though the tangent spaces at the vertices are generated in an order-independent way,
|
||||
* by this implementation, the interpolated tangent space is still affected by which diagonal is
|
||||
* chosen to split each quad. A sensible solution is to have your tools pipeline always
|
||||
* split quads by the shortest diagonal. This choice is order-independent and works with mirroring.
|
||||
* If these have the same length then compare the diagonals defined by the texture coordinates.
|
||||
* XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin
|
||||
* and also quad triangulator plugin.
|
||||
*/
|
||||
|
||||
|
||||
typedef int tbool;
|
||||
typedef struct SMikkTSpaceContext SMikkTSpaceContext;
|
||||
|
||||
typedef struct {
|
||||
// Returns the number of faces (triangles/quads) on the mesh to be processed.
|
||||
int (*m_getNumFaces)(const SMikkTSpaceContext * pContext);
|
||||
|
||||
// Returns the number of vertices on face number iFace
|
||||
// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
|
||||
int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace);
|
||||
|
||||
// returns the position/normal/texcoord of the referenced face of vertex number iVert.
|
||||
// iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
|
||||
void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert);
|
||||
void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert);
|
||||
void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert);
|
||||
|
||||
// either (or both) of the two setTSpace callbacks can be set.
|
||||
// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
|
||||
|
||||
// This function is used to return the tangent and fSign to the application.
|
||||
// fvTangent is a unit length vector.
|
||||
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
|
||||
// bitangent = fSign * cross(vN, tangent);
|
||||
// Note that the results are returned unindexed. It is possible to generate a new index list
|
||||
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
|
||||
// DO NOT! use an already existing index list.
|
||||
void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
|
||||
|
||||
// This function is used to return tangent space results to the application.
|
||||
// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
|
||||
// true magnitudes which can be used for relief mapping effects.
|
||||
// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
|
||||
// However, both are perpendicular to the vertex normal.
|
||||
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
|
||||
// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
|
||||
// bitangent = fSign * cross(vN, tangent);
|
||||
// Note that the results are returned unindexed. It is possible to generate a new index list
|
||||
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
|
||||
// DO NOT! use an already existing index list.
|
||||
void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
|
||||
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
|
||||
} SMikkTSpaceInterface;
|
||||
|
||||
struct SMikkTSpaceContext
|
||||
{
|
||||
SMikkTSpaceInterface * m_pInterface; // initialized with callback functions
|
||||
void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call)
|
||||
};
|
||||
|
||||
// these are both thread safe!
|
||||
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
|
||||
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold);
|
||||
|
||||
|
||||
// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
|
||||
// normal map sampler must use the exact inverse of the pixel shader transformation.
|
||||
// The most efficient transformation we can possibly do in the pixel shader is
|
||||
// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
|
||||
// pixel shader (fast transform out)
|
||||
// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
|
||||
// where vNt is the tangent space normal. The normal map sampler must likewise use the
|
||||
// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
|
||||
// sampler does (exact inverse of pixel shader):
|
||||
// float3 row0 = cross(vB, vN);
|
||||
// float3 row1 = cross(vN, vT);
|
||||
// float3 row2 = cross(vT, vB);
|
||||
// float fSign = dot(vT, row0)<0 ? -1 : 1;
|
||||
// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
|
||||
// where vNout is the sampled normal in some chosen 3D space.
|
||||
//
|
||||
// Should you choose to reconstruct the bitangent in the pixel shader instead
|
||||
// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
|
||||
// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
|
||||
// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
|
||||
// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
|
||||
// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
|
||||
// However, this must be used both by the sampler and your tools/rendering pipeline.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
175
thirdparty/misc/nvapi_minimal.h
vendored
Normal file
175
thirdparty/misc/nvapi_minimal.h
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
#ifndef NVAPI_MINIMAL_H
|
||||
#define NVAPI_MINIMAL_H
|
||||
typedef uint32_t NvU32;
|
||||
typedef uint16_t NvU16;
|
||||
typedef uint8_t NvU8;
|
||||
|
||||
#define MAKE_NVAPI_VERSION(typeName,ver) (NvU32)(sizeof(typeName) | ((ver)<<16))
|
||||
|
||||
#define NV_DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
|
||||
|
||||
NV_DECLARE_HANDLE(NvDRSSessionHandle);
|
||||
NV_DECLARE_HANDLE(NvDRSProfileHandle);
|
||||
|
||||
#define NVAPI_UNICODE_STRING_MAX 2048
|
||||
#define NVAPI_BINARY_DATA_MAX 4096
|
||||
typedef NvU16 NvAPI_UnicodeString[NVAPI_UNICODE_STRING_MAX];
|
||||
typedef char NvAPI_ShortString[64];
|
||||
|
||||
#define NVAPI_SETTING_MAX_VALUES 100
|
||||
|
||||
typedef enum _NVDRS_SETTING_TYPE
|
||||
{
|
||||
NVDRS_DWORD_TYPE,
|
||||
NVDRS_BINARY_TYPE,
|
||||
NVDRS_STRING_TYPE,
|
||||
NVDRS_WSTRING_TYPE
|
||||
} NVDRS_SETTING_TYPE;
|
||||
|
||||
typedef enum _NVDRS_SETTING_LOCATION
|
||||
{
|
||||
NVDRS_CURRENT_PROFILE_LOCATION,
|
||||
NVDRS_GLOBAL_PROFILE_LOCATION,
|
||||
NVDRS_BASE_PROFILE_LOCATION,
|
||||
NVDRS_DEFAULT_PROFILE_LOCATION
|
||||
} NVDRS_SETTING_LOCATION;
|
||||
|
||||
typedef struct _NVDRS_GPU_SUPPORT
|
||||
{
|
||||
NvU32 geforce : 1;
|
||||
NvU32 quadro : 1;
|
||||
NvU32 nvs : 1;
|
||||
NvU32 reserved4 : 1;
|
||||
NvU32 reserved5 : 1;
|
||||
NvU32 reserved6 : 1;
|
||||
NvU32 reserved7 : 1;
|
||||
NvU32 reserved8 : 1;
|
||||
NvU32 reserved9 : 1;
|
||||
NvU32 reserved10 : 1;
|
||||
NvU32 reserved11 : 1;
|
||||
NvU32 reserved12 : 1;
|
||||
NvU32 reserved13 : 1;
|
||||
NvU32 reserved14 : 1;
|
||||
NvU32 reserved15 : 1;
|
||||
NvU32 reserved16 : 1;
|
||||
NvU32 reserved17 : 1;
|
||||
NvU32 reserved18 : 1;
|
||||
NvU32 reserved19 : 1;
|
||||
NvU32 reserved20 : 1;
|
||||
NvU32 reserved21 : 1;
|
||||
NvU32 reserved22 : 1;
|
||||
NvU32 reserved23 : 1;
|
||||
NvU32 reserved24 : 1;
|
||||
NvU32 reserved25 : 1;
|
||||
NvU32 reserved26 : 1;
|
||||
NvU32 reserved27 : 1;
|
||||
NvU32 reserved28 : 1;
|
||||
NvU32 reserved29 : 1;
|
||||
NvU32 reserved30 : 1;
|
||||
NvU32 reserved31 : 1;
|
||||
NvU32 reserved32 : 1;
|
||||
} NVDRS_GPU_SUPPORT;
|
||||
|
||||
//! Enum to decide on the datatype of setting value.
|
||||
typedef struct _NVDRS_BINARY_SETTING
|
||||
{
|
||||
NvU32 valueLength; //!< valueLength should always be in number of bytes.
|
||||
NvU8 valueData[NVAPI_BINARY_DATA_MAX];
|
||||
} NVDRS_BINARY_SETTING;
|
||||
|
||||
typedef struct _NVDRS_SETTING_VALUES
|
||||
{
|
||||
NvU32 version; //!< Structure Version
|
||||
NvU32 numSettingValues; //!< Total number of values available in a setting.
|
||||
NVDRS_SETTING_TYPE settingType; //!< Type of setting value.
|
||||
union //!< Setting can hold either DWORD or Binary value or string. Not mixed types.
|
||||
{
|
||||
NvU32 u32DefaultValue; //!< Accessing default DWORD value of this setting.
|
||||
NVDRS_BINARY_SETTING binaryDefaultValue; //!< Accessing default Binary value of this setting.
|
||||
//!< Must be allocated by caller with valueLength specifying buffer size, or only valueLength will be filled in.
|
||||
NvAPI_UnicodeString wszDefaultValue; //!< Accessing default unicode string value of this setting.
|
||||
};
|
||||
union //!< Setting values can be of either DWORD, Binary values or String type,
|
||||
{ //!< NOT mixed types.
|
||||
NvU32 u32Value; //!< All possible DWORD values for a setting
|
||||
NVDRS_BINARY_SETTING binaryValue; //!< All possible Binary values for a setting
|
||||
NvAPI_UnicodeString wszValue; //!< Accessing current unicode string value of this setting.
|
||||
}settingValues[NVAPI_SETTING_MAX_VALUES];
|
||||
} NVDRS_SETTING_VALUES;
|
||||
|
||||
//! Macro for constructing the version field of ::_NVDRS_SETTING_VALUES
|
||||
#define NVDRS_SETTING_VALUES_VER MAKE_NVAPI_VERSION(NVDRS_SETTING_VALUES,1)
|
||||
|
||||
typedef struct _NVDRS_SETTING_V1
|
||||
{
|
||||
NvU32 version; //!< Structure Version
|
||||
NvAPI_UnicodeString settingName; //!< String name of setting
|
||||
NvU32 settingId; //!< 32 bit setting Id
|
||||
NVDRS_SETTING_TYPE settingType; //!< Type of setting value.
|
||||
NVDRS_SETTING_LOCATION settingLocation; //!< Describes where the value in CurrentValue comes from.
|
||||
NvU32 isCurrentPredefined; //!< It is different than 0 if the currentValue is a predefined Value,
|
||||
//!< 0 if the currentValue is a user value.
|
||||
NvU32 isPredefinedValid; //!< It is different than 0 if the PredefinedValue union contains a valid value.
|
||||
union //!< Setting can hold either DWORD or Binary value or string. Not mixed types.
|
||||
{
|
||||
NvU32 u32PredefinedValue; //!< Accessing default DWORD value of this setting.
|
||||
NVDRS_BINARY_SETTING binaryPredefinedValue; //!< Accessing default Binary value of this setting.
|
||||
//!< Must be allocated by caller with valueLength specifying buffer size,
|
||||
//!< or only valueLength will be filled in.
|
||||
NvAPI_UnicodeString wszPredefinedValue; //!< Accessing default unicode string value of this setting.
|
||||
};
|
||||
union //!< Setting can hold either DWORD or Binary value or string. Not mixed types.
|
||||
{
|
||||
NvU32 u32CurrentValue; //!< Accessing current DWORD value of this setting.
|
||||
NVDRS_BINARY_SETTING binaryCurrentValue; //!< Accessing current Binary value of this setting.
|
||||
//!< Must be allocated by caller with valueLength specifying buffer size,
|
||||
//!< or only valueLength will be filled in.
|
||||
NvAPI_UnicodeString wszCurrentValue; //!< Accessing current unicode string value of this setting.
|
||||
};
|
||||
} NVDRS_SETTING_V1;
|
||||
|
||||
//! Macro for constructing the version field of ::_NVDRS_SETTING
|
||||
#define NVDRS_SETTING_VER1 MAKE_NVAPI_VERSION(NVDRS_SETTING_V1, 1)
|
||||
|
||||
typedef NVDRS_SETTING_V1 NVDRS_SETTING;
|
||||
#define NVDRS_SETTING_VER NVDRS_SETTING_VER1
|
||||
|
||||
typedef struct _NVDRS_APPLICATION_V4
|
||||
{
|
||||
NvU32 version; //!< Structure Version
|
||||
NvU32 isPredefined; //!< Is the application userdefined/predefined
|
||||
NvAPI_UnicodeString appName; //!< String name of the Application
|
||||
NvAPI_UnicodeString userFriendlyName; //!< UserFriendly name of the Application
|
||||
NvAPI_UnicodeString launcher; //!< Indicates the name (if any) of the launcher that starts the Application
|
||||
NvAPI_UnicodeString fileInFolder; //!< Select this application only if this file is found.
|
||||
//!< When specifying multiple files, separate them using the ':' character.
|
||||
NvU32 isMetro:1; //!< Windows 8 style app
|
||||
NvU32 isCommandLine:1; //!< Command line parsing for the application name
|
||||
NvU32 reserved:30; //!< Reserved. Should be 0.
|
||||
NvAPI_UnicodeString commandLine; //!< If isCommandLine is set to 0 this must be an empty. If isCommandLine is set to 1
|
||||
//!< this contains application's command line as if it was returned by GetCommandLineW.
|
||||
} NVDRS_APPLICATION_V4;
|
||||
|
||||
#define NVDRS_APPLICATION_VER_V4 MAKE_NVAPI_VERSION(NVDRS_APPLICATION_V4,4)
|
||||
|
||||
typedef NVDRS_APPLICATION_V4 NVDRS_APPLICATION;
|
||||
#define NVDRS_APPLICATION_VER NVDRS_APPLICATION_VER_V4
|
||||
|
||||
typedef struct _NVDRS_PROFILE_V1
|
||||
{
|
||||
NvU32 version; //!< Structure Version
|
||||
NvAPI_UnicodeString profileName; //!< String name of the Profile
|
||||
NVDRS_GPU_SUPPORT gpuSupport; //!< This read-only flag indicates the profile support on either
|
||||
//!< Quadro, or Geforce, or both.
|
||||
NvU32 isPredefined; //!< Is the Profile user-defined, or predefined
|
||||
NvU32 numOfApps; //!< Total number of applications that belong to this profile. Read-only
|
||||
NvU32 numOfSettings; //!< Total number of settings applied for this Profile. Read-only
|
||||
} NVDRS_PROFILE_V1;
|
||||
|
||||
typedef NVDRS_PROFILE_V1 NVDRS_PROFILE;
|
||||
|
||||
//! Macro for constructing the version field of ::NVDRS_PROFILE
|
||||
#define NVDRS_PROFILE_VER1 MAKE_NVAPI_VERSION(NVDRS_PROFILE_V1,1)
|
||||
#define NVDRS_PROFILE_VER NVDRS_PROFILE_VER1
|
||||
|
||||
#endif
|
||||
688
thirdparty/misc/ok_color.h
vendored
Normal file
688
thirdparty/misc/ok_color.h
vendored
Normal file
@@ -0,0 +1,688 @@
|
||||
// Copyright(c) 2021 Björn Ottosson
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files(the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions :
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef OK_COLOR_H
|
||||
#define OK_COLOR_H
|
||||
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
class ok_color
|
||||
{
|
||||
public:
|
||||
|
||||
struct Lab { float L; float a; float b; };
|
||||
struct RGB { float r; float g; float b; };
|
||||
struct HSV { float h; float s; float v; };
|
||||
struct HSL { float h; float s; float l; };
|
||||
struct LC { float L; float C; };
|
||||
|
||||
// Alternative representation of (L_cusp, C_cusp)
|
||||
// Encoded so S = C_cusp/L_cusp and T = C_cusp/(1-L_cusp)
|
||||
// The maximum value for C in the triangle is then found as fmin(S*L, T*(1-L)), for a given L
|
||||
struct ST { float S; float T; };
|
||||
|
||||
static constexpr float pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062f;
|
||||
|
||||
static float clamp(float x, float min, float max)
|
||||
{
|
||||
if (x < min)
|
||||
return min;
|
||||
if (x > max)
|
||||
return max;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static float sgn(float x)
|
||||
{
|
||||
return (float)(0.f < x) - (float)(x < 0.f);
|
||||
}
|
||||
|
||||
static float srgb_transfer_function(float a)
|
||||
{
|
||||
return .0031308f >= a ? 12.92f * a : 1.055f * powf(a, .4166666666666667f) - .055f;
|
||||
}
|
||||
|
||||
static float srgb_transfer_function_inv(float a)
|
||||
{
|
||||
return .04045f < a ? powf((a + .055f) / 1.055f, 2.4f) : a / 12.92f;
|
||||
}
|
||||
|
||||
static Lab linear_srgb_to_oklab(RGB c)
|
||||
{
|
||||
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
|
||||
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
|
||||
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
|
||||
|
||||
float l_ = cbrtf(l);
|
||||
float m_ = cbrtf(m);
|
||||
float s_ = cbrtf(s);
|
||||
|
||||
return {
|
||||
0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_,
|
||||
1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_,
|
||||
0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_,
|
||||
};
|
||||
}
|
||||
|
||||
static RGB oklab_to_linear_srgb(Lab c)
|
||||
{
|
||||
float l_ = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
|
||||
float m_ = c.L - 0.1055613458f * c.a - 0.0638541728f * c.b;
|
||||
float s_ = c.L - 0.0894841775f * c.a - 1.2914855480f * c.b;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
return {
|
||||
+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
|
||||
-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
|
||||
-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
|
||||
};
|
||||
}
|
||||
|
||||
// Finds the maximum saturation possible for a given hue that fits in sRGB
|
||||
// Saturation here is defined as S = C/L
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
static float compute_max_saturation(float a, float b)
|
||||
{
|
||||
// Max saturation will be when one of r, g or b goes below zero.
|
||||
|
||||
// Select different coefficients depending on which component goes below zero first
|
||||
float k0, k1, k2, k3, k4, wl, wm, ws;
|
||||
|
||||
if (-1.88170328f * a - 0.80936493f * b > 1)
|
||||
{
|
||||
// Red component
|
||||
k0 = +1.19086277f; k1 = +1.76576728f; k2 = +0.59662641f; k3 = +0.75515197f; k4 = +0.56771245f;
|
||||
wl = +4.0767416621f; wm = -3.3077115913f; ws = +0.2309699292f;
|
||||
}
|
||||
else if (1.81444104f * a - 1.19445276f * b > 1)
|
||||
{
|
||||
// Green component
|
||||
k0 = +0.73956515f; k1 = -0.45954404f; k2 = +0.08285427f; k3 = +0.12541070f; k4 = +0.14503204f;
|
||||
wl = -1.2684380046f; wm = +2.6097574011f; ws = -0.3413193965f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blue component
|
||||
k0 = +1.35733652f; k1 = -0.00915799f; k2 = -1.15130210f; k3 = -0.50559606f; k4 = +0.00692167f;
|
||||
wl = -0.0041960863f; wm = -0.7034186147f; ws = +1.7076147010f;
|
||||
}
|
||||
|
||||
// Approximate max saturation using a polynomial:
|
||||
float S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b;
|
||||
|
||||
// Do one step Halley's method to get closer
|
||||
// this gives an error less than 10e6, except for some blue hues where the dS/dh is close to infinite
|
||||
// this should be sufficient for most applications, otherwise do two/three steps
|
||||
|
||||
float k_l = +0.3963377774f * a + 0.2158037573f * b;
|
||||
float k_m = -0.1055613458f * a - 0.0638541728f * b;
|
||||
float k_s = -0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
{
|
||||
float l_ = 1.f + S * k_l;
|
||||
float m_ = 1.f + S * k_m;
|
||||
float s_ = 1.f + S * k_s;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
float l_dS = 3.f * k_l * l_ * l_;
|
||||
float m_dS = 3.f * k_m * m_ * m_;
|
||||
float s_dS = 3.f * k_s * s_ * s_;
|
||||
|
||||
float l_dS2 = 6.f * k_l * k_l * l_;
|
||||
float m_dS2 = 6.f * k_m * k_m * m_;
|
||||
float s_dS2 = 6.f * k_s * k_s * s_;
|
||||
|
||||
float f = wl * l + wm * m + ws * s;
|
||||
float f1 = wl * l_dS + wm * m_dS + ws * s_dS;
|
||||
float f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
|
||||
|
||||
S = S - f * f1 / (f1 * f1 - 0.5f * f * f2);
|
||||
}
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
// finds L_cusp and C_cusp for a given hue
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
static LC find_cusp(float a, float b)
|
||||
{
|
||||
// First, find the maximum saturation (saturation S = C/L)
|
||||
float S_cusp = compute_max_saturation(a, b);
|
||||
|
||||
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1:
|
||||
RGB rgb_at_max = oklab_to_linear_srgb({ 1, S_cusp * a, S_cusp * b });
|
||||
float L_cusp = cbrtf(1.f / fmax(fmax(rgb_at_max.r, rgb_at_max.g), rgb_at_max.b));
|
||||
float C_cusp = L_cusp * S_cusp;
|
||||
|
||||
return { L_cusp , C_cusp };
|
||||
}
|
||||
|
||||
// Finds intersection of the line defined by
|
||||
// L = L0 * (1 - t) + t * L1;
|
||||
// C = t * C1;
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
static float find_gamut_intersection(float a, float b, float L1, float C1, float L0, LC cusp)
|
||||
{
|
||||
// Find the intersection for upper and lower half seprately
|
||||
float t;
|
||||
if (((L1 - L0) * cusp.C - (cusp.L - L0) * C1) <= 0.f)
|
||||
{
|
||||
// Lower half
|
||||
|
||||
t = cusp.C * L0 / (C1 * cusp.L + cusp.C * (L0 - L1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upper half
|
||||
|
||||
// First intersect with triangle
|
||||
t = cusp.C * (L0 - 1.f) / (C1 * (cusp.L - 1.f) + cusp.C * (L0 - L1));
|
||||
|
||||
// Then one step Halley's method
|
||||
{
|
||||
float dL = L1 - L0;
|
||||
float dC = C1;
|
||||
|
||||
float k_l = +0.3963377774f * a + 0.2158037573f * b;
|
||||
float k_m = -0.1055613458f * a - 0.0638541728f * b;
|
||||
float k_s = -0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
float l_dt = dL + dC * k_l;
|
||||
float m_dt = dL + dC * k_m;
|
||||
float s_dt = dL + dC * k_s;
|
||||
|
||||
|
||||
// If higher accuracy is required, 2 or 3 iterations of the following block can be used:
|
||||
{
|
||||
float L = L0 * (1.f - t) + t * L1;
|
||||
float C = t * C1;
|
||||
|
||||
float l_ = L + C * k_l;
|
||||
float m_ = L + C * k_m;
|
||||
float s_ = L + C * k_s;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
float ldt = 3 * l_dt * l_ * l_;
|
||||
float mdt = 3 * m_dt * m_ * m_;
|
||||
float sdt = 3 * s_dt * s_ * s_;
|
||||
|
||||
float ldt2 = 6 * l_dt * l_dt * l_;
|
||||
float mdt2 = 6 * m_dt * m_dt * m_;
|
||||
float sdt2 = 6 * s_dt * s_dt * s_;
|
||||
|
||||
float r = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s - 1;
|
||||
float r1 = 4.0767416621f * ldt - 3.3077115913f * mdt + 0.2309699292f * sdt;
|
||||
float r2 = 4.0767416621f * ldt2 - 3.3077115913f * mdt2 + 0.2309699292f * sdt2;
|
||||
|
||||
float u_r = r1 / (r1 * r1 - 0.5f * r * r2);
|
||||
float t_r = -r * u_r;
|
||||
|
||||
float g = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s - 1;
|
||||
float g1 = -1.2684380046f * ldt + 2.6097574011f * mdt - 0.3413193965f * sdt;
|
||||
float g2 = -1.2684380046f * ldt2 + 2.6097574011f * mdt2 - 0.3413193965f * sdt2;
|
||||
|
||||
float u_g = g1 / (g1 * g1 - 0.5f * g * g2);
|
||||
float t_g = -g * u_g;
|
||||
|
||||
b = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s - 1;
|
||||
float b1 = -0.0041960863f * ldt - 0.7034186147f * mdt + 1.7076147010f * sdt;
|
||||
float b2 = -0.0041960863f * ldt2 - 0.7034186147f * mdt2 + 1.7076147010f * sdt2;
|
||||
|
||||
float u_b = b1 / (b1 * b1 - 0.5f * b * b2);
|
||||
float t_b = -b * u_b;
|
||||
|
||||
t_r = u_r >= 0.f ? t_r : FLT_MAX;
|
||||
t_g = u_g >= 0.f ? t_g : FLT_MAX;
|
||||
t_b = u_b >= 0.f ? t_b : FLT_MAX;
|
||||
|
||||
t += fmin(t_r, fmin(t_g, t_b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static float find_gamut_intersection(float a, float b, float L1, float C1, float L0)
|
||||
{
|
||||
// Find the cusp of the gamut triangle
|
||||
LC cusp = find_cusp(a, b);
|
||||
|
||||
return find_gamut_intersection(a, b, L1, C1, L0, cusp);
|
||||
}
|
||||
|
||||
static RGB gamut_clip_preserve_chroma(RGB rgb)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L0 = clamp(L, 0, 1);
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1 - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
static RGB gamut_clip_project_to_0_5(RGB rgb)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L0 = 0.5;
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1 - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
static RGB gamut_clip_project_to_L_cusp(RGB rgb)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
// The cusp is computed here and in find_gamut_intersection, an optimized solution would only compute it once.
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
|
||||
float L0 = cusp.L;
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
|
||||
float L_clipped = L0 * (1 - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
static RGB gamut_clip_adaptive_L0_0_5(RGB rgb, float alpha = 0.05f)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float Ld = L - 0.5f;
|
||||
float e1 = 0.5f + fabs(Ld) + alpha * C;
|
||||
float L0 = 0.5f * (1.f + sgn(Ld) * (e1 - sqrtf(e1 * e1 - 2.f * fabs(Ld))));
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
static RGB gamut_clip_adaptive_L0_L_cusp(RGB rgb, float alpha = 0.05f)
|
||||
{
|
||||
if (rgb.r < 1 && rgb.g < 1 && rgb.b < 1 && rgb.r > 0 && rgb.g > 0 && rgb.b > 0)
|
||||
return rgb;
|
||||
|
||||
Lab lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.L;
|
||||
float eps = 0.00001f;
|
||||
float C = fmax(eps, sqrtf(lab.a * lab.a + lab.b * lab.b));
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
// The cusp is computed here and in find_gamut_intersection, an optimized solution would only compute it once.
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
|
||||
float Ld = L - cusp.L;
|
||||
float k = 2.f * (Ld > 0 ? 1.f - cusp.L : cusp.L);
|
||||
|
||||
float e1 = 0.5f * k + fabs(Ld) + alpha * C / k;
|
||||
float L0 = cusp.L + 0.5f * (sgn(Ld) * (e1 - sqrtf(e1 * e1 - 2.f * k * fabs(Ld))));
|
||||
|
||||
float t = find_gamut_intersection(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb({ L_clipped, C_clipped * a_, C_clipped * b_ });
|
||||
}
|
||||
|
||||
static float toe(float x)
|
||||
{
|
||||
constexpr float k_1 = 0.206f;
|
||||
constexpr float k_2 = 0.03f;
|
||||
constexpr float k_3 = (1.f + k_1) / (1.f + k_2);
|
||||
return 0.5f * (k_3 * x - k_1 + sqrtf((k_3 * x - k_1) * (k_3 * x - k_1) + 4 * k_2 * k_3 * x));
|
||||
}
|
||||
|
||||
static float toe_inv(float x)
|
||||
{
|
||||
constexpr float k_1 = 0.206f;
|
||||
constexpr float k_2 = 0.03f;
|
||||
constexpr float k_3 = (1.f + k_1) / (1.f + k_2);
|
||||
return (x * x + k_1 * x) / (k_3 * (x + k_2));
|
||||
}
|
||||
|
||||
static ST to_ST(LC cusp)
|
||||
{
|
||||
float L = cusp.L;
|
||||
float C = cusp.C;
|
||||
return { C / L, C / (1 - L) };
|
||||
}
|
||||
|
||||
// Returns a smooth approximation of the location of the cusp
|
||||
// This polynomial was created by an optimization process
|
||||
// It has been designed so that S_mid < S_max and T_mid < T_max
|
||||
static ST get_ST_mid(float a_, float b_)
|
||||
{
|
||||
float S = 0.11516993f + 1.f / (
|
||||
+7.44778970f + 4.15901240f * b_
|
||||
+ a_ * (-2.19557347f + 1.75198401f * b_
|
||||
+ a_ * (-2.13704948f - 10.02301043f * b_
|
||||
+ a_ * (-4.24894561f + 5.38770819f * b_ + 4.69891013f * a_
|
||||
)))
|
||||
);
|
||||
|
||||
float T = 0.11239642f + 1.f / (
|
||||
+1.61320320f - 0.68124379f * b_
|
||||
+ a_ * (+0.40370612f + 0.90148123f * b_
|
||||
+ a_ * (-0.27087943f + 0.61223990f * b_
|
||||
+ a_ * (+0.00299215f - 0.45399568f * b_ - 0.14661872f * a_
|
||||
)))
|
||||
);
|
||||
|
||||
return { S, T };
|
||||
}
|
||||
|
||||
struct Cs { float C_0; float C_mid; float C_max; };
|
||||
static Cs get_Cs(float L, float a_, float b_)
|
||||
{
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
|
||||
float C_max = find_gamut_intersection(a_, b_, L, 1, L, cusp);
|
||||
ST ST_max = to_ST(cusp);
|
||||
|
||||
// Scale factor to compensate for the curved part of gamut shape:
|
||||
float k = C_max / fmin((L * ST_max.S), (1 - L) * ST_max.T);
|
||||
|
||||
float C_mid;
|
||||
{
|
||||
ST ST_mid = get_ST_mid(a_, b_);
|
||||
|
||||
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
|
||||
float C_a = L * ST_mid.S;
|
||||
float C_b = (1.f - L) * ST_mid.T;
|
||||
C_mid = 0.9f * k * sqrtf(sqrtf(1.f / (1.f / (C_a * C_a * C_a * C_a) + 1.f / (C_b * C_b * C_b * C_b))));
|
||||
}
|
||||
|
||||
float C_0;
|
||||
{
|
||||
// for C_0, the shape is independent of hue, so ST are constant. Values picked to roughly be the average values of ST.
|
||||
float C_a = L * 0.4f;
|
||||
float C_b = (1.f - L) * 0.8f;
|
||||
|
||||
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
|
||||
C_0 = sqrtf(1.f / (1.f / (C_a * C_a) + 1.f / (C_b * C_b)));
|
||||
}
|
||||
|
||||
return { C_0, C_mid, C_max };
|
||||
}
|
||||
|
||||
static RGB okhsl_to_srgb(HSL hsl)
|
||||
{
|
||||
float h = hsl.h;
|
||||
float s = hsl.s;
|
||||
float l = hsl.l;
|
||||
|
||||
if (l == 1.0f)
|
||||
{
|
||||
return { 1.f, 1.f, 1.f };
|
||||
}
|
||||
|
||||
else if (l == 0.f)
|
||||
{
|
||||
return { 0.f, 0.f, 0.f };
|
||||
}
|
||||
|
||||
float a_ = cosf(2.f * pi * h);
|
||||
float b_ = sinf(2.f * pi * h);
|
||||
float L = toe_inv(l);
|
||||
|
||||
Cs cs = get_Cs(L, a_, b_);
|
||||
float C_0 = cs.C_0;
|
||||
float C_mid = cs.C_mid;
|
||||
float C_max = cs.C_max;
|
||||
|
||||
float mid = 0.8f;
|
||||
float mid_inv = 1.25f;
|
||||
|
||||
float C, t, k_0, k_1, k_2;
|
||||
|
||||
if (s < mid)
|
||||
{
|
||||
t = mid_inv * s;
|
||||
|
||||
k_1 = mid * C_0;
|
||||
k_2 = (1.f - k_1 / C_mid);
|
||||
|
||||
C = t * k_1 / (1.f - k_2 * t);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = (s - mid)/ (1 - mid);
|
||||
|
||||
k_0 = C_mid;
|
||||
k_1 = (1.f - mid) * C_mid * C_mid * mid_inv * mid_inv / C_0;
|
||||
k_2 = (1.f - (k_1) / (C_max - C_mid));
|
||||
|
||||
C = k_0 + t * k_1 / (1.f - k_2 * t);
|
||||
}
|
||||
|
||||
RGB rgb = oklab_to_linear_srgb({ L, C * a_, C * b_ });
|
||||
return {
|
||||
srgb_transfer_function(rgb.r),
|
||||
srgb_transfer_function(rgb.g),
|
||||
srgb_transfer_function(rgb.b),
|
||||
};
|
||||
}
|
||||
|
||||
static HSL srgb_to_okhsl(RGB rgb)
|
||||
{
|
||||
Lab lab = linear_srgb_to_oklab({
|
||||
srgb_transfer_function_inv(rgb.r),
|
||||
srgb_transfer_function_inv(rgb.g),
|
||||
srgb_transfer_function_inv(rgb.b)
|
||||
});
|
||||
|
||||
float C = sqrtf(lab.a * lab.a + lab.b * lab.b);
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L = lab.L;
|
||||
float h = 0.5f + 0.5f * atan2f(-lab.b, -lab.a) / pi;
|
||||
|
||||
Cs cs = get_Cs(L, a_, b_);
|
||||
float C_0 = cs.C_0;
|
||||
float C_mid = cs.C_mid;
|
||||
float C_max = cs.C_max;
|
||||
|
||||
// Inverse of the interpolation in okhsl_to_srgb:
|
||||
|
||||
float mid = 0.8f;
|
||||
float mid_inv = 1.25f;
|
||||
|
||||
float s;
|
||||
if (C < C_mid)
|
||||
{
|
||||
float k_1 = mid * C_0;
|
||||
float k_2 = (1.f - k_1 / C_mid);
|
||||
|
||||
float t = C / (k_1 + k_2 * C);
|
||||
s = t * mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
float k_0 = C_mid;
|
||||
float k_1 = (1.f - mid) * C_mid * C_mid * mid_inv * mid_inv / C_0;
|
||||
float k_2 = (1.f - (k_1) / (C_max - C_mid));
|
||||
|
||||
float t = (C - k_0) / (k_1 + k_2 * (C - k_0));
|
||||
s = mid + (1.f - mid) * t;
|
||||
}
|
||||
|
||||
float l = toe(L);
|
||||
return { h, s, l };
|
||||
}
|
||||
|
||||
|
||||
static RGB okhsv_to_srgb(HSV hsv)
|
||||
{
|
||||
float h = hsv.h;
|
||||
float s = hsv.s;
|
||||
float v = hsv.v;
|
||||
|
||||
float a_ = cosf(2.f * pi * h);
|
||||
float b_ = sinf(2.f * pi * h);
|
||||
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
ST ST_max = to_ST(cusp);
|
||||
float S_max = ST_max.S;
|
||||
float T_max = ST_max.T;
|
||||
float S_0 = 0.5f;
|
||||
float k = 1 - S_0 / S_max;
|
||||
|
||||
// first we compute L and V as if the gamut is a perfect triangle:
|
||||
|
||||
// L, C when v==1:
|
||||
float L_v = 1 - s * S_0 / (S_0 + T_max - T_max * k * s);
|
||||
float C_v = s * T_max * S_0 / (S_0 + T_max - T_max * k * s);
|
||||
|
||||
float L = v * L_v;
|
||||
float C = v * C_v;
|
||||
|
||||
// then we compensate for both toe and the curved top part of the triangle:
|
||||
float L_vt = toe_inv(L_v);
|
||||
float C_vt = C_v * L_vt / L_v;
|
||||
|
||||
float L_new = toe_inv(L);
|
||||
C = C * L_new / L;
|
||||
L = L_new;
|
||||
|
||||
RGB rgb_scale = oklab_to_linear_srgb({ L_vt, a_ * C_vt, b_ * C_vt });
|
||||
float scale_L = cbrtf(1.f / fmax(fmax(rgb_scale.r, rgb_scale.g), fmax(rgb_scale.b, 0.f)));
|
||||
|
||||
L = L * scale_L;
|
||||
C = C * scale_L;
|
||||
|
||||
RGB rgb = oklab_to_linear_srgb({ L, C * a_, C * b_ });
|
||||
return {
|
||||
srgb_transfer_function(rgb.r),
|
||||
srgb_transfer_function(rgb.g),
|
||||
srgb_transfer_function(rgb.b),
|
||||
};
|
||||
}
|
||||
|
||||
static HSV srgb_to_okhsv(RGB rgb)
|
||||
{
|
||||
Lab lab = linear_srgb_to_oklab({
|
||||
srgb_transfer_function_inv(rgb.r),
|
||||
srgb_transfer_function_inv(rgb.g),
|
||||
srgb_transfer_function_inv(rgb.b)
|
||||
});
|
||||
|
||||
float C = sqrtf(lab.a * lab.a + lab.b * lab.b);
|
||||
float a_ = lab.a / C;
|
||||
float b_ = lab.b / C;
|
||||
|
||||
float L = lab.L;
|
||||
float h = 0.5f + 0.5f * atan2f(-lab.b, -lab.a) / pi;
|
||||
|
||||
LC cusp = find_cusp(a_, b_);
|
||||
ST ST_max = to_ST(cusp);
|
||||
float S_max = ST_max.S;
|
||||
float T_max = ST_max.T;
|
||||
float S_0 = 0.5f;
|
||||
float k = 1 - S_0 / S_max;
|
||||
|
||||
// first we find L_v, C_v, L_vt and C_vt
|
||||
|
||||
float t = T_max / (C + L * T_max);
|
||||
float L_v = t * L;
|
||||
float C_v = t * C;
|
||||
|
||||
float L_vt = toe_inv(L_v);
|
||||
float C_vt = C_v * L_vt / L_v;
|
||||
|
||||
// we can then use these to invert the step that compensates for the toe and the curved top part of the triangle:
|
||||
RGB rgb_scale = oklab_to_linear_srgb({ L_vt, a_ * C_vt, b_ * C_vt });
|
||||
float scale_L = cbrtf(1.f / fmax(fmax(rgb_scale.r, rgb_scale.g), fmax(rgb_scale.b, 0.f)));
|
||||
|
||||
L = L / scale_L;
|
||||
C = C / scale_L;
|
||||
|
||||
C = C * toe(L) / L;
|
||||
L = toe(L);
|
||||
|
||||
// we can now compute v and s:
|
||||
|
||||
float v = L / L_v;
|
||||
float s = (S_0 + T_max) * C_v / ((T_max * S_0) + T_max * k * C_v);
|
||||
|
||||
return { h, s, v };
|
||||
}
|
||||
|
||||
};
|
||||
#endif // OK_COLOR_H
|
||||
663
thirdparty/misc/ok_color_shader.h
vendored
Normal file
663
thirdparty/misc/ok_color_shader.h
vendored
Normal file
@@ -0,0 +1,663 @@
|
||||
// Copyright(c) 2021 Björn Ottosson
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files(the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and /or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions :
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef OK_COLOR_SHADER_H
|
||||
#define OK_COLOR_SHADER_H
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
static String OK_COLOR_SHADER = R"(shader_type canvas_item;
|
||||
|
||||
const float M_PI = 3.1415926535897932384626433832795;
|
||||
|
||||
float cbrt( float x )
|
||||
{
|
||||
return sign(x)*pow(abs(x),1.0f/3.0f);
|
||||
}
|
||||
|
||||
float srgb_transfer_function(float a)
|
||||
{
|
||||
return .0031308f >= a ? 12.92f * a : 1.055f * pow(a, .4166666666666667f) - .055f;
|
||||
}
|
||||
|
||||
float srgb_transfer_function_inv(float a)
|
||||
{
|
||||
return .04045f < a ? pow((a + .055f) / 1.055f, 2.4f) : a / 12.92f;
|
||||
}
|
||||
|
||||
vec3 linear_srgb_to_oklab(vec3 c)
|
||||
{
|
||||
float l = 0.4122214708f * c.r + 0.5363325363f * c.g + 0.0514459929f * c.b;
|
||||
float m = 0.2119034982f * c.r + 0.6806995451f * c.g + 0.1073969566f * c.b;
|
||||
float s = 0.0883024619f * c.r + 0.2817188376f * c.g + 0.6299787005f * c.b;
|
||||
|
||||
float l_ = cbrt(l);
|
||||
float m_ = cbrt(m);
|
||||
float s_ = cbrt(s);
|
||||
|
||||
return vec3(
|
||||
0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_,
|
||||
1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_,
|
||||
0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_
|
||||
);
|
||||
}
|
||||
|
||||
vec3 oklab_to_linear_srgb(vec3 c)
|
||||
{
|
||||
float l_ = c.x + 0.3963377774f * c.y + 0.2158037573f * c.z;
|
||||
float m_ = c.x - 0.1055613458f * c.y - 0.0638541728f * c.z;
|
||||
float s_ = c.x - 0.0894841775f * c.y - 1.2914855480f * c.z;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
return vec3(
|
||||
+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
|
||||
-1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
|
||||
-0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s
|
||||
);
|
||||
}
|
||||
|
||||
// Finds the maximum saturation possible for a given hue that fits in sRGB
|
||||
// Saturation here is defined as S = C/L
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
float compute_max_saturation(float a, float b)
|
||||
{
|
||||
// Max saturation will be when one of r, g or b goes below zero.
|
||||
|
||||
// Select different coefficients depending on which component goes below zero first
|
||||
float k0, k1, k2, k3, k4, wl, wm, ws;
|
||||
|
||||
if (-1.88170328f * a - 0.80936493f * b > 1.f)
|
||||
{
|
||||
// Red component
|
||||
k0 = +1.19086277f; k1 = +1.76576728f; k2 = +0.59662641f; k3 = +0.75515197f; k4 = +0.56771245f;
|
||||
wl = +4.0767416621f; wm = -3.3077115913f; ws = +0.2309699292f;
|
||||
}
|
||||
else if (1.81444104f * a - 1.19445276f * b > 1.f)
|
||||
{
|
||||
// Green component
|
||||
k0 = +0.73956515f; k1 = -0.45954404f; k2 = +0.08285427f; k3 = +0.12541070f; k4 = +0.14503204f;
|
||||
wl = -1.2684380046f; wm = +2.6097574011f; ws = -0.3413193965f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blue component
|
||||
k0 = +1.35733652f; k1 = -0.00915799f; k2 = -1.15130210f; k3 = -0.50559606f; k4 = +0.00692167f;
|
||||
wl = -0.0041960863f; wm = -0.7034186147f; ws = +1.7076147010f;
|
||||
}
|
||||
|
||||
// Approximate max saturation using a polynomial:
|
||||
float S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b;
|
||||
|
||||
// Do one step Halley's method to get closer
|
||||
// this gives an error less than 10e6, except for some blue hues where the dS/dh is close to infinite
|
||||
// this should be sufficient for most applications, otherwise do two/three steps
|
||||
|
||||
float k_l = +0.3963377774f * a + 0.2158037573f * b;
|
||||
float k_m = -0.1055613458f * a - 0.0638541728f * b;
|
||||
float k_s = -0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
{
|
||||
float l_ = 1.f + S * k_l;
|
||||
float m_ = 1.f + S * k_m;
|
||||
float s_ = 1.f + S * k_s;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
float l_dS = 3.f * k_l * l_ * l_;
|
||||
float m_dS = 3.f * k_m * m_ * m_;
|
||||
float s_dS = 3.f * k_s * s_ * s_;
|
||||
|
||||
float l_dS2 = 6.f * k_l * k_l * l_;
|
||||
float m_dS2 = 6.f * k_m * k_m * m_;
|
||||
float s_dS2 = 6.f * k_s * k_s * s_;
|
||||
|
||||
float f = wl * l + wm * m + ws * s;
|
||||
float f1 = wl * l_dS + wm * m_dS + ws * s_dS;
|
||||
float f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
|
||||
|
||||
S = S - f * f1 / (f1 * f1 - 0.5f * f * f2);
|
||||
}
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
// finds L_cusp and C_cusp for a given hue
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
vec2 find_cusp(float a, float b)
|
||||
{
|
||||
// First, find the maximum saturation (saturation S = C/L)
|
||||
float S_cusp = compute_max_saturation(a, b);
|
||||
|
||||
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1:
|
||||
vec3 rgb_at_max = oklab_to_linear_srgb(vec3( 1, S_cusp * a, S_cusp * b ));
|
||||
float L_cusp = cbrt(1.f / max(max(rgb_at_max.r, rgb_at_max.g), rgb_at_max.b));
|
||||
float C_cusp = L_cusp * S_cusp;
|
||||
|
||||
return vec2( L_cusp , C_cusp );
|
||||
} )"
|
||||
R"(// Finds intersection of the line defined by
|
||||
// L = L0 * (1 - t) + t * L1;
|
||||
// C = t * C1;
|
||||
// a and b must be normalized so a^2 + b^2 == 1
|
||||
float find_gamut_intersection(float a, float b, float L1, float C1, float L0, vec2 cusp)
|
||||
{
|
||||
// Find the intersection for upper and lower half seprately
|
||||
float t;
|
||||
if (((L1 - L0) * cusp.y - (cusp.x - L0) * C1) <= 0.f)
|
||||
{
|
||||
// Lower half
|
||||
|
||||
t = cusp.y * L0 / (C1 * cusp.x + cusp.y * (L0 - L1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upper half
|
||||
|
||||
// First intersect with triangle
|
||||
t = cusp.y * (L0 - 1.f) / (C1 * (cusp.x - 1.f) + cusp.y * (L0 - L1));
|
||||
|
||||
// Then one step Halley's method
|
||||
{
|
||||
float dL = L1 - L0;
|
||||
float dC = C1;
|
||||
|
||||
float k_l = +0.3963377774f * a + 0.2158037573f * b;
|
||||
float k_m = -0.1055613458f * a - 0.0638541728f * b;
|
||||
float k_s = -0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
float l_dt = dL + dC * k_l;
|
||||
float m_dt = dL + dC * k_m;
|
||||
float s_dt = dL + dC * k_s;
|
||||
|
||||
|
||||
// If higher accuracy is required, 2 or 3 iterations of the following block can be used:
|
||||
{
|
||||
float L = L0 * (1.f - t) + t * L1;
|
||||
float C = t * C1;
|
||||
|
||||
float l_ = L + C * k_l;
|
||||
float m_ = L + C * k_m;
|
||||
float s_ = L + C * k_s;
|
||||
|
||||
float l = l_ * l_ * l_;
|
||||
float m = m_ * m_ * m_;
|
||||
float s = s_ * s_ * s_;
|
||||
|
||||
float ldt = 3.f * l_dt * l_ * l_;
|
||||
float mdt = 3.f * m_dt * m_ * m_;
|
||||
float sdt = 3.f * s_dt * s_ * s_;
|
||||
|
||||
float ldt2 = 6.f * l_dt * l_dt * l_;
|
||||
float mdt2 = 6.f * m_dt * m_dt * m_;
|
||||
float sdt2 = 6.f * s_dt * s_dt * s_;
|
||||
|
||||
float r = 4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s - 1.f;
|
||||
float r1 = 4.0767416621f * ldt - 3.3077115913f * mdt + 0.2309699292f * sdt;
|
||||
float r2 = 4.0767416621f * ldt2 - 3.3077115913f * mdt2 + 0.2309699292f * sdt2;
|
||||
|
||||
float u_r = r1 / (r1 * r1 - 0.5f * r * r2);
|
||||
float t_r = -r * u_r;
|
||||
|
||||
float g = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s - 1.f;
|
||||
float g1 = -1.2684380046f * ldt + 2.6097574011f * mdt - 0.3413193965f * sdt;
|
||||
float g2 = -1.2684380046f * ldt2 + 2.6097574011f * mdt2 - 0.3413193965f * sdt2;
|
||||
|
||||
float u_g = g1 / (g1 * g1 - 0.5f * g * g2);
|
||||
float t_g = -g * u_g;
|
||||
|
||||
float b = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s - 1.f;
|
||||
float b1 = -0.0041960863f * ldt - 0.7034186147f * mdt + 1.7076147010f * sdt;
|
||||
float b2 = -0.0041960863f * ldt2 - 0.7034186147f * mdt2 + 1.7076147010f * sdt2;
|
||||
|
||||
float u_b = b1 / (b1 * b1 - 0.5f * b * b2);
|
||||
float t_b = -b * u_b;
|
||||
|
||||
t_r = u_r >= 0.f ? t_r : 10000.f;
|
||||
t_g = u_g >= 0.f ? t_g : 10000.f;
|
||||
t_b = u_b >= 0.f ? t_b : 10000.f;
|
||||
|
||||
t += min(t_r, min(t_g, t_b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
float find_gamut_intersection_5(float a, float b, float L1, float C1, float L0)
|
||||
{
|
||||
// Find the cusp of the gamut triangle
|
||||
vec2 cusp = find_cusp(a, b);
|
||||
|
||||
return find_gamut_intersection(a, b, L1, C1, L0, cusp);
|
||||
})"
|
||||
R"(
|
||||
|
||||
vec3 gamut_clip_preserve_chroma(vec3 rgb)
|
||||
{
|
||||
if (rgb.r < 1.f && rgb.g < 1.f && rgb.b < 1.f && rgb.r > 0.f && rgb.g > 0.f && rgb.b > 0.f)
|
||||
return rgb;
|
||||
|
||||
vec3 lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.x;
|
||||
float eps = 0.00001f;
|
||||
float C = max(eps, sqrt(lab.y * lab.y + lab.z * lab.z));
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
float L0 = clamp(L, 0.f, 1.f);
|
||||
|
||||
float t = find_gamut_intersection_5(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb(vec3( L_clipped, C_clipped * a_, C_clipped * b_ ));
|
||||
}
|
||||
|
||||
vec3 gamut_clip_project_to_0_5(vec3 rgb)
|
||||
{
|
||||
if (rgb.r < 1.f && rgb.g < 1.f && rgb.b < 1.f && rgb.r > 0.f && rgb.g > 0.f && rgb.b > 0.f)
|
||||
return rgb;
|
||||
|
||||
vec3 lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.x;
|
||||
float eps = 0.00001f;
|
||||
float C = max(eps, sqrt(lab.y * lab.y + lab.z * lab.z));
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
float L0 = 0.5;
|
||||
|
||||
float t = find_gamut_intersection_5(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb(vec3( L_clipped, C_clipped * a_, C_clipped * b_ ));
|
||||
}
|
||||
|
||||
vec3 gamut_clip_project_to_L_cusp(vec3 rgb)
|
||||
{
|
||||
if (rgb.r < 1.f && rgb.g < 1.f && rgb.b < 1.f && rgb.r > 0.f && rgb.g > 0.f && rgb.b > 0.f)
|
||||
return rgb;
|
||||
|
||||
vec3 lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.x;
|
||||
float eps = 0.00001f;
|
||||
float C = max(eps, sqrt(lab.y * lab.y + lab.z * lab.z));
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
// The cusp is computed here and in find_gamut_intersection, an optimized solution would only compute it once.
|
||||
vec2 cusp = find_cusp(a_, b_);
|
||||
|
||||
float L0 = cusp.x;
|
||||
|
||||
float t = find_gamut_intersection_5(a_, b_, L, C, L0);
|
||||
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb(vec3( L_clipped, C_clipped * a_, C_clipped * b_ ));
|
||||
}
|
||||
|
||||
vec3 gamut_clip_adaptive_L0_0_5(vec3 rgb, float alpha)
|
||||
{
|
||||
if (rgb.r < 1.f && rgb.g < 1.f && rgb.b < 1.f && rgb.r > 0.f && rgb.g > 0.f && rgb.b > 0.f)
|
||||
return rgb;
|
||||
|
||||
vec3 lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.x;
|
||||
float eps = 0.00001f;
|
||||
float C = max(eps, sqrt(lab.y * lab.y + lab.z * lab.z));
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
float Ld = L - 0.5f;
|
||||
float e1 = 0.5f + abs(Ld) + alpha * C;
|
||||
float L0 = 0.5f * (1.f + sign(Ld) * (e1 - sqrt(e1 * e1 - 2.f * abs(Ld))));
|
||||
|
||||
float t = find_gamut_intersection_5(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb(vec3( L_clipped, C_clipped * a_, C_clipped * b_ ));
|
||||
}
|
||||
|
||||
vec3 gamut_clip_adaptive_L0_L_cusp(vec3 rgb, float alpha)
|
||||
{
|
||||
if (rgb.r < 1.f && rgb.g < 1.f && rgb.b < 1.f && rgb.r > 0.f && rgb.g > 0.f && rgb.b > 0.f)
|
||||
return rgb;
|
||||
|
||||
vec3 lab = linear_srgb_to_oklab(rgb);
|
||||
|
||||
float L = lab.x;
|
||||
float eps = 0.00001f;
|
||||
float C = max(eps, sqrt(lab.y * lab.y + lab.z * lab.z));
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
// The cusp is computed here and in find_gamut_intersection, an optimized solution would only compute it once.
|
||||
vec2 cusp = find_cusp(a_, b_);
|
||||
|
||||
float Ld = L - cusp.x;
|
||||
float k = 2.f * (Ld > 0.f ? 1.f - cusp.x : cusp.x);
|
||||
|
||||
float e1 = 0.5f * k + abs(Ld) + alpha * C / k;
|
||||
float L0 = cusp.x + 0.5f * (sign(Ld) * (e1 - sqrt(e1 * e1 - 2.f * k * abs(Ld))));
|
||||
|
||||
float t = find_gamut_intersection_5(a_, b_, L, C, L0);
|
||||
float L_clipped = L0 * (1.f - t) + t * L;
|
||||
float C_clipped = t * C;
|
||||
|
||||
return oklab_to_linear_srgb(vec3( L_clipped, C_clipped * a_, C_clipped * b_ ));
|
||||
}
|
||||
|
||||
float toe(float x)
|
||||
{
|
||||
float k_1 = 0.206f;
|
||||
float k_2 = 0.03f;
|
||||
float k_3 = (1.f + k_1) / (1.f + k_2);
|
||||
return 0.5f * (k_3 * x - k_1 + sqrt((k_3 * x - k_1) * (k_3 * x - k_1) + 4.f * k_2 * k_3 * x));
|
||||
}
|
||||
|
||||
float toe_inv(float x)
|
||||
{
|
||||
float k_1 = 0.206f;
|
||||
float k_2 = 0.03f;
|
||||
float k_3 = (1.f + k_1) / (1.f + k_2);
|
||||
return (x * x + k_1 * x) / (k_3 * (x + k_2));
|
||||
}
|
||||
)"
|
||||
R"(vec2 to_ST(vec2 cusp)
|
||||
{
|
||||
float L = cusp.x;
|
||||
float C = cusp.y;
|
||||
return vec2( C / L, C / (1.f - L) );
|
||||
}
|
||||
|
||||
// Returns a smooth approximation of the location of the cusp
|
||||
// This polynomial was created by an optimization process
|
||||
// It has been designed so that S_mid < S_max and T_mid < T_max
|
||||
vec2 get_ST_mid(float a_, float b_)
|
||||
{
|
||||
float S = 0.11516993f + 1.f / (
|
||||
+7.44778970f + 4.15901240f * b_
|
||||
+ a_ * (-2.19557347f + 1.75198401f * b_
|
||||
+ a_ * (-2.13704948f - 10.02301043f * b_
|
||||
+ a_ * (-4.24894561f + 5.38770819f * b_ + 4.69891013f * a_
|
||||
)))
|
||||
);
|
||||
|
||||
float T = 0.11239642f + 1.f / (
|
||||
+1.61320320f - 0.68124379f * b_
|
||||
+ a_ * (+0.40370612f + 0.90148123f * b_
|
||||
+ a_ * (-0.27087943f + 0.61223990f * b_
|
||||
+ a_ * (+0.00299215f - 0.45399568f * b_ - 0.14661872f * a_
|
||||
)))
|
||||
);
|
||||
|
||||
return vec2( S, T );
|
||||
}
|
||||
|
||||
vec3 get_Cs(float L, float a_, float b_)
|
||||
{
|
||||
vec2 cusp = find_cusp(a_, b_);
|
||||
|
||||
float C_max = find_gamut_intersection(a_, b_, L, 1.f, L, cusp);
|
||||
vec2 ST_max = to_ST(cusp);
|
||||
|
||||
// Scale factor to compensate for the curved part of gamut shape:
|
||||
float k = C_max / min((L * ST_max.x), (1.f - L) * ST_max.y);
|
||||
|
||||
float C_mid;
|
||||
{
|
||||
vec2 ST_mid = get_ST_mid(a_, b_);
|
||||
|
||||
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
|
||||
float C_a = L * ST_mid.x;
|
||||
float C_b = (1.f - L) * ST_mid.y;
|
||||
C_mid = 0.9f * k * sqrt(sqrt(1.f / (1.f / (C_a * C_a * C_a * C_a) + 1.f / (C_b * C_b * C_b * C_b))));
|
||||
}
|
||||
|
||||
float C_0;
|
||||
{
|
||||
// for C_0, the shape is independent of hue, so vec2 are constant. Values picked to roughly be the average values of vec2.
|
||||
float C_a = L * 0.4f;
|
||||
float C_b = (1.f - L) * 0.8f;
|
||||
|
||||
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
|
||||
C_0 = sqrt(1.f / (1.f / (C_a * C_a) + 1.f / (C_b * C_b)));
|
||||
}
|
||||
|
||||
return vec3( C_0, C_mid, C_max );
|
||||
}
|
||||
|
||||
vec3 okhsl_to_srgb(vec3 hsl)
|
||||
{
|
||||
float h = hsl.x;
|
||||
float s = hsl.y;
|
||||
float l = hsl.z;
|
||||
|
||||
if (l == 1.0f)
|
||||
{
|
||||
return vec3( 1.f, 1.f, 1.f );
|
||||
}
|
||||
|
||||
else if (l == 0.f)
|
||||
{
|
||||
return vec3( 0.f, 0.f, 0.f );
|
||||
}
|
||||
|
||||
float a_ = cos(2.f * M_PI * h);
|
||||
float b_ = sin(2.f * M_PI * h);
|
||||
float L = toe_inv(l);
|
||||
|
||||
vec3 cs = get_Cs(L, a_, b_);
|
||||
float C_0 = cs.x;
|
||||
float C_mid = cs.y;
|
||||
float C_max = cs.z;
|
||||
|
||||
float mid = 0.8f;
|
||||
float mid_inv = 1.25f;
|
||||
|
||||
float C, t, k_0, k_1, k_2;
|
||||
|
||||
if (s < mid)
|
||||
{
|
||||
t = mid_inv * s;
|
||||
|
||||
k_1 = mid * C_0;
|
||||
k_2 = (1.f - k_1 / C_mid);
|
||||
|
||||
C = t * k_1 / (1.f - k_2 * t);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = (s - mid)/ (1.f - mid);
|
||||
|
||||
k_0 = C_mid;
|
||||
k_1 = (1.f - mid) * C_mid * C_mid * mid_inv * mid_inv / C_0;
|
||||
k_2 = (1.f - (k_1) / (C_max - C_mid));
|
||||
|
||||
C = k_0 + t * k_1 / (1.f - k_2 * t);
|
||||
}
|
||||
|
||||
vec3 rgb = oklab_to_linear_srgb(vec3( L, C * a_, C * b_ ));
|
||||
return vec3(
|
||||
srgb_transfer_function(rgb.r),
|
||||
srgb_transfer_function(rgb.g),
|
||||
srgb_transfer_function(rgb.b)
|
||||
);
|
||||
}
|
||||
|
||||
vec3 srgb_to_okhsl(vec3 rgb)
|
||||
{
|
||||
vec3 lab = linear_srgb_to_oklab(vec3(
|
||||
srgb_transfer_function_inv(rgb.r),
|
||||
srgb_transfer_function_inv(rgb.g),
|
||||
srgb_transfer_function_inv(rgb.b)
|
||||
));
|
||||
|
||||
float C = sqrt(lab.y * lab.y + lab.z * lab.z);
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
float L = lab.x;
|
||||
float h = 0.5f + 0.5f * atan(-lab.z, -lab.y) / M_PI;
|
||||
|
||||
vec3 cs = get_Cs(L, a_, b_);
|
||||
float C_0 = cs.x;
|
||||
float C_mid = cs.y;
|
||||
float C_max = cs.z;
|
||||
|
||||
// Inverse of the interpolation in okhsl_to_srgb:
|
||||
|
||||
float mid = 0.8f;
|
||||
float mid_inv = 1.25f;
|
||||
|
||||
float s;
|
||||
if (C < C_mid)
|
||||
{
|
||||
float k_1 = mid * C_0;
|
||||
float k_2 = (1.f - k_1 / C_mid);
|
||||
|
||||
float t = C / (k_1 + k_2 * C);
|
||||
s = t * mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
float k_0 = C_mid;
|
||||
float k_1 = (1.f - mid) * C_mid * C_mid * mid_inv * mid_inv / C_0;
|
||||
float k_2 = (1.f - (k_1) / (C_max - C_mid));
|
||||
|
||||
float t = (C - k_0) / (k_1 + k_2 * (C - k_0));
|
||||
s = mid + (1.f - mid) * t;
|
||||
}
|
||||
|
||||
float l = toe(L);
|
||||
return vec3( h, s, l );
|
||||
}
|
||||
|
||||
|
||||
vec3 okhsv_to_srgb(vec3 hsv)
|
||||
{
|
||||
float h = hsv.x;
|
||||
float s = hsv.y;
|
||||
float v = hsv.z;
|
||||
|
||||
float a_ = cos(2.f * M_PI * h);
|
||||
float b_ = sin(2.f * M_PI * h);
|
||||
|
||||
vec2 cusp = find_cusp(a_, b_);
|
||||
vec2 ST_max = to_ST(cusp);
|
||||
float S_max = ST_max.x;
|
||||
float T_max = ST_max.y;
|
||||
float S_0 = 0.5f;
|
||||
float k = 1.f- S_0 / S_max;
|
||||
|
||||
// first we compute L and V as if the gamut is a perfect triangle:
|
||||
|
||||
// L, C when v==1:
|
||||
float L_v = 1.f - s * S_0 / (S_0 + T_max - T_max * k * s);
|
||||
float C_v = s * T_max * S_0 / (S_0 + T_max - T_max * k * s);
|
||||
|
||||
float L = v * L_v;
|
||||
float C = v * C_v;
|
||||
|
||||
// then we compensate for both toe and the curved top part of the triangle:
|
||||
float L_vt = toe_inv(L_v);
|
||||
float C_vt = C_v * L_vt / L_v;
|
||||
|
||||
float L_new = toe_inv(L);
|
||||
C = C * L_new / L;
|
||||
L = L_new;
|
||||
|
||||
vec3 rgb_scale = oklab_to_linear_srgb(vec3( L_vt, a_ * C_vt, b_ * C_vt ));
|
||||
float scale_L = cbrt(1.f / max(max(rgb_scale.r, rgb_scale.g), max(rgb_scale.b, 0.f)));
|
||||
|
||||
L = L * scale_L;
|
||||
C = C * scale_L;
|
||||
|
||||
vec3 rgb = oklab_to_linear_srgb(vec3( L, C * a_, C * b_ ));
|
||||
return vec3(
|
||||
srgb_transfer_function(rgb.r),
|
||||
srgb_transfer_function(rgb.g),
|
||||
srgb_transfer_function(rgb.b)
|
||||
);
|
||||
}
|
||||
)"
|
||||
R"(
|
||||
vec3 srgb_to_okhsv(vec3 rgb)
|
||||
{
|
||||
vec3 lab = linear_srgb_to_oklab(vec3(
|
||||
srgb_transfer_function_inv(rgb.r),
|
||||
srgb_transfer_function_inv(rgb.g),
|
||||
srgb_transfer_function_inv(rgb.b)
|
||||
));
|
||||
|
||||
float C = sqrt(lab.y * lab.y + lab.z * lab.z);
|
||||
float a_ = lab.y / C;
|
||||
float b_ = lab.z / C;
|
||||
|
||||
float L = lab.x;
|
||||
float h = 0.5f + 0.5f * atan(-lab.z, -lab.y) / M_PI;
|
||||
|
||||
vec2 cusp = find_cusp(a_, b_);
|
||||
vec2 ST_max = to_ST(cusp);
|
||||
float S_max = ST_max.x;
|
||||
float T_max = ST_max.y;
|
||||
float S_0 = 0.5f;
|
||||
float k = 1.f - S_0 / S_max;
|
||||
|
||||
// first we find L_v, C_v, L_vt and C_vt
|
||||
|
||||
float t = T_max / (C + L * T_max);
|
||||
float L_v = t * L;
|
||||
float C_v = t * C;
|
||||
|
||||
float L_vt = toe_inv(L_v);
|
||||
float C_vt = C_v * L_vt / L_v;
|
||||
|
||||
// we can then use these to invert the step that compensates for the toe and the curved top part of the triangle:
|
||||
vec3 rgb_scale = oklab_to_linear_srgb(vec3( L_vt, a_ * C_vt, b_ * C_vt ));
|
||||
float scale_L = cbrt(1.f / max(max(rgb_scale.r, rgb_scale.g), max(rgb_scale.b, 0.f)));
|
||||
|
||||
L = L / scale_L;
|
||||
C = C / scale_L;
|
||||
|
||||
C = C * toe(L) / L;
|
||||
L = toe(L);
|
||||
|
||||
// we can now compute v and s:
|
||||
|
||||
float v = L / L_v;
|
||||
float s = (S_0 + T_max) * C_v / ((T_max * S_0) + T_max * k * C_v);
|
||||
|
||||
return vec3 (h, s, v );
|
||||
})";
|
||||
|
||||
#endif
|
||||
43
thirdparty/misc/patches/FastNoiseLite-0001-namespace-warnings.patch
vendored
Normal file
43
thirdparty/misc/patches/FastNoiseLite-0001-namespace-warnings.patch
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
diff --git a/thirdparty/misc/FastNoiseLite.h b/thirdparty/misc/FastNoiseLite.h
|
||||
index ed97b0fcac..fb6dbcb92a 100644
|
||||
--- a/thirdparty/misc/FastNoiseLite.h
|
||||
+++ b/thirdparty/misc/FastNoiseLite.h
|
||||
@@ -52,6 +52,8 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
+namespace fastnoiselite {
|
||||
+
|
||||
class FastNoiseLite
|
||||
{
|
||||
public:
|
||||
@@ -1609,6 +1611,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
+// GCC raises warnings when integer overflows occur, which are needed for hashing here.
|
||||
+#if defined(__GNUC__) && !defined(__clang__)
|
||||
+#pragma GCC diagnostic push
|
||||
+#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations"
|
||||
+#endif
|
||||
+
|
||||
template <typename FNfloat>
|
||||
float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z) const
|
||||
{
|
||||
@@ -1763,6 +1771,9 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
+#if defined(__GNUC__) && !defined(__clang__)
|
||||
+#pragma GCC diagnostic pop
|
||||
+#endif
|
||||
|
||||
// Perlin Noise
|
||||
|
||||
@@ -2583,4 +2594,6 @@ const T FastNoiseLite::Lookup<T>::RandVecs3D[] =
|
||||
-0.7870349638f, 0.03447489231f, 0.6159443543f, 0, -0.2015596421f, 0.6859872284f, 0.6991389226f, 0, -0.08581082512f, -0.10920836f, -0.9903080513f, 0, 0.5532693395f, 0.7325250401f, -0.396610771f, 0, -0.1842489331f, -0.9777375055f, -0.1004076743f, 0, 0.0775473789f, -0.9111505856f, 0.4047110257f, 0, 0.1399838409f, 0.7601631212f, -0.6344734459f, 0, 0.4484419361f, -0.845289248f, 0.2904925424f, 0
|
||||
};
|
||||
|
||||
+} // namespace fastnoiselite
|
||||
+
|
||||
#endif
|
||||
32
thirdparty/misc/patches/ifaddrs-android-0001-complete-struct.patch
vendored
Normal file
32
thirdparty/misc/patches/ifaddrs-android-0001-complete-struct.patch
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
diff --git a/thirdparty/misc/ifaddrs-android.h b/thirdparty/misc/ifaddrs-android.h
|
||||
index e7d81e813f..04ff2ca58b 100644
|
||||
--- a/thirdparty/misc/ifaddrs-android.h
|
||||
+++ b/thirdparty/misc/ifaddrs-android.h
|
||||
@@ -34,6 +34,9 @@
|
||||
// Fills out a list of ifaddr structs (see below) which contain information
|
||||
// about every network interface available on the host.
|
||||
// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function).
|
||||
+#ifdef __cplusplus
|
||||
+extern "C" {
|
||||
+#endif
|
||||
struct ifaddrs {
|
||||
struct ifaddrs* ifa_next;
|
||||
char* ifa_name;
|
||||
@@ -42,7 +45,17 @@ struct ifaddrs {
|
||||
struct sockaddr* ifa_netmask;
|
||||
// Real ifaddrs has broadcast, point to point and data members.
|
||||
// We don't need them (yet?).
|
||||
+ // We never initialize the following members. We only define them to match the ifaddrs struct.
|
||||
+ union
|
||||
+ {
|
||||
+ struct sockaddr *ifu_broadaddr;
|
||||
+ struct sockaddr *ifu_dstaddr;
|
||||
+ } ifa_ifu;
|
||||
+ void *ifa_data;
|
||||
};
|
||||
+#ifdef __cplusplus
|
||||
+}
|
||||
+#endif
|
||||
|
||||
int getifaddrs(struct ifaddrs** result);
|
||||
void freeifaddrs(struct ifaddrs* addrs);
|
||||
816
thirdparty/misc/patches/polypartition-0001-godot-types.patch
vendored
Normal file
816
thirdparty/misc/patches/polypartition-0001-godot-types.patch
vendored
Normal file
@@ -0,0 +1,816 @@
|
||||
diff --git a/thirdparty/misc/polypartition.cpp b/thirdparty/misc/polypartition.cpp
|
||||
index 3a8a6efa83..a725125ed0 100644
|
||||
--- a/thirdparty/misc/polypartition.cpp
|
||||
+++ b/thirdparty/misc/polypartition.cpp
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
-#include <vector>
|
||||
|
||||
TPPLPoly::TPPLPoly() {
|
||||
hole = false;
|
||||
@@ -186,7 +185,7 @@ int TPPLPartition::Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TP
|
||||
// Removes holes from inpolys by merging them with non-holes.
|
||||
int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
TPPLPolyList polys;
|
||||
- TPPLPolyList::iterator holeiter, polyiter, iter, iter2;
|
||||
+ TPPLPolyList::Element *holeiter, *polyiter, *iter, *iter2;
|
||||
long i, i2, holepointindex, polypointindex;
|
||||
TPPLPoint holepoint, polypoint, bestpolypoint;
|
||||
TPPLPoint linep1, linep2;
|
||||
@@ -198,15 +197,15 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
|
||||
// Check for the trivial case of no holes.
|
||||
hasholes = false;
|
||||
- for (iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||
- if (iter->IsHole()) {
|
||||
+ for (iter = inpolys->front(); iter; iter = iter->next()) {
|
||||
+ if (iter->get().IsHole()) {
|
||||
hasholes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasholes) {
|
||||
- for (iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||
- outpolys->push_back(*iter);
|
||||
+ for (iter = inpolys->front(); iter; iter = iter->next()) {
|
||||
+ outpolys->push_back(iter->get());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -216,8 +215,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
while (1) {
|
||||
// Find the hole point with the largest x.
|
||||
hasholes = false;
|
||||
- for (iter = polys.begin(); iter != polys.end(); iter++) {
|
||||
- if (!iter->IsHole()) {
|
||||
+ for (iter = polys.front(); iter; iter = iter->next()) {
|
||||
+ if (!iter->get().IsHole()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -227,8 +226,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
holepointindex = 0;
|
||||
}
|
||||
|
||||
- for (i = 0; i < iter->GetNumPoints(); i++) {
|
||||
- if (iter->GetPoint(i).x > holeiter->GetPoint(holepointindex).x) {
|
||||
+ for (i = 0; i < iter->get().GetNumPoints(); i++) {
|
||||
+ if (iter->get().GetPoint(i).x > holeiter->get().GetPoint(holepointindex).x) {
|
||||
holeiter = iter;
|
||||
holepointindex = i;
|
||||
}
|
||||
@@ -237,24 +236,24 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
if (!hasholes) {
|
||||
break;
|
||||
}
|
||||
- holepoint = holeiter->GetPoint(holepointindex);
|
||||
+ holepoint = holeiter->get().GetPoint(holepointindex);
|
||||
|
||||
pointfound = false;
|
||||
- for (iter = polys.begin(); iter != polys.end(); iter++) {
|
||||
- if (iter->IsHole()) {
|
||||
+ for (iter = polys.front(); iter; iter = iter->next()) {
|
||||
+ if (iter->get().IsHole()) {
|
||||
continue;
|
||||
}
|
||||
- for (i = 0; i < iter->GetNumPoints(); i++) {
|
||||
- if (iter->GetPoint(i).x <= holepoint.x) {
|
||||
+ for (i = 0; i < iter->get().GetNumPoints(); i++) {
|
||||
+ if (iter->get().GetPoint(i).x <= holepoint.x) {
|
||||
continue;
|
||||
}
|
||||
- if (!InCone(iter->GetPoint((i + iter->GetNumPoints() - 1) % (iter->GetNumPoints())),
|
||||
- iter->GetPoint(i),
|
||||
- iter->GetPoint((i + 1) % (iter->GetNumPoints())),
|
||||
+ if (!InCone(iter->get().GetPoint((i + iter->get().GetNumPoints() - 1) % (iter->get().GetNumPoints())),
|
||||
+ iter->get().GetPoint(i),
|
||||
+ iter->get().GetPoint((i + 1) % (iter->get().GetNumPoints())),
|
||||
holepoint)) {
|
||||
continue;
|
||||
}
|
||||
- polypoint = iter->GetPoint(i);
|
||||
+ polypoint = iter->get().GetPoint(i);
|
||||
if (pointfound) {
|
||||
v1 = Normalize(polypoint - holepoint);
|
||||
v2 = Normalize(bestpolypoint - holepoint);
|
||||
@@ -263,13 +262,13 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
}
|
||||
}
|
||||
pointvisible = true;
|
||||
- for (iter2 = polys.begin(); iter2 != polys.end(); iter2++) {
|
||||
- if (iter2->IsHole()) {
|
||||
+ for (iter2 = polys.front(); iter2; iter2 = iter2->next()) {
|
||||
+ if (iter2->get().IsHole()) {
|
||||
continue;
|
||||
}
|
||||
- for (i2 = 0; i2 < iter2->GetNumPoints(); i2++) {
|
||||
- linep1 = iter2->GetPoint(i2);
|
||||
- linep2 = iter2->GetPoint((i2 + 1) % (iter2->GetNumPoints()));
|
||||
+ for (i2 = 0; i2 < iter2->get().GetNumPoints(); i2++) {
|
||||
+ linep1 = iter2->get().GetPoint(i2);
|
||||
+ linep2 = iter2->get().GetPoint((i2 + 1) % (iter2->get().GetNumPoints()));
|
||||
if (Intersects(holepoint, polypoint, linep1, linep2)) {
|
||||
pointvisible = false;
|
||||
break;
|
||||
@@ -292,18 +291,18 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- newpoly.Init(holeiter->GetNumPoints() + polyiter->GetNumPoints() + 2);
|
||||
+ newpoly.Init(holeiter->get().GetNumPoints() + polyiter->get().GetNumPoints() + 2);
|
||||
i2 = 0;
|
||||
for (i = 0; i <= polypointindex; i++) {
|
||||
- newpoly[i2] = polyiter->GetPoint(i);
|
||||
+ newpoly[i2] = polyiter->get().GetPoint(i);
|
||||
i2++;
|
||||
}
|
||||
- for (i = 0; i <= holeiter->GetNumPoints(); i++) {
|
||||
- newpoly[i2] = holeiter->GetPoint((i + holepointindex) % holeiter->GetNumPoints());
|
||||
+ for (i = 0; i <= holeiter->get().GetNumPoints(); i++) {
|
||||
+ newpoly[i2] = holeiter->get().GetPoint((i + holepointindex) % holeiter->get().GetNumPoints());
|
||||
i2++;
|
||||
}
|
||||
- for (i = polypointindex; i < polyiter->GetNumPoints(); i++) {
|
||||
- newpoly[i2] = polyiter->GetPoint(i);
|
||||
+ for (i = polypointindex; i < polyiter->get().GetNumPoints(); i++) {
|
||||
+ newpoly[i2] = polyiter->get().GetPoint(i);
|
||||
i2++;
|
||||
}
|
||||
|
||||
@@ -312,8 +311,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
polys.push_back(newpoly);
|
||||
}
|
||||
|
||||
- for (iter = polys.begin(); iter != polys.end(); iter++) {
|
||||
- outpolys->push_back(*iter);
|
||||
+ for (iter = polys.front(); iter; iter = iter->next()) {
|
||||
+ outpolys->push_back(iter->get());
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -524,13 +523,13 @@ int TPPLPartition::Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles) {
|
||||
|
||||
int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
TPPLPolyList outpolys;
|
||||
- TPPLPolyList::iterator iter;
|
||||
+ TPPLPolyList::Element *iter;
|
||||
|
||||
if (!RemoveHoles(inpolys, &outpolys)) {
|
||||
return 0;
|
||||
}
|
||||
- for (iter = outpolys.begin(); iter != outpolys.end(); iter++) {
|
||||
- if (!Triangulate_EC(&(*iter), triangles)) {
|
||||
+ for (iter = outpolys.front(); iter; iter = iter->next()) {
|
||||
+ if (!Triangulate_EC(&(iter->get()), triangles)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -543,7 +542,7 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
}
|
||||
|
||||
TPPLPolyList triangles;
|
||||
- TPPLPolyList::iterator iter1, iter2;
|
||||
+ TPPLPolyList::Element *iter1, *iter2;
|
||||
TPPLPoly *poly1 = NULL, *poly2 = NULL;
|
||||
TPPLPoly newpoly;
|
||||
TPPLPoint d1, d2, p1, p2, p3;
|
||||
@@ -578,19 +577,19 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- for (iter1 = triangles.begin(); iter1 != triangles.end(); iter1++) {
|
||||
- poly1 = &(*iter1);
|
||||
+ for (iter1 = triangles.front(); iter1; iter1 = iter1->next()) {
|
||||
+ poly1 = &(iter1->get());
|
||||
for (i11 = 0; i11 < poly1->GetNumPoints(); i11++) {
|
||||
d1 = poly1->GetPoint(i11);
|
||||
i12 = (i11 + 1) % (poly1->GetNumPoints());
|
||||
d2 = poly1->GetPoint(i12);
|
||||
|
||||
isdiagonal = false;
|
||||
- for (iter2 = iter1; iter2 != triangles.end(); iter2++) {
|
||||
+ for (iter2 = iter1; iter2; iter2 = iter2->next()) {
|
||||
if (iter1 == iter2) {
|
||||
continue;
|
||||
}
|
||||
- poly2 = &(*iter2);
|
||||
+ poly2 = &(iter2->get());
|
||||
|
||||
for (i21 = 0; i21 < poly2->GetNumPoints(); i21++) {
|
||||
if ((d2.x != poly2->GetPoint(i21).x) || (d2.y != poly2->GetPoint(i21).y)) {
|
||||
@@ -660,16 +659,16 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
}
|
||||
|
||||
triangles.erase(iter2);
|
||||
- *iter1 = newpoly;
|
||||
- poly1 = &(*iter1);
|
||||
+ iter1->get() = newpoly;
|
||||
+ poly1 = &(iter1->get());
|
||||
i11 = -1;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
- for (iter1 = triangles.begin(); iter1 != triangles.end(); iter1++) {
|
||||
- parts->push_back(*iter1);
|
||||
+ for (iter1 = triangles.front(); iter1; iter1 = iter1->next()) {
|
||||
+ parts->push_back(iter1->get());
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -677,13 +676,13 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
|
||||
int TPPLPartition::ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts) {
|
||||
TPPLPolyList outpolys;
|
||||
- TPPLPolyList::iterator iter;
|
||||
+ TPPLPolyList::Element *iter;
|
||||
|
||||
if (!RemoveHoles(inpolys, &outpolys)) {
|
||||
return 0;
|
||||
}
|
||||
- for (iter = outpolys.begin(); iter != outpolys.end(); iter++) {
|
||||
- if (!ConvexPartition_HM(&(*iter), parts)) {
|
||||
+ for (iter = outpolys.front(); iter; iter = iter->next()) {
|
||||
+ if (!ConvexPartition_HM(&(iter->get()), parts)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -824,8 +823,8 @@ int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles) {
|
||||
newdiagonal.index1 = 0;
|
||||
newdiagonal.index2 = n - 1;
|
||||
diagonals.push_back(newdiagonal);
|
||||
- while (!diagonals.empty()) {
|
||||
- diagonal = *(diagonals.begin());
|
||||
+ while (!diagonals.is_empty()) {
|
||||
+ diagonal = diagonals.front()->get();
|
||||
diagonals.pop_front();
|
||||
bestvertex = dpstates[diagonal.index2][diagonal.index1].bestvertex;
|
||||
if (bestvertex == -1) {
|
||||
@@ -873,10 +872,10 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
|
||||
pairs->push_front(newdiagonal);
|
||||
dpstates[a][b].weight = w;
|
||||
} else {
|
||||
- if ((!pairs->empty()) && (i <= pairs->begin()->index1)) {
|
||||
+ if ((!pairs->is_empty()) && (i <= pairs->front()->get().index1)) {
|
||||
return;
|
||||
}
|
||||
- while ((!pairs->empty()) && (pairs->begin()->index2 >= j)) {
|
||||
+ while ((!pairs->is_empty()) && (pairs->front()->get().index2 >= j)) {
|
||||
pairs->pop_front();
|
||||
}
|
||||
pairs->push_front(newdiagonal);
|
||||
@@ -885,7 +884,7 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
|
||||
|
||||
void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
|
||||
DiagonalList *pairs = NULL;
|
||||
- DiagonalList::iterator iter, lastiter;
|
||||
+ DiagonalList::Element *iter, *lastiter;
|
||||
long top;
|
||||
long w;
|
||||
|
||||
@@ -902,23 +901,23 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
|
||||
}
|
||||
if (j - i > 1) {
|
||||
pairs = &(dpstates[i][j].pairs);
|
||||
- iter = pairs->end();
|
||||
- lastiter = pairs->end();
|
||||
- while (iter != pairs->begin()) {
|
||||
+ iter = pairs->back();
|
||||
+ lastiter = pairs->back();
|
||||
+ while (iter != pairs->front()) {
|
||||
iter--;
|
||||
- if (!IsReflex(vertices[iter->index2].p, vertices[j].p, vertices[k].p)) {
|
||||
+ if (!IsReflex(vertices[iter->get().index2].p, vertices[j].p, vertices[k].p)) {
|
||||
lastiter = iter;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
- if (lastiter == pairs->end()) {
|
||||
+ if (lastiter == pairs->back()) {
|
||||
w++;
|
||||
} else {
|
||||
- if (IsReflex(vertices[k].p, vertices[i].p, vertices[lastiter->index1].p)) {
|
||||
+ if (IsReflex(vertices[k].p, vertices[i].p, vertices[lastiter->get().index1].p)) {
|
||||
w++;
|
||||
} else {
|
||||
- top = lastiter->index1;
|
||||
+ top = lastiter->get().index1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -927,7 +926,7 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
|
||||
|
||||
void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
|
||||
DiagonalList *pairs = NULL;
|
||||
- DiagonalList::iterator iter, lastiter;
|
||||
+ DiagonalList::Element *iter, *lastiter;
|
||||
long top;
|
||||
long w;
|
||||
|
||||
@@ -946,21 +945,21 @@ void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPS
|
||||
if (k - j > 1) {
|
||||
pairs = &(dpstates[j][k].pairs);
|
||||
|
||||
- iter = pairs->begin();
|
||||
- if ((!pairs->empty()) && (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->index1].p))) {
|
||||
+ iter = pairs->front();
|
||||
+ if ((!pairs->is_empty()) && (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->get().index1].p))) {
|
||||
lastiter = iter;
|
||||
- while (iter != pairs->end()) {
|
||||
- if (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->index1].p)) {
|
||||
+ while (iter) {
|
||||
+ if (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->get().index1].p)) {
|
||||
lastiter = iter;
|
||||
- iter++;
|
||||
+ iter = iter->next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
- if (IsReflex(vertices[lastiter->index2].p, vertices[k].p, vertices[i].p)) {
|
||||
+ if (IsReflex(vertices[lastiter->get().index2].p, vertices[k].p, vertices[i].p)) {
|
||||
w++;
|
||||
} else {
|
||||
- top = lastiter->index2;
|
||||
+ top = lastiter->get().index2;
|
||||
}
|
||||
} else {
|
||||
w++;
|
||||
@@ -981,11 +980,11 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
DiagonalList diagonals, diagonals2;
|
||||
Diagonal diagonal, newdiagonal;
|
||||
DiagonalList *pairs = NULL, *pairs2 = NULL;
|
||||
- DiagonalList::iterator iter, iter2;
|
||||
+ DiagonalList::Element *iter, *iter2;
|
||||
int ret;
|
||||
TPPLPoly newpoly;
|
||||
- std::vector<long> indices;
|
||||
- std::vector<long>::iterator iiter;
|
||||
+ List<long> indices;
|
||||
+ List<long>::Element *iiter;
|
||||
bool ijreal, jkreal;
|
||||
|
||||
n = poly->GetNumPoints();
|
||||
@@ -1110,35 +1109,35 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
newdiagonal.index1 = 0;
|
||||
newdiagonal.index2 = n - 1;
|
||||
diagonals.push_front(newdiagonal);
|
||||
- while (!diagonals.empty()) {
|
||||
- diagonal = *(diagonals.begin());
|
||||
+ while (!diagonals.is_empty()) {
|
||||
+ diagonal = diagonals.front()->get();
|
||||
diagonals.pop_front();
|
||||
if ((diagonal.index2 - diagonal.index1) <= 1) {
|
||||
continue;
|
||||
}
|
||||
pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
|
||||
- if (pairs->empty()) {
|
||||
+ if (pairs->is_empty()) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (!vertices[diagonal.index1].isConvex) {
|
||||
- iter = pairs->end();
|
||||
+ iter = pairs->back();
|
||||
iter--;
|
||||
- j = iter->index2;
|
||||
+ j = iter->get().index2;
|
||||
newdiagonal.index1 = j;
|
||||
newdiagonal.index2 = diagonal.index2;
|
||||
diagonals.push_front(newdiagonal);
|
||||
if ((j - diagonal.index1) > 1) {
|
||||
- if (iter->index1 != iter->index2) {
|
||||
+ if (iter->get().index1 != iter->get().index2) {
|
||||
pairs2 = &(dpstates[diagonal.index1][j].pairs);
|
||||
while (1) {
|
||||
- if (pairs2->empty()) {
|
||||
+ if (pairs2->is_empty()) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
- iter2 = pairs2->end();
|
||||
+ iter2 = pairs2->back();
|
||||
iter2--;
|
||||
- if (iter->index1 != iter2->index1) {
|
||||
+ if (iter->get().index1 != iter2->get().index1) {
|
||||
pairs2->pop_back();
|
||||
} else {
|
||||
break;
|
||||
@@ -1153,21 +1152,21 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
diagonals.push_front(newdiagonal);
|
||||
}
|
||||
} else {
|
||||
- iter = pairs->begin();
|
||||
- j = iter->index1;
|
||||
+ iter = pairs->front();
|
||||
+ j = iter->get().index1;
|
||||
newdiagonal.index1 = diagonal.index1;
|
||||
newdiagonal.index2 = j;
|
||||
diagonals.push_front(newdiagonal);
|
||||
if ((diagonal.index2 - j) > 1) {
|
||||
- if (iter->index1 != iter->index2) {
|
||||
+ if (iter->get().index1 != iter->get().index2) {
|
||||
pairs2 = &(dpstates[j][diagonal.index2].pairs);
|
||||
while (1) {
|
||||
- if (pairs2->empty()) {
|
||||
+ if (pairs2->is_empty()) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
- iter2 = pairs2->begin();
|
||||
- if (iter->index2 != iter2->index2) {
|
||||
+ iter2 = pairs2->front();
|
||||
+ if (iter->get().index2 != iter2->get().index2) {
|
||||
pairs2->pop_front();
|
||||
} else {
|
||||
break;
|
||||
@@ -1197,8 +1196,8 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
newdiagonal.index1 = 0;
|
||||
newdiagonal.index2 = n - 1;
|
||||
diagonals.push_front(newdiagonal);
|
||||
- while (!diagonals.empty()) {
|
||||
- diagonal = *(diagonals.begin());
|
||||
+ while (!diagonals.is_empty()) {
|
||||
+ diagonal = diagonals.front()->get();
|
||||
diagonals.pop_front();
|
||||
if ((diagonal.index2 - diagonal.index1) <= 1) {
|
||||
continue;
|
||||
@@ -1210,8 +1209,8 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
indices.push_back(diagonal.index2);
|
||||
diagonals2.push_front(diagonal);
|
||||
|
||||
- while (!diagonals2.empty()) {
|
||||
- diagonal = *(diagonals2.begin());
|
||||
+ while (!diagonals2.is_empty()) {
|
||||
+ diagonal = diagonals2.front()->get();
|
||||
diagonals2.pop_front();
|
||||
if ((diagonal.index2 - diagonal.index1) <= 1) {
|
||||
continue;
|
||||
@@ -1220,16 +1219,16 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
jkreal = true;
|
||||
pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
|
||||
if (!vertices[diagonal.index1].isConvex) {
|
||||
- iter = pairs->end();
|
||||
+ iter = pairs->back();
|
||||
iter--;
|
||||
- j = iter->index2;
|
||||
- if (iter->index1 != iter->index2) {
|
||||
+ j = iter->get().index2;
|
||||
+ if (iter->get().index1 != iter->get().index2) {
|
||||
ijreal = false;
|
||||
}
|
||||
} else {
|
||||
- iter = pairs->begin();
|
||||
- j = iter->index1;
|
||||
- if (iter->index1 != iter->index2) {
|
||||
+ iter = pairs->front();
|
||||
+ j = iter->get().index1;
|
||||
+ if (iter->get().index1 != iter->get().index2) {
|
||||
jkreal = false;
|
||||
}
|
||||
}
|
||||
@@ -1253,11 +1252,12 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
indices.push_back(j);
|
||||
}
|
||||
|
||||
- std::sort(indices.begin(), indices.end());
|
||||
+ //std::sort(indices.begin(), indices.end());
|
||||
+ indices.sort();
|
||||
newpoly.Init((long)indices.size());
|
||||
k = 0;
|
||||
- for (iiter = indices.begin(); iiter != indices.end(); iiter++) {
|
||||
- newpoly[k] = vertices[*iiter].p;
|
||||
+ for (iiter = indices.front(); iiter != indices.back(); iiter = iiter->next()) {
|
||||
+ newpoly[k] = vertices[iiter->get()].p;
|
||||
k++;
|
||||
}
|
||||
parts->push_back(newpoly);
|
||||
@@ -1281,7 +1281,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
// "Computational Geometry: Algorithms and Applications"
|
||||
// by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars.
|
||||
int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys) {
|
||||
- TPPLPolyList::iterator iter;
|
||||
+ TPPLPolyList::Element *iter;
|
||||
MonotoneVertex *vertices = NULL;
|
||||
long i, numvertices, vindex, vindex2, newnumvertices, maxnumvertices;
|
||||
long polystartindex, polyendindex;
|
||||
@@ -1291,11 +1291,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
bool error = false;
|
||||
|
||||
numvertices = 0;
|
||||
- for (iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||
- if (!iter->Valid()) {
|
||||
- return 0;
|
||||
- }
|
||||
- numvertices += iter->GetNumPoints();
|
||||
+ for (iter = inpolys->front(); iter; iter = iter->next()) {
|
||||
+ numvertices += iter->get().GetNumPoints();
|
||||
}
|
||||
|
||||
maxnumvertices = numvertices * 3;
|
||||
@@ -1303,8 +1300,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
newnumvertices = numvertices;
|
||||
|
||||
polystartindex = 0;
|
||||
- for (iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||
- poly = &(*iter);
|
||||
+ for (iter = inpolys->front(); iter; iter = iter->next()) {
|
||||
+ poly = &(iter->get());
|
||||
polyendindex = polystartindex + poly->GetNumPoints() - 1;
|
||||
for (i = 0; i < poly->GetNumPoints(); i++) {
|
||||
vertices[i + polystartindex].p = poly->GetPoint(i);
|
||||
@@ -1360,14 +1357,14 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
// Note that while set doesn't actually have to be implemented as
|
||||
// a tree, complexity requirements for operations are the same as
|
||||
// for the balanced binary search tree.
|
||||
- std::set<ScanLineEdge> edgeTree;
|
||||
+ RBSet<ScanLineEdge> edgeTree;
|
||||
// Store iterators to the edge tree elements.
|
||||
// This makes deleting existing edges much faster.
|
||||
- std::set<ScanLineEdge>::iterator *edgeTreeIterators, edgeIter;
|
||||
- edgeTreeIterators = new std::set<ScanLineEdge>::iterator[maxnumvertices];
|
||||
- std::pair<std::set<ScanLineEdge>::iterator, bool> edgeTreeRet;
|
||||
+ RBSet<ScanLineEdge>::Element **edgeTreeIterators, *edgeIter;
|
||||
+ edgeTreeIterators = new RBSet<ScanLineEdge>::Element *[maxnumvertices];
|
||||
+ //Pair<RBSet<ScanLineEdge>::iterator, bool> edgeTreeRet;
|
||||
for (i = 0; i < numvertices; i++) {
|
||||
- edgeTreeIterators[i] = edgeTree.end();
|
||||
+ edgeTreeIterators[i] = nullptr;
|
||||
}
|
||||
|
||||
// For each vertex.
|
||||
@@ -1387,13 +1384,14 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
newedge.p1 = v->p;
|
||||
newedge.p2 = vertices[v->next].p;
|
||||
newedge.index = vindex;
|
||||
- edgeTreeRet = edgeTree.insert(newedge);
|
||||
- edgeTreeIterators[vindex] = edgeTreeRet.first;
|
||||
+ //edgeTreeRet = edgeTree.insert(newedge);
|
||||
+ //edgeTreeIterators[vindex] = edgeTreeRet.first;
|
||||
+ edgeTreeIterators[vindex] = edgeTree.insert(newedge);
|
||||
helpers[vindex] = vindex;
|
||||
break;
|
||||
|
||||
case TPPL_VERTEXTYPE_END:
|
||||
- if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||
+ if (edgeTreeIterators[v->previous] == edgeTree.back()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
@@ -1412,29 +1410,30 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
newedge.p1 = v->p;
|
||||
newedge.p2 = v->p;
|
||||
edgeIter = edgeTree.lower_bound(newedge);
|
||||
- if (edgeIter == edgeTree.begin()) {
|
||||
+ if (edgeIter == nullptr || edgeIter == edgeTree.front()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
edgeIter--;
|
||||
// Insert the diagonal connecting vi to helper(e_j) in D.
|
||||
- AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->index],
|
||||
+ AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->get().index],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
vindex2 = newnumvertices - 2;
|
||||
v2 = &(vertices[vindex2]);
|
||||
// helper(e_j) in v_i.
|
||||
- helpers[edgeIter->index] = vindex;
|
||||
+ helpers[edgeIter->get().index] = vindex;
|
||||
// Insert e_i in T and set helper(e_i) to v_i.
|
||||
newedge.p1 = v2->p;
|
||||
newedge.p2 = vertices[v2->next].p;
|
||||
newedge.index = vindex2;
|
||||
- edgeTreeRet = edgeTree.insert(newedge);
|
||||
- edgeTreeIterators[vindex2] = edgeTreeRet.first;
|
||||
+ //edgeTreeRet = edgeTree.insert(newedge);
|
||||
+ //edgeTreeIterators[vindex2] = edgeTreeRet.first;
|
||||
+ edgeTreeIterators[vindex2] = edgeTree.insert(newedge);
|
||||
helpers[vindex2] = vindex2;
|
||||
break;
|
||||
|
||||
case TPPL_VERTEXTYPE_MERGE:
|
||||
- if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||
+ if (edgeTreeIterators[v->previous] == edgeTree.back()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
@@ -1452,25 +1451,25 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
newedge.p1 = v->p;
|
||||
newedge.p2 = v->p;
|
||||
edgeIter = edgeTree.lower_bound(newedge);
|
||||
- if (edgeIter == edgeTree.begin()) {
|
||||
+ if (edgeIter == nullptr || edgeIter == edgeTree.front()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
edgeIter--;
|
||||
// If helper(e_j) is a merge vertex.
|
||||
- if (vertextypes[helpers[edgeIter->index]] == TPPL_VERTEXTYPE_MERGE) {
|
||||
+ if (vertextypes[helpers[edgeIter->get().index]] == TPPL_VERTEXTYPE_MERGE) {
|
||||
// Insert the diagonal connecting v_i to helper(e_j) in D.
|
||||
- AddDiagonal(vertices, &newnumvertices, vindex2, helpers[edgeIter->index],
|
||||
+ AddDiagonal(vertices, &newnumvertices, vindex2, helpers[edgeIter->get().index],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
}
|
||||
// helper(e_j) <- v_i
|
||||
- helpers[edgeIter->index] = vindex2;
|
||||
+ helpers[edgeIter->get().index] = vindex2;
|
||||
break;
|
||||
|
||||
case TPPL_VERTEXTYPE_REGULAR:
|
||||
// If the interior of P lies to the right of v_i.
|
||||
if (Below(v->p, vertices[v->previous].p)) {
|
||||
- if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||
+ if (edgeTreeIterators[v->previous] == edgeTree.back()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
@@ -1488,27 +1487,28 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
newedge.p1 = v2->p;
|
||||
newedge.p2 = vertices[v2->next].p;
|
||||
newedge.index = vindex2;
|
||||
- edgeTreeRet = edgeTree.insert(newedge);
|
||||
- edgeTreeIterators[vindex2] = edgeTreeRet.first;
|
||||
+ //edgeTreeRet = edgeTree.insert(newedge);
|
||||
+ //edgeTreeIterators[vindex2] = edgeTreeRet.first;
|
||||
+ edgeTreeIterators[vindex2] = edgeTree.insert(newedge);
|
||||
helpers[vindex2] = vindex;
|
||||
} else {
|
||||
// Search in T to find the edge e_j directly left of v_i.
|
||||
newedge.p1 = v->p;
|
||||
newedge.p2 = v->p;
|
||||
edgeIter = edgeTree.lower_bound(newedge);
|
||||
- if (edgeIter == edgeTree.begin()) {
|
||||
+ if (edgeIter == nullptr || edgeIter == edgeTree.front()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
- edgeIter--;
|
||||
+ edgeIter = edgeIter->prev();
|
||||
// If helper(e_j) is a merge vertex.
|
||||
- if (vertextypes[helpers[edgeIter->index]] == TPPL_VERTEXTYPE_MERGE) {
|
||||
+ if (vertextypes[helpers[edgeIter->get().index]] == TPPL_VERTEXTYPE_MERGE) {
|
||||
// Insert the diagonal connecting v_i to helper(e_j) in D.
|
||||
- AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->index],
|
||||
+ AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->get().index],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
}
|
||||
// helper(e_j) <- v_i.
|
||||
- helpers[edgeIter->index] = vindex;
|
||||
+ helpers[edgeIter->get().index] = vindex;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1569,8 +1569,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
|
||||
|
||||
// Adds a diagonal to the doubly-connected list of vertices.
|
||||
void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
|
||||
- TPPLVertexType *vertextypes, std::set<ScanLineEdge>::iterator *edgeTreeIterators,
|
||||
- std::set<ScanLineEdge> *edgeTree, long *helpers) {
|
||||
+ TPPLVertexType *vertextypes, RBSet<ScanLineEdge>::Element **edgeTreeIterators,
|
||||
+ RBSet<ScanLineEdge> *edgeTree, long *helpers) {
|
||||
long newindex1, newindex2;
|
||||
|
||||
newindex1 = *numvertices;
|
||||
@@ -1597,14 +1597,14 @@ void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, lon
|
||||
vertextypes[newindex1] = vertextypes[index1];
|
||||
edgeTreeIterators[newindex1] = edgeTreeIterators[index1];
|
||||
helpers[newindex1] = helpers[index1];
|
||||
- if (edgeTreeIterators[newindex1] != edgeTree->end()) {
|
||||
- edgeTreeIterators[newindex1]->index = newindex1;
|
||||
+ if (edgeTreeIterators[newindex1] != edgeTree->back()) {
|
||||
+ edgeTreeIterators[newindex1]->get().index = newindex1;
|
||||
}
|
||||
vertextypes[newindex2] = vertextypes[index2];
|
||||
edgeTreeIterators[newindex2] = edgeTreeIterators[index2];
|
||||
helpers[newindex2] = helpers[index2];
|
||||
- if (edgeTreeIterators[newindex2] != edgeTree->end()) {
|
||||
- edgeTreeIterators[newindex2]->index = newindex2;
|
||||
+ if (edgeTreeIterators[newindex2] != edgeTree->back()) {
|
||||
+ edgeTreeIterators[newindex2]->get().index = newindex2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1830,13 +1830,13 @@ int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles
|
||||
|
||||
int TPPLPartition::Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
TPPLPolyList monotone;
|
||||
- TPPLPolyList::iterator iter;
|
||||
+ TPPLPolyList::Element *iter;
|
||||
|
||||
if (!MonotonePartition(inpolys, &monotone)) {
|
||||
return 0;
|
||||
}
|
||||
- for (iter = monotone.begin(); iter != monotone.end(); iter++) {
|
||||
- if (!TriangulateMonotone(&(*iter), triangles)) {
|
||||
+ for (iter = monotone.front(); iter; iter = iter->next()) {
|
||||
+ if (!TriangulateMonotone(&(iter->get()), triangles)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
diff --git a/thirdparty/misc/polypartition.h b/thirdparty/misc/polypartition.h
|
||||
index e1df6cef9e..c084bdf74c 100644
|
||||
--- a/thirdparty/misc/polypartition.h
|
||||
+++ b/thirdparty/misc/polypartition.h
|
||||
@@ -24,8 +24,9 @@
|
||||
#ifndef POLYPARTITION_H
|
||||
#define POLYPARTITION_H
|
||||
|
||||
-#include <list>
|
||||
-#include <set>
|
||||
+#include "core/math/vector2.h"
|
||||
+#include "core/templates/list.h"
|
||||
+#include "core/templates/rb_set.h"
|
||||
|
||||
typedef double tppl_float;
|
||||
|
||||
@@ -44,49 +45,7 @@ enum TPPLVertexType {
|
||||
};
|
||||
|
||||
// 2D point structure.
|
||||
-struct TPPLPoint {
|
||||
- tppl_float x;
|
||||
- tppl_float y;
|
||||
- // User-specified vertex identifier. Note that this isn't used internally
|
||||
- // by the library, but will be faithfully copied around.
|
||||
- int id;
|
||||
-
|
||||
- TPPLPoint operator+(const TPPLPoint &p) const {
|
||||
- TPPLPoint r;
|
||||
- r.x = x + p.x;
|
||||
- r.y = y + p.y;
|
||||
- return r;
|
||||
- }
|
||||
-
|
||||
- TPPLPoint operator-(const TPPLPoint &p) const {
|
||||
- TPPLPoint r;
|
||||
- r.x = x - p.x;
|
||||
- r.y = y - p.y;
|
||||
- return r;
|
||||
- }
|
||||
-
|
||||
- TPPLPoint operator*(const tppl_float f) const {
|
||||
- TPPLPoint r;
|
||||
- r.x = x * f;
|
||||
- r.y = y * f;
|
||||
- return r;
|
||||
- }
|
||||
-
|
||||
- TPPLPoint operator/(const tppl_float f) const {
|
||||
- TPPLPoint r;
|
||||
- r.x = x / f;
|
||||
- r.y = y / f;
|
||||
- return r;
|
||||
- }
|
||||
-
|
||||
- bool operator==(const TPPLPoint &p) const {
|
||||
- return ((x == p.x) && (y == p.y));
|
||||
- }
|
||||
-
|
||||
- bool operator!=(const TPPLPoint &p) const {
|
||||
- return !((x == p.x) && (y == p.y));
|
||||
- }
|
||||
-};
|
||||
+typedef Vector2 TPPLPoint;
|
||||
|
||||
// Polygon implemented as an array of points with a "hole" flag.
|
||||
class TPPLPoly {
|
||||
@@ -168,9 +127,9 @@ class TPPLPoly {
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
-typedef std::list<TPPLPoly, TPPL_ALLOCATOR(TPPLPoly)> TPPLPolyList;
|
||||
+typedef List<TPPLPoly, TPPL_ALLOCATOR(TPPLPoly)> TPPLPolyList;
|
||||
#else
|
||||
-typedef std::list<TPPLPoly> TPPLPolyList;
|
||||
+typedef List<TPPLPoly> TPPLPolyList;
|
||||
#endif
|
||||
|
||||
class TPPLPartition {
|
||||
@@ -209,9 +168,9 @@ public:
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
- typedef std::list<Diagonal, TPPL_ALLOCATOR(Diagonal)> DiagonalList;
|
||||
+ typedef List<Diagonal, TPPL_ALLOCATOR(Diagonal)> DiagonalList;
|
||||
#else
|
||||
- typedef std::list<Diagonal> DiagonalList;
|
||||
+ typedef List<Diagonal> DiagonalList;
|
||||
#endif
|
||||
|
||||
// Dynamic programming state for minimum-weight triangulation.
|
||||
@@ -265,8 +224,8 @@ public:
|
||||
// Helper functions for MonotonePartition.
|
||||
bool Below(TPPLPoint &p1, TPPLPoint &p2);
|
||||
void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
|
||||
- TPPLVertexType *vertextypes, std::set<ScanLineEdge>::iterator *edgeTreeIterators,
|
||||
- std::set<ScanLineEdge> *edgeTree, long *helpers);
|
||||
+ TPPLVertexType *vertextypes, RBSet<ScanLineEdge>::Element **edgeTreeIterators,
|
||||
+ RBSet<ScanLineEdge> *edgeTree, long *helpers);
|
||||
|
||||
// Triangulates a monotone polygon, used in Triangulate_MONO.
|
||||
int TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles);
|
||||
15
thirdparty/misc/patches/polypartition-0002-shadow-warning.patch
vendored
Normal file
15
thirdparty/misc/patches/polypartition-0002-shadow-warning.patch
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
diff --git a/thirdparty/misc/polypartition.h b/thirdparty/misc/polypartition.h
|
||||
index fae7909079..c084bdf74c 100644
|
||||
--- a/thirdparty/misc/polypartition.h
|
||||
+++ b/thirdparty/misc/polypartition.h
|
||||
@@ -71,8 +71,8 @@ class TPPLPoly {
|
||||
return hole;
|
||||
}
|
||||
|
||||
- void SetHole(bool hole) {
|
||||
- this->hole = hole;
|
||||
+ void SetHole(bool p_hole) {
|
||||
+ this->hole = p_hole;
|
||||
}
|
||||
|
||||
TPPLPoint &GetPoint(long i) {
|
||||
54
thirdparty/misc/patches/smaz-0001-write-string-warning.patch
vendored
Normal file
54
thirdparty/misc/patches/smaz-0001-write-string-warning.patch
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
diff --git a/thirdparty/misc/smaz.c b/thirdparty/misc/smaz.c
|
||||
index aa674c3858..6f9ea20e67 100644
|
||||
--- a/thirdparty/misc/smaz.c
|
||||
+++ b/thirdparty/misc/smaz.c
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <string.h>
|
||||
|
||||
/* Our compression codebook, used for compression */
|
||||
-static char *Smaz_cb[241] = {
|
||||
+static const char *Smaz_cb[241] = {
|
||||
"\002s,\266", "\003had\232\002leW", "\003on \216", "", "\001yS",
|
||||
"\002ma\255\002li\227", "\003or \260", "", "\002ll\230\003s t\277",
|
||||
"\004fromg\002mel", "", "\003its\332", "\001z\333", "\003ingF", "\001>\336",
|
||||
@@ -76,7 +76,7 @@ static char *Smaz_rcb[254] = {
|
||||
"e, ", " it", "whi", " ma", "ge", "x", "e c", "men", ".com"
|
||||
};
|
||||
|
||||
-int smaz_compress(char *in, int inlen, char *out, int outlen) {
|
||||
+int smaz_compress(const char *in, int inlen, char *out, int outlen) {
|
||||
unsigned int h1,h2,h3=0;
|
||||
int verblen = 0, _outlen = outlen;
|
||||
char verb[256], *_out = out;
|
||||
@@ -154,7 +154,7 @@ out:
|
||||
return out-_out;
|
||||
}
|
||||
|
||||
-int smaz_decompress(char *in, int inlen, char *out, int outlen) {
|
||||
+int smaz_decompress(const char *in, int inlen, char *out, int outlen) {
|
||||
unsigned char *c = (unsigned char*) in;
|
||||
char *_out = out;
|
||||
int _outlen = outlen;
|
||||
@@ -179,7 +179,7 @@ int smaz_decompress(char *in, int inlen, char *out, int outlen) {
|
||||
inlen -= 2+len;
|
||||
} else {
|
||||
/* Codebook entry */
|
||||
- char *s = Smaz_rcb[*c];
|
||||
+ const char *s = Smaz_rcb[*c];
|
||||
int len = strlen(s);
|
||||
|
||||
if (outlen < len) return _outlen+1;
|
||||
diff --git a/thirdparty/misc/smaz.h b/thirdparty/misc/smaz.h
|
||||
index ce9c35d6b2..d8e1f8a6a5 100644
|
||||
--- a/thirdparty/misc/smaz.h
|
||||
+++ b/thirdparty/misc/smaz.h
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _SMAZ_H
|
||||
#define _SMAZ_H
|
||||
|
||||
-int smaz_compress(char *in, int inlen, char *out, int outlen);
|
||||
-int smaz_decompress(char *in, int inlen, char *out, int outlen);
|
||||
+int smaz_compress(const char *in, int inlen, char *out, int outlen);
|
||||
+int smaz_decompress(const char *in, int inlen, char *out, int outlen);
|
||||
|
||||
#endif
|
||||
58
thirdparty/misc/pcg.cpp
vendored
Normal file
58
thirdparty/misc/pcg.cpp
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
|
||||
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
|
||||
|
||||
#include "pcg.h"
|
||||
|
||||
uint32_t pcg32_random_r(pcg32_random_t* rng)
|
||||
{
|
||||
uint64_t oldstate = rng->state;
|
||||
// Advance internal state
|
||||
rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
|
||||
// Calculate output function (XSH RR), uses old state for max ILP
|
||||
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||
uint32_t rot = oldstate >> 59u;
|
||||
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
// Source from http://www.pcg-random.org/downloads/pcg-c-basic-0.9.zip
|
||||
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
|
||||
{
|
||||
rng->state = 0U;
|
||||
rng->inc = (initseq << 1u) | 1u;
|
||||
pcg32_random_r(rng);
|
||||
rng->state += initstate;
|
||||
pcg32_random_r(rng);
|
||||
}
|
||||
|
||||
// Source from https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c
|
||||
// pcg32_boundedrand_r(rng, bound):
|
||||
// Generate a uniformly distributed number, r, where 0 <= r < bound
|
||||
uint32_t pcg32_boundedrand_r(pcg32_random_t *rng, uint32_t bound) {
|
||||
// To avoid bias, we need to make the range of the RNG a multiple of
|
||||
// bound, which we do by dropping output less than a threshold.
|
||||
// A naive scheme to calculate the threshold would be to do
|
||||
//
|
||||
// uint32_t threshold = 0x100000000ull % bound;
|
||||
//
|
||||
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
|
||||
// 32-bit platforms). In essence, we do
|
||||
//
|
||||
// uint32_t threshold = (0x100000000ull-bound) % bound;
|
||||
//
|
||||
// because this version will calculate the same modulus, but the LHS
|
||||
// value is less than 2^32.
|
||||
uint32_t threshold = -bound % bound;
|
||||
|
||||
// Uniformity guarantees that this loop will terminate. In practice, it
|
||||
// should usually terminate quickly; on average (assuming all bounds are
|
||||
// equally likely), 82.25% of the time, we can expect it to require just
|
||||
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
|
||||
// (i.e., 2147483649), which invalidates almost 50% of the range. In
|
||||
// practice, bounds are typically small and only a tiny amount of the range
|
||||
// is eliminated.
|
||||
for (;;) {
|
||||
uint32_t r = pcg32_random_r(rng);
|
||||
if (r >= threshold)
|
||||
return r % bound;
|
||||
}
|
||||
}
|
||||
16
thirdparty/misc/pcg.h
vendored
Normal file
16
thirdparty/misc/pcg.h
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
|
||||
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
|
||||
|
||||
#ifndef RANDOM_H
|
||||
#define RANDOM_H
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#define PCG_DEFAULT_INC_64 1442695040888963407ULL
|
||||
|
||||
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
|
||||
uint32_t pcg32_random_r(pcg32_random_t* rng);
|
||||
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq);
|
||||
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);
|
||||
|
||||
#endif // RANDOM_H
|
||||
1851
thirdparty/misc/polypartition.cpp
vendored
Normal file
1851
thirdparty/misc/polypartition.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
378
thirdparty/misc/polypartition.h
vendored
Normal file
378
thirdparty/misc/polypartition.h
vendored
Normal file
@@ -0,0 +1,378 @@
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2011-2021 Ivan Fratric and contributors. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef POLYPARTITION_H
|
||||
#define POLYPARTITION_H
|
||||
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/rb_set.h"
|
||||
|
||||
typedef double tppl_float;
|
||||
|
||||
enum TPPLOrientation {
|
||||
TPPL_ORIENTATION_CW = -1,
|
||||
TPPL_ORIENTATION_NONE = 0,
|
||||
TPPL_ORIENTATION_CCW = 1,
|
||||
};
|
||||
|
||||
enum TPPLVertexType {
|
||||
TPPL_VERTEXTYPE_REGULAR = 0,
|
||||
TPPL_VERTEXTYPE_START = 1,
|
||||
TPPL_VERTEXTYPE_END = 2,
|
||||
TPPL_VERTEXTYPE_SPLIT = 3,
|
||||
TPPL_VERTEXTYPE_MERGE = 4,
|
||||
};
|
||||
|
||||
// 2D point structure.
|
||||
typedef Vector2 TPPLPoint;
|
||||
|
||||
// Polygon implemented as an array of points with a "hole" flag.
|
||||
class TPPLPoly {
|
||||
protected:
|
||||
TPPLPoint *points;
|
||||
long numpoints;
|
||||
bool hole;
|
||||
|
||||
public:
|
||||
// Constructors and destructors.
|
||||
TPPLPoly();
|
||||
~TPPLPoly();
|
||||
|
||||
TPPLPoly(const TPPLPoly &src);
|
||||
TPPLPoly &operator=(const TPPLPoly &src);
|
||||
|
||||
// Getters and setters.
|
||||
long GetNumPoints() const {
|
||||
return numpoints;
|
||||
}
|
||||
|
||||
bool IsHole() const {
|
||||
return hole;
|
||||
}
|
||||
|
||||
void SetHole(bool p_hole) {
|
||||
this->hole = p_hole;
|
||||
}
|
||||
|
||||
TPPLPoint &GetPoint(long i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
const TPPLPoint &GetPoint(long i) const {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
TPPLPoint *GetPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
TPPLPoint &operator[](int i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
const TPPLPoint &operator[](int i) const {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
// Clears the polygon points.
|
||||
void Clear();
|
||||
|
||||
// Inits the polygon with numpoints vertices.
|
||||
void Init(long numpoints);
|
||||
|
||||
// Creates a triangle with points p1, p2, and p3.
|
||||
void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3);
|
||||
|
||||
// Inverts the orfer of vertices.
|
||||
void Invert();
|
||||
|
||||
// Returns the orientation of the polygon.
|
||||
// Possible values:
|
||||
// TPPL_ORIENTATION_CCW: Polygon vertices are in counter-clockwise order.
|
||||
// TPPL_ORIENTATION_CW: Polygon vertices are in clockwise order.
|
||||
// TPPL_ORIENTATION_NONE: The polygon has no (measurable) area.
|
||||
TPPLOrientation GetOrientation() const;
|
||||
|
||||
// Sets the polygon orientation.
|
||||
// Possible values:
|
||||
// TPPL_ORIENTATION_CCW: Sets vertices in counter-clockwise order.
|
||||
// TPPL_ORIENTATION_CW: Sets vertices in clockwise order.
|
||||
// TPPL_ORIENTATION_NONE: Reverses the orientation of the vertices if there
|
||||
// is one, otherwise does nothing (if orientation is already NONE).
|
||||
void SetOrientation(TPPLOrientation orientation);
|
||||
|
||||
// Checks whether a polygon is valid or not.
|
||||
inline bool Valid() const { return this->numpoints >= 3; }
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
typedef List<TPPLPoly, TPPL_ALLOCATOR(TPPLPoly)> TPPLPolyList;
|
||||
#else
|
||||
typedef List<TPPLPoly> TPPLPolyList;
|
||||
#endif
|
||||
|
||||
class TPPLPartition {
|
||||
protected:
|
||||
struct PartitionVertex {
|
||||
bool isActive;
|
||||
bool isConvex;
|
||||
bool isEar;
|
||||
|
||||
TPPLPoint p;
|
||||
tppl_float angle;
|
||||
PartitionVertex *previous;
|
||||
PartitionVertex *next;
|
||||
|
||||
PartitionVertex();
|
||||
};
|
||||
|
||||
struct MonotoneVertex {
|
||||
TPPLPoint p;
|
||||
long previous;
|
||||
long next;
|
||||
};
|
||||
|
||||
class VertexSorter {
|
||||
MonotoneVertex *vertices;
|
||||
|
||||
public:
|
||||
VertexSorter(MonotoneVertex *v) :
|
||||
vertices(v) {}
|
||||
bool operator()(long index1, long index2);
|
||||
};
|
||||
|
||||
struct Diagonal {
|
||||
long index1;
|
||||
long index2;
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
typedef List<Diagonal, TPPL_ALLOCATOR(Diagonal)> DiagonalList;
|
||||
#else
|
||||
typedef List<Diagonal> DiagonalList;
|
||||
#endif
|
||||
|
||||
// Dynamic programming state for minimum-weight triangulation.
|
||||
struct DPState {
|
||||
bool visible;
|
||||
tppl_float weight;
|
||||
long bestvertex;
|
||||
};
|
||||
|
||||
// Dynamic programming state for convex partitioning.
|
||||
struct DPState2 {
|
||||
bool visible;
|
||||
long weight;
|
||||
DiagonalList pairs;
|
||||
};
|
||||
|
||||
// Edge that intersects the scanline.
|
||||
struct ScanLineEdge {
|
||||
mutable long index;
|
||||
TPPLPoint p1;
|
||||
TPPLPoint p2;
|
||||
|
||||
// Determines if the edge is to the left of another edge.
|
||||
bool operator<(const ScanLineEdge &other) const;
|
||||
|
||||
bool IsConvex(const TPPLPoint &p1, const TPPLPoint &p2, const TPPLPoint &p3) const;
|
||||
};
|
||||
|
||||
// Standard helper functions.
|
||||
bool IsConvex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3);
|
||||
bool IsReflex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3);
|
||||
bool IsInside(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p);
|
||||
|
||||
bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p);
|
||||
bool InCone(PartitionVertex *v, TPPLPoint &p);
|
||||
|
||||
int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22);
|
||||
|
||||
TPPLPoint Normalize(const TPPLPoint &p);
|
||||
tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2);
|
||||
|
||||
// Helper functions for Triangulate_EC.
|
||||
void UpdateVertexReflexity(PartitionVertex *v);
|
||||
void UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices);
|
||||
|
||||
// Helper functions for ConvexPartition_OPT.
|
||||
void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates);
|
||||
void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
|
||||
// Helper functions for MonotonePartition.
|
||||
bool Below(TPPLPoint &p1, TPPLPoint &p2);
|
||||
void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
|
||||
TPPLVertexType *vertextypes, RBSet<ScanLineEdge>::Element **edgeTreeIterators,
|
||||
RBSet<ScanLineEdge> *edgeTree, long *helpers);
|
||||
|
||||
// Triangulates a monotone polygon, used in Triangulate_MONO.
|
||||
int TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles);
|
||||
|
||||
public:
|
||||
// Simple heuristic procedure for removing holes from a list of polygons.
|
||||
// It works by creating a diagonal from the right-most hole vertex
|
||||
// to some other visible vertex.
|
||||
// Time complexity: O(h*(n^2)), h is the # of holes, n is the # of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// inpolys:
|
||||
// A list of polygons that can contain holes.
|
||||
// Vertices of all non-hole polys have to be in counter-clockwise order.
|
||||
// Vertices of all hole polys have to be in clockwise order.
|
||||
// outpolys:
|
||||
// A list of polygons without holes.
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys);
|
||||
|
||||
// Triangulates a polygon by ear clipping.
|
||||
// Time complexity: O(n^2), n is the number of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// poly:
|
||||
// An input polygon to be triangulated.
|
||||
// Vertices have to be in counter-clockwise order.
|
||||
// triangles:
|
||||
// A list of triangles (result).
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
// Triangulates a list of polygons that may contain holes by ear clipping
|
||||
// algorithm. It first calls RemoveHoles to get rid of the holes, and then
|
||||
// calls Triangulate_EC for each resulting polygon.
|
||||
// Time complexity: O(h*(n^2)), h is the # of holes, n is the # of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// inpolys:
|
||||
// A list of polygons to be triangulated (can contain holes).
|
||||
// Vertices of all non-hole polys have to be in counter-clockwise order.
|
||||
// Vertices of all hole polys have to be in clockwise order.
|
||||
// triangles:
|
||||
// A list of triangles (result).
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles);
|
||||
|
||||
// Creates an optimal polygon triangulation in terms of minimal edge length.
|
||||
// Time complexity: O(n^3), n is the number of vertices
|
||||
// Space complexity: O(n^2)
|
||||
// params:
|
||||
// poly:
|
||||
// An input polygon to be triangulated.
|
||||
// Vertices have to be in counter-clockwise order.
|
||||
// triangles:
|
||||
// A list of triangles (result).
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
// Triangulates a polygon by first partitioning it into monotone polygons.
|
||||
// Time complexity: O(n*log(n)), n is the number of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// poly:
|
||||
// An input polygon to be triangulated.
|
||||
// Vertices have to be in counter-clockwise order.
|
||||
// triangles:
|
||||
// A list of triangles (result).
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
// Triangulates a list of polygons by first
|
||||
// partitioning them into monotone polygons.
|
||||
// Time complexity: O(n*log(n)), n is the number of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// inpolys:
|
||||
// A list of polygons to be triangulated (can contain holes).
|
||||
// Vertices of all non-hole polys have to be in counter-clockwise order.
|
||||
// Vertices of all hole polys have to be in clockwise order.
|
||||
// triangles:
|
||||
// A list of triangles (result).
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles);
|
||||
|
||||
// Creates a monotone partition of a list of polygons that
|
||||
// can contain holes. Triangulates a set of polygons by
|
||||
// first partitioning them into monotone polygons.
|
||||
// Time complexity: O(n*log(n)), n is the number of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// inpolys:
|
||||
// A list of polygons to be triangulated (can contain holes).
|
||||
// Vertices of all non-hole polys have to be in counter-clockwise order.
|
||||
// Vertices of all hole polys have to be in clockwise order.
|
||||
// monotonePolys:
|
||||
// A list of monotone polygons (result).
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys);
|
||||
|
||||
// Partitions a polygon into convex polygons by using the
|
||||
// Hertel-Mehlhorn algorithm. The algorithm gives at most four times
|
||||
// the number of parts as the optimal algorithm, however, in practice
|
||||
// it works much better than that and often gives optimal partition.
|
||||
// It uses triangulation obtained by ear clipping as intermediate result.
|
||||
// Time complexity O(n^2), n is the number of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// poly:
|
||||
// An input polygon to be partitioned.
|
||||
// Vertices have to be in counter-clockwise order.
|
||||
// parts:
|
||||
// Resulting list of convex polygons.
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts);
|
||||
|
||||
// Partitions a list of polygons into convex parts by using the
|
||||
// Hertel-Mehlhorn algorithm. The algorithm gives at most four times
|
||||
// the number of parts as the optimal algorithm, however, in practice
|
||||
// it works much better than that and often gives optimal partition.
|
||||
// It uses triangulation obtained by ear clipping as intermediate result.
|
||||
// Time complexity O(n^2), n is the number of vertices.
|
||||
// Space complexity: O(n)
|
||||
// params:
|
||||
// inpolys:
|
||||
// An input list of polygons to be partitioned. Vertices of
|
||||
// all non-hole polys have to be in counter-clockwise order.
|
||||
// Vertices of all hole polys have to be in clockwise order.
|
||||
// parts:
|
||||
// Resulting list of convex polygons.
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts);
|
||||
|
||||
// Optimal convex partitioning (in terms of number of resulting
|
||||
// convex polygons) using the Keil-Snoeyink algorithm.
|
||||
// For reference, see M. Keil, J. Snoeyink, "On the time bound for
|
||||
// convex decomposition of simple polygons", 1998.
|
||||
// Time complexity O(n^3), n is the number of vertices.
|
||||
// Space complexity: O(n^3)
|
||||
// params:
|
||||
// poly:
|
||||
// An input polygon to be partitioned.
|
||||
// Vertices have to be in counter-clockwise order.
|
||||
// parts:
|
||||
// Resulting list of convex polygons.
|
||||
// Returns 1 on success, 0 on failure.
|
||||
int ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts);
|
||||
};
|
||||
|
||||
#endif
|
||||
4
thirdparty/misc/qoa.c
vendored
Normal file
4
thirdparty/misc/qoa.c
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#define QOA_IMPLEMENTATION
|
||||
#define QOA_NO_STDIO
|
||||
|
||||
#include "qoa.h"
|
||||
742
thirdparty/misc/qoa.h
vendored
Normal file
742
thirdparty/misc/qoa.h
vendored
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2023, Dominic Szablewski - https://phoboslab.org
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
QOA - The "Quite OK Audio" format for fast, lossy audio compression
|
||||
|
||||
|
||||
-- Data Format
|
||||
|
||||
QOA encodes pulse-code modulated (PCM) audio data with up to 255 channels,
|
||||
sample rates from 1 up to 16777215 hertz and a bit depth of 16 bits.
|
||||
|
||||
The compression method employed in QOA is lossy; it discards some information
|
||||
from the uncompressed PCM data. For many types of audio signals this compression
|
||||
is "transparent", i.e. the difference from the original file is often not
|
||||
audible.
|
||||
|
||||
QOA encodes 20 samples of 16 bit PCM data into slices of 64 bits. A single
|
||||
sample therefore requires 3.2 bits of storage space, resulting in a 5x
|
||||
compression (16 / 3.2).
|
||||
|
||||
A QOA file consists of an 8 byte file header, followed by a number of frames.
|
||||
Each frame contains an 8 byte frame header, the current 16 byte en-/decoder
|
||||
state per channel and 256 slices per channel. Each slice is 8 bytes wide and
|
||||
encodes 20 samples of audio data.
|
||||
|
||||
All values, including the slices, are big endian. The file layout is as follows:
|
||||
|
||||
struct {
|
||||
struct {
|
||||
char magic[4]; // magic bytes "qoaf"
|
||||
uint32_t samples; // samples per channel in this file
|
||||
} file_header;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
uint8_t num_channels; // no. of channels
|
||||
uint24_t samplerate; // samplerate in hz
|
||||
uint16_t fsamples; // samples per channel in this frame
|
||||
uint16_t fsize; // frame size (includes this header)
|
||||
} frame_header;
|
||||
|
||||
struct {
|
||||
int16_t history[4]; // most recent last
|
||||
int16_t weights[4]; // most recent last
|
||||
} lms_state[num_channels];
|
||||
|
||||
qoa_slice_t slices[256][num_channels];
|
||||
|
||||
} frames[ceil(samples / (256 * 20))];
|
||||
} qoa_file_t;
|
||||
|
||||
Each `qoa_slice_t` contains a quantized scalefactor `sf_quant` and 20 quantized
|
||||
residuals `qrNN`:
|
||||
|
||||
.- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------.
|
||||
| Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] |
|
||||
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 |
|
||||
|------------+--------+--------+--------+---------+---------+-\ \--+---------|
|
||||
| sf_quant | qr00 | qr01 | qr02 | qr03 | qr04 | / / | qr19 |
|
||||
`-------------------------------------------------------------\ \------------`
|
||||
|
||||
Each frame except the last must contain exactly 256 slices per channel. The last
|
||||
frame may contain between 1 .. 256 (inclusive) slices per channel. The last
|
||||
slice (for each channel) in the last frame may contain less than 20 samples; the
|
||||
slice still must be 8 bytes wide, with the unused samples zeroed out.
|
||||
|
||||
Channels are interleaved per slice. E.g. for 2 channel stereo:
|
||||
slice[0] = L, slice[1] = R, slice[2] = L, slice[3] = R ...
|
||||
|
||||
A valid QOA file or stream must have at least one frame. Each frame must contain
|
||||
at least one channel and one sample with a samplerate between 1 .. 16777215
|
||||
(inclusive).
|
||||
|
||||
If the total number of samples is not known by the encoder, the samples in the
|
||||
file header may be set to 0x00000000 to indicate that the encoder is
|
||||
"streaming". In a streaming context, the samplerate and number of channels may
|
||||
differ from frame to frame. For static files (those with samples set to a
|
||||
non-zero value), each frame must have the same number of channels and same
|
||||
samplerate.
|
||||
|
||||
Note that this implementation of QOA only handles files with a known total
|
||||
number of samples.
|
||||
|
||||
A decoder should support at least 8 channels. The channel layout for channel
|
||||
counts 1 .. 8 is:
|
||||
|
||||
1. Mono
|
||||
2. L, R
|
||||
3. L, R, C
|
||||
4. FL, FR, B/SL, B/SR
|
||||
5. FL, FR, C, B/SL, B/SR
|
||||
6. FL, FR, C, LFE, B/SL, B/SR
|
||||
7. FL, FR, C, LFE, B, SL, SR
|
||||
8. FL, FR, C, LFE, BL, BR, SL, SR
|
||||
|
||||
QOA predicts each audio sample based on the previously decoded ones using a
|
||||
"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the
|
||||
dequantized residual forms the final output sample.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Header - Public functions */
|
||||
|
||||
#ifndef QOA_H
|
||||
#define QOA_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define QOA_MIN_FILESIZE 16
|
||||
#define QOA_MAX_CHANNELS 8
|
||||
|
||||
#define QOA_SLICE_LEN 20
|
||||
#define QOA_SLICES_PER_FRAME 256
|
||||
#define QOA_FRAME_LEN (QOA_SLICES_PER_FRAME * QOA_SLICE_LEN)
|
||||
#define QOA_LMS_LEN 4
|
||||
#define QOA_MAGIC 0x716f6166 /* 'qoaf' */
|
||||
|
||||
#define QOA_FRAME_SIZE(channels, slices) \
|
||||
(8 + QOA_LMS_LEN * 4 * channels + 8 * slices * channels)
|
||||
|
||||
typedef struct {
|
||||
int history[QOA_LMS_LEN];
|
||||
int weights[QOA_LMS_LEN];
|
||||
} qoa_lms_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int channels;
|
||||
unsigned int samplerate;
|
||||
unsigned int samples;
|
||||
qoa_lms_t lms[QOA_MAX_CHANNELS];
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
double error;
|
||||
#endif
|
||||
} qoa_desc;
|
||||
|
||||
unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes);
|
||||
unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes);
|
||||
void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len);
|
||||
|
||||
unsigned int qoa_max_frame_size(qoa_desc *qoa);
|
||||
unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa);
|
||||
unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len);
|
||||
short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file);
|
||||
|
||||
#ifndef QOA_NO_STDIO
|
||||
|
||||
int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa);
|
||||
void *qoa_read(const char *filename, qoa_desc *qoa);
|
||||
|
||||
#endif /* QOA_NO_STDIO */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* QOA_H */
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Implementation */
|
||||
|
||||
#ifdef QOA_IMPLEMENTATION
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef QOA_MALLOC
|
||||
#define QOA_MALLOC(sz) malloc(sz)
|
||||
#define QOA_FREE(p) free(p)
|
||||
#endif
|
||||
|
||||
typedef unsigned long long qoa_uint64_t;
|
||||
|
||||
|
||||
/* The quant_tab provides an index into the dequant_tab for residuals in the
|
||||
range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at
|
||||
the higher end. Note that the residual zero is identical to the lowest positive
|
||||
value. This is mostly fine, since the qoa_div() function always rounds away
|
||||
from zero. */
|
||||
|
||||
static const int qoa_quant_tab[17] = {
|
||||
7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */
|
||||
0, /* 0 */
|
||||
0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */
|
||||
};
|
||||
|
||||
|
||||
/* We have 16 different scalefactors. Like the quantized residuals these become
|
||||
less accurate at the higher end. In theory, the highest scalefactor that we
|
||||
would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we
|
||||
rely on the LMS filter to predict samples accurately enough that a maximum
|
||||
residual of one quarter of the 16 bit range is sufficient. I.e. with the
|
||||
scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14.
|
||||
|
||||
The scalefactor values are computed as:
|
||||
scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */
|
||||
|
||||
static const int qoa_scalefactor_tab[16] = {
|
||||
1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048
|
||||
};
|
||||
|
||||
|
||||
/* The reciprocal_tab maps each of the 16 scalefactors to their rounded
|
||||
reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in
|
||||
the encoder with just one multiplication instead of an expensive division. We
|
||||
do this in .16 fixed point with integers, instead of floats.
|
||||
|
||||
The reciprocal_tab is computed as:
|
||||
reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */
|
||||
|
||||
static const int qoa_reciprocal_tab[16] = {
|
||||
65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32
|
||||
};
|
||||
|
||||
|
||||
/* The dequant_tab maps each of the scalefactors and quantized residuals to
|
||||
their unscaled & dequantized version.
|
||||
|
||||
Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4
|
||||
instead of 1. The dequant_tab assumes the following dequantized values for each
|
||||
of the quant_tab indices and is computed as:
|
||||
float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7};
|
||||
dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q])
|
||||
|
||||
The rounding employed here is "to nearest, ties away from zero", i.e. positive
|
||||
and negative values are treated symmetrically.
|
||||
*/
|
||||
|
||||
static const int qoa_dequant_tab[16][8] = {
|
||||
{ 1, -1, 3, -3, 5, -5, 7, -7},
|
||||
{ 5, -5, 18, -18, 32, -32, 49, -49},
|
||||
{ 16, -16, 53, -53, 95, -95, 147, -147},
|
||||
{ 34, -34, 113, -113, 203, -203, 315, -315},
|
||||
{ 63, -63, 210, -210, 378, -378, 588, -588},
|
||||
{ 104, -104, 345, -345, 621, -621, 966, -966},
|
||||
{ 158, -158, 528, -528, 950, -950, 1477, -1477},
|
||||
{ 228, -228, 760, -760, 1368, -1368, 2128, -2128},
|
||||
{ 316, -316, 1053, -1053, 1895, -1895, 2947, -2947},
|
||||
{ 422, -422, 1405, -1405, 2529, -2529, 3934, -3934},
|
||||
{ 548, -548, 1828, -1828, 3290, -3290, 5117, -5117},
|
||||
{ 696, -696, 2320, -2320, 4176, -4176, 6496, -6496},
|
||||
{ 868, -868, 2893, -2893, 5207, -5207, 8099, -8099},
|
||||
{1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933},
|
||||
{1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005},
|
||||
{1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336},
|
||||
};
|
||||
|
||||
|
||||
/* The Least Mean Squares Filter is the heart of QOA. It predicts the next
|
||||
sample based on the previous 4 reconstructed samples. It does so by continuously
|
||||
adjusting 4 weights based on the residual of the previous prediction.
|
||||
|
||||
The next sample is predicted as the sum of (weight[i] * history[i]).
|
||||
|
||||
The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or
|
||||
subtracts the residual to each weight, based on the corresponding sample from
|
||||
the history. This, surprisingly, is sufficient to get worthwhile predictions.
|
||||
|
||||
This is all done with fixed point integers. Hence the right-shifts when updating
|
||||
the weights and calculating the prediction. */
|
||||
|
||||
static int qoa_lms_predict(qoa_lms_t *lms) {
|
||||
int prediction = 0;
|
||||
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||
prediction += lms->weights[i] * lms->history[i];
|
||||
}
|
||||
return prediction >> 13;
|
||||
}
|
||||
|
||||
static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) {
|
||||
int delta = residual >> 4;
|
||||
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||
lms->weights[i] += lms->history[i] < 0 ? -delta : delta;
|
||||
}
|
||||
|
||||
for (int i = 0; i < QOA_LMS_LEN-1; i++) {
|
||||
lms->history[i] = lms->history[i+1];
|
||||
}
|
||||
lms->history[QOA_LMS_LEN-1] = sample;
|
||||
}
|
||||
|
||||
|
||||
/* qoa_div() implements a rounding division, but avoids rounding to zero for
|
||||
small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still
|
||||
returns as 0, which is handled in the qoa_quant_tab[].
|
||||
qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an
|
||||
argument, so it can do the division with a cheaper integer multiplication. */
|
||||
|
||||
static inline int qoa_div(int v, int scalefactor) {
|
||||
int reciprocal = qoa_reciprocal_tab[scalefactor];
|
||||
int n = (v * reciprocal + (1 << 15)) >> 16;
|
||||
n = n + ((v > 0) - (v < 0)) - ((n > 0) - (n < 0)); /* round away from 0 */
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int qoa_clamp(int v, int min, int max) {
|
||||
if (v < min) { return min; }
|
||||
if (v > max) { return max; }
|
||||
return v;
|
||||
}
|
||||
|
||||
/* This specialized clamp function for the signed 16 bit range improves decode
|
||||
performance quite a bit. The extra if() statement works nicely with the CPUs
|
||||
branch prediction as this branch is rarely taken. */
|
||||
|
||||
static inline int qoa_clamp_s16(int v) {
|
||||
if ((unsigned int)(v + 32768) > 65535) {
|
||||
if (v < -32768) { return -32768; }
|
||||
if (v > 32767) { return 32767; }
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) {
|
||||
bytes += *p;
|
||||
*p += 8;
|
||||
return
|
||||
((qoa_uint64_t)(bytes[0]) << 56) | ((qoa_uint64_t)(bytes[1]) << 48) |
|
||||
((qoa_uint64_t)(bytes[2]) << 40) | ((qoa_uint64_t)(bytes[3]) << 32) |
|
||||
((qoa_uint64_t)(bytes[4]) << 24) | ((qoa_uint64_t)(bytes[5]) << 16) |
|
||||
((qoa_uint64_t)(bytes[6]) << 8) | ((qoa_uint64_t)(bytes[7]) << 0);
|
||||
}
|
||||
|
||||
static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) {
|
||||
bytes += *p;
|
||||
*p += 8;
|
||||
bytes[0] = (v >> 56) & 0xff;
|
||||
bytes[1] = (v >> 48) & 0xff;
|
||||
bytes[2] = (v >> 40) & 0xff;
|
||||
bytes[3] = (v >> 32) & 0xff;
|
||||
bytes[4] = (v >> 24) & 0xff;
|
||||
bytes[5] = (v >> 16) & 0xff;
|
||||
bytes[6] = (v >> 8) & 0xff;
|
||||
bytes[7] = (v >> 0) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Encoder */
|
||||
|
||||
unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes) {
|
||||
unsigned int p = 0;
|
||||
qoa_write_u64(((qoa_uint64_t)QOA_MAGIC << 32) | qoa->samples, bytes, &p);
|
||||
return p;
|
||||
}
|
||||
|
||||
unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes) {
|
||||
unsigned int channels = qoa->channels;
|
||||
|
||||
unsigned int p = 0;
|
||||
unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN;
|
||||
unsigned int frame_size = QOA_FRAME_SIZE(channels, slices);
|
||||
int prev_scalefactor[QOA_MAX_CHANNELS] = {0};
|
||||
|
||||
/* Write the frame header */
|
||||
qoa_write_u64((
|
||||
(qoa_uint64_t)qoa->channels << 56 |
|
||||
(qoa_uint64_t)qoa->samplerate << 32 |
|
||||
(qoa_uint64_t)frame_len << 16 |
|
||||
(qoa_uint64_t)frame_size
|
||||
), bytes, &p);
|
||||
|
||||
|
||||
for (unsigned int c = 0; c < channels; c++) {
|
||||
/* Write the current LMS state */
|
||||
qoa_uint64_t weights = 0;
|
||||
qoa_uint64_t history = 0;
|
||||
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||
history = (history << 16) | (qoa->lms[c].history[i] & 0xffff);
|
||||
weights = (weights << 16) | (qoa->lms[c].weights[i] & 0xffff);
|
||||
}
|
||||
qoa_write_u64(history, bytes, &p);
|
||||
qoa_write_u64(weights, bytes, &p);
|
||||
}
|
||||
|
||||
/* We encode all samples with the channels interleaved on a slice level.
|
||||
E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/
|
||||
for (unsigned int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
|
||||
|
||||
for (unsigned int c = 0; c < channels; c++) {
|
||||
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
|
||||
int slice_start = sample_index * channels + c;
|
||||
int slice_end = (sample_index + slice_len) * channels + c;
|
||||
|
||||
/* Brute force search for the best scalefactor. Just go through all
|
||||
16 scalefactors, encode all samples for the current slice and
|
||||
meassure the total squared error. */
|
||||
qoa_uint64_t best_rank = -1;
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
qoa_uint64_t best_error = -1;
|
||||
#endif
|
||||
qoa_uint64_t best_slice = 0;
|
||||
qoa_lms_t best_lms;
|
||||
int best_scalefactor = 0;
|
||||
|
||||
for (int sfi = 0; sfi < 16; sfi++) {
|
||||
/* There is a strong correlation between the scalefactors of
|
||||
neighboring slices. As an optimization, start testing
|
||||
the best scalefactor of the previous slice first. */
|
||||
int scalefactor = (sfi + prev_scalefactor[c]) & (16 - 1);
|
||||
|
||||
/* We have to reset the LMS state to the last known good one
|
||||
before trying each scalefactor, as each pass updates the LMS
|
||||
state when encoding. */
|
||||
qoa_lms_t lms = qoa->lms[c];
|
||||
qoa_uint64_t slice = scalefactor;
|
||||
qoa_uint64_t current_rank = 0;
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
qoa_uint64_t current_error = 0;
|
||||
#endif
|
||||
|
||||
for (int si = slice_start; si < slice_end; si += channels) {
|
||||
int sample = sample_data[si];
|
||||
int predicted = qoa_lms_predict(&lms);
|
||||
|
||||
int residual = sample - predicted;
|
||||
int scaled = qoa_div(residual, scalefactor);
|
||||
int clamped = qoa_clamp(scaled, -8, 8);
|
||||
int quantized = qoa_quant_tab[clamped + 8];
|
||||
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||
int reconstructed = qoa_clamp_s16(predicted + dequantized);
|
||||
|
||||
|
||||
/* If the weights have grown too large, we introduce a penalty
|
||||
here. This prevents pops/clicks in certain problem cases */
|
||||
int weights_penalty = ((
|
||||
lms.weights[0] * lms.weights[0] +
|
||||
lms.weights[1] * lms.weights[1] +
|
||||
lms.weights[2] * lms.weights[2] +
|
||||
lms.weights[3] * lms.weights[3]
|
||||
) >> 18) - 0x8ff;
|
||||
if (weights_penalty < 0) {
|
||||
weights_penalty = 0;
|
||||
}
|
||||
|
||||
long long error = (sample - reconstructed);
|
||||
qoa_uint64_t error_sq = error * error;
|
||||
|
||||
current_rank += error_sq + weights_penalty * weights_penalty;
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
current_error += error_sq;
|
||||
#endif
|
||||
if (current_rank > best_rank) {
|
||||
break;
|
||||
}
|
||||
|
||||
qoa_lms_update(&lms, reconstructed, dequantized);
|
||||
slice = (slice << 3) | quantized;
|
||||
}
|
||||
|
||||
if (current_rank < best_rank) {
|
||||
best_rank = current_rank;
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
best_error = current_error;
|
||||
#endif
|
||||
best_slice = slice;
|
||||
best_lms = lms;
|
||||
best_scalefactor = scalefactor;
|
||||
}
|
||||
}
|
||||
|
||||
prev_scalefactor[c] = best_scalefactor;
|
||||
|
||||
qoa->lms[c] = best_lms;
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
qoa->error += best_error;
|
||||
#endif
|
||||
|
||||
/* If this slice was shorter than QOA_SLICE_LEN, we have to left-
|
||||
shift all encoded data, to ensure the rightmost bits are the empty
|
||||
ones. This should only happen in the last frame of a file as all
|
||||
slices are completely filled otherwise. */
|
||||
best_slice <<= (QOA_SLICE_LEN - slice_len) * 3;
|
||||
qoa_write_u64(best_slice, bytes, &p);
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) {
|
||||
if (
|
||||
qoa->samples == 0 ||
|
||||
qoa->samplerate == 0 || qoa->samplerate > 0xffffff ||
|
||||
qoa->channels == 0 || qoa->channels > QOA_MAX_CHANNELS
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Calculate the encoded size and allocate */
|
||||
unsigned int num_frames = (qoa->samples + QOA_FRAME_LEN-1) / QOA_FRAME_LEN;
|
||||
unsigned int num_slices = (qoa->samples + QOA_SLICE_LEN-1) / QOA_SLICE_LEN;
|
||||
unsigned int encoded_size = 8 + /* 8 byte file header */
|
||||
num_frames * 8 + /* 8 byte frame headers */
|
||||
num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */
|
||||
num_slices * 8 * qoa->channels; /* 8 byte slices */
|
||||
|
||||
unsigned char *bytes = QOA_MALLOC(encoded_size);
|
||||
|
||||
for (unsigned int c = 0; c < qoa->channels; c++) {
|
||||
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
|
||||
prediction of the first few ms of a file. */
|
||||
qoa->lms[c].weights[0] = 0;
|
||||
qoa->lms[c].weights[1] = 0;
|
||||
qoa->lms[c].weights[2] = -(1<<13);
|
||||
qoa->lms[c].weights[3] = (1<<14);
|
||||
|
||||
/* Explicitly set the history samples to 0, as we might have some
|
||||
garbage in there. */
|
||||
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||
qoa->lms[c].history[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Encode the header and go through all frames */
|
||||
unsigned int p = qoa_encode_header(qoa, bytes);
|
||||
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||
qoa->error = 0;
|
||||
#endif
|
||||
|
||||
int frame_len = QOA_FRAME_LEN;
|
||||
for (unsigned int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
|
||||
frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);
|
||||
const short *frame_samples = sample_data + sample_index * qoa->channels;
|
||||
unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
|
||||
p += frame_size;
|
||||
}
|
||||
|
||||
*out_len = p;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Decoder */
|
||||
|
||||
unsigned int qoa_max_frame_size(qoa_desc *qoa) {
|
||||
return QOA_FRAME_SIZE(qoa->channels, QOA_SLICES_PER_FRAME);
|
||||
}
|
||||
|
||||
unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa) {
|
||||
unsigned int p = 0;
|
||||
if (size < QOA_MIN_FILESIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read the file header, verify the magic number ('qoaf') and read the
|
||||
total number of samples. */
|
||||
qoa_uint64_t file_header = qoa_read_u64(bytes, &p);
|
||||
|
||||
if ((file_header >> 32) != QOA_MAGIC) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qoa->samples = file_header & 0xffffffff;
|
||||
if (!qoa->samples) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Peek into the first frame header to get the number of channels and
|
||||
the samplerate. */
|
||||
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
|
||||
qoa->channels = (frame_header >> 56) & 0x0000ff;
|
||||
qoa->samplerate = (frame_header >> 32) & 0xffffff;
|
||||
|
||||
if (qoa->channels == 0 || qoa->samples == 0 || qoa->samplerate == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len) {
|
||||
unsigned int p = 0;
|
||||
*frame_len = 0;
|
||||
|
||||
if (size < 8 + QOA_LMS_LEN * 4 * qoa->channels) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read and verify the frame header */
|
||||
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
|
||||
unsigned int channels = (frame_header >> 56) & 0x0000ff;
|
||||
unsigned int samplerate = (frame_header >> 32) & 0xffffff;
|
||||
unsigned int samples = (frame_header >> 16) & 0x00ffff;
|
||||
unsigned int frame_size = (frame_header ) & 0x00ffff;
|
||||
|
||||
unsigned int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
|
||||
unsigned int num_slices = data_size / 8;
|
||||
unsigned int max_total_samples = num_slices * QOA_SLICE_LEN;
|
||||
|
||||
if (
|
||||
channels != qoa->channels ||
|
||||
samplerate != qoa->samplerate ||
|
||||
frame_size > size ||
|
||||
samples * channels > max_total_samples
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
|
||||
for (unsigned int c = 0; c < channels; c++) {
|
||||
qoa_uint64_t history = qoa_read_u64(bytes, &p);
|
||||
qoa_uint64_t weights = qoa_read_u64(bytes, &p);
|
||||
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||
qoa->lms[c].history[i] = ((signed short)(history >> 48));
|
||||
history <<= 16;
|
||||
qoa->lms[c].weights[i] = ((signed short)(weights >> 48));
|
||||
weights <<= 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Decode all slices for all channels in this frame */
|
||||
for (unsigned int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
|
||||
for (unsigned int c = 0; c < channels; c++) {
|
||||
qoa_uint64_t slice = qoa_read_u64(bytes, &p);
|
||||
|
||||
int scalefactor = (slice >> 60) & 0xf;
|
||||
slice <<= 4;
|
||||
|
||||
int slice_start = sample_index * channels + c;
|
||||
int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c;
|
||||
|
||||
for (int si = slice_start; si < slice_end; si += channels) {
|
||||
int predicted = qoa_lms_predict(&qoa->lms[c]);
|
||||
int quantized = (slice >> 61) & 0x7;
|
||||
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||
int reconstructed = qoa_clamp_s16(predicted + dequantized);
|
||||
|
||||
sample_data[si] = reconstructed;
|
||||
slice <<= 3;
|
||||
|
||||
qoa_lms_update(&qoa->lms[c], reconstructed, dequantized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*frame_len = samples;
|
||||
return p;
|
||||
}
|
||||
|
||||
short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) {
|
||||
unsigned int p = qoa_decode_header(bytes, size, qoa);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Calculate the required size of the sample buffer and allocate */
|
||||
int total_samples = qoa->samples * qoa->channels;
|
||||
short *sample_data = QOA_MALLOC(total_samples * sizeof(short));
|
||||
|
||||
unsigned int sample_index = 0;
|
||||
unsigned int frame_len;
|
||||
unsigned int frame_size;
|
||||
|
||||
/* Decode all frames */
|
||||
do {
|
||||
short *sample_ptr = sample_data + sample_index * qoa->channels;
|
||||
frame_size = qoa_decode_frame(bytes + p, size - p, qoa, sample_ptr, &frame_len);
|
||||
|
||||
p += frame_size;
|
||||
sample_index += frame_len;
|
||||
} while (frame_size && sample_index < qoa->samples);
|
||||
|
||||
qoa->samples = sample_index;
|
||||
return sample_data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
File read/write convenience functions */
|
||||
|
||||
#ifndef QOA_NO_STDIO
|
||||
#include <stdio.h>
|
||||
|
||||
int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa) {
|
||||
FILE *f = fopen(filename, "wb");
|
||||
unsigned int size;
|
||||
void *encoded;
|
||||
|
||||
if (!f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
encoded = qoa_encode(sample_data, qoa, &size);
|
||||
if (!encoded) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fwrite(encoded, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
QOA_FREE(encoded);
|
||||
return size;
|
||||
}
|
||||
|
||||
void *qoa_read(const char *filename, qoa_desc *qoa) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
int size, bytes_read;
|
||||
void *data;
|
||||
short *sample_data;
|
||||
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
if (size <= 0) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
data = QOA_MALLOC(size);
|
||||
if (!data) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes_read = fread(data, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
sample_data = qoa_decode(data, bytes_read, qoa);
|
||||
QOA_FREE(data);
|
||||
return sample_data;
|
||||
}
|
||||
|
||||
#endif /* QOA_NO_STDIO */
|
||||
#endif /* QOA_IMPLEMENTATION */
|
||||
2
thirdparty/misc/r128.c
vendored
Normal file
2
thirdparty/misc/r128.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define R128_IMPLEMENTATION
|
||||
#include "r128.h"
|
||||
2160
thirdparty/misc/r128.h
vendored
Normal file
2160
thirdparty/misc/r128.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
207
thirdparty/misc/smaz.c
vendored
Normal file
207
thirdparty/misc/smaz.c
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Copyright (c) 2006-2009, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Smaz nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Our compression codebook, used for compression */
|
||||
static const char *Smaz_cb[241] = {
|
||||
"\002s,\266", "\003had\232\002leW", "\003on \216", "", "\001yS",
|
||||
"\002ma\255\002li\227", "\003or \260", "", "\002ll\230\003s t\277",
|
||||
"\004fromg\002mel", "", "\003its\332", "\001z\333", "\003ingF", "\001>\336",
|
||||
"\001 \000\003 (\002nc\344", "\002nd=\003 on\312",
|
||||
"\002ne\213\003hat\276\003re q", "", "\002ngT\003herz\004have\306\003s o\225",
|
||||
"", "\003ionk\003s a\254\002ly\352", "\003hisL\003 inN\003 be\252", "",
|
||||
"\003 fo\325\003 of \003 ha\311", "", "\002of\005",
|
||||
"\003 co\241\002no\267\003 ma\370", "", "", "\003 cl\356\003enta\003 an7",
|
||||
"\002ns\300\001\"e", "\003n t\217\002ntP\003s, \205",
|
||||
"\002pe\320\003 we\351\002om\223", "\002on\037", "", "\002y G", "\003 wa\271",
|
||||
"\003 re\321\002or*", "", "\002=\"\251\002ot\337", "\003forD\002ou[",
|
||||
"\003 toR", "\003 th\r", "\003 it\366",
|
||||
"\003but\261\002ra\202\003 wi\363\002</\361", "\003 wh\237", "\002 4",
|
||||
"\003nd ?", "\002re!", "", "\003ng c", "",
|
||||
"\003ly \307\003ass\323\001a\004\002rir", "", "", "", "\002se_", "\003of \"",
|
||||
"\003div\364\002ros\003ere\240", "", "\002ta\310\001bZ\002si\324", "",
|
||||
"\003and\a\002rs\335", "\002rt\362", "\002teE", "\003ati\316", "\002so\263",
|
||||
"\002th\021", "\002tiJ\001c\034\003allp", "\003ate\345", "\002ss\246",
|
||||
"\002stM", "", "\002><\346", "\002to\024", "\003arew", "\001d\030",
|
||||
"\002tr\303", "", "\001\n1\003 a \222", "\003f tv\002veo", "\002un\340", "",
|
||||
"\003e o\242", "\002a \243\002wa\326\001e\002", "\002ur\226\003e a\274",
|
||||
"\002us\244\003\n\r\n\247", "\002ut\304\003e c\373", "\002we\221", "", "",
|
||||
"\002wh\302", "\001f,", "", "", "", "\003d t\206", "", "", "\003th \343",
|
||||
"\001g;", "", "", "\001\r9\003e s\265", "\003e t\234", "", "\003to Y",
|
||||
"\003e\r\n\236", "\002d \036\001h\022", "", "\001,Q", "\002 a\031", "\002 b^",
|
||||
"\002\r\n\025\002 cI", "\002 d\245", "\002 e\253", "\002 fh\001i\b\002e \v",
|
||||
"", "\002 hU\001-\314", "\002 i8", "", "", "\002 l\315", "\002 m{",
|
||||
"\002f :\002 n\354", "\002 o\035", "\002 p}\001.n\003\r\n\r\250", "",
|
||||
"\002 r\275", "\002 s>", "\002 t\016", "", "\002g \235\005which+\003whi\367",
|
||||
"\002 w5", "\001/\305", "\003as \214", "\003at \207", "", "\003who\331", "",
|
||||
"\001l\026\002h \212", "", "\002, $", "", "\004withV", "", "", "", "\001m-", "",
|
||||
"", "\002ac\357", "\002ad\350", "\003TheH", "", "", "\004this\233\001n\t",
|
||||
"", "\002. y", "", "\002alX\003e, \365", "\003tio\215\002be\\",
|
||||
"\002an\032\003ver\347", "", "\004that0\003tha\313\001o\006", "\003was2",
|
||||
"\002arO", "\002as.", "\002at'\003the\001\004they\200\005there\322\005theird",
|
||||
"\002ce\210", "\004were]", "", "\002ch\231\002l \264\001p<", "", "",
|
||||
"\003one\256", "", "\003he \023\002dej", "\003ter\270", "\002cou", "",
|
||||
"\002by\177\002di\201\002eax", "", "\002ec\327", "\002edB", "\002ee\353", "",
|
||||
"", "\001r\f\002n )", "", "", "", "\002el\262", "", "\003in i\002en3", "",
|
||||
"\002o `\001s\n", "", "\002er\033", "\003is t\002es6", "", "\002ge\371",
|
||||
"\004.com\375", "\002fo\334\003our\330", "\003ch \301\001t\003", "\002hab", "",
|
||||
"\003men\374", "", "\002he\020", "", "", "\001u&", "\002hif", "",
|
||||
"\003not\204\002ic\203", "\003ed @\002id\355", "", "", "\002ho\273",
|
||||
"\002r K\001vm", "", "", "", "\003t t\257\002il\360", "\002im\342",
|
||||
"\003en \317\002in\017", "\002io\220", "\002s \027\001wA", "", "\003er |",
|
||||
"\003es ~\002is%", "\002it/", "", "\002iv\272", "",
|
||||
"\002t #\ahttp://C\001x\372", "\002la\211", "\001<\341", "\003, a\224"
|
||||
};
|
||||
|
||||
/* Reverse compression codebook, used for decompression */
|
||||
static char *Smaz_rcb[254] = {
|
||||
" ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th",
|
||||
" t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an",
|
||||
"er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at",
|
||||
" ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en",
|
||||
" ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ",
|
||||
"w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his",
|
||||
"st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ",
|
||||
"b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"",
|
||||
"hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ",
|
||||
"ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p",
|
||||
"es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la",
|
||||
"h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o",
|
||||
"ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere",
|
||||
" co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e",
|
||||
"s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no",
|
||||
"ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr",
|
||||
"ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ",
|
||||
"pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z",
|
||||
"fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad",
|
||||
" we", "ly", "ee", " n", "id", " cl", "ac", "il", "</", "rt", " wi", "div",
|
||||
"e, ", " it", "whi", " ma", "ge", "x", "e c", "men", ".com"
|
||||
};
|
||||
|
||||
int smaz_compress(const char *in, int inlen, char *out, int outlen) {
|
||||
unsigned int h1,h2,h3=0;
|
||||
int verblen = 0, _outlen = outlen;
|
||||
char verb[256], *_out = out;
|
||||
|
||||
while(inlen) {
|
||||
int j = 7, needed;
|
||||
char *flush = NULL;
|
||||
char *slot;
|
||||
|
||||
h1 = h2 = in[0]<<3;
|
||||
if (inlen > 1) h2 += in[1];
|
||||
if (inlen > 2) h3 = h2^in[2];
|
||||
if (j > inlen) j = inlen;
|
||||
|
||||
/* Try to lookup substrings into the hash table, starting from the
|
||||
* longer to the shorter substrings */
|
||||
for (; j > 0; j--) {
|
||||
switch(j) {
|
||||
case 1: slot = Smaz_cb[h1%241]; break;
|
||||
case 2: slot = Smaz_cb[h2%241]; break;
|
||||
default: slot = Smaz_cb[h3%241]; break;
|
||||
}
|
||||
while(slot[0]) {
|
||||
if (slot[0] == j && memcmp(slot+1,in,j) == 0) {
|
||||
/* Match found in the hash table,
|
||||
* prepare a verbatim bytes flush if needed */
|
||||
if (verblen) {
|
||||
needed = (verblen == 1) ? 2 : 2+verblen;
|
||||
flush = out;
|
||||
out += needed;
|
||||
outlen -= needed;
|
||||
}
|
||||
/* Emit the byte */
|
||||
if (outlen <= 0) return _outlen+1;
|
||||
out[0] = slot[slot[0]+1];
|
||||
out++;
|
||||
outlen--;
|
||||
inlen -= j;
|
||||
in += j;
|
||||
goto out;
|
||||
} else {
|
||||
slot += slot[0]+2;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Match not found - add the byte to the verbatim buffer */
|
||||
verb[verblen] = in[0];
|
||||
verblen++;
|
||||
inlen--;
|
||||
in++;
|
||||
out:
|
||||
/* Prepare a flush if we reached the flush length limit, and there
|
||||
* is not already a pending flush operation. */
|
||||
if (!flush && (verblen == 256 || (verblen > 0 && inlen == 0))) {
|
||||
needed = (verblen == 1) ? 2 : 2+verblen;
|
||||
flush = out;
|
||||
out += needed;
|
||||
outlen -= needed;
|
||||
if (outlen < 0) return _outlen+1;
|
||||
}
|
||||
/* Perform a verbatim flush if needed */
|
||||
if (flush) {
|
||||
if (verblen == 1) {
|
||||
flush[0] = (signed char)254;
|
||||
flush[1] = verb[0];
|
||||
} else {
|
||||
flush[0] = (signed char)255;
|
||||
flush[1] = (signed char)(verblen-1);
|
||||
memcpy(flush+2,verb,verblen);
|
||||
}
|
||||
flush = NULL;
|
||||
verblen = 0;
|
||||
}
|
||||
}
|
||||
return out-_out;
|
||||
}
|
||||
|
||||
int smaz_decompress(const char *in, int inlen, char *out, int outlen) {
|
||||
unsigned char *c = (unsigned char*) in;
|
||||
char *_out = out;
|
||||
int _outlen = outlen;
|
||||
|
||||
while(inlen) {
|
||||
if (*c == 254) {
|
||||
/* Verbatim byte */
|
||||
if (outlen < 1) return _outlen+1;
|
||||
*out = *(c+1);
|
||||
out++;
|
||||
outlen--;
|
||||
c += 2;
|
||||
inlen -= 2;
|
||||
} else if (*c == 255) {
|
||||
/* Verbatim string */
|
||||
int len = (*(c+1))+1;
|
||||
if (outlen < len) return _outlen+1;
|
||||
memcpy(out,c+2,len);
|
||||
out += len;
|
||||
outlen -= len;
|
||||
c += 2+len;
|
||||
inlen -= 2+len;
|
||||
} else {
|
||||
/* Codebook entry */
|
||||
const char *s = Smaz_rcb[*c];
|
||||
int len = strlen(s);
|
||||
|
||||
if (outlen < len) return _outlen+1;
|
||||
memcpy(out,s,len);
|
||||
out += len;
|
||||
outlen -= len;
|
||||
c++;
|
||||
inlen--;
|
||||
}
|
||||
}
|
||||
return out-_out;
|
||||
}
|
||||
20
thirdparty/misc/smaz.h
vendored
Normal file
20
thirdparty/misc/smaz.h
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (c) 2006-2009, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Smaz nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SMAZ_H
|
||||
#define _SMAZ_H
|
||||
|
||||
int smaz_compress(const char *in, int inlen, char *out, int outlen);
|
||||
int smaz_decompress(const char *in, int inlen, char *out, int outlen);
|
||||
|
||||
#endif
|
||||
2108
thirdparty/misc/smolv.cpp
vendored
Normal file
2108
thirdparty/misc/smolv.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
169
thirdparty/misc/smolv.h
vendored
Normal file
169
thirdparty/misc/smolv.h
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
// smol-v - public domain - https://github.com/aras-p/smol-v
|
||||
// authored 2016-2024 by Aras Pranckevicius
|
||||
// no warranty implied; use at your own risk
|
||||
// See end of file for license information.
|
||||
//
|
||||
//
|
||||
// ### OVERVIEW:
|
||||
//
|
||||
// SMOL-V encodes Vulkan/Khronos SPIR-V format programs into a form that is smaller, and is more
|
||||
// compressible. Normally no changes to the programs are done; they decode
|
||||
// into exactly same program as what was encoded. Optionally, debug information
|
||||
// can be removed too.
|
||||
//
|
||||
// SPIR-V is a very verbose format, several times larger than same programs expressed in other
|
||||
// shader formats (e.g. DX11 bytecode, GLSL, DX9 bytecode etc.). The SSA-form with ever increasing
|
||||
// IDs is not very appreciated by regular data compressors either. SMOL-V does several things
|
||||
// to improve this:
|
||||
// - Many words, especially ones that most often have small values, are encoded using
|
||||
// "varint" scheme (1-5 bytes per word, with just one byte for values in 0..127 range).
|
||||
// See https://developers.google.com/protocol-buffers/docs/encoding
|
||||
// - Some IDs used in the program are delta-encoded, relative to previously seen IDs (e.g. Result
|
||||
// IDs). Often instructions reference things that were computed just before, so this results in
|
||||
// small deltas. These values are also encoded using "varint" scheme.
|
||||
// - Reordering instruction opcodes so that the most common ones are the smallest values, for smaller
|
||||
// varint encoding.
|
||||
// - Encoding several instructions in a more compact form, e.g. the "typical <=4 component swizzle"
|
||||
// shape of a VectorShuffle instruction, or sequences of MemberDecorate instructions.
|
||||
//
|
||||
// A somewhat similar utility is spirv-remap from glslang, see
|
||||
// https://github.com/KhronosGroup/glslang/blob/master/README-spirv-remap.txt
|
||||
//
|
||||
//
|
||||
// ### USAGE:
|
||||
//
|
||||
// Add source/smolv.h and source/smolv.cpp to your C++ project build.
|
||||
// Currently it might require C++11 or somesuch; I only tested with Visual Studio 2017/2019, Mac Xcode 11 and Gcc 5.4.
|
||||
//
|
||||
// smolv::Encode and smolv::Decode is the basic functionality.
|
||||
//
|
||||
// Other functions are for development/statistics purposes, to figure out frequencies and
|
||||
// distributions of the instructions.
|
||||
//
|
||||
// There's a test + compression benchmarking suite in testing/testmain.cpp; using that needs adding
|
||||
// other files under testing/external to the build too (3rd party code: glslang remapper, Zstd, LZ4).
|
||||
//
|
||||
//
|
||||
// ### LIMITATIONS / TODO:
|
||||
//
|
||||
// - SPIR-V where the words got stored in big-endian layout is not supported yet.
|
||||
// - The whole thing might not work on Big-Endian CPUs. It might, but I'm not 100% sure.
|
||||
// - Not much prevention is done against malformed/corrupted inputs, TODO.
|
||||
// - Out of memory cases are not handled. The code will either throw exception
|
||||
// or crash, depending on your compilation flags.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
|
||||
namespace smolv
|
||||
{
|
||||
typedef std::vector<uint8_t> ByteArray;
|
||||
|
||||
enum EncodeFlags
|
||||
{
|
||||
kEncodeFlagNone = 0,
|
||||
kEncodeFlagStripDebugInfo = (1<<0), // Strip all optional SPIR-V instructions (debug names etc.)
|
||||
};
|
||||
enum DecodeFlags
|
||||
{
|
||||
kDecodeFlagNone = 0,
|
||||
kDecodeFlagUse20160831AsZeroVersion = (1 << 0), // For "version zero" of SMOL-V encoding, use 2016 08 31 code path (this is what happens to be used by Unity 2017-2020)
|
||||
};
|
||||
|
||||
// Preserve *some* OpName debug names.
|
||||
// Return true to preserve, false to strip.
|
||||
// This is really only used to implement a workaround for problems with some Vulkan drivers.
|
||||
typedef bool(*StripOpNameFilterFunc)(const char* name);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Encoding / Decoding
|
||||
|
||||
// Encode SPIR-V into SMOL-V.
|
||||
//
|
||||
// Resulting data is appended to outSmolv array (the array is not cleared).
|
||||
//
|
||||
// flags is bitset of EncodeFlags values.
|
||||
//
|
||||
// Returns false on malformed SPIR-V input; if that happens the output array might get
|
||||
// partial/broken SMOL-V program.
|
||||
bool Encode(const void* spirvData, size_t spirvSize, ByteArray& outSmolv, uint32_t flags = kEncodeFlagNone, StripOpNameFilterFunc stripFilter = 0);
|
||||
|
||||
|
||||
// Decode SMOL-V into SPIR-V.
|
||||
//
|
||||
// Resulting data is written into the passed buffer. Get required buffer space with
|
||||
// GetDecodeBufferSize; this is the size of decoded SPIR-V program.
|
||||
//
|
||||
// flags is bitset of DecodeFlags values.
|
||||
|
||||
// Decoding does no memory allocations.
|
||||
//
|
||||
// Returns false on malformed input; if that happens the output buffer might be only partially
|
||||
// written to.
|
||||
bool Decode(const void* smolvData, size_t smolvSize, void* spirvOutputBuffer, size_t spirvOutputBufferSize, uint32_t flags = kDecodeFlagNone);
|
||||
|
||||
|
||||
// Given a SMOL-V program, get size of the decoded SPIR-V program.
|
||||
// This is the buffer size that Decode expects.
|
||||
//
|
||||
// Returns zero on malformed input (just checks the header, not the full input).
|
||||
size_t GetDecodedBufferSize(const void* smolvData, size_t smolvSize);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Computing instruction statistics on SPIR-V/SMOL-V programs
|
||||
|
||||
struct Stats;
|
||||
|
||||
Stats* StatsCreate();
|
||||
void StatsDelete(Stats* s);
|
||||
|
||||
bool StatsCalculate(Stats* stats, const void* spirvData, size_t spirvSize);
|
||||
bool StatsCalculateSmol(Stats* stats, const void* smolvData, size_t smolvSize);
|
||||
void StatsPrint(const Stats* stats);
|
||||
|
||||
} // namespace smolv
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// This software is available under 2 licenses -- choose whichever you prefer.
|
||||
// ------------------------------------------------------------------------------
|
||||
// ALTERNATIVE A - MIT License
|
||||
// Copyright (c) 2016-2024 Aras Pranckevicius
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is furnished to do
|
||||
// so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
// ------------------------------------------------------------------------------
|
||||
// ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
// software, either in source code form or as a compiled binary, for any purpose,
|
||||
// commercial or non-commercial, and by any means.
|
||||
// In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
// software dedicate any and all copyright interest in the software to the public
|
||||
// domain. We make this dedication for the benefit of the public at large and to
|
||||
// the detriment of our heirs and successors. We intend this dedication to be an
|
||||
// overt act of relinquishment in perpetuity of all present and future rights to
|
||||
// this software under copyright law.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ------------------------------------------------------------------------------
|
||||
623
thirdparty/misc/stb_rect_pack.h
vendored
Normal file
623
thirdparty/misc/stb_rect_pack.h
vendored
Normal file
@@ -0,0 +1,623 @@
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
1102
thirdparty/misc/yuv2rgb.h
vendored
Normal file
1102
thirdparty/misc/yuv2rgb.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user