364 lines
14 KiB
C
364 lines
14 KiB
C
static inline
|
|
float texed_map_value(float v, float min, float max);
|
|
|
|
static inline
|
|
Image texed_generate_noise(uint32_t seed, int width, int height, float factor);
|
|
|
|
static inline
|
|
Image texed_generate_cellular(uint32_t seed, int width, int height, int tileSize);
|
|
|
|
void texed_process_ops(void) {
|
|
for (int i = 0; i <= ctx.img_pos; i+=1)
|
|
UnloadImage(ctx.img[i]);
|
|
ctx.img_pos = -1;
|
|
|
|
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
|
td_op *op = &ctx.ops[i];
|
|
if (op->is_hidden) continue;
|
|
zpl_printf("processing op: %s ... \n", op->name);
|
|
|
|
switch (op->kind) {
|
|
case TOP_PUSH_IMAGE:
|
|
case TOP_NEW_IMAGE: {
|
|
texed_img_push(op->params[0].i32, op->params[1].i32, op->params[2].color);
|
|
}break;
|
|
case TOP_POP_IMAGE: {
|
|
texed_img_pop(op->params[0].i32,
|
|
op->params[1].i32,
|
|
op->params[2].i32,
|
|
op->params[3].i32,
|
|
op->params[4].color);
|
|
}break;
|
|
case TOP_IMAGE_ALPHA_MASK: {
|
|
if (ctx.img_pos == 0) break;
|
|
|
|
Image *oi = &ctx.img[ctx.img_pos];
|
|
Image *di = &ctx.img[ctx.img_pos-1];
|
|
ImageAlphaMask(di, *oi);
|
|
ctx.img_pos--;
|
|
}break;
|
|
case TOP_DRAW_RECT: {
|
|
ImageDrawRectangle(&ctx.img[ctx.img_pos],
|
|
op->params[0].i32,
|
|
op->params[1].i32,
|
|
op->params[2].i32,
|
|
op->params[3].i32,
|
|
op->params[4].color);
|
|
}break;
|
|
case TOP_DRAW_LINE: {
|
|
ImageDrawLine(&ctx.img[ctx.img_pos],
|
|
op->params[0].i32,
|
|
op->params[1].i32,
|
|
op->params[2].i32,
|
|
op->params[3].i32,
|
|
op->params[4].color);
|
|
}break;
|
|
case TOP_DITHER: {
|
|
ImageDither(&ctx.img[ctx.img_pos],
|
|
op->params[0].i32,
|
|
op->params[1].i32,
|
|
op->params[2].i32,
|
|
op->params[3].i32);
|
|
}break;
|
|
case TOP_DRAW_IMAGE: {
|
|
char const *str = zpl_bprintf("art/%s", op->params[0].str);
|
|
if (FileExists(str)) {
|
|
Image img = LoadImage(str);
|
|
int x = op->params[1].i32;
|
|
int y = op->params[2].i32;
|
|
int w = op->params[3].i32;
|
|
int h = op->params[4].i32;
|
|
int flip = op->params[5].i32;
|
|
int rotate = op->params[6].i32;
|
|
|
|
if (w != 0 || h != 0) {
|
|
ImageResize(&img, w != 0 ? w : img.width, h != 0 ? h : img.height);
|
|
}
|
|
|
|
if (flip == 1) {
|
|
ImageFlipVertical(&img);
|
|
} else if (flip == 2) {
|
|
ImageFlipHorizontal(&img);
|
|
}
|
|
|
|
if (rotate == 1) {
|
|
ImageRotateCW(&img);
|
|
} else if (rotate == 2) {
|
|
ImageRotateCCW(&img);
|
|
}
|
|
|
|
ImageDraw(&ctx.img[ctx.img_pos], img,
|
|
(Rectangle){0.0f, 0.0f, img.width, img.height},
|
|
(Rectangle){x, y, img.width, img.height},
|
|
op->params[5].color);
|
|
|
|
UnloadImage(img);
|
|
} else {
|
|
zpl_printf("TOP_LOAD_IMAGE: src %s not found!\n", str);
|
|
}
|
|
}break;
|
|
case TOP_DRAW_TEXT: {
|
|
char const *str = op->params[0].str;
|
|
int x = op->params[1].i32;
|
|
int y = op->params[2].i32;
|
|
int size = op->params[3].i32;
|
|
Color color = op->params[4].color;
|
|
ImageDrawText(&ctx.img[ctx.img_pos], str, x, y, size, color);
|
|
}break;
|
|
case TOP_RESIZE_IMAGE: {
|
|
if (ctx.img[ctx.img_pos].width == 0) break;
|
|
int w = op->params[0].i32;
|
|
int h = op->params[1].i32;
|
|
int mode = op->params[2].i32;
|
|
if (mode) {
|
|
ImageResize(&ctx.img[ctx.img_pos], w, h);
|
|
} else {
|
|
ImageResizeNN(&ctx.img[ctx.img_pos], w, h);
|
|
}
|
|
}break;
|
|
case TOP_COLOR_TWEAKS: {
|
|
ImageColorContrast(&ctx.img[ctx.img_pos], texed_map_value(op->params[0].flt, -100.0f, 100.0f));
|
|
ImageColorBrightness(&ctx.img[ctx.img_pos], (int)texed_map_value(op->params[1].flt, -255.0f, 255.0f));
|
|
ImageColorTint(&ctx.img[ctx.img_pos], op->params[2].color);
|
|
|
|
if (op->params[3].i32) {
|
|
ImageColorInvert(&ctx.img[ctx.img_pos]);
|
|
}
|
|
if (op->params[4].i32) {
|
|
ImageColorGrayscale(&ctx.img[ctx.img_pos]);
|
|
}
|
|
}break;
|
|
case TOP_COLOR_REPLACE: {
|
|
ImageColorReplace(&ctx.img[ctx.img_pos], op->params[0].color, op->params[1].color);
|
|
}break;
|
|
case TOP_IMAGE_GRAD_V: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = GenImageGradientV(w, h, op->params[0].color, op->params[1].color);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
case TOP_IMAGE_GRAD_H: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = GenImageGradientH(w, h, op->params[0].color, op->params[1].color);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
case TOP_IMAGE_GRAD_RAD: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = GenImageGradientRadial(w, h,
|
|
op->params[0].flt,
|
|
op->params[1].color,
|
|
op->params[2].color);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
case TOP_IMAGE_CHECKED: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = GenImageChecked(w, h,
|
|
op->params[0].i32,
|
|
op->params[1].i32,
|
|
op->params[2].color,
|
|
op->params[3].color);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
case TOP_IMAGE_NOISE_WHITE: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = texed_generate_noise(op->params[0].u32,
|
|
w, h,
|
|
op->params[1].flt);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
case TOP_IMAGE_NOISE_PERLIN: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = GenImagePerlinNoise(w, h,
|
|
op->params[0].i32,
|
|
op->params[1].i32,
|
|
op->params[2].flt);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
case TOP_IMAGE_CELLULAR: {
|
|
Image *dst = &ctx.img[ctx.img_pos];
|
|
int w = dst->width;
|
|
int h = dst->height;
|
|
Image img = texed_generate_cellular(op->params[0].u32,
|
|
w, h,
|
|
op->params[1].i32);
|
|
Rectangle rec = {0, 0, w, h};
|
|
ImageDraw(dst, img, rec, rec, WHITE);
|
|
UnloadImage(img);
|
|
}break;
|
|
default: {
|
|
zpl_printf("%s\n", "unsupported op!");
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void texed_process_params(void) {
|
|
for (int i = 0; i < zpl_array_count(ctx.ops); i += 1) {
|
|
td_op *op = &ctx.ops[i];
|
|
|
|
for (int j = 0; j < op->num_params; j += 1) {
|
|
td_param *p = &op->params[j];
|
|
|
|
switch (p->kind) {
|
|
case TPARAM_SLIDER:
|
|
case TPARAM_FLOAT: {
|
|
p->flt = (float)zpl_str_to_f64(p->str, NULL);
|
|
}break;
|
|
case TPARAM_INT:
|
|
case TPARAM_COORD: {
|
|
p->i32 = (int32_t)zpl_str_to_i64(p->str, NULL, 10);
|
|
}break;
|
|
case TPARAM_COLOR: {
|
|
uint32_t color = (uint32_t)zpl_str_to_u64(p->str, NULL, 16);
|
|
p->color = GetColor(color);
|
|
}break;
|
|
case TPARAM_STRING: {
|
|
// NOTE(zaklaus): no-op
|
|
}break;
|
|
default: {
|
|
zpl_printf("%s\n", "unsupported param!");
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline
|
|
float texed_map_value(float v, float min, float max) {
|
|
float slope = max-min;
|
|
return min + zpl_round(slope * v);
|
|
}
|
|
|
|
/* This algorithm is mentioned in the ISO C standard, here extended
|
|
for 32 bits. */
|
|
static inline
|
|
int _rand_r(unsigned int *seed) {
|
|
unsigned int next = *seed;
|
|
int result;
|
|
|
|
next *= 1103515245;
|
|
next += 12345;
|
|
result = (unsigned int) (next / 65536) % 2048;
|
|
|
|
next *= 1103515245;
|
|
next += 12345;
|
|
result <<= 10;
|
|
result ^= (unsigned int) (next / 65536) % 1024;
|
|
|
|
next *= 1103515245;
|
|
next += 12345;
|
|
result <<= 10;
|
|
result ^= (unsigned int) (next / 65536) % 1024;
|
|
|
|
*seed = next;
|
|
|
|
return result;
|
|
}
|
|
|
|
static inline
|
|
Image texed_generate_noise(uint32_t seed, int width, int height, float factor) {
|
|
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
|
|
|
|
for (int i = 0; i < width*height; i++) {
|
|
if ((_rand_r(&seed)%99) < (int)(factor*100.0f)) pixels[i] = WHITE;
|
|
else pixels[i] = BLACK;
|
|
}
|
|
|
|
Image image = {
|
|
.data = pixels,
|
|
.width = width,
|
|
.height = height,
|
|
.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
|
|
.mipmaps = 1
|
|
};
|
|
|
|
return image;
|
|
}
|
|
|
|
static inline
|
|
Image texed_generate_cellular(uint32_t seed, int width, int height, int tileSize)
|
|
{
|
|
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
|
|
|
|
int seedsPerRow = width/tileSize;
|
|
int seedsPerCol = height/tileSize;
|
|
int seedsCount = seedsPerRow*seedsPerCol;
|
|
|
|
Vector2 *seeds = (Vector2 *)RL_MALLOC(seedsCount*sizeof(Vector2));
|
|
|
|
for (int i = 0; i < seedsCount; i++)
|
|
{
|
|
int y = (i/seedsPerRow)*tileSize + _rand_r(&seed)%(tileSize - 1);
|
|
int x = (i%seedsPerRow)*tileSize + _rand_r(&seed)%(tileSize - 1);
|
|
seeds[i] = (Vector2){ (float)x, (float)y};
|
|
}
|
|
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
int tileY = y/tileSize;
|
|
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
int tileX = x/tileSize;
|
|
|
|
float minDistance = (float)strtod("Inf", NULL);
|
|
|
|
// Check all adjacent tiles
|
|
for (int i = -1; i < 2; i++)
|
|
{
|
|
if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue;
|
|
|
|
for (int j = -1; j < 2; j++)
|
|
{
|
|
if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue;
|
|
|
|
Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
|
|
|
|
float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
|
|
minDistance = (float)fmin(minDistance, dist);
|
|
}
|
|
}
|
|
|
|
// I made this up but it seems to give good results at all tile sizes
|
|
int intensity = (int)(minDistance*256.0f/tileSize);
|
|
if (intensity > 255) intensity = 255;
|
|
|
|
pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
|
|
}
|
|
}
|
|
|
|
RL_FREE(seeds);
|
|
|
|
Image image = {
|
|
.data = pixels,
|
|
.width = width,
|
|
.height = height,
|
|
.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
|
|
.mipmaps = 1
|
|
};
|
|
|
|
return image;
|
|
} |