如何处理动态图像的平铺 [英] how to handle tiling of images on the fly

查看:138
本文介绍了如何处理动态图像的平铺的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个应用程序,它会将图像平铺为256 * 256并将这些图块文件写回目录中。如果有任何更新,我每次更新我的URL并将这些图像平铺回来并存储在iphone文件夹中。我担心两个主要问题:
1)内存消耗 - 5个大小为200 KB的图像的内存消耗量是多少?
2)如果我必须同时使用图像平铺5个不同的URL,我可以多快处理我的应用程序?

I am writing an app which would tile the images 256 * 256 and write those tile files back in the directory. I am updating my URL each time if there are any updates and tile those images back and store in the iphone folder. I am worried about two main things : 1) Memory consumption -- will the memory consumption for 5 images of size 200 KB a lot ? 2) How fast I can process my app if I have to tile 5 different URL with images at the same time ?

我已经写了一个代码到tile并保存在一个URL的目录中,并希望对5个URL执行相同的操作。是建议采用这种方法还是有人采用不同的方法?

I have written a code to tile and save in the directory for one URL and would like to do the same for 5 URLs. Is it recommended to go with this approach or if anyone has a different approach?

    - (void)viewDidLoad
    {
      [super viewDidLoad];

      NSString *URLString = @"http://www.abc.com/abc.html?event=123";   
      NSURL *url = [[NSURL alloc] initWithString:URLString];
      NSData * dataImage = [NSData dataWithContentsOfURL:url];

      NSString *directoryPath = [[NSBundle mainBundle] bundlePath];

      UIImage *big = [UIImage imageWithData:dataImage];
      [self saveTilesOfSize:(CGSize){256,256} forImage:big toDirectory:directoryPath          usingPrefix:@"image_124_"];  

       TileView *tv = [[TileView alloc] initWithFrame:(CGRect){{0,0}, (CGSize){5000,5000}}];
      [tv setTileTag:@"image_110_"];
      [tv setTileDirectory:directoryPath];

      [scrollView addSubview:tv];

      [scrollView setContentSize:(CGSize){5000,5000}];
    }


     - (void)saveTilesOfSize:(CGSize)size 
        forImage:(UIImage*)image 
        toDirectory:(NSString*)directoryPath 
        usingPrefix:(NSString*)prefix
     {
      CGFloat cols = [image size].width / size.width;
      CGFloat rows = [image size].height / size.height;

      int fullColumns = floorf(cols);
      int fullRows = floorf(rows);

      CGFloat remainderWidth = [image size].width - 
                      (fullColumns * size.width);
      CGFloat remainderHeight = [image size].height - 
                      (fullRows * size.height);


      if (cols > fullColumns) fullColumns++;
      if (rows > fullRows) fullRows++;

      CGImageRef fullImage = [image CGImage];

      for (int y = 0; y < fullRows; ++y) {
       for (int x = 0; x < fullColumns; ++x) {
       CGSize tileSize = size;
       if (x + 1 == fullColumns && remainderWidth > 0) {
       // Last column
       tileSize.width = remainderWidth;
       }

       if (y + 1 == fullRows && remainderHeight > 0) {
       // Last row
       tileSize.height = remainderHeight;
       }

       CGImageRef tileImage = CGImageCreateWithImageInRect(fullImage, 
                                    (CGRect){{x*size.width, y*size.height}, 
                                      tileSize});

        NSData *imageData = UIImagePNGRepresentation([UIImage imageWithCGImage:tileImage]);
       NSString *path = [NSString stringWithFormat:@"%@/%d.png", 
                    directoryPath, prefix];
  [imageData writeToFile:path atomically:NO];
 }
}    
}


推荐答案

我已经为类似的问题实现了解决方案(区别在于,我没有将它们保存在目录中,仅用于显示目的。),采用不同的方法。

I have implemented solution for the similar problem(the difference is, I was not saving them in directory, those were for display purpose only.), with different approach.

在我的问题中,我有84张250x250尺寸,每张尺寸8KB的图像(我在 scrollView 上添加了它们并且在滚动时我加载它们,有点类似于谷歌地图,但更加流畅)。起初我使用的是与你相同的方法,但性能却有问题。所以,我使用了异步加载概念。我用连接委托编写了一个UIImageView子类,因此UIImageView子类负责加载它的图像。因为加载是异步的所以性能要好得多。

In my problem, I have 84 images of 250x250 dimension with size 8KB each( I added them on scrollView and on scrolling I load them, a bit similar to google maps, but more smooth). At first I was using the same approach as yours, but performance was problem. So, I used asynchornous loading concept. I wrote an UIImageView subclass with connectiond delegates, so the UIImageView subclass was responsible for loading it's image. And as loading is asynchronous so performance is far better.

正如你所说的那样

1)内存消费 - 5张大小为200 KB的图片的内存消耗量是多少?

Ans :5x200KB = 1MB~1.2MB左右(所以你需要那么多的内存来显示,如果你有那么多的内存,那么你不用担心。)..在我的情况下84x8KB = 672~900KB(我正在使用一些额外的事情,比如每个图像视图的活动指示器)。

