- Added Job class.

- Added SpriteSizeJob class.
- Added ScopedLock class.
- Fixed some bugs.
This commit is contained in:
David Capello 2009-05-31 20:15:38 +00:00
parent dd003a8f33
commit 8f92f78702
13 changed files with 474 additions and 125 deletions

View File

@ -1,3 +1,13 @@
2009-05-31 David A. Capello <davidcapello@gmail.com>
* src/file/file.cpp (fop_free): Fixed a mutex-handle leak.
* src/raster/undo.cpp (update_undo): Fixed a bug where
the current open-undo-chunk were discarded if the undo-limit
were reached.
* src/core/job.cpp: Added Job class to do background jobs more easily.
2009-05-30 David A. Capello <davidcapello@gmail.com>
* src/raster/image_impl.h (ImageImpl): Changed the implementation of

View File

@ -2,6 +2,7 @@ High priority work
------------------
- search for TODO;
- fix bilinear: when getpixel have alpha = 0 get a neighbor color.
- Ctrl+X / Ctrl+C / Ctrl+V should Cut/Copy/Paste in jentries.
- fix problems with tiled mode XY in drawing corners (I cannot
reproduce the exact scenario).
@ -22,13 +23,7 @@ High priority work
- add two DrawClick2:
- DrawClick2FreeHand
- DrawClick2Shape
- remove the jfilesel.c & jquickmenu.c
- see the new Allegro's load_font
- fix Jinete examples:
+ 05fsel
+ 09lists
+ 20combo
+ 21manage
+ finished the support for ICO files.
- add "size" to GUI font (for TTF fonts);
- layer movement between sets in animation-editor;
@ -45,10 +40,6 @@ High priority work
Wish-list
---------
- ideas from freelunchdesign.com forum:
Geti: angle locking, like, so the line tool can be
snapped to 90, 45, 30 & 12.5 degree increments.
would just be handy for isos.
- dacap wish-list:
+ added starred file-items in the file-selector.
+ add AseContext structure to handle the current state of the
@ -90,3 +81,9 @@ Low priority stuff
various sizes have errors).
- optimize the *_to_allegro image methods (I profiled ASE, and these
are the more slow routines in all the program);
- remove the jfilesel.c & jquickmenu.c
- fix Jinete examples:
+ 05fsel
+ 09lists
+ 20combo
+ 21manage

View File

@ -86,6 +86,7 @@ COMMON_SOURCES = \
src/core/dirs.cpp \
src/core/drop_files.cpp \
src/core/file_system.cpp \
src/core/job.cpp \
src/core/modules.cpp \
src/dialogs/aniedit.cpp \
src/dialogs/canvasze.cpp \

View File

@ -18,15 +18,11 @@ endif
CFLAGS =
LFLAGS = -mwindows -lshlwapi
ifdef DEBUGMODE
LFLAGS_LAST = -lalld
LFLAGS_LAST = -lalld -lpsapi
else
LFLAGS_LAST = -lalleg
endif
ifdef MEMLEAK
LFLAGS += -lpsapi
endif
WITHICON = 1
######################################################################

View File

@ -39,7 +39,7 @@ LIBS = User32.lib Shell32.lib ComCtl32.lib ComDlg32.lib Gdi32.lib \
ifdef DEBUGMODE
CFLAGS += -Zi -MDd -DDEBUGMODE
LFLAGS += -DEBUG
LIBS += Alld.lib
LIBS += Alld.lib psapi.lib
else
CFLAGS += -O2 -MD -DNDEBUG
LFLAGS += -RELEASE

View File

@ -20,7 +20,7 @@
#include <stdio.h>
#include <allegro.h>
#if defined ALLEGRO_WINDOWS && defined MEMLEAK
#if defined ALLEGRO_WINDOWS && defined DEBUGMODE
#include <winalleg.h>
#include <psapi.h>
#endif
@ -29,6 +29,7 @@
#include "commands/commands.h"
#include "core/app.h"
#include "widgets/statebar.h"
static void cmd_refresh_execute(const char *argument)
{
@ -39,14 +40,15 @@ static void cmd_refresh_execute(const char *argument)
app_refresh_screen();
/* print memory information */
#if defined ALLEGRO_WINDOWS && defined MEMLEAK
#if defined ALLEGRO_WINDOWS && defined DEBUGMODE
{
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
printf("----\n");
printf("current memory: %.16g KB (%lu)\n", pmc.WorkingSetSize / 1024.0, pmc.WorkingSetSize);
printf("peak of memory: %.16g KB (%lu)\n", pmc.PeakWorkingSetSize / 1024.0, pmc.PeakWorkingSetSize);
fflush(stdout);
statusbar_show_tip(app_get_statusbar(), 1000,
"Current memory: %.16g KB (%lu)\n"
"Peak of memory: %.16g KB (%lu)",
pmc.WorkingSetSize / 1024.0, pmc.WorkingSetSize,
pmc.PeakWorkingSetSize / 1024.0, pmc.PeakWorkingSetSize);
}
}
#endif

