feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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 _TVG_ARRAY_H_
#define _TVG_ARRAY_H_
#include <memory.h>
#include <cstdint>
#include <cstdlib>
namespace tvg
{
template<class T>
struct Array
{
T* data = nullptr;
uint32_t count = 0;
uint32_t reserved = 0;
Array(){}
Array(int32_t size)
{
reserve(size);
}
Array(const Array& rhs)
{
reset();
*this = rhs;
}
void push(T element)
{
if (count + 1 > reserved) {
reserved = count + (count + 2) / 2;
data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
}
data[count++] = element;
}
void push(Array<T>& rhs)
{
if (rhs.count == 0) return;
grow(rhs.count);
memcpy(data + count, rhs.data, rhs.count * sizeof(T));
count += rhs.count;
}
bool reserve(uint32_t size)
{
if (size > reserved) {
reserved = size;
data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
}
return true;
}
bool grow(uint32_t size)
{
return reserve(count + size);
}
const T& operator[](size_t idx) const
{
return data[idx];
}
T& operator[](size_t idx)
{
return data[idx];
}
const T* begin() const
{
return data;
}
T* begin()
{
return data;
}
T* end()
{
return data + count;
}
const T* end() const
{
return data + count;
}
const T& last() const
{
return data[count - 1];
}
const T& first() const
{
return data[0];
}
T& last()
{
return data[count - 1];
}
T& first()
{
return data[0];
}
void pop()
{
if (count > 0) --count;
}
void reset()
{
free(data);
data = nullptr;
count = reserved = 0;
}
void clear()
{
count = 0;
}
bool empty() const
{
return count == 0;
}
template<class COMPARE>
void sort()
{
qsort<COMPARE>(data, 0, static_cast<int32_t>(count) - 1);
}
void operator=(const Array& rhs)
{
reserve(rhs.count);
if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count);
count = rhs.count;
}
~Array()
{
free(data);
}
private:
template<class COMPARE>
void qsort(T* arr, int32_t low, int32_t high)
{
if (low < high) {
int32_t i = low;
int32_t j = high;
T tmp = arr[low];
while (i < j) {
while (i < j && !COMPARE{}(arr[j], tmp)) --j;
if (i < j) {
arr[i] = arr[j];
++i;
}
while (i < j && COMPARE{}(arr[i], tmp)) ++i;
if (i < j) {
arr[j] = arr[i];
--j;
}
}
arr[i] = tmp;
qsort<COMPARE>(arr, low, i - 1);
qsort<COMPARE>(arr, i + 1, high);
}
}
};
}
#endif //_TVG_ARRAY_H_

View file

