Hilbert curves have slightly better locality than Z-curves, but Z-Curves are trivial to produce.
If you have a N dimensional space over unsigned integers, then you can place each point on a one dimensional Z-Curve by interleaving their bits into a new integer.
So for any point (X, Y) in the X-Y coordinates of an image the Z-Curve coordinate is X_1,Y_1,X_2,Y_2...
Which can be achieved with a bunch of bit shifts and masks, (or a bunch of wires if you happen to use it in an FPGA ^^')
__m128i src = _mm_set_epi32(0, 0, x, y);
__m128i res = _mm_clmulepi64_si128(src, src, 0);
uint64_t zeorder = _mm_extract_epi32(res, 0) | (_mm_extract_epi32(res, 2) << 1);
Though a blocked iteration order should perform better due to better locality.If anybody else want to know how this works:
https://stackoverflow.com/questions/30539347/2d-morton-code-...
Locality is a good thing to exploit.
The nice thing about this is that just by changing the tables, you can get things like z-curve, tiles, row-major, or column-major pixel orders (or even combinations), plus swizzled, interleaved, or planar layouts for the color channels.
* Swapping to z-order requires about 5 lines of changes if you ignore all the little edge cases like non power-of-two image sizes. Images used in testing may or may not be representative of anything.
Related to this method, it's sometimes used for texture mapping on GPUs.