An Update On Aseprite

For my latest assignment in DPS909, I've been working on this issue in Aseprite. This wasn't really planned -- to be honest, I actually was going to deal with a bug in filer first -- but one thing led to the next, and I suddenly found myself working on this project instead. I talked about how I've dealt with something similar -- making a fill tool -- in last week's blog post -- and much to my delight, that past experience has served me quite well. In fact, modifying the current code for the fill tool to fill pixels connected on a diagonal was hands-down the easiest part in tackling this issue. It took me maybe 20 minutes to implement -- a few lines copied from existing code, a +1 here, a -1 there... Throw in a couple of if-statements, and voila! It's done. The only tricky part came when using the fill tool on a large area caused the program to crash. To determine where the issue was, I used the most basic of debugging techniques -- commenting bits of code out and seeing if the program would run without them. It took all of two minutes to figure out the problem, which was this little bit here:

if (check_flood_line(image, mask, p->y-1, p->lpos-1, p->rpos, bounds, src_color, tolerance, data, proc)) { done = false; }

Amazing, how such a small if statement can ruin your day. ...Well, function call, actually -- it was a bad parameter passed to check_flood_line that caused everything to grind to a halt. After a bit of testing, I figured out that when p->lpos-1 was less than zero, it crashed the program. Now, I'll be honest -- I didn't really delve into the why. Reading the source code for Aseprite is not my favourite thing; the style is not what I'm used to, and I find it difficult to keep track of a lot of the variables (many of which are called very descriptive things like c or p). Add that to a large (by my standards) and fairly complicated (again, by my standards) file system, and it's difficult to parse through things.

That said, I did take a quick look at check_flood_line to try and see if I could understand, at least at a surface level, what was happening when the passed l->lpos-1 was less than zero. I am 95% certain that it's because 0,0 represents the top-left corner of the canvas, so anything less than zero goes beyond the valid bounds of said canvas. Trying to access these non-existent parts of the canvas causes the program to crash -- understandably, since you can't get or set values of something that doesn't exist. But since there's no check for that in check_flood_line, the function still tries to access these nonexistent parts of the canvas anyway.

Why only one of the eight snippets of code I added is affected by this is beyond me. But the fix is pretty easy -- before passing l->lpos-1 to the function, check it to make sure it's equal to or greater than zero. I could have done this check in check_flood_line itself, but since it seemed to work with everything except one specific instance, I decided to err on the side of caution and include the test only in what I added. If need be, I can always move it later.

And with that, it was on to adding the option to toggle between this new fill option and the old one. Truth be told, I haven't quite figured that out yet -- though I did get some pointers from the project owner! I put up a work-in-progress pull request and asked for a bit of help. dacap pointed me in the right direction, and I've actually managed to add some buttons to the UI! This is a first for me, as I've never done anything UI-related in C++. I don't particularly enjoy it, but I think part of that's because it's still very new and unfamiliar. Once I understand it a bit more, I think I'll like it a bit more, too.

Despite having spent most of this post talking about the code, I didn't spend a whole lot of time messing about in the files. I think I spent more time trying to find files -- and then even more time trying to figure out how to install Aseprite. It is a task and a half to set-up -- requiring CMake, SKIA, Ninja, and probably a few others I can't remember off the top of my head, a lot of work needs to be done before even cloning the repository. Now, to be fair, a lot of it involves copy-pasting things into the command line and letting the computer do all the hard work for you. The hard part comes with a) installing the several other dependencies and b) remembering what steps you need to take to compile Aseprite after you've set it up.

For me, this meant re-adding certain things to the PATH, running CMake on Ninja, and then compiling Aseprite with Ninja. Depending on your machine, you may or may not have to do more or less. Just make sure to remember it, because let me tell you, nothing is worse than seeing a wall of errors in your console after trying to compile a project that worked perfectly yesterday.

But, hey, overall it's been a very positive experience, and I'm definitely looking forward to seeing the end product when this issue is fixed. I'm sure there's a fair bit of work left to do -- most of it probably reading existing code -- but right now, that's more of an exciting task than a daunting one.

Comments