About Me
Michael Zucchi
B.E. (Comp. Sys. Eng.)
also known as Zed
to his mates & enemies!
< notzed at gmail >
< fosstodon.org/@notzed >
And then there was none
Should've been working today but I was feeling very agitated and couldn't concentrate ... I borrowed a ladder yesterday so I thought I'd just take a couple of sheets of plastic off of the verandah ...
Except once I got going I couldn't stop. Ladder was pretty shaky to start with - I first tried to go up to the highest spot but it was just too much and I had to come down again. For reference the sliding doors are about 2100mm or 7' tall. But by the end I was walking down forwards carrying a few tools at the same time.
Now just a few dyna-bolts to take out for the end plank and it's done. Guy's coming to do the final measure tomorrow so i'll see what he has to say about a few mistakes I might've made - like some of the retaining wall and if I have to take out the palm tree or the rear fence ...
Although now without a verandah I think it's about to rain - might wash away some of the dust anyway.
Super Affine Tool Thing
Quite a bit of progress on ImageZ over the last few nights. Didn't get a lot done in the yard over the weekend - a visitor on Saturday and I was lazy today but I got a couple of hours of gardening in - mowed the lawns, transplanted some rhyzomes from the patch of grass i have to the patch of non-grass I don't want, weeded one of the vegetable patches and shifted most of my pots out of the way ready for more work readying the yard.
ImageZ
Fixed up a few issues with using non-pre-multiplied alpha, but only for normal blending mode - I know all the other blending modes are still doing it wrong.
Actually I can't really remember where I was at last time and how much i've done since then, it's all blurring into one long indistinct memory.
But I did work on what i've been calling a 'super affine tool'. It combines the various affine operations into a single tool in a way that should still be accessible.
A scaled, rotated and translated cat
I've been thinking about this for quite a while but finally sat down to nut it out. When you bring up the tool the following controls pop up in the middle of the image:
- Centre Disc
- This is the location of the image relative to the centre of the image. i.e. a simple translate.
- Inner Ring
- Sets the location of the rotation centre point on the source image.
- The two Parallelograms
- Adjust the shear in X or Y. The shapes move as you drag them in proportion to the shear amount.
- The two Rectangles
- Adjust the scaling in X or Y. The shapes scale as you drag the mouse in proportion to the scaling amount.
- Outer Ring
- Adjusts the rotation angle. Yes it probably needs some sort of indicator, although the moving image gives you that.
I'm still tweaking the affine transform matrix it produces to make it behave nicely. It applies the rotation, then the shear, scale and finally offset. This makes it behave mostly how you'd expect it to, although in some cases you'd have to run it multiple times to get exactly what you wanted. And it doesn't work properly if the view is zoomed ... and you can't actually commit the operation yet.
Damnation in hell Java is fast at this though - it must be accelerating it using the graphics card. I'm even using linear interpolation with alpha blending for the real-time preview and it's really nice and quick - at least when using one of the native layer types. Even a full-resolution image from my digital camera at 4288x2848 is flinging around at ~15-20fps.
I'm not entirely happy with the tool options on the left - there's a lot of numbers and spin buttons - but I guess it's not completely out of the question.
Actually I was also looking at putting tool options there which is why it's shown up there in the first place. But i'm not sure about that yet - if I do that then most tool options will really need to be per-image otherwise the behaviour will be a little odd. I'm not sure if this is desirable. For example if you set the tool to 'pen' you might reasonably expect it to be 'pen' on another window. Or maybe you might not - it probably doesn't matter either way.
Menus revisited
I also had a good long dig at fixing some issues with the Amiga style menus. Basically some bugs(?) in the lightweight comboboxes were giving me nightmares. I was using an event listener on the glass pane to capture mouse button events. But the problem is when you do that you end up having to re-route the mouse events manually! I did manage to get that working quite well over the last few weeks except for the combo boxes. If you hold down the button and drag when selecting a combobox item it works fine, but if you click twice it doesn't! I eventually found similar discussions in the java forums and adopted a new version which just attaches a low-level event listener instead and simply snarfs the menu-button event. It reduces some of the flexibility I might have had in say allowing context menus as well, although I can probably hack that if required. But now the menu works reliably and doesn't interfere with the rest of the application. It's probably something that would be turned off if you wanted to use a Macintosh since that separates menus from windows already, but that is of no concern to me.
Finally the rain stopped
What a shit weekend, although at least I didn't get flooded out or anything.
Spent the whole weekend hacking on ImageZ ... although mostly hacking it to bits and making myself grumpy. I decided that obviously storing images as pre-multiplied alpha wasn't going to cut it for the integer types, so I converted the code to store using non-pre-multiplied alpha but blend to the target using pre-multiplied alpha. Straightforward (once i got the damn equations right) except for the common case where the tool layer is blended to the target layer first. God what a headache that was.
Also played with adding tool markers for things like editing the selection. For that I'm using Piccolo2d. Probably a bit of overkill but it'll let me do whatever I need for tool controls and it's easy as pie to use. It was also easy to connect up to the main window. I would also like to eventually use it for structured layers ... although I'm not sure how doable that will be since the layers are not rendered as normal components. It did throw a spanner in the works for the whole input event handling which I still haven't removed though.
And I got deeply into the image loader stuff. I was just loading into whatever ImageIO gave me and then copying across to a target layer but that doesn't scale terribly well and is pretty slow. I found out you can force it to write to a compatible type of image fairly easily (it doesn't have to match exactly the image type specifiers it gives you when queried) and is usually pretty fast. So I added yet another few more backend image types to support the common formats of images I work with and cleaned up the image loader to write directly to the target layers.
Finally I played with a translation tool which moves the origin of the layer. And that left a lot of crap to fix up which i'm sure I haven't finished yet. Everything from undo to painting broke in big or little ways.
I was hoping to just focus on a few polishing details for a while to clean up the whole thing and make it solid, stable and usable. But given how much I broke on the weekend that could be a long way off ... I had even started poking around looking for somewhere to put it, but i'm not sure I could be bothered with the approval process for Savannah, Google Code i'm not sure about since I seem to do everything on Google, and i'm not considering clitorous or github because git is offensive and the whole repository maintenance thing is a hassle I don't want to deal with. Tar is starting to look attractive.
Shed Be Gone
The council *FINALLY* came through with the approval for some building I'm doing around the house. Jesus, it only took them 10 months ... how shit is that. So given today wasn't drenching the yard with rain I thought i'd get cracking and start on some of the big tasks that need doing.
This spot is destined to be for a big shed ... so all that shit has to go, and the ground needs levelling off. And I want to kill that horrid huge ivy growing in the corner.
So I made pretty good progress today. I also loaded up the ivy with some glycophosphate which managed to kill most of the rest I have. Would've helped to have a hand but I managed doing it alone, although I might be sore in the morning. My foot was very sore last week and I don't think it was really healed enough so I'll probably aggravate that too which isn't a good thing :( Just got some tendonitis again and last time I had it that took a couple of weeks of doing almost nothing to get better. But the yard can't wait forever ... i'll have under 2 months to finish it off.
I'm surprised the yard was so relatively dry - we had over 40mm of rain over the weekend. And a shitload of wind but the house protects most of the yard from that.
Invisible man
I was in a weird state on the weekend, probably stressing about the yard more than anything (e.g. I think i put a retaining wall in the wrong place before I should have, so I may have to undo some ... which is an extreme amount of hassle) and for some reason I thought i'd see how easy this blog was to find. Not very it seems, at least with google. And obviously it is something only google is doing because it's on the first page of a search for 'notzed' on yahoo - maybe i swear too much or pick on google too much. I gave up trying to find it on google - the best I saw was a link to my 'blogger profile' on page 5. Given that Advogato was the first result I even considered re-starting to use that, but without pictures it's not much use and it seems pretty quiet on there. I went and reset Advogato anyway and might look at restarting the internode blog again and doing it all manually using static html (oh the shame!). It's not like comments are thick on the ground around here.
New
Kept plugging away and added options for new images and new layers at last.
Not all of the layer types work 100% yet, but they work for normal drawing and layer operations and PNG at least saves mono images as mono. I probably don't need them all there - I just did it because I could.
All the calculations and blending is just done in float RGBA so none of the code needed changing, although it does mean it's doing a lot of redundant work if the result is only monochrome.
Save Take 2 of 2
Had another stab at saving again today. Damn i forgot how tedious it is to try and make relatively nice looking GUI components, particularly when the layout toolkit seems to want to fight you at every stage ...
It's pretty basic but it should suffice for what I need. Actually this is the first post where I created the images using ImageZ itself, so it marks a bit of a milestone.
I haven't actually hooked up the comment stuff yet, but i've done it before and it's a matter of frobbing around with the metadata. Unfortunately it is in XML ...
I decided to leave in the RGBA support for JPEG files after reading the JPEG metadata specification for Java. Maybe nothing else will read them properly but it looks like Java will. But it defaults to flattening the image for JPEG files just to avoid confusion and i'll probably add a warning.
While doing testing I got really sick of the childish 'this file exists are you sure' crap (yeah no fucking shit sherlock!) and found I think a reasonable way to get rid of it. The file requester is used as normal to select the file to save to, and if you happen to select or enter a file that exists it just shows a warning in the bottom of the window and relabels the Save button to Overwrite. That's almost always what I want to do when I save over the same name. So the idea is not to interfere with typical use, but still let you back out if you truly made a mistake. Actually it lets you just type in a name from this window so you don't have to back out too far either.
I'm still not sure about the `Save Version' button here - the idea is that it will just create a name automatically if there is a clash. But I have a feeling that without filesystem multi-version support it's just a good way to lose what you're working on too easily.
Unfortunately behind the scenes there's still a load of rubbish to deal with such as keeping track of the meta-data from the image loading, transferring meta-data between formats, and so on.
But with this weather I might be spending a lot of time inside ... sigh, really cold, wet, windy, shitty weather. And I hurt my foot doing something I can't remember although it's getting better quickly because i'm giving it plenty of rest. Both are at quite inconvenient times, I finally got council approval for my shed and I still have a lot of work to do getting the yard ready - and soon will have a hard deadline in which to achieve it.
Saaave Meee!
Had a poke at saving images tonight. I don't know where the damn day went but I only started looking at doing some hacking at 10pm tonight.
To start with I thought i'd skip any esoteric floating point formats or bothering with layers and just stick with the standard formats and trying to fit the images to suit. So about the best format is PNG with 16 bits per channel. It's a bit of a pigs breakfast but basically it scans the image to find the highest depth/format image in the layer stack and then starts trying to find an image writer for that format and just keeps dropping down to lower depth resolution until one works. e.g. it tries floats, then shorts, then bytes.
Well, things are a bit mixed ... to say the least.
PNG isn't too bad - it's saving as 16 bit per channel too - but JPEG does weird things. Actually I was surprised it even saved a 4 channel image - I expected it to fail. What's strange is that Java loads this weird 4 channel JPG just fine as an RGBA image but nothing else does. Java's in the wrong here I'm pretty sure?
Unfortunately the image writer interface doesn't let you find out what type of individual things the image format supports and all you can do is query if it supports writing a given complete format. I guess i'll just have to hardcode it for every format I will support - I suppose I need to anyway to handle the meta-data properly and stuff like compression options. The standard JRE doesn't support much anyway and besides I don't need to either. I suppose i'll use OpenRaster as the 'native' format, although TBH anything with XML and interoperability mentioned together just sounds like a nightmare.
Hmm, then there's all the state and metadata to muck about with from inside the app. Oh wont that be fun.
Storing everything as RGBA also complicates matters ... although I can cheat a bit and put options on the per-format requester for target bit depth & channels saved.
I got a fufut.
I had a go at porting Apple's OpenCL FFT to JOCL and coming up with a simple demo to add to the JOCL demo project.
Rotated image from previous post with rotated motion blur. The image on the left is used to generate the convolution kernel.
So here's a screenshot of a 'blur tool thing' I came up with. Because of screen-size I forced it to only process a 512x512 image, but even at 1024x1024 it does the convolution as fast as the mouse can send events (the raw gpu time for a 1024x1024 convolution is about 1200uS per plane, excluding data conversion). I had previously written a separable convolution for OpenCL and this is about on par with the 63x63 convolution processing time - but isn't limited to separable convolutions or small kernels (e.g. no rotation like above). Does take somewhat more processing to build the kernel though since it's the same size as the image but that's something easily off-loaded to the GPU as well.
More bits. And pieces.
I don't know why the default size of the Java file requester is so unusably small - perhaps it is some microsoft legacy, but it's really quite nice when it's sized up a bit, and set to the vertical list mode. If only you could drive it from the keyboard ...
Well I added an image preview to the one in ImageZ anyway. The info area is a bit naff but I just grabbed some of the generic meta-data from the ImageReader. The images are loaded/iconised in the background, of course. I first tried using a SwingWorker and cancelling it if a new image was browsed - the problem is that the image loading ignores the interruption and keeps going until it's just about finished anyway. So it didn't really suit.
Then I tried writing a loader using the lower-level api's which have an explicit cancel request. Still no go - it just ignores the abort until it's finished anyway. Damn. So I gave up and just launch a new thread every time. If the thread gets interrupted/cancelled I quit the thread as soon as I can, otherwise the image is loaded, scaled, and then dropped into the preview box. Given it loads the full image to create the icon (if there's no thumbnail in the image, which there usually isn't), I should hook the loaded (or loading) image to the OK button too - images will load immediately then.
For 'normal' sized images it's quite quick anyway although I guess it needs to handle the edge cases to avoid a backlog of threads. Actually loading a bunch of images using multi-select is pretty fast - loading all of the 14 images so far from this series of posts at once is under a second - at least 3x faster than GIMP on this box. Maybe it's the threading (and yes, not entirely fair, a full app vs a toy).
Tool Display
I also hacked up a very nasty bit of code which lets tools add information to the display window itself.
e.g. the current brush 'shape'.
Right now the tool has to handle calculating the dirty regions and other crap so I need a better solution, but having it there will let me investigate.
On the other hand it is pretty flexible. I thought i'd start poking at my 'affine' tool - trying a rotation to start with. First just to see how slow it would be, and then to try ways of controlling it. The tool just tells the image window to repaint every time the angle changes, and then its paint callback just blats the image over the top. And it's not too shabby by default speedwise - even the bicubic filter is around the same speed as GIMP's nearest neighbour for medium sized images.
Then I played with a few other ideas - in the above shot the image is scaled down first, set to acceleratable, and then drawn as the mouse moves (using nearest-neighbour in this shot). i.e. it runs very very quick once you start rotating the image (as fast as the mouse sends events). Unfortunately for very large images the initial scaling can be quite slow, although it is probably still worth it. Scaling down large BufferedImages seems unusually slow so perhaps scaling it myself in integer increments would be faster for things like this where I just need an approximation. Just 'scaling' the image by 1, and converting from a `BufferedImage' to a `Toolkit' `Image' makes a huge difference too as it presumably gets some hardware acceleration.
As can be seen from the shots I also worked out some problems with the Nimbus theme - it's JTable widget handles the default renderer types a little differently so I had to change the column type to an IconImage rather than just an Icon for the layer icon. Nimbus looks ok - the scrollbars are a bit ugly/out of character and the general spacing is a little loose but it looks modern enough. And I added a little info box to the bottom of the toolbox. Now what to put in the white empty area ...
And I fixed some bugs in the handling of layer translation. I haven't hooked any tools up for that yet but it should hopefully just work when I get to that point.
The GIMP's taken to hiding all the control boxes when I change virtual desktops now, and wont let me move them to other desktops either. I wonder if it's trying to tell me something.
Copyright (C) 2019 Michael Zucchi, All Rights Reserved.
Powered by gcc & me!