Spade-Editor / Spade

Cross-platform raster graphics editor inspired by Paint.NET
GNU General Public License v3.0
41 stars 10 forks source link

Refactor core classes #76

Closed HeroesGrave closed 9 years ago

HeroesGrave commented 10 years ago

Progress Check

BurntPizza commented 10 years ago

Have a canvas testbed managing 200 FPS in a maximized window while drawing with a size 400 brush LOL Not at all done yet, but I'm considering making a library out of this if I can solidly beat J2D.

HeroesGrave commented 10 years ago

That's pretty amazing. I'm quite interested to see how your algorithm for drawing large brushes works.

BurntPizza commented 10 years ago

Its a re-ordered and unrolled (for cache awareness) version of the standard midpoint circle algorithm. Not quite perfect, and it requires a stencil buffer if any blending is to be done, as there is some overlapping within the circles, but luckily my stencil buffer impl is also fast as lightning.

Here's a sample (not ready to post whole thing, it's nearly 1ksloc): https://gist.github.com/BurntPizza/66943580fdb59ac237bd

The JIT + branch predictor is quite the dynamic duo. EDIT: I guess technically it's loop fission or splitting more than unrollling, but whatevs

HeroesGrave commented 10 years ago

Well, I'm feeling better now, and have one day left before exams.

@Longor1996: Is your PaintCanvas ready to integrate yet?

Longor1996 commented 10 years ago

I will have to synchronize my Fork, make a new Branch, then try to incorporate the new renderer. Let's see if I can do so without breaking the whole thing...

Edit: Oh god... My Fork is corrupt.... AGAIN! What the hell am I doing wrong?! Gonna delete my Fork and remake it.

Edit 2: Okay, being sick (Stupid Flu!) and trying to untangle that giant mess of a renderer is nearly impossible. I will try to make it work, but I can't promise anything.

Edit 3: Done! It works! But there seems to be a memory leak... Weird. There wasn't one in the original PaintCanvas!

Edit 4: Great... I just wasted 2 hours of my life with fixing bugs. Then comes the Git-Client and overwrites EVERYTHING. Now I can fix all the bugs AGAIN.

Edit 5: F**K THIS! ALL changes I made in the last 5 HOURS just got RESET. I need a break ... I HATE the git-client.

Edit 6: And now I just want to die. The entire branch just got deleted. Great.

Edit 7: I cant fix the problem, so I will just recode the entire thing tomorrow. At least I got my original PaintCanvas in my experimental code folder!

HeroesGrave commented 10 years ago

I know how that feels. When Git goes wrong you lose a lot of time/work.

I've just repushed everything to the refactor branch and I'll leave it alone until you can get it working.

Longor1996 commented 10 years ago

Can you quickly look at Issue #83 ? I got a tiny problem there. Its impossible for me to even OPEN the Git-Client.

HeroesGrave commented 10 years ago

@Longor1996 & @BurntPizza: How are things going?

(I realise that everybody has probably moved back to work on their own things, and that's fine with me, but I want what you've done merged and tied up so I can freely work on it whenever)

BurntPizza commented 10 years ago

I was sort of waiting on Longor's renderer, but I'm thinking I might just do it myself if it doesn't show.

HeroesGrave commented 10 years ago

I realise I'm just as equally guilty of slacking off, but once this is done, I think we'll all be able to work without stepping on each others toes.

Longor1996 commented 10 years ago

Working on something like a Renderer and then losing all of the progress really takes the fun out of working on this. I think I will use my time to work on small things, like new tools and effects, instead of rewriting important parts of the Application.

I will probably spend some time writing a small DSL for a 'Shader'-effect soon.

@BurntPizza Here is the sourcecode for the original version of the new PaintCanvas: https://gist.github.com/Longor1996/6a3f70c0cb125c4c8274 Feel free to use the code, take it apart, rebuild it, and merge it into Paint.JAVA.

The only problems with the old PaintCanvas sourcecode is that the controls don't work on MacOSX, and that it is only a (very buggy + undocumented + messy) prototype.

HeroesGrave commented 10 years ago

That's fine with me. As long as we have something to use from all the work you've put in.

HeroesGrave commented 9 years ago

I've integrated PaintCanvas into the refactor branch. I'm going to stop making that commit any bigger. I made a few adjustments, mainly around how the zoom worked and the movement of the background.

@BurntPizza: Things are more or less ready for you to integrate your graphics stuff. If you're not up for it, can you perhaps dump what you've got in a gist or something so I can do it myself?

HeroesGrave commented 9 years ago

Pinging @BurntPizza again.

BurntPizza commented 9 years ago

Crap, sorry. What graphics stuff exactly? You have most of what I was thinking already with the BlendModes and RawImage etc. If you already have a working canvas and stuff you don't need any of what I have. The refactor looks pretty good btw, but I'm in the middle of midterms and don't really have time to contribute much.

HeroesGrave commented 9 years ago

I was wondering about your RenderContext/drawing methods. I would rather take the algorithms from someone who already knows what they're doing than try to work it out myself.

I'm not too concerned about the time frame. I've got exams coming up too. Just wanted to know if when I did get around to it, whether I should wait for you or just do it myself.

BurntPizza commented 9 years ago

This is the bulk of it: https://gist.github.com/BurntPizza/f0d2d31f843c47ea55f4 It might be lower level than what you want, and beware, it's been months since I've even looked at the code, so I don't remember exactly how everything works atm.

HeroesGrave commented 9 years ago

Awesome, thanks!

BurntPizza commented 9 years ago

Noticed drawing with fillCircle wasn't working, updated. Probably others don't work right yet, but it's past 1 AM here so I have to sleep.