@ -0,0 +1,490 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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.
*/
/*
* LempelZivWelch (LZW) encoder/decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
* This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
* Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
* are not stored in the output and the max code length in bits is 12, vs 16 in compress.
*
* EOI is simply detected by the end of the data stream, while CC happens if the
* dictionary gets filled. Data is written/read from bit streams, which handle
* byte-alignment for us in a transparent way.
* The decoder relies on the hardcoded data layout produced by the encoder, since
* no additional reconstruction data is added to the output, so they must match.
* The nice thing about LZW is that we can reconstruct the dictionary directly from
* the stream of codes generated by the encoder, so this avoids storing additional
* headers in the bit stream.
* The output code length is variable. It starts with the minimum number of bits
* required to store the base byte-sized dictionary and automatically increases
* as the dictionary gets larger (it starts at 9-bits and grows to 10-bits when
* code 512 is added, then 11-bits when 1024 is added, and so on). If the dictionary
* is filled (4096 items for a 12-bits dictionary), the whole thing is cleared and
* the process starts over. This is the main reason why the encoder and the decoder
* must match perfectly, since the lengths of the codes will not be specified with
* the data itself.
* USEFUL LINKS:
* https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
* http://rosettacode.org/wiki/LZW_compression
* http://www.cs.duke.edu/csed/curious/compression/lzw.html
* http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
* http://marknelson.us/1989/10/01/lzw-data-compression/
*/
#include "config.h"
#include <string>
#include <memory.h>
#include "tvgCompressor.h"
namespace tvg {
/************************************************************************/
/* LZW Implementation */
/************************************************************************/
//LZW Dictionary helper:
constexpr int Nil = -1;
constexpr int MaxDictBits = 12;
constexpr int StartBits = 9;
constexpr int FirstCode = (1 << (StartBits - 1)); // 256
constexpr int MaxDictEntries = (1 << MaxDictBits); // 4096
//Round up to the next power-of-two number, e.g. 37 => 64
static int nextPowerOfTwo(int num)
{
--num;
for (size_t i = 1; i < sizeof(num) * 8; i <<= 1) {
num = num | num >> i;
}
return ++num;
}
struct BitStreamWriter
{
uint8_t* stream; //Growable buffer to store our bits. Heap allocated & owned by the class instance.
int bytesAllocated; //Current size of heap-allocated stream buffer *in bytes*.
int granularity; //Amount bytesAllocated multiplies by when auto-resizing in appendBit().
int currBytePos; //Current byte being written to, from 0 to bytesAllocated-1.
int nextBitPos; //Bit position within the current byte to access next. 0 to 7.
int numBitsWritten; //Number of bits in use from the stream buffer, not including byte-rounding padding.
void internalInit()
{
stream = nullptr;
bytesAllocated = 0;
granularity = 2;
currBytePos = 0;
nextBitPos = 0;
numBitsWritten = 0;
}
uint8_t* allocBytes(const int bytesWanted, uint8_t * oldPtr, const int oldSize)
{
auto newMemory = static_cast<uint8_t *>(malloc(bytesWanted));
memset(newMemory, 0, bytesWanted);
if (oldPtr) {
memcpy(newMemory, oldPtr, oldSize);
free(oldPtr);
}
return newMemory;
}
BitStreamWriter()
{
/* 8192 bits for a start (1024 bytes). It will resize if needed.
Default granularity is 2. */
internalInit();
allocate(8192);
}
BitStreamWriter(const int initialSizeInBits, const int growthGranularity = 2)
{
internalInit();
setGranularity(growthGranularity);
allocate(initialSizeInBits);
}
~BitStreamWriter()
{
free(stream);
}
void allocate(int bitsWanted)
{
//Require at least a byte.
if (bitsWanted <= 0) bitsWanted = 8;
//Round upwards if needed:
if ((bitsWanted % 8) != 0) bitsWanted = nextPowerOfTwo(bitsWanted);
//We might already have the required count.
const int sizeInBytes = bitsWanted / 8;
if (sizeInBytes <= bytesAllocated) return;
stream = allocBytes(sizeInBytes, stream, bytesAllocated);
bytesAllocated = sizeInBytes;
}
void appendBit(const int bit)
{
const uint32_t mask = uint32_t(1) << nextBitPos;
stream[currBytePos] = (stream[currBytePos] & ~mask) | (-bit & mask);
++numBitsWritten;
if (++nextBitPos == 8) {
nextBitPos = 0;
if (++currBytePos == bytesAllocated) allocate(bytesAllocated * granularity * 8);
}
}
void appendBitsU64(const uint64_t num, const int bitCount)
{
for (int b = 0; b < bitCount; ++b) {
const uint64_t mask = uint64_t(1) << b;
const int bit = !!(num & mask);
appendBit(bit);
}
}
uint8_t* release()
{
auto oldPtr = stream;
internalInit();
return oldPtr;
}
void setGranularity(const int growthGranularity)
{
granularity = (growthGranularity >= 2) ? growthGranularity : 2;
}
int getByteCount() const
{
int usedBytes = numBitsWritten / 8;
int leftovers = numBitsWritten % 8;
if (leftovers != 0) ++usedBytes;
return usedBytes;
}
};
struct BitStreamReader
{
const uint8_t* stream; // Pointer to the external bit stream. Not owned by the reader.
const int sizeInBytes; // Size of the stream *in bytes*. Might include padding.
const int sizeInBits; // Size of the stream *in bits*, padding *not* include.
int currBytePos = 0; // Current byte being read in the stream.
int nextBitPos = 0; // Bit position within the current byte to access next. 0 to 7.
int numBitsRead = 0; // Total bits read from the stream so far. Never includes byte-rounding padding.
BitStreamReader(const uint8_t* bitStream, const int byteCount, const int bitCount) : stream(bitStream), sizeInBytes(byteCount), sizeInBits(bitCount)
{
}
bool readNextBit(int& bitOut)
{
if (numBitsRead >= sizeInBits) return false; //We are done.
const uint32_t mask = uint32_t(1) << nextBitPos;
bitOut = !!(stream[currBytePos] & mask);
++numBitsRead;
if (++nextBitPos == 8) {
nextBitPos = 0;
++currBytePos;
}
return true;
}
uint64_t readBitsU64(const int bitCount)
{
uint64_t num = 0;
for (int b = 0; b < bitCount; ++b) {
int bit;
if (!readNextBit(bit)) break;
/* Based on a "Stanford bit-hack":
http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
const uint64_t mask = uint64_t(1) << b;
num = (num & ~mask) | (-bit & mask);
}
return num;
}
bool isEndOfStream() const
{
return numBitsRead >= sizeInBits;
}
};
struct Dictionary
{
struct Entry
{
int code;
int value;
};
//Dictionary entries 0-255 are always reserved to the byte/ASCII range.
int size;
Entry entries[MaxDictEntries];
Dictionary()
{
/* First 256 dictionary entries are reserved to the byte/ASCII range.
Additional entries follow for the character sequences found in the input.
Up to 4096 - 256 (MaxDictEntries - FirstCode). */
size = FirstCode;
for (int i = 0; i < size; ++i) {
entries[i].code = Nil;
entries[i].value = i;
}
}
int findIndex(const int code, const int value) const
{
if (code == Nil) return value;
//Linear search for now.
//TODO: Worth optimizing with a proper hash-table?
for (int i = 0; i < size; ++i) {
if (entries[i].code == code && entries[i].value == value) return i;
}
return Nil;
}
bool add(const int code, const int value)
{
if (size == MaxDictEntries) return false;
entries[size].code = code;
entries[size].value = value;
++size;
return true;
}
bool flush(int & codeBitsWidth)
{
if (size == (1 << codeBitsWidth)) {
++codeBitsWidth;
if (codeBitsWidth > MaxDictBits) {
//Clear the dictionary (except the first 256 byte entries).
codeBitsWidth = StartBits;
size = FirstCode;
return true;
}
}
return false;
}
};
static bool outputByte(int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar)
{
if (bytesDecodedSoFar >= outputSizeBytes) return false;
*output++ = static_cast<uint8_t>(code);
++bytesDecodedSoFar;
return true;
}
static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar, int& firstByte)
{
/* A sequence is stored backwards, so we have to write
it to a temp then output the buffer in reverse. */
int i = 0;
uint8_t sequence[MaxDictEntries];
do {
sequence[i++] = dict.entries[code].value;
code = dict.entries[code].code;
} while (code >= 0);
firstByte = sequence[--i];
for (; i >= 0; --i) {
if (!outputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar)) return false;
}
return true;
}
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
{
int code = Nil;
int prevCode = Nil;
int firstByte = 0;
int bytesDecoded = 0;
int codeBitsWidth = StartBits;
auto uncompressed = (uint8_t*) malloc(sizeof(uint8_t) * uncompressedSizeBytes);
auto ptr = uncompressed;
/* We'll reconstruct the dictionary based on the bit stream codes.
Unlike Huffman encoding, we don't store the dictionary as a prefix to the data. */
Dictionary dictionary;
BitStreamReader bitStream(compressed, compressedSizeBytes, compressedSizeBits);
/* We check to avoid an overflow of the user buffer.
If the buffer is smaller than the decompressed size, we break the loop and return the current decompression count. */
while (!bitStream.isEndOfStream()) {
code = static_cast<int>(bitStream.readBitsU64(codeBitsWidth));
if (prevCode == Nil) {
if (!outputByte(code, ptr, uncompressedSizeBytes, bytesDecoded)) break;
firstByte = code;
prevCode = code;
continue;
}
if (code >= dictionary.size) {
if (!outputSequence(dictionary, prevCode, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
if (!outputByte(firstByte, ptr, uncompressedSizeBytes, bytesDecoded)) break;
} else if (!outputSequence(dictionary, code, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
dictionary.add(prevCode, firstByte);
if (dictionary.flush(codeBitsWidth)) prevCode = Nil;
else prevCode = code;
}
return uncompressed;
}
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits)
{
//LZW encoding context:
int code = Nil;
int codeBitsWidth = StartBits;
Dictionary dictionary;
//Output bit stream we write to. This will allocate memory as needed to accommodate the encoded data.
BitStreamWriter bitStream;
for (; uncompressedSizeBytes > 0; --uncompressedSizeBytes, ++uncompressed) {
const int value = *uncompressed;
const int index = dictionary.findIndex(code, value);
if (index != Nil) {
code = index;
continue;
}
//Write the dictionary code using the minimum bit-with:
bitStream.appendBitsU64(code, codeBitsWidth);
//Flush it when full so we can restart the sequences.
if (!dictionary.flush(codeBitsWidth)) {
//There's still space for this sequence.
dictionary.add(code, value);
}
code = value;
}
//Residual code at the end:
if (code != Nil) bitStream.appendBitsU64(code, codeBitsWidth);
//Pass ownership of the compressed data buffer to the user pointer:
*compressedSizeBytes = bitStream.getByteCount();
*compressedSizeBits = bitStream.numBitsWritten;
return bitStream.release();
}
/************************************************************************/
/* B64 Implementation */
/************************************************************************/
size_t b64Decode(const char* encoded, const size_t len, char** decoded)
{
static constexpr const char B64_INDEX[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
if (!decoded || !encoded || len == 0) return 0;
auto reserved = 3 * (1 + (len >> 2)) + 1;
auto output = static_cast<char*>(malloc(reserved * sizeof(char)));
if (!output) return 0;
output[reserved - 1] = '\0';
size_t idx = 0;
while (*encoded && *(encoded + 1)) {
if (*encoded <= 0x20) {
++encoded;
continue;
}
auto value1 = B64_INDEX[(size_t)encoded[0]];
auto value2 = B64_INDEX[(size_t)encoded[1]];
output[idx++] = (value1 << 2) + ((value2 & 0x30) >> 4);
if (!encoded[2] || encoded[3] < 0 || encoded[2] == '=' || encoded[2] == '.') break;
auto value3 = B64_INDEX[(size_t)encoded[2]];
output[idx++] = ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
if (!encoded[3] || encoded[3] < 0 || encoded[3] == '=' || encoded[3] == '.') break;
auto value4 = B64_INDEX[(size_t)encoded[3]];
output[idx++] = ((value3 & 0x03) << 6) + value4;
encoded += 4;
}
*decoded = output;
return reserved;
}
/************************************************************************/
/* DJB2 Implementation */
/************************************************************************/
unsigned long djb2Encode(const char* str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
return hash;
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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 _TVG_COMPRESSOR_H_
#define _TVG_COMPRESSOR_H_
#include <cstdint>
namespace tvg
{
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
unsigned long djb2Encode(const char* str);
}
#endif //_TVG_COMPRESSOR_H_

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
* 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 _TVG_INLIST_H_
#define _TVG_INLIST_H_
namespace tvg {
//NOTE: declare this in your list item
#define INLIST_ITEM(T) \
T* prev; \
T* next
template<typename T>
struct Inlist
{
T* head = nullptr;
T* tail = nullptr;
void free()
{
while (head) {
auto t = head;
head = t->next;
delete(t);
}
head = tail = nullptr;
}
void back(T* element)
{
if (tail) {
tail->next = element;
element->prev = tail;
element->next = nullptr;
tail = element;
} else {
head = tail = element;
element->prev = nullptr;
element->next = nullptr;
}
}
void front(T* element)
{
if (head) {
head->prev = element;
element->prev = nullptr;
element->next = head;
head = element;
} else {
head = tail = element;
element->prev = nullptr;
element->next = nullptr;
}
}
T* back()
{
if (!tail) return nullptr;
auto t = tail;
tail = t->prev;
if (!tail) head = nullptr;
return t;
}
T* front()
{
if (!head) return nullptr;
auto t = head;
head = t->next;
if (!head) tail = nullptr;
return t;
}
void remove(T* element)
{
if (element->prev) element->prev->next = element->next;
if (element->next) element->next->prev = element->prev;
if (element == head) head = element->next;
if (element == tail) tail = element->prev;
}
bool empty()
{
return head ? false : true;
}
};
}
#endif // _TVG_INLIST_H_

View file

@ -0,0 +1,245 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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 "tvgMath.h"
#include "tvgLines.h"
#define BEZIER_EPSILON 1e-2f
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static float _lineLengthApprox(const Point& pt1, const Point& pt2)
{
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
With alpha = 1, beta = 3/8, giving results with the largest error less
than 7% compared to the exact value. */
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
if (diff.x < 0) diff.x = -diff.x;
if (diff.y < 0) diff.y = -diff.y;
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
}
static float _lineLength(const Point& pt1, const Point& pt2)
{
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
return sqrtf(diff.x * diff.x + diff.y * diff.y);
}
template<typename LengthFunc>
float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc)
{
Bezier left, right;
auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end);
auto chord = lineLengthFunc(cur.start, cur.end);
if (fabsf(len - chord) > BEZIER_EPSILON) {
tvg::bezSplit(cur, left, right);
return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc);
}
return len;
}
template<typename LengthFunc>
float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc)
{
auto biggest = 1.0f;
auto smallest = 0.0f;
auto t = 0.5f;
//just in case to prevent an infinite loop
if (at <= 0) return 0.0f;
if (at >= length) return 1.0f;
while (true) {
auto right = bz;
Bezier left;
bezSplitLeft(right, t, left);
length = _bezLength(left, lineLengthFunc);
if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
break;
}
if (length < at) {
smallest = t;
t = (t + biggest) * 0.5f;
} else {
biggest = t;
t = (smallest + t) * 0.5f;
}
}
return t;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
namespace tvg
{
float lineLength(const Point& pt1, const Point& pt2)
{
return _lineLength(pt1, pt2);
}
void lineSplitAt(const Line& cur, float at, Line& left, Line& right)
{
auto len = lineLength(cur.pt1, cur.pt2);
auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
left.pt1 = cur.pt1;
left.pt2.x = left.pt1.x + dx;
left.pt2.y = left.pt1.y + dy;
right.pt1 = left.pt2;
right.pt2 = cur.pt2;
}
void bezSplit(const Bezier& cur, Bezier& left, Bezier& right)
{
auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
left.start.x = cur.start.x;
right.end.x = cur.end.x;
left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
left.start.y = cur.start.y;
right.end.y = cur.end.y;
left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
}
float bezLength(const Bezier& cur)
{
return _bezLength(cur, _lineLength);
}
float bezLengthApprox(const Bezier& cur)
{
return _bezLength(cur, _lineLengthApprox);
}
void bezSplitLeft(Bezier& cur, float at, Bezier& left)
{
left.start = cur.start;
left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot
left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot
cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
}
float bezAt(const Bezier& bz, float at, float length)
{
return _bezAt(bz, at, length, _lineLength);
}
float bezAtApprox(const Bezier& bz, float at, float length)
{
return _bezAt(bz, at, length, _lineLengthApprox);
}
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
{
right = cur;
auto t = bezAt(right, at, bezLength(right));
bezSplitLeft(right, t, left);
}
Point bezPointAt(const Bezier& bz, float t)
{
Point cur;
auto it = 1.0f - t;
auto ax = bz.start.x * it + bz.ctrl1.x * t;
auto bx = bz.ctrl1.x * it + bz.ctrl2.x * t;
auto cx = bz.ctrl2.x * it + bz.end.x * t;
ax = ax * it + bx * t;
bx = bx * it + cx * t;
cur.x = ax * it + bx * t;
float ay = bz.start.y * it + bz.ctrl1.y * t;
float by = bz.ctrl1.y * it + bz.ctrl2.y * t;
float cy = bz.ctrl2.y * it + bz.end.y * t;
ay = ay * it + by * t;
by = by * it + cy * t;
cur.y = ay * it + by * t;
return cur;
}
float bezAngleAt(const Bezier& bz, float t)
{
if (t < 0 || t > 1) return 0;
//derivate
// p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
// t^2) * p2 + t^2 * p3)
float mt = 1.0f - t;
float d = t * t;
float a = -mt * mt;
float b = 1 - 4 * t + 3 * d;
float c = 2 * t - 3 * d;
Point pt ={a * bz.start.x + b * bz.ctrl1.x + c * bz.ctrl2.x + d * bz.end.x, a * bz.start.y + b * bz.ctrl1.y + c * bz.ctrl2.y + d * bz.end.y};
pt.x *= 3;
pt.y *= 3;
return mathRad2Deg(mathAtan2(pt.y, pt.x));
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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 _TVG_LINES_H_
#define _TVG_LINES_H_
#include "tvgCommon.h"
namespace tvg
{
struct Line
{
Point pt1;
Point pt2;
};
float lineLength(const Point& pt1, const Point& pt2);
void lineSplitAt(const Line& cur, float at, Line& left, Line& right);
struct Bezier
{
Point start;
Point ctrl1;
Point ctrl2;
Point end;
};
void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
float bezLength(const Bezier& cur);
void bezSplitLeft(Bezier& cur, float at, Bezier& left);
float bezAt(const Bezier& bz, float at, float length);
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
Point bezPointAt(const Bezier& bz, float t);
float bezAngleAt(const Bezier& bz, float t);
float bezLengthApprox(const Bezier& cur);
float bezAtApprox(const Bezier& bz, float at, float length);
}
#endif //_TVG_LINES_H_

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2024 the ThorVG project. All rights reserved.
* 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 _TVG_LOCK_H_
#define _TVG_LOCK_H_
#ifdef THORVG_THREAD_SUPPORT
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex>
#include "tvgTaskScheduler.h"
namespace tvg {
struct Key
{
std::mutex mtx;
};
struct ScopedLock
{
Key* key = nullptr;
ScopedLock(Key& k)
{
if (TaskScheduler::threads() > 0) {
k.mtx.lock();
key = &k;
}
}
~ScopedLock()
{
if (TaskScheduler::threads() > 0) {
key->mtx.unlock();
}
}
};
}
#else //THORVG_THREAD_SUPPORT
namespace tvg {
struct Key {};
struct ScopedLock
{
ScopedLock(Key& key) {}
};
}
#endif //THORVG_THREAD_SUPPORT
#endif //_TVG_LOCK_H_

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
* 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 "tvgMath.h"
//see: https://en.wikipedia.org/wiki/Remez_algorithm
float mathAtan2(float y, float x)
{
if (y == 0.0f && x == 0.0f) return 0.0f;
auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y));
auto s = a * a;
auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a;
if (fabsf(y) > fabsf(x)) r = 1.57079637f - r;
if (x < 0) r = 3.14159274f - r;
if (y < 0) return -r;
return r;
}
bool mathInverse(const Matrix* m, Matrix* out)
{
auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
m->e13 * (m->e21 * m->e32 - m->e22 * m->e31);
if (mathZero(det)) return false;
auto invDet = 1 / det;
out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet;
out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet;
out->e13 = (m->e12 * m->e23 - m->e13 * m->e22) * invDet;
out->e21 = (m->e23 * m->e31 - m->e21 * m->e33) * invDet;
out->e22 = (m->e11 * m->e33 - m->e13 * m->e31) * invDet;
out->e23 = (m->e21 * m->e13 - m->e11 * m->e23) * invDet;
out->e31 = (m->e21 * m->e32 - m->e31 * m->e22) * invDet;
out->e32 = (m->e31 * m->e12 - m->e11 * m->e32) * invDet;
out->e33 = (m->e11 * m->e22 - m->e21 * m->e12) * invDet;
return true;
}
bool mathIdentity(const Matrix* m)
{
if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
return false;
}
return true;
}
void mathRotate(Matrix* m, float degree)
{
if (degree == 0.0f) return;
auto radian = degree / 180.0f * MATH_PI;
auto cosVal = cosf(radian);
auto sinVal = sinf(radian);
m->e12 = m->e11 * -sinVal;
m->e11 *= cosVal;
m->e21 = m->e22 * sinVal;
m->e22 *= cosVal;
}
Matrix operator*(const Matrix& lhs, const Matrix& rhs)
{
Matrix m;
m.e11 = lhs.e11 * rhs.e11 + lhs.e12 * rhs.e21 + lhs.e13 * rhs.e31;
m.e12 = lhs.e11 * rhs.e12 + lhs.e12 * rhs.e22 + lhs.e13 * rhs.e32;
m.e13 = lhs.e11 * rhs.e13 + lhs.e12 * rhs.e23 + lhs.e13 * rhs.e33;
m.e21 = lhs.e21 * rhs.e11 + lhs.e22 * rhs.e21 + lhs.e23 * rhs.e31;
m.e22 = lhs.e21 * rhs.e12 + lhs.e22 * rhs.e22 + lhs.e23 * rhs.e32;
m.e23 = lhs.e21 * rhs.e13 + lhs.e22 * rhs.e23 + lhs.e23 * rhs.e33;
m.e31 = lhs.e31 * rhs.e11 + lhs.e32 * rhs.e21 + lhs.e33 * rhs.e31;
m.e32 = lhs.e31 * rhs.e12 + lhs.e32 * rhs.e22 + lhs.e33 * rhs.e32;
m.e33 = lhs.e31 * rhs.e13 + lhs.e32 * rhs.e23 + lhs.e33 * rhs.e33;
return m;
}
bool operator==(const Matrix& lhs, const Matrix& rhs)
{
if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) ||
!mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) ||
!mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) {
return false;
}
return true;
}
void operator*=(Point& pt, const Matrix& m)
{
auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13;
auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23;
pt.x = tx;
pt.y = ty;
}
Point operator*(const Point& pt, const Matrix& m)
{
auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13;
auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23;
return {tx, ty};
}

