Skip to content

Commit

Permalink
main: add unlimited (100x) undo buffer with compression
Browse files Browse the repository at this point in the history
* main: compression

* main: undo buffer lz4 compression

* main: undo buffer lz4 compression

* undo-unrelated code removed

* undo-unrelated code removed, 2nd attempt

* CMakelist fixed

* --amend

---------

Co-authored-by: Christian Beier <[email protected]>
  • Loading branch information
pascal-niklaus and bk138 authored Mar 24, 2024
1 parent ecd22fe commit 4a19157
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 33 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ if(NOT appindicator3_FOUND)
pkg_check_modules(appindicator3 REQUIRED "appindicator3-0.1 >= 0.4.92")
set(APPINDICATOR_IS_LEGACY 1)
endif()
pkg_check_modules(lz4 REQUIRED liblz4)

configure_file(build-config.h_cmake_in build-config.h)

Expand All @@ -38,13 +39,15 @@ include_directories(
${appindicator3_INCLUDE_DIRS}
${xinput_INCLUDE_DIRS}
${x11_INCLUDE_DIRS}
${lz4_INCLUDE_DIRS}
)

link_directories(
${gtk3_LIBRARY_DIRS}
${appindicator3_LIBRARY_DIRS}
${xinput_LIBRARY_DIRS}
${x11_LIBRARY_DIRS}
${lz4_LIBRARY_DIRS}
)

set(sources
Expand All @@ -71,6 +74,7 @@ target_link_libraries(${target_name}
${appindicator3_LIBRARIES}
${xinput_LIBRARIES}
${x11_LIBRARIES}
${lz4_LIBRARIES}
-lm
)

Expand Down
108 changes: 80 additions & 28 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Gromit-MPX -- a program for painting on the screen
*
* Gromit Copyright (C) 2000 Simon Budig <[email protected]>
Expand All @@ -23,6 +23,7 @@

#include <string.h>
#include <stdlib.h>
#include <lz4.h>

#include "callbacks.h"
#include "config.h"
Expand Down Expand Up @@ -430,7 +431,8 @@ void snap_undo_state (GromitData *data)
if(data->debug)
g_printerr ("DEBUG: Snapping undo buffer %d.\n", data->undo_head);

copy_surface(data->undobuffer[data->undo_head], data->backbuffer);
undo_compress(data, data->backbuffer);
undo_temp_buffer_to_slot(data, data->undo_head);

// Increment head position
data->undo_head++;
Expand All @@ -456,23 +458,8 @@ void copy_surface (cairo_surface_t *dst, cairo_surface_t *src)
}



void swap_surfaces (cairo_surface_t *a, cairo_surface_t *b)
{
int width = cairo_image_surface_get_width(a);
int height = cairo_image_surface_get_height(a);
cairo_surface_t *temp = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
copy_surface(temp, a);
copy_surface(a, b);
copy_surface(b, temp);
cairo_surface_destroy(temp);
}



void undo_drawing (GromitData *data)
{
/* Swap undobuffer[head-1]->backbuffer */
if(data->undo_depth <= 0)
return;
data->undo_depth--;
Expand All @@ -483,7 +470,9 @@ void undo_drawing (GromitData *data)
if(data->undo_head < 0)
data->undo_head += GROMIT_MAX_UNDO;

swap_surfaces(data->backbuffer, data->undobuffer[data->undo_head]);
undo_compress(data, data->backbuffer);
undo_decompress(data, data->undo_head, data->backbuffer);
undo_temp_buffer_to_slot(data, data->undo_head);

GdkRectangle rect = {0, 0, data->width, data->height};
gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);
Expand All @@ -501,7 +490,9 @@ void redo_drawing (GromitData *data)
if(data->redo_depth <= 0)
return;

swap_surfaces(data->backbuffer, data->undobuffer[data->undo_head]);
undo_compress(data, data->backbuffer);
undo_decompress(data, data->undo_head, data->backbuffer);
undo_temp_buffer_to_slot(data, data->undo_head);

data->redo_depth--;
data->undo_depth++;
Expand All @@ -513,15 +504,74 @@ void redo_drawing (GromitData *data)
gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);

data->modified = 1;

if(data->debug)
g_printerr("DEBUG: Redo drawing.\n");
}

/*
* compress image data and store it in undo_temp_buffer
*
* the undo_temp_buffer is successively grown in case it is too small
*/
void undo_compress(GromitData *data, cairo_surface_t *surface)
{
char *raw_data = (char *)cairo_image_surface_get_data(surface);
guint bytes_per_row = cairo_image_surface_get_stride(surface);
guint rows = cairo_image_surface_get_height(surface);
size_t src_bytes = rows * bytes_per_row;

size_t dest_bytes;
for (;;)
{
dest_bytes = LZ4_compress_default(raw_data, data->undo_temp, src_bytes, data->undo_temp_size);
if (dest_bytes == 0)
{
data->undo_temp_size *= 2;
data->undo_temp = g_realloc(data->undo_temp, data->undo_temp_size);
}
else
{
data->undo_temp_used = dest_bytes;
break;
}
}
}


