views:

557

answers:

1

Hi,

I am currently working on an iPhone game, and need to create some animation on the fly. The animation is based on several images, and as the range of image combinations is too large and it is impossible to pre-create all of sequences, I'd like to be able to recalculate a set of images every time the situation changes.

The function creates a series of images, to be used as an animation sequence. I need help with two things:

  1. There are several large memory leaks that I'm having trouble finding - I need help locating them
  2. Since I'm new to the iPhone, I want to know if there is a better way to do it, if you have a better suggestion on how to write it, please share...

Code:

(void) updateImages:(int) smallPicID
{
    CGRect smallPicRect;
    smallPicRect.size = CGSizeMake(25.0f, 25.0f);

    //TODO: add smallPic location for every image. Currently the values only match the first image...
    static const CGPoint smallPicLocation[11][SMALLPIC_LOCATION_COUNT] = 
    {
     { {126.0f, 153.0f},{105.0f, 176.0f}, {115.0f, 205.0f}, {145.0f, 211.0f}, {166.0f, 188.0f}, {156.0f, 159.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} },
     { {183.0f, 91.0f}, {201.0f, 97.0f}, {205.0f, 115.0f}, {191.0f, 127.0f}, {173.0f, 122.0f}, {169.0f, 124.0f} }
    };

    for(int i = 0; i < self.m_finalImages.count; ++i)
    {
     // start with base image
     UIImage * baseImg = [self.m_finalImagesEmpty objectAtIndex:i];
     CGImageRef baseImgCtx = baseImg.CGImage;

     // Create the bitmap context that matches the baseImg parameters
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
     CGContextRef newImgCtx = CGBitmapContextCreate(NULL, 
                   CGImageGetWidth(baseImgCtx), 
                   CGImageGetHeight(baseImgCtx), 
                   8, 0, colorSpace, 
                   (kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst));
     CGColorSpaceRelease(colorSpace); 

     if (newImgCtx == NULL) 
     { 
      // error creating context
      assert(0);
      return;
     }

     // Get empty drum image width, height.
     size_t w = CGImageGetWidth(baseImgCtx);
     size_t h = CGImageGetHeight(baseImgCtx);
     CGRect rect = {{0,0},{w,h}}; 

     // Draw the image to the bitmap context.
     // newImgCtx now contains the full empty drum image.
     CGContextDrawImage(newImgCtx, rect, baseImgCtx); 

     CGContextSaveGState(newImgCtx);
     // translate & scale because of origin difference between UIKit and CG
     CGContextTranslateCTM(newImgCtx, 0, h);
     CGContextScaleCTM(newImgCtx, 1, -1);

     int startLocation = 0;
     int endLocation = SMALLPIC_LOCATION_COUNT;

     // for each location
     for(int j = startLocation; j < endLocation; j++)
     {
      // if its empty, nothing to do, move to next bullet
      if (m_locationStatus[j] == CY_EMPTY)
       continue;

      UIImage * srcImg;
      switch (m_locationStatus[j]) 
      {
       case CY_FULL: srcImg = [self.m_finalImagesLoaded objectAtIndex:i]; break;
       case CY_USED:  srcImg = [self.m_finalImagesSpent objectAtIndex:i]; break;
      }

      // create small image containing only the smallpic from the src img
      smallPicRect.origin = smallPicLocation[i][j];
      CGImageRef smallpicCGImg = CGImageCreateWithImageInRect(srcImg.CGImage, smallPicRect);

      // draw the smallpic into the new context
      CGContextDrawImage(newImgCtx, smallPicRect, smallpicCGImg);   

      CGImageRelease(smallpicCGImg);
     }

     CGContextRestoreGState(newImgCtx);

     // update the image from the context
     UIImage *baseImg = [self.m_finalImages objectAtIndex:i];
     CGImageRef imgref = CGBitmapContextCreateImage(newImgCtx);
     //[baseImg release];
     [baseImg initWithCGImage:imgref];

     CGContextRelease(newImgCtx);
    }
}
+1  A: 

The first things that jump out at me are the creation of imgref near the end with a CGBitmapContextCreateImage(), but no matching CGImageRelease(), and the baseImg initWithCGImage: message that does nothing but chew up memory. I think you want to replace the section starting with "update the image from the context" with something like:

CGImageRef imgref = CGBitmapContextCreateImage(newImgCtx);
UIImage *replacmentBaseImg = [[UIImage alloc] initWithCGImage:imgref];
CGImageRelease(imgref);

[self.m_finalImages replaceObjectAtIndex:i withObject:replacementBaseImg];
[replacementBaseImg release];

CGContextRelease(newImgCtx);

This assumes that m_finalImages is an NSMutableArray and that you've properly released your other images that have been inserted in that array, so that when you replace them they get deallocated.

On a larger structural note, you may wish to look at drawing your smaller subimages into individual CALayers, and then swapping those layers in and out of the screen at the various locations to accomplish your animation. Quartz drawing is expensive, and CALayers are cached images stored as textures on the GPU. Other people have performed these sprite-based animations using CALayers and achieved impressive performance.

Brad Larson