| 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. |