Ans : 5x200KB = 1MB ~ 1.2MB or so(so you will need that much memory for displaying, if you have that much amount of memory then you should not worry.).. in my case 84x8KB = 672 ~ 900KB(as I was using some additional things like activity indicator for each imageview).

2)如果我必须在图像处理5个不同的URL,我可以多快处理我的应用程序同时?

Ans :当你在viewDidLoad ...或main中加载它时线程然后性能将是一个问题(可能会发生阻塞,因为我不完全确定您是否使用线程)。

Ans : As you are loading it in viewDidLoad ... or in main thread then performance will be an issue(blocking may happen, as I am not completely sure whether you are using threads or not).

快速建议:

1. write an UIImageView subclass which has connection delegate methods.
2. have some method that you can call from outside to message this imageView to start loading.(give the url)
3. do proper deallocation of resources like responseData and connection object, once the downloading is complete.
4. when you move from this view to other view do proper deallocation and removal of all these imageviews.
5. use intruments to look for the allocations by this.

代码:

TileImageView.h

@interface TileImageView : UIImageView 
{
    NSURLConnection *serverConnection;
    BOOL isImageRequested;
    NSMutableData *responseData;

}
-(void) startImageDownloading:(NSString *)pRequestURL
-(void) deallocateResources;
-(BOOL) isImageRequested;
-(void)cancelConnectionRequest;

-(void) addActivityIndicator;
-(void) removeActivityIndicator;
@end

TileImageView.m

@implementation TileImageView


- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) 
    {
        // Initialization code.
        isImageRequested = NO;

    }
    return self;
}

-(BOOL) isImageRequested
{
    return isImageRequested;
}

-(void) startImageDownloading:(NSString *)pRequestURL
{


    if (!isImageRequested)
    {



        NSURL *pServerURL = [[NSURL alloc] initWithString:pRequestURL];
        if (pServerURL != nil) 
        {
            isImageRequested = YES;
            [self addActivityIndicator];
            [self setBackgroundColor:[UIColor lightGrayColor]];
            NSURLRequest *pServerRequest = [[NSURLRequest alloc]initWithURL:pServerURL];
            serverConnection = [[NSURLConnection alloc] initWithRequest:pServerRequest delegate:self];
            if(serverConnection)
            {
                responseData = [[NSMutableData alloc] init];
            }
            [pServerURL release];
            [pServerRequest release];
        }


    }

}

-(void) addActivityIndicator
{
    UIActivityIndicatorView *tempActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
    CGFloat size = self.frame.size.width*0.12;
    [tempActivityIndicator setFrame:CGRectMake(0, 0, size, size)];
    [tempActivityIndicator setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2)];
    [tempActivityIndicator setTag:1000];
    [tempActivityIndicator setHidesWhenStopped:YES];
    [tempActivityIndicator startAnimating];
    [self addSubview:tempActivityIndicator];
    [tempActivityIndicator release];
}

-(void) removeActivityIndicator
{
    UIActivityIndicatorView *tempActivityIndicator = (UIActivityIndicatorView *)[self viewWithTag:1000];
    if (tempActivityIndicator != nil)
    {
        [tempActivityIndicator stopAnimating];
        [tempActivityIndicator removeFromSuperview];
    }
}


-(void)cancelConnectionRequest
{
    if (isImageRequested && serverConnection != nil)
    {
        [serverConnection cancel];
        [self removeActivityIndicator];
        [self deallocateResources];
        isImageRequested = NO;
    }

}



// Name         :   connection: didReceiveAuthenticationChallenge:
// Description  :   NSURLConnectionDelegate method. Method that gets called when server sends an authentication challenge.
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
{
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    {
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    }
    [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

// Name         :   connection: didReceiveResponse:
// Description  :   NSURLConnectionDelegate method. Method that gets called when response for the launched URL is received..
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response 
{
    [responseData setLength:0]; 

}

// Name         :   connection: didReceiveData:
// Description  :   NSURLConnectionDelegate method. Method that gets called when data for the launched URL is received..
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data 
{
    [responseData appendData:data];
}

// Name         :   connection: didFailWithError:
// Description  :   NSURLConnectionDelegate method. Method that gets called when an error for the launched URL is received..
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error 
{
    NSLog(@"Error occured while loading image : %@",error);
    [self removeActivityIndicator];
    [self deallocateResources];


    UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 30)];
    [tempLabel setBackgroundColor:[UIColor clearColor]];
    [tempLabel setFont:[UIFont systemFontOfSize:11.0f]];
    [tempLabel setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/2)];
    [tempLabel setText:@"Image not available."];
    [self addSubview:tempLabel];
    [tempLabel release];

}

// Name         :   connectionDidFinishLoading
// Description  :   NSURLConnectionDelegate method. Method that gets called when connection loading gets finished.
-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    [self removeActivityIndicator];
    UIImage *tempImage = [[UIImage alloc] initWithData:responseData];
    self.image = tempImage;
    [tempImage release];
    [self deallocateResources];
}

