|
|
8535a3 |
|
|
|
28a314 |
#include <dirent.h></dirent.h>
|
|
|
28a314 |
#include <sys stat.h=""></sys>
|
|
|
28a314 |
|
|
|
8535a3 |
#include "private.h"
|
|
|
ca6bde |
#include "drawing.h"
|
|
|
8535a3 |
|
|
|
8535a3 |
|
|
|
8eb855 |
int heliInitialized;
|
|
|
8eb855 |
HeliArray heliObjectsSet;
|
|
|
8535a3 |
|
|
|
8535a3 |
|
|
|
1015c5 |
struct _Directory {
|
|
|
dac862 |
size_t pathLen;
|
|
|
1015c5 |
HeliArray files;
|
|
|
1015c5 |
};
|
|
|
1015c5 |
|
|
|
1015c5 |
|
|
|
1d641c |
static const float yuvToRgb[3][3] = {
|
|
|
1d641c |
{ 1.0, 0.0 , 1.402 },
|
|
|
1d641c |
{ 1.0, -0.344136, -0.714136 },
|
|
|
1d641c |
{ 1.0, 1.772 , 0.0 } };
|
|
|
1d641c |
|
|
|
1d641c |
static const float rgbToYuv[3][3] = {
|
|
|
1d641c |
{ 0.299 , 0.587 , 0.114 },
|
|
|
1d641c |
{ -0.168736, -0.331264, 0.5 },
|
|
|
1d641c |
{ 0.5 , -0.418688, -0.081312 } };
|
|
|
1d641c |
|
|
|
c7535b |
#include "colors.inc.h"
|
|
|
09c823 |
|
|
|
09c823 |
|
|
|
8535a3 |
int randomNumber(int min, int max)
|
|
|
8535a3 |
{ return max <= min ? min : rand()%(max - min + 1) + min; }
|
|
|
8535a3 |
|
|
|
8535a3 |
double randomFloat()
|
|
|
8535a3 |
{ return (double)rand()/(double)RAND_MAX; }
|
|
|
8535a3 |
|
|
|
909bc2 |
void rotateVector(double *x, double *y, double angle) {
|
|
|
909bc2 |
double a = angle*PI/180.0;
|
|
|
909bc2 |
double xx = x ? *x : 0.0;
|
|
|
909bc2 |
double yy = y ? *y : 0.0;
|
|
|
909bc2 |
double c = cos(a);
|
|
|
909bc2 |
double s = sin(a);
|
|
|
909bc2 |
if (x) *x = xx*c - yy*s;
|
|
|
909bc2 |
if (y) *y = xx*s + yy*c;
|
|
|
909bc2 |
}
|
|
|
8535a3 |
|
|
|
dac862 |
Directory openDirectoryEx(
|
|
|
dac862 |
const char *path,
|
|
|
dac862 |
const char *prefix,
|
|
|
dac862 |
const char *suffix,
|
|
|
dac862 |
int caseSensitive,
|
|
|
dac862 |
int showFiles,
|
|
|
dac862 |
int showDirectories )
|
|
|
dac862 |
{
|
|
|
8eb855 |
if (!heliInitialized) return NULL;
|
|
|
dac862 |
if (!path) return NULL;
|
|
|
28a314 |
|
|
|
28a314 |
DIR *dir = opendir(path);
|
|
|
1015c5 |
if (!dir) return NULL;
|
|
|
1015c5 |
|
|
|
87fe10 |
Directory d = calloc(1, sizeof(*d));
|
|
|
28a314 |
|
|
|
dac862 |
d->pathLen = strlen(path);
|
|
|
dac862 |
size_t prefixLen = prefix ? strlen(prefix) : 0u;
|
|
|
dac862 |
size_t suffixLen = suffix ? strlen(suffix) : 0u;
|
|
|
dac862 |
|
|
|
dac862 |
size_t bufLen = d->pathLen + 1024;
|
|
|
dac862 |
char *buf = calloc(1, bufLen);
|
|
|
dac862 |
strcpy(buf, path);
|
|
|
dac862 |
if (d->pathLen > 0 && buf[d->pathLen - 1] != '\\' && buf[d->pathLen - 1] != '/') {
|
|
|
dac862 |
char lastSlash = '/';
|
|
|
dac862 |
for(const char *c = buf; *c; ++c)
|
|
|
dac862 |
if (*c == '\\' || *c == '/') lastSlash = *c;
|
|
|
dac862 |
buf[d->pathLen++] = lastSlash;
|
|
|
dac862 |
}
|
|
|
dac862 |
|
|
|
28a314 |
struct dirent *ent;
|
|
|
28a314 |
while((ent = readdir(dir)) != NULL) {
|
|
|
87fe10 |
if ( !ent->d_name[0]
|
|
|
87fe10 |
|| strcmp(ent->d_name, ".") == 0
|
|
|
87fe10 |
|| strcmp(ent->d_name, "..") == 0 ) continue;
|
|
|
dac862 |
|
|
|
dac862 |
if (prefixLen) {
|
|
|
dac862 |
if ( caseSensitive ? strncmp(prefix, ent->d_name, prefixLen)
|
|
|
dac862 |
: heliStringCompareNCi(prefix, ent->d_name, prefixLen) )
|
|
|
dac862 |
continue;
|
|
|
dac862 |
}
|
|
|
dac862 |
|
|
|
dac862 |
size_t nameLen = strlen(ent->d_name);
|
|
|
dac862 |
|
|
|
dac862 |
if (suffixLen) {
|
|
|
dac862 |
if (nameLen < suffixLen) continue;
|
|
|
dac862 |
const char *sub = ent->d_name + nameLen - suffixLen;
|
|
|
dac862 |
if ( caseSensitive ? strcmp(suffix, sub)
|
|
|
dac862 |
: heliStringCompareCi(suffix, sub) )
|
|
|
dac862 |
continue;
|
|
|
dac862 |
}
|
|
|
dac862 |
|
|
|
dac862 |
if (bufLen <= d->pathLen + nameLen) {
|
|
|
dac862 |
bufLen = d->pathLen + nameLen + 1024;
|
|
|
dac862 |
buf = realloc(buf, bufLen);
|
|
|
dac862 |
}
|
|
|
dac862 |
|
|
|
dac862 |
strcpy(buf + d->pathLen, ent->d_name);
|
|
|
dac862 |
|
|
|
dac862 |
if ( (showFiles && fileExists(buf))
|
|
|
dac862 |
|| (showDirectories && directoryExists(buf)) )
|
|
|
dac862 |
heliStringmapAdd(&d->files, buf, NULL, NULL);
|
|
|
1015c5 |
}
|
|
|
28a314 |
closedir(dir);
|
|
|
1015c5 |
|
|
|
8eb855 |
heliObjectRegister(d, (HeliFreeCallback)&closeDirectory);
|
|
|
1015c5 |
return d;
|
|
|
1015c5 |
}
|
|
|
1015c5 |
|
|
|
dac862 |
Directory openDirectory(const char *path)
|
|
|
dac862 |
{ return openDirectoryEx(path, NULL, NULL, TRUE, TRUE, TRUE); }
|
|
|
dac862 |
|
|
|
1015c5 |
void closeDirectory(Directory directory) {
|
|
|
1015c5 |
if (!directory) return;
|
|
|
8eb855 |
heliObjectUnregister(directory);
|
|
|
1015c5 |
heliArrayDestroy(&directory->files);
|
|
|
1015c5 |
free(directory);
|
|
|
1015c5 |
}
|
|
|
1015c5 |
|
|
|
1015c5 |
int directoryGetCount(Directory directory)
|
|
|
1015c5 |
{ return directory->files.count; }
|
|
|
1015c5 |
|
|
|
dac862 |
const char* directoryGetFull(Directory directory, int i)
|
|
|
1015c5 |
{ return (const char*)heliArrayGetKey(&directory->files, i); }
|
|
|
1015c5 |
|
|
|
dac862 |
const char* directoryGet(Directory directory, int i) {
|
|
|
dac862 |
const char *path = directoryGetFull(directory, i);
|
|
|
dac862 |
return path ? path + directory->pathLen : NULL;
|
|
|
dac862 |
}
|
|
|
1015c5 |
|
|
|
28a314 |
int fileExists(const char *path) {
|
|
|
28a314 |
struct stat s = {};
|
|
|
28a314 |
return stat(path, &s) == 0 && !(s.st_mode & S_IFDIR);
|
|
|
28a314 |
}
|
|
|
28a314 |
|
|
|
28a314 |
int directoryExists(const char *path) {
|
|
|
28a314 |
struct stat s = {};
|
|
|
28a314 |
return stat(path, &s) == 0 && (s.st_mode & S_IFDIR);
|
|
|
28a314 |
}
|
|
|
1015c5 |
|
|
|
1015c5 |
|
|
|
8535a3 |
char* heliStringCopy(const char *x) {
|
|
|
8535a3 |
int len = strlen(x) + 1;
|
|
|
f63775 |
char *cp = malloc(len);
|
|
|
8535a3 |
memcpy(cp, x, len);
|
|
|
8535a3 |
return cp;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
bcaca0 |
char* heliStringCopyLower(const char *x) {
|
|
|
bcaca0 |
char *xx = heliStringCopy(x);
|
|
|
bcaca0 |
heliStringLower(xx);
|
|
|
bcaca0 |
return xx;
|
|
|
bcaca0 |
}
|
|
|
bcaca0 |
|
|
|
dba3fc |
char* heliStringConcat(const char *a, const char *b) {
|
|
|
dba3fc |
int la = strlen(a);
|
|
|
dba3fc |
int lb = strlen(b);
|
|
|
dba3fc |
char *s = malloc(la + lb + 1);
|
|
|
dba3fc |
memcpy(s, a, la);
|
|
|
dba3fc |
memcpy(s + la, b, lb);
|
|
|
dba3fc |
s[la + lb] = 0;
|
|
|
dba3fc |
return s;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
8535a3 |
char* heliStringConcat3(const char *a, const char *b, const char *c) {
|
|
|
8535a3 |
int la = strlen(a);
|
|
|
8535a3 |
int lb = strlen(b);
|
|
|
8535a3 |
int lc = strlen(c);
|
|
|
8535a3 |
char *s = malloc(la + lb + lc + 1);
|
|
|
8535a3 |
memcpy(s, a, la);
|
|
|
8535a3 |
memcpy(s + la, b, lb);
|
|
|
8535a3 |
memcpy(s + la + lb, c, lc);
|
|
|
8535a3 |
s[la + lb + lc] = 0;
|
|
|
8535a3 |
return s;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
dac862 |
int heliStringCompareNCi(const char *a, const char *b, size_t n) {
|
|
|
bcaca0 |
if (!a && !b) return 0;
|
|
|
bcaca0 |
if (!a) return -1;
|
|
|
bcaca0 |
if (!b) return 1;
|
|
|
dac862 |
while((*a || *b) && n) {
|
|
|
bcaca0 |
if (!a) return -1;
|
|
|
bcaca0 |
if (!b) return 1;
|
|
|
bcaca0 |
char aa = tolower(*a);
|
|
|
bcaca0 |
char bb = tolower(*b);
|
|
|
bcaca0 |
if (aa < bb) return -1;
|
|
|
bcaca0 |
if (bb < aa) return 1;
|
|
|
dac862 |
++a, ++b, --n;
|
|
|
bcaca0 |
}
|
|
|
bcaca0 |
return 0;
|
|
|
bcaca0 |
}
|
|
|
bcaca0 |
|
|
|
dac862 |
int heliStringCompareCi(const char *a, const char *b)
|
|
|
dac862 |
{ return heliStringCompareNCi(a, b, SIZE_MAX); }
|
|
|
dac862 |
|
|
|
8535a3 |
int heliStringEndsWithLowcase(const char *s, const char *tail) {
|
|
|
8535a3 |
int ls = strlen(s);
|
|
|
8535a3 |
int lt = strlen(tail);
|
|
|
8535a3 |
if (lt > ls) return FALSE;
|
|
|
8535a3 |
for(int i = 0; i < lt; ++i)
|
|
|
8535a3 |
if (tolower(s[ls - i]) != tolower(tail[lt - i]))
|
|
|
8535a3 |
return FALSE;
|
|
|
8535a3 |
return TRUE;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
bcaca0 |
void heliStringLower(char *x)
|
|
|
981405 |
{ while(*x) { *x = tolower(*x); ++x; } }
|
|
|
8535a3 |
|
|
|
09c823 |
|
|
|
1d641c |
static double colorClamp(double c)
|
|
|
1d641c |
{ return c > 0 ? (c < 1 ? c : 1) : 0; }
|
|
|
1d641c |
static unsigned int colorToInt(double c)
|
|
|
1d641c |
{ return (unsigned int)floor(colorClamp(c)*255.99); }
|
|
|
d4e89f |
|
|
|
7ac038 |
unsigned int colorWithAlpha(unsigned int colorCode, double a)
|
|
|
7ac038 |
{ return (0xffffff00 & colorCode) | colorToInt(colorGetAlpha(colorCode)*a); }
|
|
|
d4e89f |
unsigned int colorByRGBA(double r, double g, double b, double a) {
|
|
|
d4e89f |
return (colorToInt(r) << 24)
|
|
|
d4e89f |
| (colorToInt(g) << 16)
|
|
|
d4e89f |
| (colorToInt(b) << 8)
|
|
|
d4e89f |
| colorToInt(a);
|
|
|
d4e89f |
}
|
|
|
7ac038 |
|
|
|
1d641c |
unsigned int colorByYUVA(double y, double u, double v, double a) {
|
|
|
1d641c |
return colorByRGBA(
|
|
|
1d641c |
yuvToRgb[0][0]*y + yuvToRgb[0][1]*u + yuvToRgb[0][2]*v,
|
|
|
1d641c |
yuvToRgb[1][0]*y + yuvToRgb[1][1]*u + yuvToRgb[1][2]*v,
|
|
|
1d641c |
yuvToRgb[2][0]*y + yuvToRgb[2][1]*u + yuvToRgb[2][2]*v,
|
|
|
1d641c |
a );
|
|
|
1d641c |
}
|
|
|
1d641c |
unsigned int colorByHSVA(double h, double s, double v, double a) {
|
|
|
1d641c |
h -= floor(h/360)*360;
|
|
|
1d641c |
h /= 60.0;
|
|
|
1d641c |
int i = (int)h;
|
|
|
1d641c |
double f = h - i;
|
|
|
1d641c |
double p = v*(1 - s);
|
|
|
1d641c |
double q = v*(1 - s*f);
|
|
|
1d641c |
double t = v*(1 - s*(1 - f));
|
|
|
1d641c |
switch(i) {
|
|
|
1d641c |
case 0: return colorByRGBA(v, t, p, a);
|
|
|
1d641c |
case 1: return colorByRGBA(q, v, p, a);
|
|
|
1d641c |
case 2: return colorByRGBA(p, v, t, a);
|
|
|
1d641c |
case 3: return colorByRGBA(p, q, v, a);
|
|
|
1d641c |
case 4: return colorByRGBA(t, p, v, a);
|
|
|
1d641c |
case 5: return colorByRGBA(v, p, q, a);
|
|
|
1d641c |
}
|
|
|
1d641c |
return colorByRGBA(v, t, p, a);
|
|
|
1d641c |
}
|
|
|
d4e89f |
|
|
|
d4e89f |
unsigned int colorByRGB(double r, double g, double b)
|
|
|
d4e89f |
{ return colorByRGBA(r, g, b, 1); }
|
|
|
1d641c |
unsigned int colorByHSV(double h, double s, double v)
|
|
|
1d641c |
{ return colorByHSVA(h, s, v, 1); }
|
|
|
1d641c |
unsigned int colorByYUV(double y, double u, double v)
|
|
|
1d641c |
{ return colorByYUVA(y, u, v, 1); }
|
|
|
1d641c |
|
|
|
d6f40c |
double colorGetRed (unsigned int colorCode) { return ( colorCode >> 24 )/255.0; }
|
|
|
d6f40c |
double colorGetGreen(unsigned int colorCode) { return ((colorCode >> 16) & 0xFFu)/255.0; }
|
|
|
d6f40c |
double colorGetBlue (unsigned int colorCode) { return ((colorCode >> 8) & 0xFFu)/255.0; }
|
|
|
d6f40c |
double colorGetAlpha(unsigned int colorCode) { return ( colorCode & 0xFFu)/255.0; }
|
|
|
1d641c |
void heliColorToDouble(unsigned int code, double *rgba) {
|
|
|
1d641c |
rgba[0] = colorGetRed (code);
|
|
|
1d641c |
rgba[1] = colorGetGreen(code);
|
|
|
1d641c |
rgba[2] = colorGetBlue (code);
|
|
|
1d641c |
rgba[3] = colorGetAlpha(code);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
d6f40c |
double colorGetYLuminance(unsigned int colorCode) {
|
|
|
d6f40c |
return rgbToYuv[0][0]*colorGetRed (colorCode)
|
|
|
d6f40c |
+ rgbToYuv[0][1]*colorGetGreen(colorCode)
|
|
|
d6f40c |
+ rgbToYuv[0][2]*colorGetBlue (colorCode);
|
|
|
1d641c |
}
|
|
|
d6f40c |
double colorGetUChrominance(unsigned int colorCode) {
|
|
|
d6f40c |
return rgbToYuv[1][0]*colorGetRed (colorCode)
|
|
|
d6f40c |
+ rgbToYuv[1][1]*colorGetGreen(colorCode)
|
|
|
d6f40c |
+ rgbToYuv[1][2]*colorGetBlue (colorCode);
|
|
|
1d641c |
}
|
|
|
d6f40c |
double colorGetVChrominance(unsigned int colorCode) {
|
|
|
d6f40c |
return rgbToYuv[1][0]*colorGetRed (colorCode)
|
|
|
d6f40c |
+ rgbToYuv[1][1]*colorGetGreen(colorCode)
|
|
|
d6f40c |
+ rgbToYuv[1][2]*colorGetBlue (colorCode);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
1d641c |
static int colorMin(double *c)
|
|
|
1d641c |
{ return c[0] < c[1] ? (c[0] < c[2] ? 0 : 2) : (c[1] < c[2] ? 1 : 2); }
|
|
|
1d641c |
static int colorMax(double *c)
|
|
|
1d641c |
{ return c[0] > c[1] ? (c[0] > c[2] ? 0 : 2) : (c[1] > c[2] ? 1 : 2); }
|
|
|
d6f40c |
double colorGetHue(unsigned int colorCode) {
|
|
|
d6f40c |
double rgb[] = { colorGetRed (colorCode),
|
|
|
d6f40c |
colorGetGreen(colorCode),
|
|
|
d6f40c |
colorGetBlue (colorCode) };
|
|
|
1d641c |
int cmin = colorMin(rgb);
|
|
|
1d641c |
int cmax = colorMax(rgb);
|
|
|
1d641c |
double d = rgb[cmax] - rgb[cmin];
|
|
|
1d641c |
double h = 0;
|
|
|
1d641c |
if (d > HELI_PRECISION) {
|
|
|
1d641c |
d = 1.0/d;
|
|
|
1d641c |
switch(cmax){
|
|
|
1d641c |
case 0: h = (rgb[1] - rgb[2])*d + 0; break;
|
|
|
1d641c |
case 1: h = (rgb[2] - rgb[0])*d + 2; break;
|
|
|
1d641c |
case 2: h = (rgb[0] - rgb[1])*d + 4; break;
|
|
|
1d641c |
}
|
|
|
1d641c |
h /= 6; h -= floor(h); h *= 360;
|
|
|
1d641c |
}
|
|
|
1d641c |
return h;
|
|
|
1d641c |
}
|
|
|
d6f40c |
double colorGetSaturation(unsigned int colorCode) {
|
|
|
d6f40c |
double rgb[] = { colorGetRed (colorCode),
|
|
|
d6f40c |
colorGetGreen(colorCode),
|
|
|
d6f40c |
colorGetBlue (colorCode) };
|
|
|
1d641c |
int cmin = colorMin(rgb);
|
|
|
1d641c |
int cmax = colorMax(rgb);
|
|
|
1d641c |
return rgb[cmax] > HELI_PRECISION ? (rgb[cmax] - rgb[cmin])/rgb[cmax] : 0;
|
|
|
1d641c |
}
|
|
|
d6f40c |
double colorGetValue(unsigned int colorCode) {
|
|
|
d6f40c |
double rgb[] = { colorGetRed (colorCode),
|
|
|
d6f40c |
colorGetGreen(colorCode),
|
|
|
d6f40c |
colorGetBlue (colorCode) };
|
|
|
1d641c |
return rgb[colorMax(rgb)];
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
d4e89f |
|
|
|
7ac038 |
unsigned int colorByNameA(const char *colorName, double a)
|
|
|
7ac038 |
{ return colorWithAlpha(colorByName(colorName), a); }
|
|
|
7ac038 |
unsigned int colorByName(const char *colorName) {
|
|
|
d4e89f |
unsigned int code = 0;
|
|
|
09c823 |
|
|
|
d4e89f |
const char *x = colorName;
|
|
|
09c823 |
if (*x == '#') {
|
|
|
09c823 |
++x;
|
|
|
09c823 |
int hex[8] = { 0, 0, 0, 0, 0, 0, 15, 15 };
|
|
|
59dae5 |
for(int i = 0; *x && i < 8; ++i, ++x) {
|
|
|
09c823 |
char c = tolower(*x);
|
|
|
09c823 |
if (c >= '0' && c <= '9') hex[i] = c - '0';
|
|
|
09c823 |
if (c >= 'a' && c <= 'f') hex[i] = c - 'a' + 10;
|
|
|
09c823 |
}
|
|
|
ca6bde |
for(int i = 0; i < 6; ++i)
|
|
|
d4e89f |
code |= ( hex[i] << ((7-i)*4) );
|
|
|
7ac038 |
code |= colorToInt( (hex[6]*16 + hex[7])/255.0 );
|
|
|
09c823 |
} else
|
|
|
09c823 |
if (isalpha(*x)) {
|
|
|
59dae5 |
int count = (int)(sizeof(colors)/sizeof(*colors));
|
|
|
bcaca0 |
for(int i = 0; i < count; ++i)
|
|
|
7ac038 |
if (heliStringCompareCi(x, colors[i].name) == 0)
|
|
|
7ac038 |
return colors[i].code;
|
|
|
09c823 |
} else {
|
|
|
7ac038 |
double r = 0, g = 0, b = 0, a = 1;
|
|
|
7ac038 |
sscanf(x, "%lf %lf %lf %lf", &r, &g, &b, &a);
|
|
|
7ac038 |
code = colorByRGBA(r, g, b, a);
|
|
|
09c823 |
}
|
|
|
d4e89f |
return code;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
09c823 |
|
|
|
8eb855 |
static int objectsCompare(const void *a, const void *b)
|
|
|
8eb855 |
{ return a < b ? -1 : b < a ? 1 : 0; }
|
|
|
8eb855 |
|
|
|
8eb855 |
static void* objectsKeyClone(void *k)
|
|
|
8eb855 |
{ return k; }
|
|
|
8eb855 |
|
|
|
8eb855 |
void heliObjectRegister(void *o, HeliFreeCallback fo)
|
|
|
8eb855 |
{ heliMapAdd(&heliObjectsSet, o, &objectsCompare, (HeliCloneCallback)objectsKeyClone, fo, NULL, NULL); }
|
|
|
8eb855 |
|
|
|
8eb855 |
void heliObjectUnregister(void *o) {
|
|
|
8eb855 |
int i;
|
|
|
8eb855 |
HeliPair *item = heliMapFind(&heliObjectsSet, o, &objectsCompare, &i);
|
|
|
8eb855 |
if (item) {
|
|
|
8eb855 |
item->freeKey = NULL;
|
|
|
8eb855 |
heliArrayRemove(&heliObjectsSet, i);
|
|
|
8eb855 |
}
|
|
|
8eb855 |
}
|