I am trying to create thumb images for every pdf page in PDF document and place it in a UISCrollVIew. I have succeeded in this, but scrolling is not so smooth as I want when it's too fast.
And I want to optimize thumb images creating for Pdf page.
I want to create one CGContextRef and reset its content after CGContextDrawPDFPage, as a consequence I wouldn't have to create a context each time and perform some other calculation, which takes a lot of resources.
Is it possible to reset CGContextRef content after CGContextDrawPDFPage? CGContextRestoreGState and CGContextSaveGState seems to doesn't help in this situation.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
GCPdfSource *pdfSource = [GCPdfSource sharedInstance];
for (int i = pageRange.location; i <= pageRange.length; i++) {
UIView *thumbPdfView = [scrollView viewWithTag:i+1];
if (thumbPdfView == nil) {
CGPDFPageRef pdfPage = [pdfSource pageAt:i + 1];
float xPosition = THUMB_H_PADDING + THUMB_H_PADDING * i + THUMB_WIDTH * i;
CGRect frame = CGRectMake(xPosition, THUMB_H_PADDING, THUMB_WIDTH, THUMB_HEIGHT);
thumbPdfView = [[UIView alloc] initWithFrame:frame];
thumbPdfView.opaque = YES;
thumbPdfView.backgroundColor = [UIColor whiteColor];
[thumbPdfView setTag:i+1];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL,
frame.size.width,
frame.size.height,
8, /* bits per component*/
frame.size.width * 4, /* bytes per row */
colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextClipToRect(context, CGRectMake(0, 0, frame.size.width,frame.size.height));
CGRect pdfPageRect = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
CGRect contextRect = CGContextGetClipBoundingBox(context);
CGAffineTransform transform = aspectFit(pdfPageRect, contextRect);
CGContextConcatCTM(context, transform);
CGContextDrawPDFPage(context, pdfPage);
CGImageRef image = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *uiImage = [[UIImage alloc]initWithCGImage:image];
CGImageRelease(image);
UIImageView *imageVIew = [[UIImageView alloc]initWithImage:uiImage];
[uiImage release];
[thumbPdfView addSubview:imageVIew];
[imageVIew release];
[scrollView addSubview:thumbPdfView];
[thumbPdfView release];
}
}
[pool release];//release
and aspectFit function...
CGAffineTransform aspectFit(CGRect innerRect, CGRect outerRect) {
CGFloat scaleFactor = MIN(outerRect.size.width/innerRect.size.width, outerRect.size.height/innerRect.size.height);
CGAffineTransform scale = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
CGRect scaledInnerRect = CGRectApplyAffineTransform(innerRect, scale);
CGAffineTransform translation =
CGAffineTransformMakeTranslation((outerRect.size.width - scaledInnerRect.size.width) / 2 - scaledInnerRect.origin.x,
(outerRect.size.height - scaledInnerRect.size.height) / 2 - scaledInnerRect.origin.y);
return CGAffineTransformConcat(scale, translation);
}
Try CGContextClearRect(context, contextBounds).
CGContextSaveGState and CGContextRestoreGState do not have any effect on the content of a context. They push and pop changes made to state aspects of the context like the current fill color.
Related
Currently I'm applying a corner radius and shadow to multiple UIView's in a scrollview. I've noticed that adding a corner radius and shadow makes the scrollview lag like crazy whenever I scroll. How can I apply these affects without having my performance suffer?
Try by setting also the shadowPath of the layer:
view.layer.cornerRadius=6.0f;
view.layer.borderWidth=2.0f;
view.layer.borderColor=[UIColor grayColor].CGColor;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOpacity = 0.3f;
view.layer.shadowOffset = CGSizeMake(0, 0.0f);
view.layer.masksToBounds = NO;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.shadowPath = path.CGPath;
+(UIImage *)makeRoundCornerImage : (UIImage*) img : (int) cornerWidth : (int) cornerHeight
{
UIImage * newImage = nil;
if( nil != img)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int w = img.size.width;
int h = img.size.height;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGContextBeginPath(context);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
addRoundedRectToPath(context, rect, cornerWidth, cornerHeight);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
[img release];
newImage = [[UIImage imageWithCGImage:imageMasked] retain];
CGImageRelease(imageMasked);
[pool release];
}
return newImage;
}
// With the help of This method we can create the round corner of any PNG file
UIImage *imageFromFile = [UIImage imageNamed:#"FilterFields.png"];
imageFromFile = [ImageManipulator
makeRoundCornerImage:imageFromFile : 10 : 10];
[SmallTable setImage:imageFromFile];
I have a photo app where you can add stickers in one section. When you're finished I want to save the image. Here is the code that I have to do that.
if(UIGraphicsBeginImageContextWithOptions != NULL)
{
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 2.5);
} else {
UIGraphicsBeginImageContext(self.view.frame.size);
}
CGContextRef contextNew=UIGraphicsGetCurrentContext();
[self.view.layer renderInContext:contextNew];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Now the image that gets saved is the full screen of the image, which is fine, but now I need to crop the image and I don't know how. You can see the image at the link below:
http://dl.dropbox.com/u/19130454/Photo%202012-04-09%201%2036%2018%20PM.png
I need to crop:
91px from the left and right
220px from the bottom
Any help would be greatly appreciated. If I haven't explained things clearly, please let me know and I'll do my best to re-explain.
How about something like this
CGRect clippedRect = CGRectMake(self.view.frame.origin.x+91, self.view.frame.origin.y, self.view.frame.size.width-91*2, self.view.frame.size.height-220);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
Following code may help you.
You should get the correct cropFrame fist by below method
-(CGRect)cropRectForFrame:(CGRect)frame
{
// NSAssert(self.contentMode == UIViewContentModeScaleAspectFit, #"content mode must be aspect fit");
CGFloat widthScale = imageview.superview.bounds.size.width / imageview.image.size.width;
CGFloat heightScale = imageview.superview.bounds.size.height / imageview.image.size.height;
float x, y, w, h, offset;
if (widthScale<heightScale) {
offset = (imageview.superview.bounds.size.height - (imageview.image.size.height*widthScale))/2;
x = frame.origin.x / widthScale;
y = (frame.origin.y-offset) / widthScale;
w = frame.size.width / widthScale;
h = frame.size.height / widthScale;
} else {
offset = (imageview.superview.bounds.size.width - (imageview.image.size.width*heightScale))/2;
x = (frame.origin.x-offset) / heightScale;
y = frame.origin.y / heightScale;
w = frame.size.width / heightScale;
h = frame.size.height / heightScale;
}
return CGRectMake(x, y, w, h);
}
Then you need to call this method to get cropped image
- (UIImage *)imageByCropping:(UIImage *)image toRect:(CGRect)rect
{
// you need to update scaling factor value if deice is not retina display
UIGraphicsBeginImageContextWithOptions(rect.size,
/* your view opaque */ NO,
/* scaling factor */ 2.0);
// stick to methods on UIImage so that orientation etc. are automatically
// dealt with for us
[image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
- (UIImage*)imageByCropping:(CGRect)rect
{
//create a context to do our clipping in
UIGraphicsBeginImageContext(rect.size);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
//create a rect with the size we want to crop the image to
//the X and Y here are zero so we start at the beginning of our
//newly created context
CGRect clippedRect = CGRectMake(0, 0, rect.size.width, rect.size.height);
CGContextClipToRect( currentContext, clippedRect);
//create a rect equivalent to the full size of the image
//offset the rect by the X and Y we want to start the crop
//from in order to cut off anything before them
CGRect drawRect = CGRectMake(rect.origin.x * -1,
rect.origin.y * -1,
self.size.width,
self.size.height);
//draw the image to our clipped context using our offset rect
// CGContextDrawImage(currentContext, drawRect, self.CGImage);
[self drawInRect:drawRect]; // This will fix getting inverted image from context.
//pull the image from our cropped context
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
//pop the context to get back to the default
UIGraphicsEndImageContext();
//Note: this is autoreleased
return cropped;
}
Refer the below link for crop image
https://github.com/myang-git/iOS-Image-Crop-View
** How to Use **
Very easy! It is created to be a drop-in component, so no static library, no extra dependencies. Just copy ImageCropView.h and ImageCropView.m to your project, and implement ImageCropViewControllerDelegate protocol.
Use it like UIImagePicker:
- (void)cropImage:(UIImage *)image{
ImageCropViewController *controller = [[ImageCropViewController alloc] initWithImage:image];
controller.delegate = self;
[[self navigationController] pushViewController:controller animated:YES];
}
- (void)ImageCropViewController:(ImageCropViewController *)controller didFinishCroppingImage:(UIImage *)croppedImage{
image = croppedImage;
imageView.image = croppedImage;
[[self navigationController] popViewControllerAnimated:YES];
}
- (void)ImageCropViewControllerDidCancel:(ImageCropViewController *)controller{
imageView.image = image;
[[self navigationController] popViewControllerAnimated:YES];
}
I'm developing an app and i'm implementing an application to manage orders some orders of a client.
In this view i have implemented a facebook style menu ( the new one that appears by shifting the whole window right) and i'have added a greyscale effect to the main view when it's shifted to right.
I've accomplished it by creating a UIImage of the current screen and by adding it over the real view and animating it's alpha from 1 to 0
Here's the code i've used to
-(void)toggleMenu {
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
if (![menu isOpened]){
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage *blackAndWhiteImage = [UIImage getBlackAndWhiteVersionOfImage:viewImage];
overlayImage = [[UIImageView alloc]initWithImage:blackAndWhiteImage];
overlayImage.alpha = 0.1;
overlayImage.userInteractionEnabled=NO;
[self.view addSubview:overlayImage];
}
[UIView animateWithDuration:0.3 animations:^{
CGRect newRect = [self tabBarController].view.frame;
if (![menu isOpened]){
newRect.origin.x += 150;
[menu setOpened:YES];
overlayImage.alpha = 1.0;
} else {
newRect.origin.x -= 150;
[menu setOpened:NO];
overlayImage.alpha = 0;
}
[self tabBarController].view.frame = newRect;
} completion:^(BOOL finished){
if(![menu isOpened]){
[overlayImage removeFromSuperview];
overlayImage = nil;
}
}];
}
The problem is that i'm having issues with performances during animation ( little with iPhone 4 i'm trying an 3gs in next hours... )
Does anyone have any suggestions on what to do to get better performances ?
Regards
+ (UIImage *)getBlackAndWhiteVersionOfImage:(UIImage *)anImage {
UIImage *newImage;
if (anImage) {
CGColorSpaceRef colorSapce = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(nil, anImage.size.width * anImage.scale, anImage.size.height * anImage.scale, 8, anImage.size.width * anImage.scale, colorSapce, kCGImageAlphaNone);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetShouldAntialias(context, NO);
CGContextDrawImage(context, CGRectMake(0, 0, anImage.size.width, anImage.size.height), [anImage CGImage]);
CGImageRef bwImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSapce);
UIImage *resultImage = [UIImage imageWithCGImage:bwImage];
CGImageRelease(bwImage);
UIGraphicsBeginImageContextWithOptions(anImage.size, NO, anImage.scale);
[resultImage drawInRect:CGRectMake(0.0, 0.0, anImage.size.width, anImage.size.height)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return newImage;
}
Making an image of the screen is costly. I'm not clear exactly what effect you want but I would overlay another view instead or animate a move of the main view instead.
For example, you can overlay a UIView that has a black background but an alpha of 0.1 to grey out a region.
I have a problem with my UIImageView that doesn't update.
So, it's like this:
I have a UIScrollView that contains an UIImageView (called imageView).
Now, imageView , should contain more UIImageViews. Those UIImageViews I add from code but they do not appear.
This is the code:
for(i = 0 ; i < NrOfTilesPerHeight ; i++)
for(j = 0 ; j < NrOfTilesPerWidth ; j++)
{
imageRect = CGRectMake(j*TILE_WIDTH,i*TILE_HEIGHT,TILE_WIDTH, TILE_HEIGHT);
image = CGImageCreateWithImageInRect(aux.CGImage, imageRect);
if(!data[i][j])
NSLog(#"data[%d][%d] is nil",i,j);
context = CGBitmapContextCreate (data[i][j], TILE_WIDTH, TILE_HEIGHT,
bitsPerComponent, bitmapBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
if (context == NULL)
{
free (data);
printf ("Context not created!");
CGColorSpaceRelease( colorSpace );
}
CGContextDrawImage(context, CGRectMake(0, 0, TILE_WIDTH, TILE_HEIGHT), image);
data[i][j] = CGBitmapContextGetData (context);
memcpy(originalData[i][j],data[i][j],TILE_WIDTH*TILE_HEIGHT*numberOfCompponents);
CGContextFlush(context);
CGImageRelease(image);
UIImageView *imgView = [[UIImageView alloc] init];
[imgView setTag:i*10+j];
CGRect frame = imgView.frame;
frame.origin.x = j * (TILE_WIDTH+5) * initialScale;
frame.origin.y = i * (TILE_HEIGHT+5) * initialScale;
frame.size.width *= initialScale;
frame.size.height *= initialScale;
[imgView setFrame:frame];
[imageView addSubview:imgView];
[self updateTileAtLine:i andRow:j];
[imgView release];
CGDataProviderRelease(dataProvider);
CGImageRelease(cgImage);
}
- (void) updateTileAtLine: (int) i andRow: (int) j
{
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, data[i][j], bitmapByteCount, NULL);
CGImageRef cgImage = CGImageCreate(TILE_WIDTH, TILE_HEIGHT, bitsPerComponent,
bitsPerPixel, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big, dataProvider, NULL, false, kCGRenderingIntentDefault);
UIImage *myImg = [UIImage imageWithCGImage:cgImage];
UIImageView *auxImageView = (UIImageView*) [imageView viewWithTag:(i*10+j)];
[auxImageView setImage:myImg];
CGDataProviderRelease(dataProvider);
CGImageRelease(cgImage);
}
Now...this doesn't crashes...so everything is non-nil and ok.
If instead of using viewWithTag , I alloc init a new UIImageView and add it to imageView, it will appear. But I don't want to do another copy of the view since this updateTile method will be called quite often.
My question is: Why doesn't the auxImageView appear? It very much should.
Thank you.
Regards,
George
Try this
for(UIView *view in [imageView subviews]) {
if(view.tag == i*10+j) {
UIImageView *auxImageView = (UIImageView*) view;
[auxImageView setImage:myImg];
}
}
Currently I am using UIView instead of UIImageview due to Memory consumption in large scale images. following is same code I am using.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
[myImage drawInRect:rect];
}
-(void) SetImage:(UIImage*) aImage
{
if(!aImage)
return;
if(myImage)
{
[myImage release];
myImage = nil;
}
myImage = [[[UIImage alloc]initWithCGImage:aImage.CGImage] retain];
[self setNeedsDisplay];
}
This is causing now memory leak of 8 MB ( checked with Instrument ) every time when Update and set the same image again. if I comment
[self setNeedsDisplay];
There is no leak. can anyone help me here if I am doing something wrong. OR can anyone help me to Subclass UIImageview to handle large scale image.
// Calling functions
-(void) FitToCardStart
{
UIImage* temp = ScaleImage([iImageBgView GetImage]);
[iImageBgView SetImage:temp];
[temp release];
temp = nil;
}
// ScaleImage
UIImage* ScaleImage(UIImage* image)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
int kMaxResolution = 1800;
CGImageRef imgRef = image.CGImage;
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGAffineTransform transform = CGAffineTransformIdentity;
CGRect bounds = CGRectMake(0, 0, width, height);
if (width < kMaxResolution || height < kMaxResolution)
{
CGFloat ratio = width/height;
if (ratio > 1)
{
bounds.size.width = kMaxResolution;
bounds.size.height = bounds.size.width / ratio;
}
else
{
bounds.size.height = kMaxResolution;
bounds.size.width = bounds.size.height * ratio;
}
}
CGFloat scaleRatio = bounds.size.width / width;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
UIImageOrientation orient = image.imageOrientation;
switch(orient)
{
case UIImageOrientationUp: //default
transform = CGAffineTransformIdentity;
break;
default:
[NSException raise:NSInternalInconsistencyException format:#"Invalid image orientation"];
}
UIGraphicsBeginImageContext(bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, scaleRatio, -scaleRatio);
CGContextTranslateCTM(context, 0, -height);
CGContextConcatCTM(context, transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIImage* temp = [[[UIImage alloc] initWithCGImage:imageCopy.CGImage] retain];
CGImageRelease(imgRef);
CGContextRelease(context);
[pool release];
return temp;
}
Thanks,
Sagar
Your problem is this line:
myImage = [[[UIImage alloc]initWithCGImage:aImage.CGImage] retain];
alloc already gives you a retain count of 1, with the additional retain you end up with a retain count of 2 which is too high. Remove the retain and everything will be fine.
myImage = [[[UIImage alloc]initWithCGImage:aImage.CGImage] retain];
There's redundant retain in this line - as you're allocating new UIIMage object (use +alloc) method you don't need to extra retain it.
Edit: ScaleImage method has the same problem with redundant retain:
// remove extra retain here
UIImage* temp = [[[UIImage alloc] initWithCGImage:imageCopy.CGImage] retain];
// should be
UIImage* temp = [[UIImage alloc] initWithCGImage:imageCopy.CGImage];
And a code-style comment - it is better to indicate in your method names what memory management behavior required for returned objects - as image returned by your method needs to be released method name should contain something from "new", "alloc", "copy", "create"...
I suggest not creating a new image, but just keeping the aImage instance.
myImage = [aImage retain];
I you absolutely must make it a new instance, you are doing it in a very roundabout way.
Copying would be a much better alternative.
myImage = [aImage copy];