View File

@ -23,6 +23,7 @@
#include "jinete/jinete.h"
#include "core/cfg.h"
#include "core/job.h"
#include "commands/commands.h"
#include "modules/gui.h"
#include "modules/palettes.h"
@ -35,6 +36,84 @@
#define PERC_FORMAT "%.1f%%"
class SpriteSizeJob : public Job
{
Sprite* m_sprite;
int m_new_width;
int m_new_height;
ResizeMethod m_resize_method;
public:
SpriteSizeJob(Sprite* sprite, int new_width, int new_height, ResizeMethod resize_method)
{
m_sprite = sprite;
m_new_width = new_width;
m_new_height = new_height;
m_resize_method = resize_method;
}
protected:
/**
* [working thread]
*/
virtual void on_job()
{
Undoable undoable(m_sprite, "Sprite Size");
// get all sprite cels
JList cels = jlist_new();
sprite_get_cels(m_sprite, cels);
// for each cel...
JLink link;
JI_LIST_FOR_EACH(cels, link) {
Cel* cel = (Cel*)link->data;
// change it location
undoable.set_cel_position(cel,
cel->x * m_new_width / m_sprite->w,
cel->y * m_new_height / m_sprite->h);
}
jlist_free(cels);
// for each stock's image
for (int i=0; i<m_sprite->stock->nimage; ++i) {
Image* image = stock_get_image(m_sprite->stock, i);
if (!image)
continue;
// resize the image
int w = image->w * m_new_width / m_sprite->w;
int h = image->h * m_new_height / m_sprite->h;
Image* new_image = image_new(image->imgtype, MAX(1, w), MAX(1, h));
image_resize(image, new_image,
m_resize_method,
get_current_palette(),
orig_rgb_map);
undoable.replace_stock_image(i, new_image);
job_progress((float)i / m_sprite->stock->nimage);
// cancel all the operation?
if (is_canceled())
return; // Undoable destructor will undo all operations
}
// resize sprite
undoable.set_sprite_size(m_new_width, m_new_height);
// TODO resize mask
// commit changes
undoable.commit();
}
};
static bool lock_ratio_change_hook(JWidget widget, void *data);
static bool width_px_change_hook(JWidget widget, void *data);
static bool height_px_change_hook(JWidget widget, void *data);
@ -92,55 +171,16 @@ static void cmd_sprite_size_execute(const char *argument)
if (jwindow_get_killer(window) == ok) {
int new_width = width_px->text_int();
int new_height = height_px->text_int();
ResizeMethod resize_method =
(ResizeMethod)jcombobox_get_selected_index(method);
set_config_int("SpriteSize", "Method", resize_method);
{
Undoable undoable(sprite, "Sprite Size");
ResizeMethod resize_method =
(ResizeMethod)jcombobox_get_selected_index(method);
set_config_int("SpriteSize", "Method", resize_method);
// get all sprite cels
JList cels = jlist_new();
sprite_get_cels(sprite, cels);
// for each cel...
JLink link;
JI_LIST_FOR_EACH(cels, link) {
Cel* cel = (Cel*)link->data;
// change it location
undoable.set_cel_position(cel,
cel->x * new_width / sprite->w,
cel->y * new_height / sprite->h);
}
jlist_free(cels);
// for each stock's image
for (int i=0; i<sprite->stock->nimage; ++i) {
Image* image = stock_get_image(sprite->stock, i);
if (!image)
continue;
// resize the image
int w = image->w * new_width / sprite->w;
int h = image->h * new_height / sprite->h;
Image* new_image = image_new(image->imgtype, MAX(1, w), MAX(1, h));
image_resize(image, new_image,
resize_method,
get_current_palette(),
orig_rgb_map);
undoable.replace_stock_image(i, new_image);
}
// resize sprite
undoable.set_sprite_size(new_width, new_height);
// TODO resize mask
undoable.commit();
SpriteSizeJob job(sprite, new_width, new_height, resize_method);
job.do_job();
}
sprite_generate_mask_boundaries(sprite);
update_screen_for_sprite(sprite);
}

