texed: image stacking ops

isolation_bkp/dynres
Dominik Madarász 2021-05-17 07:53:10 +02:00
parent 88f77b709f
commit 9284086a3d
9 changed files with 653 additions and 864 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

View File

@ -21,6 +21,7 @@
#define TD_UI_PADDING 5.0f
#define TD_UI_PREVIEW_BORDER 4.0f
#define TD_UI_DEFAULT_ZOOM 4.0f
#define TD_IMAGES_MAX_STACK 128
static uint16_t screenWidth = 1280;
static uint16_t screenHeight = 720;
@ -71,6 +72,9 @@ typedef enum {
TOP_FLIP_IMAGE,
TOP_ROTATE_IMAGE,
TOP_PUSH_IMAGE,
TOP_POP_IMAGE,
TOP_FORCE_UINT8 = UINT8_MAX
} td_op_kind;
@ -96,7 +100,8 @@ typedef struct {
typedef struct {
char *filepath;
Image img;
int32_t img_pos;
Image img[TD_IMAGES_MAX_STACK];
Texture2D tex;
GuiFileDialogState fileDialog;
td_msgbox msgbox;
@ -112,7 +117,8 @@ static char filename[200];
#include "texed_ops_list.c"
void texed_new(int32_t w, int32_t h);
void texed_new(int w, int h);
void texed_clear(void);
void texed_destroy(void);
void texed_load(void);
void texed_save(void);
@ -124,6 +130,9 @@ void texed_msgbox_init(char const *title, char const *message, char const *butto
void texed_process_ops(void);
void texed_process_params(void);
void texed_img_push(int w, int h, Color color);
void texed_img_pop(int x, int y, int w, int h, Color tint);
void texed_add_op(int kind);
void texed_rem_op(int idx);
void texed_swp_op(int idx, int idx2);
@ -310,7 +319,9 @@ void texed_run(int argc, char **argv) {
}
void texed_new(int32_t w, int32_t h) {
ctx.img = GenImageColor(w, h, WHITE);
ctx.img_pos = -1;
zpl_memset(ctx.img, 0, sizeof(Image)*TD_IMAGES_MAX_STACK);
texed_img_push(w, h, WHITE);
ctx.filepath = NULL;
ctx.selected_op = 0;
ctx.msgbox.result = -1;
@ -327,21 +338,59 @@ void texed_new(int32_t w, int32_t h) {
ctx.is_saved = true;
}
void texed_clear(void) {
zpl_array_clear(ctx.ops);
for (int i = 0; i <= ctx.img_pos; i+=1)
UnloadImage(ctx.img[i]);
ctx.img_pos = -1;
}
void texed_destroy(void) {
UnloadTexture(ctx.tex);
UnloadImage(ctx.img);
texed_clear();
CloseWindow();
zpl_array_free(ctx.ops);
}
void texed_export_cc(char const *path) {
zpl_printf("Building texture %s.h ...\n", path);
ExportImageAsCode(ctx.img, zpl_bprintf("art/gen/%s.h", GetFileNameWithoutExt(path)));
ExportImageAsCode(ctx.img[ctx.img_pos], zpl_bprintf("art/gen/%s.h", GetFileNameWithoutExt(path)));
}
void texed_export_png(char const *path) {
zpl_printf("Exporting texture %s.png ...\n", path);
ExportImage(ctx.img, zpl_bprintf("art/gen/%s.png", GetFileNameWithoutExt(path)));
ExportImage(ctx.img[ctx.img_pos], zpl_bprintf("art/gen/%s.png", GetFileNameWithoutExt(path)));
}
void texed_img_push(int w, int h, Color color) {
if (ctx.img_pos == TD_IMAGES_MAX_STACK)
return;
ctx.img_pos += 1;
ctx.img[ctx.img_pos] = GenImageColor(w, h, color);
}
void texed_img_pop(int x, int y, int w, int h, Color tint) {
if (ctx.img_pos == 0)
return;
Image *oi = &ctx.img[ctx.img_pos];
Image *di = &ctx.img[ctx.img_pos-1];
Rectangle src = {
0, 0,
oi->width, oi->height
};
w = (w == 0) ? di->width : w;
h = (h == 0) ? di->height : h;
Rectangle dst = {
x, y,
w, h,
};
ImageDraw(di, *oi, src, dst, tint);
UnloadImage(ctx.img[ctx.img_pos]);
ctx.img_pos -= 1;
}
void texed_repaint_preview(void) {
@ -350,7 +399,7 @@ void texed_repaint_preview(void) {
if (!IsWindowReady()) return;
UnloadTexture(ctx.tex);
ctx.tex = LoadTextureFromImage(ctx.img);
ctx.tex = LoadTextureFromImage(ctx.img[ctx.img_pos]);
}
void texed_compose_image(void) {
@ -369,7 +418,6 @@ void texed_msgbox_init(char const *title, char const *message, char const *butto
}
int texed_find_op(int kind) {
assert(kind >= 0 && kind < DEF_OPS_LEN);
for (int i = 0; i < DEF_OPS_LEN; i += 1) {
if (default_ops[i].kind == kind) {
return i;
@ -379,8 +427,9 @@ int texed_find_op(int kind) {
}
void texed_add_op(int kind) {
assert(kind >= 0 && kind < DEF_OPS_LEN);
td_op *dop = &default_ops[texed_find_op(kind)];
int idx = texed_find_op(kind);
assert(idx >= 0);
td_op *dop = &default_ops[idx];
td_op op = {
.kind = dop->kind,

View File

@ -2,18 +2,29 @@ static inline
float texed_map_value(float v, float min, float max);
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: {
UnloadImage(ctx.img);
ctx.img = GenImageColor(op->params[0].i32, op->params[1].i32, op->params[2].color);
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_DRAW_RECT: {
ImageDrawRectangle(&ctx.img,
ImageDrawRectangle(&ctx.img[ctx.img_pos],
op->params[0].i32,
op->params[1].i32,
op->params[2].i32,
@ -21,7 +32,7 @@ void texed_process_ops(void) {
op->params[4].color);
}break;
case TOP_DRAW_LINE: {
ImageDrawLine(&ctx.img,
ImageDrawLine(&ctx.img[ctx.img_pos],
op->params[0].i32,
op->params[1].i32,
op->params[2].i32,
@ -29,7 +40,7 @@ void texed_process_ops(void) {
op->params[4].color);
}break;
case TOP_DITHER: {
ImageDither(&ctx.img,
ImageDither(&ctx.img[ctx.img_pos],
op->params[0].i32,
op->params[1].i32,
op->params[2].i32,
@ -46,8 +57,8 @@ void texed_process_ops(void) {
int flip = op->params[5].i32;
int rotate = op->params[6].i32;
if (w != -1 || h != -1) {
ImageResize(&img, w != -1 ? w : img.width, h != -1 ? h : img.height);
if (w != 0 || h != 0) {
ImageResize(&img, w != 0 ? w : img.width, h != 0 ? h : img.height);
}
if (flip == 1) {
@ -62,7 +73,7 @@ void texed_process_ops(void) {
ImageRotateCCW(&img);
}
ImageDraw(&ctx.img, 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);
@ -78,29 +89,29 @@ void texed_process_ops(void) {
int y = op->params[2].i32;
int size = op->params[3].i32;
Color color = op->params[4].color;
ImageDrawText(&ctx.img, str, x, y, size, color);
ImageDrawText(&ctx.img[ctx.img_pos], str, x, y, size, color);
}break;
case TOP_RESIZE_IMAGE: {
if (ctx.img.width == 0) break;
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, w, h);
ImageResize(&ctx.img[ctx.img_pos], w, h);
} else {
ImageResizeNN(&ctx.img, w, h);
ImageResizeNN(&ctx.img[ctx.img_pos], w, h);
}
}break;
case TOP_COLOR_TWEAKS: {
ImageColorContrast(&ctx.img, texed_map_value(op->params[0].flt, -100.0f, 100.0f));
ImageColorBrightness(&ctx.img, (int)texed_map_value(op->params[1].flt, -255.0f, 255.0f));
ImageColorTint(&ctx.img, op->params[2].color);
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);
ImageColorInvert(&ctx.img[ctx.img_pos]);
}
if (op->params[4].i32) {
ImageColorGrayscale(&ctx.img);
ImageColorGrayscale(&ctx.img[ctx.img_pos]);
}
}break;
default: {

View File

@ -12,6 +12,22 @@ static td_op default_ops[] = {
PARAM(TPARAM_COORD, "h", "64"),
PARAM(TPARAM_COLOR, "color", "ffffffff"),
}
},{
OP(TOP_PUSH_IMAGE),
PARAMS(3) {
PARAM(TPARAM_COORD, "w", "64"),
PARAM(TPARAM_COORD, "h", "64"),
PARAM(TPARAM_COLOR, "color", "ffffffff"),
}
},{
OP(TOP_POP_IMAGE),
PARAMS(5) {
PARAM(TPARAM_COORD, "x", "0"),
PARAM(TPARAM_COORD, "y", "0"),
PARAM(TPARAM_COORD, "w", "0"),
PARAM(TPARAM_COORD, "h", "0"),
PARAM(TPARAM_COLOR, "tint", "ffffffff"),
}
},{
OP(TOP_DRAW_RECT),
PARAMS(5) {
@ -36,8 +52,8 @@ static td_op default_ops[] = {
PARAM(TPARAM_STRING, "src", "samples/test.png"),
PARAM(TPARAM_COORD, "x", "0"),
PARAM(TPARAM_COORD, "y", "0"),
PARAM(TPARAM_COORD, "w", "-1"),
PARAM(TPARAM_COORD, "h", "-1"),
PARAM(TPARAM_COORD, "w", "0"),
PARAM(TPARAM_COORD, "h", "0"),
PARAM(TPARAM_COLOR, "tint", "ffffffff"),
PARAM(TPARAM_INT, "flip?", "0"),
PARAM(TPARAM_INT, "rotate?", "0"),
@ -54,10 +70,10 @@ static td_op default_ops[] = {
},{
OP(TOP_DITHER),
PARAMS(4) {
PARAM(TPARAM_INT, "r_bpp", "8"),
PARAM(TPARAM_INT, "g_bpp", "8"),
PARAM(TPARAM_INT, "b_bpp", "8"),
PARAM(TPARAM_INT, "a_bpp", "8"),
PARAM(TPARAM_INT, "r_bpp", "4"),
PARAM(TPARAM_INT, "g_bpp", "4"),
PARAM(TPARAM_INT, "b_bpp", "4"),
PARAM(TPARAM_INT, "a_bpp", "4"),
}
},{
OP(TOP_RESIZE_IMAGE),

View File

@ -9,8 +9,7 @@ void texed_load(void) {
assert(ctx.filepath);
zpl_printf("Loading %s ...\n", ctx.filepath);
is_repaint_locked = true;
zpl_array_clear(ctx.ops);
texed_clear();
uint32_t size = 0;
uint8_t *databuf = LoadFileData(zpl_bprintf("art/%s", ctx.filepath), &size);
@ -24,7 +23,7 @@ void texed_load(void) {
int selected_op = (int)uc.item.as.u64;
UNPACK(CWP_ITEM_FLOAT);
zoom = uc.item.as.real;
old_zoom = zoom = uc.item.as.real;
UNPACK(CWP_ITEM_ARRAY);
int arrsize = (int)uc.item.as.array.size;

View File

@ -2,6 +2,8 @@ static inline
void int_to_hex_color(uint32_t color, char *text);
static inline
int GuiDropdownBoxEco(Rectangle bounds, char const *text, char const *caption, int *active, bool editMode);
static inline
bool GuiValueBoxEco(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode);
static inline
bool IsCtrlAcceleratorPressed(char key);
@ -22,7 +24,7 @@ void texed_draw_topbar(zpl_aabb2 r) {
if (GuiButton(aabb2_ray(new_prj_r), "NEW") || IsCtrlAcceleratorPressed('n')) {
if (ctx.is_saved) {
texed_destroy();
texed_clear();
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT);
} else {
new_pending = true;
@ -33,7 +35,7 @@ void texed_draw_topbar(zpl_aabb2 r) {
if (new_pending && ctx.msgbox.result != -1) {
new_pending = false;
if (ctx.msgbox.result == 1) {
texed_destroy();
texed_clear();
texed_new(TD_DEFAULT_IMG_WIDTH, TD_DEFAULT_IMG_HEIGHT);
}
ctx.msgbox.result = -1; // NOTE(zaklaus): ensure we don't re-trigger this branch next frame
@ -293,12 +295,14 @@ void texed_draw_props_pane(zpl_aabb2 r) {
}break;
case TPARAM_INT:
case TPARAM_COORD: {
if (GuiValueBox(aabb2_ray(tbox_r), NULL, &p->i32, INT32_MIN, INT32_MAX, p->edit_mode)) {
if (GuiValueBoxEco(aabb2_ray(tbox_r), NULL, &p->i32, INT32_MIN, INT32_MAX, p->edit_mode)) {
p->edit_mode = !p->edit_mode;
if (!p->edit_mode) {
sprintf(p->str, "%d", p->i32);
texed_repaint_preview();
} else if (IsKeyDown(KEY_LEFT_SHIFT)) {
p->i32 = 0;
}
};
}break;
@ -486,3 +490,123 @@ static inline
bool IsCtrlAcceleratorPressed(char key) {
return (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)) && (char)GetKeyPressed() == key;
}
static inline
bool GuiValueBoxEco(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) {
#if !defined(VALUEBOX_MAX_CHARS)
#define VALUEBOX_MAX_CHARS 32
#endif
static int framesCounter = 0; // Required for blinking cursor
GuiControlState state = guiState;
bool pressed = false;
char textValue[VALUEBOX_MAX_CHARS + 1] = "\0";
sprintf(textValue, "%i", *value);
Rectangle textBounds = { 0 };
if (text != NULL)
{
textBounds.width = (float)GetTextWidth(text);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
}
// Update control
//--------------------------------------------------------------------
if ((state != GUI_STATE_DISABLED) && !guiLocked)
{
Vector2 mousePoint = GetMousePosition();
bool valueHasChanged = false;
if (editMode)
{
state = GUI_STATE_PRESSED;
framesCounter++;
int keyCount = (int)strlen(textValue);
// Only allow keys in range [48..57]
if (keyCount < VALUEBOX_MAX_CHARS)
{
if (GetTextWidth(textValue) < bounds.width)
{
int key = GetCharPressed();
if ((key >= 48) && (key <= 57))
{
textValue[keyCount] = (char)key;
keyCount++;
valueHasChanged = true;
}
}
}
// Delete text
if (keyCount > 0)
{
if (IsKeyPressed(KEY_BACKSPACE))
{
keyCount--;
textValue[keyCount] = '\0';
framesCounter = 0;
if (keyCount < 0) keyCount = 0;
valueHasChanged = true;
}
else if (IsKeyDown(KEY_BACKSPACE))
{
if ((framesCounter > TEXTEDIT_CURSOR_BLINK_FRAMES) && (framesCounter%2) == 0) keyCount--;
textValue[keyCount] = '\0';
if (keyCount < 0) keyCount = 0;
valueHasChanged = true;
}
}
if (valueHasChanged) *value = TextToInteger(textValue);
if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true;
}
else
{
if (*value > maxValue) *value = maxValue;
else if (*value < minValue) *value = minValue;
if (CheckCollisionPointRec(mousePoint, bounds))
{
state = GUI_STATE_FOCUSED;
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true;
}
}
if (pressed) framesCounter = 0;
}
//--------------------------------------------------------------------
// Draw control
//--------------------------------------------------------------------
Color baseColor = BLANK;
if (state == GUI_STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
else if (state == GUI_STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
// WARNING: BLANK color does not work properly with Fade()
GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha), baseColor);
GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), GUI_TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha));
// Draw blinking cursor
if ((state == GUI_STATE_PRESSED) && (editMode && ((framesCounter/20)%2 == 0)))
{
// NOTE: ValueBox internal text is always centered
Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 2, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 1, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha));
}
// Draw text label if provided
if (text != NULL) GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == GUI_TEXT_ALIGN_RIGHT)? GUI_TEXT_ALIGN_LEFT : GUI_TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha));
//--------------------------------------------------------------------
return pressed;
}