View file

@ -0,0 +1,252 @@
/*
* Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved.
* 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 _TVG_MATH_H_
#define _TVG_MATH_H_
#define _USE_MATH_DEFINES
#include <float.h>
#include <math.h>
#include "tvgCommon.h"
#define MATH_PI 3.14159265358979323846f
#define MATH_PI2 1.57079632679489661923f
#define FLOAT_EPSILON 1.0e-06f //1.192092896e-07f
#define PATH_KAPPA 0.552284f
#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
/************************************************************************/
/* General functions */
/************************************************************************/
float mathAtan2(float y, float x);
static inline float mathDeg2Rad(float degree)
{
return degree * (MATH_PI / 180.0f);
}
static inline float mathRad2Deg(float radian)
{
return radian * (180.0f / MATH_PI);
}
static inline bool mathZero(float a)
{
return (fabsf(a) <= FLOAT_EPSILON) ? true : false;
}
static inline bool mathEqual(float a, float b)
{
return mathZero(a - b);
}
/************************************************************************/
/* Matrix functions */
/************************************************************************/
void mathRotate(Matrix* m, float degree);
bool mathInverse(const Matrix* m, Matrix* out);
bool mathIdentity(const Matrix* m);
Matrix operator*(const Matrix& lhs, const Matrix& rhs);
bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m)
{
auto radian = fabsf(mathAtan2(m->e21, m->e11));
if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
return false;
}
static inline bool mathSkewed(const Matrix* m)
{
return !mathZero(m->e21 + m->e12);
}
static inline void mathIdentity(Matrix* m)
{
m->e11 = 1.0f;
m->e12 = 0.0f;
m->e13 = 0.0f;
m->e21 = 0.0f;
m->e22 = 1.0f;
m->e23 = 0.0f;
m->e31 = 0.0f;
m->e32 = 0.0f;
m->e33 = 1.0f;
}
static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
m->e22 *= sy;
}
static inline void mathScaleR(Matrix* m, float x, float y)
{
if (x != 1.0f) {
m->e11 *= x;
m->e21 *= x;
}
if (y != 1.0f) {
m->e22 *= y;
m->e12 *= y;
}
}
static inline void mathTranslate(Matrix* m, float x, float y)
{
m->e13 += x;
m->e23 += y;
}
static inline void mathTranslateR(Matrix* m, float x, float y)
{
if (x == 0.0f && y == 0.0f) return;
m->e13 += (x * m->e11 + y * m->e12);
m->e23 += (x * m->e21 + y * m->e22);
}
static inline bool operator!=(const Matrix& lhs, const Matrix& rhs)
{
return !(lhs == rhs);
}
static inline void operator*=(Matrix& lhs, const Matrix& rhs)
{
lhs = lhs * rhs;
}
static inline void mathLog(const Matrix& m)
{
TVGLOG("COMMON", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m.e11, m.e12, m.e13, m.e21, m.e22, m.e23, m.e31, m.e32, m.e33);
}
/************************************************************************/
/* Point functions */
/************************************************************************/
void operator*=(Point& pt, const Matrix& m);
Point operator*(const Point& pt, const Matrix& m);
static inline bool mathZero(const Point& p)
{
return mathZero(p.x) && mathZero(p.y);
}
static inline float mathLength(const Point* a, const Point* b)
{
auto x = b->x - a->x;
auto y = b->y - a->y;
if (x < 0) x = -x;
if (y < 0) y = -y;
return (x > y) ? (x + 0.375f * y) : (y + 0.375f * x);
}
static inline float mathLength(const Point& a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
static inline bool operator==(const Point& lhs, const Point& rhs)
{
return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y);
}
static inline bool operator!=(const Point& lhs, const Point& rhs)
{
return !(lhs == rhs);
}
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
}
static inline Point operator+(const Point& lhs, const Point& rhs)
{
return {lhs.x + rhs.x, lhs.y + rhs.y};
}
static inline Point operator*(const Point& lhs, float rhs)
{
return {lhs.x * rhs, lhs.y * rhs};
}
static inline Point operator*(const float& lhs, const Point& rhs)
{
return {lhs * rhs.x, lhs * rhs.y};
}
static inline Point operator/(const Point& lhs, const float rhs)
{
return {lhs.x / rhs, lhs.y / rhs};
}
static inline void mathLog(const Point& pt)
{
TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);
}
/************************************************************************/
/* Interpolation functions */
/************************************************************************/
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
{
return static_cast<T>(start + (end - start) * t);
}
#endif //_TVG_MATH_H_