183
src/core/job.cpp Normal file
View File

@ -0,0 +1,183 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2009 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "jinete/jalert.h"
#include "jinete/jmutex.h"
#include "jinete/jthread.h"
#include "jinete/jwidget.h"
#include "jinete/jwindow.h"
#include "core/app.h"
#include "core/job.h"
#include "modules/gui.h"
#include "widgets/statebar.h"
Job::Job()
{
m_mutex = NULL;
m_thread = NULL;
m_progress = NULL;
m_monitor = NULL;
m_alert_window = NULL;
m_last_progress = 0.0f;
m_done_flag = false;
m_canceled_flag = false;
m_mutex = jmutex_new();
m_progress = progress_new(app_get_statusbar());
m_monitor = add_gui_monitor(&Job::monitor_proc,
&Job::monitor_free,
(void*)this);
m_alert_window = jalert_new(PACKAGE "<<Working...||&Cancel");
}
Job::~Job()
{
// The job was canceled by the user?
{
ScopedLock hold(m_mutex);
if (!m_done_flag)
m_canceled_flag = true;
}
if (m_monitor) {
remove_gui_monitor(m_monitor);
m_monitor = NULL;
}
if (m_thread)
jthread_join(m_thread);
if (m_progress)
progress_free(m_progress);
if (m_mutex)
jmutex_free(m_mutex);
if (m_alert_window)
jwidget_free(m_alert_window);
}
void Job::do_job()
{
m_thread = jthread_new(&Job::thread_proc, (void*)this);
jwindow_open_fg(m_alert_window);
}
void Job::job_progress(float f)
{
ScopedLock hold(m_mutex);
m_last_progress = f;
}
bool Job::is_canceled()
{
ScopedLock hold(m_mutex);
return m_canceled_flag;
}
/**
* Called from another thread to do the hard work (image processing).
*
* [working thread]
*/
void Job::on_job()
{
// do nothing
}
/**
* Called each 1000 msecs by the GUI queue processing.
*
* [main thread]
*/
void Job::on_monitor_tick()
{
ScopedLock hold(m_mutex);
// update progress
progress_update(m_progress, m_last_progress);
// is job done? we can close the monitor
if (m_done_flag)
remove_gui_monitor(m_monitor);
}
/**
* Called when the monitor is destroyed.
*
* [main thread]
*/
void Job::on_monitor_destroyed()
{
if (m_alert_window != NULL) {
m_monitor = NULL;
jwindow_close(m_alert_window, NULL);
}
}
void Job::done()
{
ScopedLock hold(m_mutex);
m_done_flag = true;
}
//////////////////////////////////////////////////////////////////////
// Static methods
/**
* Called to start the worker thread.
*
* [worker thread]
*/
void Job::thread_proc(void* data)
{
Job* self = (Job*)data;
try {
self->on_job();
}
catch (...) {
// TODO handle this exception
}
self->done();
}
/**
* Procedure called from the GUI loop to monitoring each 100 milliseconds.
*
* [main thread]
*/
void Job::monitor_proc(void* data)
{
Job* self = (Job*)data;
self->on_monitor_tick();
}
/**
* Function called when the GUI monitor is deleted.
*
* [main thread]
*/
void Job::monitor_free(void* data)
{
Job* self = (Job*)data;
self->on_monitor_destroyed();
}

63
src/core/job.h Normal file
View File

@ -0,0 +1,63 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2009 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CORE_JOB_H
#define CORE_JOB_H
#include "jinete/jbase.h"
struct Monitor;
struct Progress;
class Job
{
JThread m_thread;
Monitor* m_monitor;
Progress* m_progress;
JMutex m_mutex;
JWidget m_alert_window;
float m_last_progress;
bool m_done_flag;
bool m_canceled_flag;
public:
Job();
virtual ~Job();
void do_job();
void job_progress(float f);
bool is_canceled();
protected:
virtual void on_job();
virtual void on_monitor_tick();
virtual void on_monitor_destroyed();
private:
void done();
static void thread_proc(void* data);
static void monitor_proc(void* data);
static void monitor_free(void* data);
};
#endif // CORE_JOB_H

