Image Scaling Using ITransform
Developers of applications for Brew 2.x need not despair; they need only work a little harder. Using the ITransform interface, bitmaps can be manipulated in a variety of ways, including scaling. To use ITransform, you must:
- Draw your image to an offscreen bitmap.
- Construct an AEETransformMatrix that indicates how you'd like your image to be scaled.
- Create a compatible bitmap from the offscreen bitmap.
- Obtain an instance of ITransform from the new bitmap.
- Invoke the ITransform's TransformBltComplex method to blit the image into your new bitmap applying the parameters of the transform matrix.
- Blit the scaled bitmap to the location on the screen where you want to draw the scaled image using IDisplay's BitBlt method.
- Release the source image, source image bitmap, and the ITransform instance.
Sounds like a lot of work, doesn't it? In practice, it's not that hardjust a lot of typing. Modifying the previous example to use ITransform instead of IPARM_SCALE
, you would write something like Listing 1
A lot of code, to be sure, and not all of it obvious in terms of functionality. Let's take it step-by-step, correlating each step with a step in the algorithm outlined previously.
The code begins by getting the bitmap used by the display, and creating another bitmap in the same format large enough to hold the original, unscaled image by invoking CreateCompatibleBitmap. Next, the routine swaps the display bitmap with the newly created, offscreen bitmap, and invokes Draw to draw the image to the unscaled bitmap. Invoking SetDestination again, this time with NULL, returns the display's bitmap to the original screen bitmap, so subsequent draw operations will appear on the display, not the off-screen bitmap. This is the first step of the algorithm described previously.
Next, the code creates an AEETransformMatrix on the stack. The matrix has four elements, organized as a 2x2 matrix, like this:
[ A B
C D ]
Here, each value is a fixed-point value, scaled up from the floating-point value by a fixed coefficient of 256. The transform matrix is applied to each pixel in the image, letting you scale, rotate, or shear the image, or any combination of these by choosing the appropriate values for A, B, C, and D. For scaling, the matrix should be:
[ scale-x 0
0 scale-y ]
Now, scale-x is the amount to scale along the x axis (larger numbers indicate greater scaling), and scale-y is the amount to scale along the y axis. Don't forget that you're working with fixed-point math, here, so really, the value to stuff into the AEETransformMatrix
should be 256 times the actual scale factor! This is the second step of the algorithm.
Next, create a bitmap to hold the resulting scaled image, and from it obtain an instance of ITransform using QueryInterface (steps 3 and 4). From there, it's as simple as performing a TransformBltComplex (step 5), which uses the AEETranformMatrix you created. This performs a bit block transfer similar to IBITMAP_BltIn or IBITMAP_BltOut, the difference being the application of the transformation matrix and the honoring of transparent values when the last argument is COMPOSITE_KEYCOLOR instead of COMPOSITE_OPAQUE. Once you create the scaled image, it's a simple matter to blit it on the screen at the desired location using IDISPLAY_BitBlt (step 6). Finally, you need to clean up all the interfaces you've created, too, so you don't leak memory (step 7).
I hinted that you could perform other scaling operations with different values in the transformation matrix. If you want to rotate the bitmap, you can do so using this matrix:
[ cos theta –sin theta
sin theta cos theta ]
Here, theta is the angle by which to rotate the source image.
| Author's Note: Don't forget to use Brew's floating-point helpers FSIN and FCOS, not the standard sin and cos functions in the math library, if you're computing the rotation programmatically.
If you want to shear an image, you can use the following transformation:
[ 1 k1
k2 1 ]
indicates shearing parallel to the x-axis
, and k2
indicates shearing parallel to the y-axis
. You can also combine terms; for example, the matrix:
[ s cos theta –sin theta
sin theta s cos theta ]
This scales an image by s on both axes and rotates the image by theta.
Of course, all of these transformation matrices must be converted to fixed-point, just as I did in the code above; don't forget to multiply each of the values by 256. For more details on other transformations you can perform, have a look at the Brew documentation for ITransform. Another good resource on matrix transformations is Wikipedia.
Finally, it's worth noting that IPARM_SCALE in Brew 3.x doesn't replace ITransform; it just makes it easy if all you want to do is scale an image before drawing. All of the transforms I've just discussed work in Brew 3.x just as well as Brew 2.x.
Ahead of the Curve
Although Brew 3.x provides an easy way to scale images, many application developers must still write to the lowest common denominator platform in consumer hands, which is generally a variant of Brew 2.x. Fortunately, the ITransform interface has been around since Brew 2.0, and lets developers not just re-scale, but rotate and shear bitmaps as well, providing you with great flexibility in how you draw images as long as you don't mind using bitmaps instead of IImage to draw.