-(void) deallocateResources
{

    if (serverConnection != nil) 
    {
        [serverConnection release];
        serverConnection = nil;
    }
    if (responseData != nil)
    {
        [responseData release];
        responseData = nil;
    }
}

- (void)dealloc {
    [super dealloc];
}


@end

所以,如果您使用上面的代码,那么您只需要添加TileImageView的对象,只需调用方法 - (void)startImageDownloading:(NSString *)pRequestURL

So, If you use above code then only thing you have to do is to add the object of TileImageView and just call method -(void) startImageDownloading:(NSString *)pRequestURL.

Please use instruments to track allocations.

更新:

**How do I add TileImageView on scrollView ? :**

//像这样我在2D形状(12 x 7)网格中添加了84个图像...一旦添加了图像,我按照完整的网格大小设置scrollView的contentSize。

//like this I add 84 images in a 2D shape( 12 x 7) grid ... and once Images are added I set scrollView's contentSize as per complete grid size.

TileImageView *tileImageView  = [[TileImageView alloc]initWithFrame:<myFrameAsPerMyNeeds>];
[tileImageView setTag:<this is the identifier I use for recognizing the image>];
[myImageScrollView addSubView:tileImageView];
[tileImageView release];

..稍后在代码中,当用户滚动和其他图像视图进入可见性时。我使用以下代码.. 。

..later in code when user scroll's and other imageviews come in visibility.I use following code...

TileImageView * loadableImageView =(TileImageView *)[myImageScrollView viewWithTag:];
[loadableImageView startImageDownloading:];

TileImageView *loadableImageView = (TileImageView *)[myImageScrollView viewWithTag:]; [loadableImageView startImageDownloading:];

我不需要在 drawRect 中执行任何操作:我没有必要进行自定义绘图。

I do not need to do anything in drawRect: , as I have no need to do custome drawing.

对于图像名称,您可以使用 imageView 中的标记属性,但是如果您需要一些更像字符串的不同名称,然后您可以在 imageView 中为图像名称添加另一个属性,并在添加图像视图时进行设置。为了保存数据,您可以在 didFinishLoading 方法 TileImageView 中下载图像后调用您的方法,您可以使用该方法名称。

For Image names you can use tag property from imageView, but if you need some different name that are more like string then you can put another property in imageView for image name and set it while adding the image view. for saving data you can call your method once the image is downloaded in didFinishLoading method of TileImageView, where you can use that name.

SECODN UPDATE

我如何添加 TileImageView on ScrollView

gridCount = 0;
rows = 7;
columns = 12;
totalGrids = rows*columns;
//*above : all are NSInteger type variable declared at class level

chunkWidth = 250;
chunkHeight = 250;
contentWidth = 0.0;
contentHeight = 0.0;
//*above : all are CGFloat type variable declared at class level
for (int i=0; i<rows; i++) 
{
    contentWidth = 0.0;
    for (int j=0 ; j<columns; j++)
    {

        gridCount++;
        CGRect frame = CGRectMake(contentWidth, contentHeight, chunkWidth, chunkHeight);
        [self addNewImageViewWithTag:gridCount frame:frame];
        contentWidth += chunkWidth;
    }
    contentHeight += chunkHeight;
}

[imageScrollView setContentSize:CGSizeMake(contentWidth, contentHeight)];
[imageScrollView setContentOffset:CGPointMake(0, 0)];
[imageScrollView setUserInteractionEnabled:YES];

并且在 ScrollViewDelegate 方法。

- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (isZoomed)
    {
        xOffset = scrollView.contentOffset.x;
        yOffset = scrollView.contentOffset.y;
        //*above : both are CGFloat type variable declared at class level

        visibleColumn = xOffset/chunkWidth+1;
        visibleRow = yOffset/chunkHeight+1;
        gridNumber = (visibleRow-1)*columns+visibleColumn;
        adjGrid1 = gridNumber+1;
        adjGrid2 = gridNumber+columns;
        adjGrid3 = adjGrid2+1;
        //*above : all are NSInteger type variable declared at class level
        if (gridNumber ==1)
        {
            [self createAndSendScrollRequest:gridNumber];
        }
        if (adjGrid1 > 0 && adjGrid1 <= totalGrids) 
        {
            [self createAndSendScrollRequest:adjGrid1];
        }
        if (adjGrid2 > 0 && adjGrid2 <= totalGrids) 
        {
            [self createAndSendScrollRequest:adjGrid2];
        }
        if (adjGrid3 > 0 && adjGrid3 <= totalGrids) 
        {
            [self createAndSendScrollRequest:adjGrid3];
        }
    }


}

这就是 createAndSendScrollRequest 的实现方式。

- (void) createAndSendScrollRequest:(NSInteger)chunkId
{

    TileImageView *loadingImageView = (TileImageView *)[imageScrollView viewWithTag:chunkId];
    if ([loadingImageView image]==nil) 
    {
        [loadingImageView startImageDownloading:<and here I pass url my url is based on tag so In reality I dont pass anything I just use it from the imageview's tag property>];
    }

}

谢谢,

这篇关于如何处理动态图像的平铺的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