View File

@ -704,6 +704,9 @@ void fop_free(FileOp *fop)
if (fop->seq.palette != NULL)
palette_free(fop->seq.palette);
if (fop->mutex)
jmutex_free(fop->mutex);
jfree(fop);
}

View File

@ -34,34 +34,43 @@
#include "jinete/jmutex.h"
struct {
void (*destroy)(JMutex mutex);
void (*lock)(JMutex mutex);
void (*unlock)(JMutex mutex);
} mutex_vtable = { NULL, NULL, NULL };
JMutex jmutex_new()
{
assert(system_driver != NULL);
assert(system_driver->create_mutex != NULL);
if (!mutex_vtable.destroy) {
mutex_vtable.destroy = system_driver->destroy_mutex;
mutex_vtable.lock = system_driver->lock_mutex;
mutex_vtable.unlock = system_driver->unlock_mutex;
}
return (JMutex)system_driver->create_mutex();
}
void jmutex_free(JMutex mutex)
{
assert(system_driver != NULL);
assert(system_driver->destroy_mutex != NULL);
assert(mutex_vtable.destroy != NULL);
system_driver->destroy_mutex(mutex);
(*mutex_vtable.destroy)(mutex);
}
void jmutex_lock(JMutex mutex)
{
assert(system_driver != NULL);
assert(system_driver->lock_mutex != NULL);
assert(mutex_vtable.lock != NULL);
system_driver->lock_mutex(mutex);
(*mutex_vtable.lock)(mutex);
}
void jmutex_unlock(JMutex mutex)
{
assert(system_driver != NULL);
assert(system_driver->unlock_mutex != NULL);
assert(mutex_vtable.unlock != NULL);
system_driver->unlock_mutex(mutex);
(*mutex_vtable.unlock)(mutex);
}

View File

@ -40,4 +40,15 @@ void jmutex_free(JMutex mutex);
void jmutex_lock(JMutex mutex);
void jmutex_unlock(JMutex mutex);
//////////////////////////////////////////////////////////////////////
// ScopedLock
class ScopedLock
{
JMutex m_mutex;
public:
ScopedLock(JMutex mutex) : m_mutex(mutex) { jmutex_lock(m_mutex); }
~ScopedLock() { jmutex_unlock(m_mutex); }
};
#endif /* JINETE_MUTEX_H */

View File