EDIT: stenciling probably isn't the right call there, I'll investigate later. It works for now. Also, fun fact: I named the packages before the programming language was released :P Maybe I should rename them...

HeroesGrave commented 9 years ago

Just got a better freezebuffer/keyframe system working.

Every N changes it moves the front of the freezebuffer to the back, and stores the back in a list with a number to show how many keyframes it represents. If the top of the list has a buffer with the same number, it doubles the the number and merges them together, and so on down the list.

It's quite tricky to explain, but the space it takes up is quite small and the only performance hit is when the user spams undo very far back. After 64 keyframes have been created, only 6 will be stored. After 256, 8. After 1024, 10.

BurntPizza commented 9 years ago

If it's O(log n) in space, wouldn't it have to be at least approx O(2^n) in time to undo them? Assuming they are inversely proportional, which I believe they would be. I'll take your word for it until I try it out though

EDIT: or are the collapsed changes serialized piecemeal, and thus still O(n) plus IO constant?

HeroesGrave commented 9 years ago

Yeah. Undoing them gets nastier the further back you go. It recursively pops off the top of the list, splits in in half, applies the changes up to the second half, and pushes the new halves back.

Hopefully that means that you'll only get performance hits precisely on the powers of two. ie. undoing everything from 128 to 0 would be the same cost as undoing 384 to 256.

If it gets too much, I can put in a cap for the maximum size that can be merged.

BurntPizza commented 9 years ago

If there is some cheap heuristic Changes could implement that are indicative of their execution time, that would be good, instead of merging solely on number of changes.

HeroesGrave commented 9 years ago

I've got nearly all the hard stuff working.

The last big one is selection.

Before now, selection worked by extracting the selected pixels to a special layer that contained a mask. It worked reasonably well, but under the new system, creating and deleting layers has a more significant overhead (because the history has to be stored even after deletion).

Either this overhead will have to be reduced/removed (probably by sending the entire history to a file for deleted layers), or the mask will have to be applied directly to the layer (which is not a bad idea).

I think the best step to take is continue the move away from bufferedimages to just a raw int[], which will allow the second option to be more easily implemented.

BurntPizza commented 9 years ago

I think the best step to take is continue the move away from bufferedimages to just a raw int[], which will allow the second option to be more easily implemented.

I would say do both, create a BI, but pass around it's backing array, that way you can quickly render it if you need to.

That was kind of the idea with my BufferedContext.

HeroesGrave commented 9 years ago

I think I'm going to scrap the recursive rendering.

The layers will still be stored as a tree, but rendering will operate on them as a flat list. (Hopefully) in most cases the differences should be unnoticable, and it simplifies a lot of the render process.

HeroesGrave commented 9 years ago

Got selection (both masking and manipulation) working. That's the last big change.

Just need to reimplement the rest of the editing functions under the new system.

BurntPizza commented 9 years ago

Is there anything safe for me to work on that won't conflict, or should I just wait until the refactor is complete and sent to master? Even stuff not related to the refactor if you have that all tied up yourself.

HeroesGrave commented 9 years ago

Actually. What I've got is good enough to commit to master. The rest is really just reimplementing features rather than messing with the core stuff.

I have one more fix to make and then I'll push things.

HeroesGrave commented 9 years ago

Look at what deleting the docs did to the github graphs:

https://github.com/PaintDotJava/Paint.JAVA/graphs/contributors?from=2013-09-29&to=2014-11-04&type=d

HeroesGrave commented 9 years ago

I think the framework for editing functions (tools, effects, generators) is pretty stable.

If you're up for it, you could reimplement some of the old functions, or add some new ones.

Things to avoid would be any operations that modify the mask, because I need to make it a global thing rather than per-layer. Also avoid anything that requires the image to be resized, as I haven't got that working yet.

I've also got some work to do in writing/reading the history from a file, but you shouldn't come across anything that will interfere with that.

BurntPizza commented 9 years ago

I implemented a grayscale op: https://github.com/BurntPizza/Paint.JAVA/commit/b38b70347dd3e06bc93d13221430eeff8a897d36

BurntPizza commented 9 years ago

Drag-able flood fill tool now: https://github.com/BurntPizza/Paint.JAVA/commit/c67ca67d871a5471a58e0df53a28f58ac8aa754f Doesn't do anything fancy like alpha thresholds, etc.

The flood algo should probably be put into RawImage along with the other image operations, but I didn't as you might be modifying RawImage for mask stuff.

Tell me if you notice any issues.

HeroesGrave commented 9 years ago

Awesome

BurntPizza commented 9 years ago

Also, what's the amount of change needed per PR to not be annoying?

HeroesGrave commented 9 years ago

I think just keeping features separate is good. That way if one thing breaks, I can merge the rest without any trouble.

But when you're just adding new functions like you are now, just throw it all in together if it's easier.

I don't really mind.

HeroesGrave commented 9 years ago

Moving the masking stuff to a global state is going to require more stuff being rewritten than I thought. Still shouldn't affect much outside of the core though.

HeroesGrave commented 9 years ago

The mask thing is actually quite tricky.

All the layers can't share a mask because it will get out of sync as the image is pushed and pulled out of the history.

And it can't have a global state because each layer's history needs to replay the mask changes alongside the image changes to make sure that they don't affect the wrong area when undoing/redoing.

A solution could be to copy the mask to the new layer when switching, but then this adds extra changes which the user would have to undo. Despite this drawback, it would not be too confusing as the user could still see how the mask changed as they pressed undo/redo.

It's not pretty, but the workarounds that would be required to make the other solutions work are much much worse.

HeroesGrave commented 9 years ago

Done!

BurntPizza commented 9 years ago

Nice!