Test Code/TinselMipMap.c

/******************************************************************************
 **                                                                          **
 **     Module:     TinselMipMap.c                                           **
 **                                                                          **
 **     Purpose:    Support file for mipmapping                              **
 **                                                                          **
 **     Author:     Mike W. Kelley                                           **
 **                                                                          **
 **                 2/3/95  Revised for 0.9 SDK release                      **
 **                                                                          **
 **     Copyright (C) 1994-95 Apple Computer, Inc.  All rights reserved.     **
 **                                                                          **
 *****************************************************************************/
 
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
 
/* Macintosh */
#include <Types.h>
#include <Resources.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <OSEvents.h>
#include <DiskInit.h>
#include <Packages.h>
 
/* Project */
#include "RAVE.h"
#include "TinselTest.h"
 
/*******************************************************************************************
 *
 * Create the mipmap for 'pixmap', and load it into 'texture'. Returns NULL on error.
 *
 ******************************************************************************************/
 
TQATexture *CreateMipMapTexture (
    TQAEngine       *engine,
    TexturePixel    *pixmap,
    long            rowBytes,
    long            widthNBits,
    long            heightNBits)
{
    long            nImages;
    long            width, height;
    long            nPixels;
    TexturePixel    *mipmap;
    TQAImage        images [20];        /* Maximum conceivable number of TQAImages! */
    long            i;
    long            x, y;
    long            oneHalf;
    TQATexture      *texture;
    TQAError        status;
    
    /*
     * Allocate working space for the mip map images (starting with the
     * 2^(widthNBits-1) x 2^(heightNBits-1) map), and for the array of
     * TQAImages that describe the mip map and the parent map.
     */
    
    nImages = ((widthNBits > heightNBits) ? widthNBits : heightNBits) + 1;
    
    width = 1 << (widthNBits - 1);      /* ??? Doesn't work for nBits == 0 */
    height = 1 << (heightNBits - 1);    /* ??? Doesn't work for nBits == 0 */
    
    for (i = nImages - 1, nPixels = 0; i-- > 0; /* Nothing */)
    {
        nPixels += width * height;
        
        width >>= 1;
        height >>= 1;
        
        if (width == 0)
        {
            width = 1;
        }
        if (height == 0)
        {
            height = 1;
        }
    }
 
    if ( ! (mipmap = malloc (sizeof (*pixmap) * nPixels)))
    {
        return (NULL);
    }
    
    /*
     * Fill in the TQAImage info.
     */
    
    images[0].width = 1 << widthNBits;
    images[0].height = 1 << heightNBits;
    images[0].pixmap = pixmap;
    images[0].rowBytes = rowBytes;
    
    width = 1 << (widthNBits - 1);      /* ??? Doesn't work for nBits == 0 */
    height = 1 << (heightNBits - 1);    /* ??? Doesn't work for nBits == 0 */
    
    for (i = 1; i < nImages; ++i)
    {
        images[i].width = width;
        images[i].height = height;
        images[i].pixmap = mipmap;
        images[i].rowBytes = width * sizeof (*mipmap);
        
        mipmap += width * height;
        
        width >>= 1;
        height >>= 1;
        
        if (width == 0)
        {
            width = 1;
        }
        if (height == 0)
        {
            height = 1;
        }
    }
 
    /*
     * oneHalf is used to round off the box filter of four pixels.
     */
    
    oneHalf = 4 / 2;
 
    /*
     * Create the mipmapped images.
     */
    
    for (i = 1; i < nImages; ++i)
    {
        TQAImage            *prevImage, *image;
        long            sourceRowBytes, targetRowBytes;
        unsigned char   *sourceLine0, *sourceLine1;
        unsigned char   *targetLine;
        
        prevImage = &images [i - 1];
        image = &images [i];
        
        sourceLine0 = prevImage->pixmap;
        
        if (prevImage->height == 1)
        {
            /*
             * Source image is height 1 (widthNBits must be greater than heightNBits).
             * Make sourceLine1 duplicate sourceLine0, and set sourceRowBytes to 0 so we
             * just keep re-reading the single source line.
             */
            
            sourceRowBytes = 0;
            sourceLine1 = sourceLine0;
        }
        else
        {
            /*
             * Normal case. sourceLine1 points one scanline above sourceLine0.
             */
            
            sourceRowBytes = prevImage->rowBytes;
            sourceLine1 = sourceLine0 + sourceRowBytes;
        }
        
        targetRowBytes = image->rowBytes;
        targetLine = image->pixmap;
 
        for (y = image->height; y-- > 0; /* Nothing */)
        {
            unsigned char   *sourcePixel0, *sourcePixel1;
            unsigned char   *targetPixel;
            
            sourcePixel0 = sourceLine0;
            sourcePixel1 = sourceLine1;
            targetPixel = targetLine;
            
            if (prevImage->width == 1)
            {
                /*
                 * Source image has width 1 (heightNBits must be greater than widthNBits).
                 * Use a special inner loop that doesn't advance past the first pixel of
                 * the source lines.
                 */
                
                for (x = image->width; x-- > 0; /* Nothing */)
                {
                    long    a, r, g, b;
                    
                    /*
                     * Read and average two pixels from source into target.
                     */
                    
                    a = sourcePixel0 [0] + (oneHalf >> 1);
                    r = sourcePixel0 [1] + (oneHalf >> 1);
                    g = sourcePixel0 [2] + (oneHalf >> 1);
                    b = sourcePixel0 [3] + (oneHalf >> 1);
                    
                    a += sourcePixel1 [0];
                    r += sourcePixel1 [1];
                    g += sourcePixel1 [2];
                    b += sourcePixel1 [3];
                    
                    *targetPixel++ = a >> 1;
                    *targetPixel++ = r >> 1;
                    *targetPixel++ = g >> 1;
                    *targetPixel++ = b >> 1;
                }
            }
            else
            {
                /*
                 * Source image has at least width 2. This is the normal case.
                 */
                
                for (x = image->width; x-- > 0; /* Nothing */)
                {
                    long    a, r, g, b;
                    
                    /*
                     * Read and average four pixels from source into target.
                     */
                    
                    a = *sourcePixel0++ + oneHalf;
                    r = *sourcePixel0++ + oneHalf;
                    g = *sourcePixel0++ + oneHalf;
                    b = *sourcePixel0++ + oneHalf;
                    
                    a += *sourcePixel1++;
                    r += *sourcePixel1++;
                    g += *sourcePixel1++;
                    b += *sourcePixel1++;
                    
                    a += *sourcePixel0++;
                    r += *sourcePixel0++;
                    g += *sourcePixel0++;
                    b += *sourcePixel0++;
                    
                    a += *sourcePixel1++;
                    r += *sourcePixel1++;
                    g += *sourcePixel1++;
                    b += *sourcePixel1++;
                    
                    *targetPixel++ = a >> 2;
                    *targetPixel++ = r >> 2;
                    *targetPixel++ = g >> 2;
                    *targetPixel++ = b >> 2;
                }
            }
            sourceLine0 += (sourceRowBytes << 1);
            sourceLine1 += (sourceRowBytes << 1);
            targetLine += targetRowBytes;
        }
    }
    
    status = QATextureNew (engine, kQATexture_Mipmap, kQAPixel_RGB32, images, &texture);
    if (status != kQANoErr)
    {
        return (NULL);
    }
    
    return (texture);
}