/*
* copy undo_temp to undo slot, growing undo buffer if required
*/
void undo_temp_buffer_to_slot(GromitData *data, gint undo_slot)
{
const size_t required = data->undo_temp_used;
if (data->undo_buffer_size[undo_slot] < required)
{
data->undo_buffer[undo_slot] = g_realloc(data->undo_buffer[undo_slot], required);
data->undo_buffer_size[undo_slot] = required;
}
data->undo_buffer_used[undo_slot] = required;
memcpy(data->undo_buffer[undo_slot], data->undo_temp, required);
}

/*
* decompress undo slot data and store it in cairo surface
*/
void undo_decompress(GromitData *data, gint undo_slot, cairo_surface_t *surface)
{
char *dest_data = (char *)cairo_image_surface_get_data(surface);
guint bytes_per_row = cairo_image_surface_get_stride(surface);
guint rows = cairo_image_surface_get_height(surface);
size_t dest_bytes = rows * bytes_per_row;

char *src_data = data->undo_buffer[undo_slot];
size_t src_bytes = data->undo_buffer_used[undo_slot];

if (LZ4_decompress_safe(src_data, dest_data, src_bytes, dest_bytes) < 0) {
g_printerr("Fatal error occurred decompressing image data\n");
exit(1);
}
}

/*
* Functions for handling various (GTK+)-Events
Expand Down Expand Up @@ -625,23 +675,25 @@ void setup_main_app (GromitData *data, int argc, char ** argv)
cairo_surface_destroy(data->backbuffer);
data->backbuffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height);

// original state for LINE and RECT tool
cairo_surface_destroy(data->aux_backbuffer);
data->aux_backbuffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height);

/*
UNDO STATE
*/
data->undo_head = 0;
data->undo_depth = 0;
data->redo_depth = 0;
int i;
for (i = 0; i < GROMIT_MAX_UNDO; i++)
data->undo_temp_size = 0x10000;
data->undo_temp = g_malloc(data->undo_temp_size);
data->undo_temp_used = 0;
for (int i = 0; i < GROMIT_MAX_UNDO; i++)
{
cairo_surface_destroy(data->undobuffer[i]);
data->undobuffer[i] = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height);
data->undo_buffer_size[i] = 0;
data->undo_buffer[i] = NULL;
}

// original state for LINE and RECT tool
cairo_surface_destroy(data->aux_backbuffer);
data->aux_backbuffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height);

/* EVENTS */
gtk_widget_add_events (data->win, GROMIT_WINDOW_EVENTS);
g_signal_connect (data->win, "draw",
Expand Down
18 changes: 13 additions & 5 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
#define GA_TOGGLEDATA gdk_atom_intern ("Gromit/toggledata", FALSE)
#define GA_LINEDATA gdk_atom_intern ("Gromit/linedata", FALSE)

#define GROMIT_MAX_UNDO 4
#define GROMIT_MAX_UNDO 100

typedef enum
{
Expand Down Expand Up @@ -160,8 +160,14 @@ typedef struct

gchar *clientdata;

cairo_surface_t *undobuffer[GROMIT_MAX_UNDO];
gint undo_head, undo_depth, redo_depth;
/* undo buffer */
gchar *undo_buffer[GROMIT_MAX_UNDO];
size_t undo_buffer_size[GROMIT_MAX_UNDO];
size_t undo_buffer_used[GROMIT_MAX_UNDO];
gchar *undo_temp;
size_t undo_temp_size;
size_t undo_temp_used;
gint undo_head, undo_depth, redo_depth;

gboolean show_intro_on_startup;

Expand All @@ -177,10 +183,12 @@ void parse_print_help (gpointer key, gpointer value, gpointer user_data);
void select_tool (GromitData *data, GdkDevice *device, GdkDevice *slave_device, guint state);

void copy_surface (cairo_surface_t *dst, cairo_surface_t *src);
void swap_surfaces (cairo_surface_t *a, cairo_surface_t *b);
void snap_undo_state (GromitData *data);
void snap_undo_state(GromitData *data);
void undo_drawing (GromitData *data);
void redo_drawing (GromitData *data);
void undo_compress(GromitData *data, cairo_surface_t *surface);
void undo_temp_buffer_to_slot(GromitData *data, gint undo_slot);
void undo_decompress(GromitData *data, gint undo_slot, cairo_surface_t *surface);

void clear_screen (GromitData *data);

Expand Down

0 comments on commit 4a19157

Please sign in to comment.