| Duh, an 'easy' way to replicate Giess's behavior: |
| |
| For each frame, you have to mutate it by a transform matrix. This is |
| easy, thought not cheap. First you precalculate the transform matrix how |
| you want it, based on whatever rotations or whatever you want. |
| |
| The data stored in each spot on the matrix tells you how to transform a |
| single pixel. The simple case is dx,dy, where both are relatively small. |
| The probably ought to be a byte in any case, so you can scale the |
| transform matrix on slow machines. A more complex case is some trick |
| whereby a single pixel ends up splattered in several places. Idea below. |
| |
| The matrix consists of some number of 8bit arrays of the same size as the |
| image. They'd probably be line-interleaved or better to help with cache |
| effects (which are VERY serious here). Each channel represents some |
| aspect of the transform. The first two would likely be dx and dy, the |
| third might be a multiplier if that wasn't done statically. |
| |
| The idea: any number of transform sets could be applied, given available |
| processing power. Just set the static scalar or the multiplier matrices |
| so you don't completely swamp the output pixels. |
| |
| Note that this is fastest in 8-bit, but theoretically could be applied to |
| 32 bit. 15 and 16 are hard, since you can't easily apply the multipliers |
| unless they're 1/2^n, and even then it's significantly heavier (you'd have |
| to mask the top n bits of each color out). |
| |
| This SCREAMS for MMX, in case you haven't figured it out yet. |
| Unfortunately, MMX is only directly useful for the scalar matrix, unless |
| you do a trick where all the pixels in that fit in 64 bits (8 8bit, 4 |
| 16bit, or 2 32bit) are always moved in a group. This is very possible, |
| and might be a significant perf increase by being able to use MMX all the |
| way through. Otherwise you have to place each pixel by extracting the MMX |
| stuff back into normal registers, and that just plain sucks. |
| |
| A pseudo-C implementation: |
| |
| ----- BEGIN ----- |
| gint x,y; /* image x and y size */ |
| guchar old_image[x][y]; /* original image */ |
| guchar new_image[x][y]; /* new image */ |
| gchar x_xform[x][y]; /* dx matrix */ |
| gchar y_xform[x][y]; /* dy matrix */ |
| guchar s_xform[x][y]; /* intensity scalar matrix */ |
| guchar scalar; /* global scalar */ |
| |
| gint i,j; /* indixes */ |
| gulong p; /* pixel value in question */ |
| guchar u,v,w; /* modifier variables */ |
| |
| /* clear the new image, we don't want anything getting in the way */ |
| /* NOT NECESSARILY A GOOD THING, THOUGH */ |
| memset(new_image,0,x*y); |
| |
| /* loop through all the lines in the image */ |
| for (j=0;j<y;j++) { |
| /* loop through all the pixels in the line */ |
| for (i=0;i<x;i++) { |
| p = old_image[i][j]; |
| u = x_xform[i][j]; |
| v = y_xform[i][j]; |
| w = s_xform[i][j]; |
| new_image[i+u][j+v] = (guchar)((p<<14) / (w * scalar)); |
| } |
| } |
| ----- END ----- |
| |
| Note that the above really, *REALLY* sucks performance-wise. Throw it a |
| 80x60 image and it'll swamp my poor laptop. Also note that I simply set |
| the pixel value, not merge it. That means you'd better be sure your |
| transform matrix doesn't have overlapping destinations. |
| |
| Other notes about the above code: x_xform and y_xform are signed chars, |
| which means pixels can move in all directions. The intensity matrix is |
| unsigned, with a range from 0 to 255, so is the global scalar. Note the |
| shift of 14bits (2 * 7bits), then divide by each. That means identity for |
| both scalars is at 128. The FP range of each is thus 0.0 to 2.0. Very |
| handy. |