Closed HeroesGrave closed 9 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.
That's pretty amazing. I'm quite interested to see how your algorithm for drawing large brushes works.
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
Well, I'm feeling better now, and have one day left before exams.
@Longor1996: Is your PaintCanvas ready to integrate yet?
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!
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.
Can you quickly look at Issue #83 ? I got a tiny problem there. Its impossible for me to even OPEN the Git-Client.
@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)
I was sort of waiting on Longor's renderer, but I'm thinking I might just do it myself if it doesn't show.
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.
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.
That's fine with me. As long as we have something to use from all the work you've put in.
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?
Pinging @BurntPizza again.
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.
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.
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.
Awesome, thanks!
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...
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.
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?
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
I implemented a grayscale op: https://github.com/BurntPizza/Paint.JAVA/commit/b38b70347dd3e06bc93d13221430eeff8a897d36
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.
Awesome
Also, what's the amount of change needed per PR to not be annoying?
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.
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.
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.
Done!
Nice!
Progress Check