View file

@ -0,0 +1,242 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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 "config.h"
#include <cmath>
#include <cstring>
#include <memory.h>
#include "tvgMath.h"
#include "tvgStr.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static inline bool _floatExact(float a, float b)
{
return memcmp(&a, &b, sizeof(float)) == 0;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
namespace tvg {
/*
* https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
*
* src should be one of the following form :
*
* [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
* [whitespace] [sign] {INF | INFINITY}
* [whitespace] [sign] NAN [sequence]
*
* No hexadecimal form supported
* no sequence supported after NAN
*/
float strToFloat(const char *nPtr, char **endPtr)
{
if (endPtr) *endPtr = (char *) (nPtr);
if (!nPtr) return 0.0f;
auto a = nPtr;
auto iter = nPtr;
auto val = 0.0f;
unsigned long long integerPart = 0;
int minus = 1;
//ignore leading whitespaces
while (isspace(*iter)) iter++;
//signed or not
if (*iter == '-') {
minus = -1;
iter++;
} else if (*iter == '+') {
iter++;
}
if (tolower(*iter) == 'i') {
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
else goto error;
if (tolower(*(iter)) == 'i') {
if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'i') && (tolower(*(iter + 3)) == 't') &&
(tolower(*(iter + 4)) == 'y'))
iter += 5;
else goto error;
}
if (endPtr) *endPtr = (char *) (iter);
return (minus == -1) ? -INFINITY : INFINITY;
}
if (tolower(*iter) == 'n') {
if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
else goto error;
if (endPtr) *endPtr = (char *) (iter);
return (minus == -1) ? -NAN : NAN;
}
//Optional: integer part before dot
if (isdigit(*iter)) {
for (; isdigit(*iter); iter++) {
integerPart = integerPart * 10ULL + (unsigned long long) (*iter - '0');
}
a = iter;
} else if (*iter != '.') {
goto success;
}
val = static_cast<float>(integerPart);
//Optional: decimal part after dot
if (*iter == '.') {
unsigned long long decimalPart = 0;
unsigned long long pow10 = 1;
int count = 0;
iter++;
if (isdigit(*iter)) {
for (; isdigit(*iter); iter++, count++) {
if (count < 19) {
decimalPart = decimalPart * 10ULL + +static_cast<unsigned long long>(*iter - '0');
pow10 *= 10ULL;
}
}
} else if (isspace(*iter)) { //skip if there is a space after the dot.
a = iter;
goto success;
}
val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
a = iter;
}
//Optional: exponent
if (*iter == 'e' || *iter == 'E') {
++iter;
//Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
if ((*iter == 'm') || (*iter == 'M')) {
//TODO: We don't support font em unit now, but has to multiply val * font size later...
a = iter + 1;
goto success;
}
//signed or not
int minus_e = 1;
if (*iter == '-') {
minus_e = -1;
++iter;
} else if (*iter == '+') {
iter++;
}
unsigned int exponentPart = 0;
if (isdigit(*iter)) {
while (*iter == '0') iter++;
for (; isdigit(*iter); iter++) {
exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
}
} else if (!isdigit(*(a - 1))) {
a = nPtr;
goto success;
} else if (*iter == 0) {
goto success;
}
//if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
//val *= 1.0e-308f;
val *= 1.0e-38f;
a = iter;
goto success;
}
a = iter;
auto scale = 1.0f;
while (exponentPart >= 8U) {
scale *= 1E8;
exponentPart -= 8U;
}
while (exponentPart > 0U) {
scale *= 10.0f;
exponentPart--;
}
val = (minus_e == -1) ? (val / scale) : (val * scale);
} else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
a = nPtr;
goto success;
}
success:
if (endPtr) *endPtr = (char *)(a);
if (!std::isfinite(val)) return 0.0f;
return minus * val;
error:
if (endPtr) *endPtr = (char *)(nPtr);
return 0.0f;
}
int str2int(const char* str, size_t n)
{
int ret = 0;
for(size_t i = 0; i < n; ++i) {
ret = ret * 10 + (str[i] - '0');
}
return ret;
}
char* strDuplicate(const char *str, size_t n)
{
auto len = strlen(str);
if (len < n) n = len;
auto ret = (char *) malloc(n + 1);
if (!ret) return nullptr;
ret[n] = '\0';
return (char *) memcpy(ret, str, n);
}
char* strDirname(const char* path)
{
const char *ptr = strrchr(path, '/');
#ifdef _WIN32
if (ptr) ptr = strrchr(ptr + 1, '\\');
#endif
int len = int(ptr + 1 - path); // +1 to include '/'
return strDuplicate(path, len);
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* 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 _TVG_STR_H_
#define _TVG_STR_H_
#include <cstddef>
namespace tvg
{
float strToFloat(const char *nPtr, char **endPtr); //convert to float
int str2int(const char* str, size_t n); //convert to integer
char* strDuplicate(const char *str, size_t n); //copy the string
char* strDirname(const char* path); //return the full directory name
}
#endif //_TVG_STR_H_