@ -113,8 +113,10 @@ typedef struct UndoAction
void (*invert)(UndoStream* stream, UndoChunk* chunk, int state);
} UndoAction;
static void run_undo(Undo* undo, int state, bool discard);
static void run_undo(Undo* undo, int state);
static void discard_undo_tail(Undo* undo);
static int count_undo_groups(UndoStream* undo_stream);
static bool out_of_group(UndoStream* undo_stream);
static void update_undo(Undo* undo);
/* Undo actions */
@ -230,7 +232,7 @@ static int get_raw_mask_size(Mask* mask);
static UndoStream* undo_stream_new(Undo* undo);
static void undo_stream_free(UndoStream* stream);
static UndoChunk* undo_stream_pop_chunk(UndoStream* stream, int tail);
static UndoChunk* undo_stream_pop_chunk(UndoStream* stream, bool tail);
static void undo_stream_push_chunk(UndoStream* stream, UndoChunk* chunk);
//////////////////////////////////////////////////////////////////////
@ -314,13 +316,13 @@ bool undo_can_redo(Undo* undo)
void undo_do_undo(Undo* undo)
{
assert(undo);
run_undo(undo, DO_UNDO, FALSE);
run_undo(undo, DO_UNDO);
}
void undo_do_redo(Undo* undo)
{
assert(undo);
run_undo(undo, DO_REDO, FALSE);
run_undo(undo, DO_REDO);
}
void undo_clear_redo(Undo* undo)
@ -357,7 +359,7 @@ const char *undo_get_next_redo_label(Undo* undo)
return chunk->label;
}
static void run_undo(Undo* undo, int state, bool discard_tail)
static void run_undo(Undo* undo, int state)
{
UndoStream* undo_stream = ((state == DO_UNDO)? undo->undo_stream:
undo->redo_stream);
@ -366,50 +368,55 @@ static void run_undo(Undo* undo, int state, bool discard_tail)
UndoChunk* chunk;
int level = 0;
if (!discard_tail) {
do {
chunk = undo_stream_pop_chunk(undo_stream, FALSE); /* read from head */
if (!chunk)
break;
do {
chunk = undo_stream_pop_chunk(undo_stream, false); // read from head
if (!chunk)
break;
{ int c;
for (c=0; c<ABS(level); c++)
PRINTF(" ");
PRINTF("%s: %s (Label: %s)\n",
(state == DO_UNDO) ? "Undo": "Redo",
undo_actions[chunk->type].name,
chunk->label); }
{ int c;
for (c=0; c<ABS(level); c++)
PRINTF(" ");
PRINTF("%s: %s (Label: %s)\n",
(state == DO_UNDO) ? "Undo": "Redo",
undo_actions[chunk->type].name,
chunk->label); }
undo_set_label(undo, chunk->label);
(undo_actions[chunk->type].invert)(redo_stream, chunk, state);
undo_set_label(undo, chunk->label);
(undo_actions[chunk->type].invert)(redo_stream, chunk, state);
if (chunk->type == UNDO_TYPE_OPEN)
level++;
else if (chunk->type == UNDO_TYPE_CLOSE)
level--;
if (chunk->type == UNDO_TYPE_OPEN)
level++;
else if (chunk->type == UNDO_TYPE_CLOSE)
level--;
undo_chunk_free(chunk);
undo_chunk_free(chunk);
if (state == DO_UNDO)
undo->diff_count--;
else if (state == DO_REDO)
undo->diff_count++;
} while (level);
}
else {
do {
chunk = undo_stream_pop_chunk(undo_stream, TRUE); /* read from tail */
if (!chunk)
break;
if (state == DO_UNDO)
undo->diff_count--;
else if (state == DO_REDO)
undo->diff_count++;
} while (level);
}
if (chunk->type == UNDO_TYPE_OPEN)
level++;
else if (chunk->type == UNDO_TYPE_CLOSE)
level--;
static void discard_undo_tail(Undo* undo)
{
UndoStream* undo_stream = undo->undo_stream;
UndoStream* redo_stream = undo->redo_stream;
UndoChunk* chunk;
int level = 0;
undo_chunk_free(chunk);
} while (level);
}
do {
chunk = undo_stream_pop_chunk(undo_stream, true); // read from tail
if (!chunk)
break;
if (chunk->type == UNDO_TYPE_OPEN)
level++;
else if (chunk->type == UNDO_TYPE_CLOSE)
level--;
undo_chunk_free(chunk);
} while (level);
}
static int count_undo_groups(UndoStream* undo_stream)
@ -440,10 +447,33 @@ static int count_undo_groups(UndoStream* undo_stream)
return groups;
}
static bool out_of_group(UndoStream* undo_stream)
{
UndoChunk* chunk;
int level;
JLink link;
link = jlist_first(undo_stream->chunks);
while (link != undo_stream->chunks->end) {
level = 0;
do {
chunk = reinterpret_cast<UndoChunk*>(link->data);
link = link->next;
if (chunk->type == UNDO_TYPE_OPEN)
level++;
else if (chunk->type == UNDO_TYPE_CLOSE)
level--;
} while (level && (link != undo_stream->chunks->end));
}
return level == 0;
}
/* called every time a new undo is added */
static void update_undo(Undo* undo)
{
int groups = count_undo_groups(undo->undo_stream);
int undo_size_limit = get_config_int("Options", "UndoSizeLimit", 8)*1024*1024;
/* more diff */
@ -452,10 +482,14 @@ static void update_undo(Undo* undo)
/* reset the "redo" stream */
undo_clear_redo(undo);
/* "undo" is too big? */
while (groups > 1 && undo->undo_stream->size > undo_size_limit) {
run_undo(undo, DO_UNDO, TRUE);
groups--;
if (out_of_group(undo->undo_stream)) {
int groups = count_undo_groups(undo->undo_stream);
/* "undo" is too big? */
while (groups > 1 && undo->undo_stream->size > undo_size_limit) {
discard_undo_tail(undo);
groups--;
}
}
}
@ -1990,7 +2024,7 @@ static void undo_stream_free(UndoStream* stream)
jfree(stream);
}
static UndoChunk* undo_stream_pop_chunk(UndoStream* stream, int tail)
static UndoChunk* undo_stream_pop_chunk(UndoStream* stream, bool tail)
{
UndoChunk* chunk;
JLink link;