I am creating thumbnails and showing those thumbnails in gridView(using AQgridView). Thumbnails shown in gridView is too blurry and is not what I want. I am attached my code. Please help.
Here is my code:-
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSMutableArray alloc] init];
self.gridView = [[AQGridView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
self.gridView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.gridView.autoresizesSubviews = YES;
self.gridView.delegate = self;
self.gridView.dataSource = self;
[self.view addSubview:gridView];
[self makeThumbs];
// Do any additional setup after loading the view from its nib.
}
///////////Making thumb from PDF file///////////////
-(void)makeThumbs{
NSString* finalPath = [[NSBundle mainBundle] pathForResource:#"Maths" ofType:#"pdf" inDirectory:nil];
NSURL* pdfFileUrl = [NSURL fileURLWithPath:finalPath];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdfFileUrl);
CGPDFPageRef page;
CGRect aRect = CGRectMake(0, 0, 70, 100); // thumbnail size
UIGraphicsBeginImageContext(aRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
NSUInteger totalNum = CGPDFDocumentGetNumberOfPages(pdf);
for(int i = 0; i < totalNum; i++ ) {
UIImage* thumbnailImage;
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, aRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetGrayFillColor(context, 1.0, 1.0);
CGContextFillRect(context, aRect);
// Grab the first PDF page
page = CGPDFDocumentGetPage(pdf, i + 1);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, aRect, 0, true);
// And apply the transform.
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
// Create the new UIImage from the context
thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
//Use thumbnailImage (e.g. drawing, saving it to a file, etc)
CGContextRestoreGState(context);
[imageArray addObject:thumbnailImage];
}
NSLog(#"image array count is %d",[imageArray count]);
if ([imageArray count]>0) {
[self.gridView reloadData];
}
UIGraphicsEndImageContext();
CGPDFDocumentRelease(pdf);
}
Here image showing blurry thumbnails that are created on iPad Simulator
Try using UIGraphicsBeginImageContextWithOptions passing 0.0 as scale (to use the screen scale).
Related
I tried to capture image from UIWebView using this method but the image contains only visible area of the screen. How do I capture full content of UIWebView including invisible areas, i.e. the entire web page into one single image?
-(UIImage*)captureScreen:(UIView*) viewToCapture{
UIGraphicsBeginImageContext(viewToCapture.bounds.size);
[viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
Check this out Rendering a UIWebView into an ImageContext
or just use this :) :
(UIImage*) imageFromWebview:(UIWebView*) webview{
//store the original framesize to put it back after the snapshot
CGRect originalFrame = webview.frame;
//get the width and height of webpage using js (you might need to use another call, this doesn't work always)
int webViewHeight = [[webview stringByEvaluatingJavaScriptFromString:#"document.body.scrollHeight;"] integerValue];
int webViewWidth = [[webview stringByEvaluatingJavaScriptFromString:#"document.body.scrollWidth;"] integerValue];
//set the webview's frames to match the size of the page
[webview setFrame:CGRectMake(0, 0, webViewWidth, webViewHeight)];
//make the snapshot
UIGraphicsBeginImageContextWithOptions(webview.frame.size, false, 0.0);
[webview.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//set the webview's frame to the original size
[webview setFrame:originalFrame];
//and VOILA :)
return image;
}
EDIT (from a comment by Vad)
Solution was to call
webView.scalesPageToFit = YES;
in the initialization, and
[webView sizeToFit]
when the page did finish loading.
You are currently capturing only the visible part because you are limiting the image context to what's visible. You should limit it to what's available.
UIView has a scrollView property that has contentSize, telling you what is the size of the web view inside the scroll view. You can use that size to set your image context like this:
-(UIImage*)captureScreen:(UIView*) viewToCapture{
CGSize overallSize = overallSize;
UIGraphicsBeginImageContext(viewToCapture.scrollView.contentSize);
// Save the current bounds
CGRect tmp = viewToCapture.bounds;
viewToCapture.bounds = CGRectMake(0, 0, overallSize.width, overallSize.height);
// Wait for the view to finish loading.
// This is not very nice, but it should work. A better approach would be
// to use a delegate, and run the capturing on the did finish load event.
while (viewToCapture.loading) {
[NSThread sleepForTimeInterval:0.1];
}
[viewToCapture.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Restore the bounds
viewToCapture.bounds = tmp;
return viewImage;
}
EDIT : New answer from me with tested code.
Add below method to capture UIWebViewinto UIImage. It will also capture unvisible area as well.
- (UIImage*)webviewToImage:(UIWebView*)webView
{
int currentWebViewHeight = webView.scrollView.contentSize.height;
int scrollByY = webView.frame.size.height;
[webView.scrollView setContentOffset:CGPointMake(0, 0)];
NSMutableArray* images = [[NSMutableArray alloc] init];
CGRect screenRect = webView.frame;
int pages = currentWebViewHeight/scrollByY;
if (currentWebViewHeight%scrollByY > 0) {
pages ++;
}
for (int i = 0; i< pages; i++)
{
if (i == pages-1) {
if (pages>1)
screenRect.size.height = currentWebViewHeight - scrollByY;
}
if (IS_RETINA)
UIGraphicsBeginImageContextWithOptions(screenRect.size, NO, 0);
else
UIGraphicsBeginImageContext( screenRect.size );
if ([webView.layer respondsToSelector:#selector(setContentsScale:)]) {
webView.layer.contentsScale = [[UIScreen mainScreen] scale];
}
//UIGraphicsBeginImageContext(screenRect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);
[webView.layer renderInContext:ctx];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (i == 0)
{
scrollByY = webView.frame.size.height;
}
else
{
scrollByY += webView.frame.size.height;
}
[webView.scrollView setContentOffset:CGPointMake(0, scrollByY)];
[images addObject:newImage];
}
[webView.scrollView setContentOffset:CGPointMake(0, 0)];
UIImage *resultImage;
if(images.count > 1) {
//join all images together..
CGSize size;
for(int i=0;i<images.count;i++) {
size.width = MAX(size.width, ((UIImage*)[images objectAtIndex:i]).size.width );
size.height += ((UIImage*)[images objectAtIndex:i]).size.height;
}
if (IS_RETINA)
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
else
UIGraphicsBeginImageContext(size);
if ([webView.layer respondsToSelector:#selector(setContentsScale:)]) {
webView.layer.contentsScale = [[UIScreen mainScreen] scale];
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] set];
CGContextFillRect(ctx, screenRect);
int y=0;
for(int i=0;i<images.count;i++) {
UIImage* img = [images objectAtIndex:i];
[img drawAtPoint:CGPointMake(0,y)];
y += img.size.height;
}
resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} else {
resultImage = [images objectAtIndex:0];
}
[images removeAllObjects];
return resultImage;
}
Also add these macro for checking if iOS is retina display
#define IS_RETINA ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))
I am trying to render pdf pages on CGContext by converting them into images and then rendering them in tiled layer. The application runs well on simulator but crashes on device for particular pdf page. On running the app on istruments, it seems to consume a lot of memory.
There have been many questions on this before on this platform and have tried all the solution but the problem persists. Please find the code below. It always crashes on calling CGContextDrawPDFPage
CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
pdfScale = self.frame.size.width/pageRect.size.width;
originalPdfScale = 2 * pdfScale;
//pdfScale = 1.39444;
pageRect.size = CGSizeMake(pageRect.size.width*pdfScale, pageRect.size.height*pdfScale);
//---Create a low res image representation of the PDF page to display before the TiledPDFView renders its content---
UIGraphicsBeginImageContext(pageRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//---First fill the background with white---
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,pageRect);
NSLog(#"context = %# :::: pdfScale = %f ::::: page=%# ", context, pdfScale, page );
CGContextSaveGState(context);
//---Flip the context so that the PDF page is rendered right side up---
CGContextTranslateCTM(context, 0.0, pageRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
//---Scale the context so that the PDF page is rendered at the correct size for the zoom level---
CGContextScaleCTM(context, pdfScale,pdfScale);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (backgroundImageView) {
[backgroundImageView removeFromSuperview];
[backgroundImageView release];
backgroundImageView = nil;
}
backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
backgroundImageView.frame = pageRect;
backgroundImageView.userInteractionEnabled =YES;
backgroundImageView.contentMode = UIViewContentModeScaleAspectFit;
[self addSubview:backgroundImageView];
[self sendSubviewToBack:backgroundImageView];
// Create the TiledPDFView based on the size of the PDF page and scale it to fit the view.
if (pdfView) {
[pdfView removeFromSuperview];
[pdfView release];
pdfView = nil;
}
pdfView = [[ProM_TiledPDFView alloc] initWithFrame:pageRect andScale:pdfScale];
[pdfView setPage:page];
[self addSubview:pdfView];
[backgroundImage release];
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.
I'm not sure I did understand that very well in the Apple doc. I'm considering using CATiledLayer to display a JPEG image. However, I only have an entire JPEG file at my disposal and no small tiles. Is it still possible to use a CATiledLayer and let it "tile" the JPEG?
Thanks!
No. You will have to tile them yourself, unfortunately. The WWDC 2010 videos on Core Animation discuss how to do this and in their sample code, they demonstrate how to use a CATileLayer when the tiles already exist.
Correction
I meant to say watch the Scroll View session. It's session 104 "Designing Apps with Scroll Views"
You may use This for making CATileLayer with image and Zoom functionality in scrollview.
- (void)viewDidLoad
{
[super viewDidLoad];
// You do not need to and must not autorelease [NSBundle mainBundle]
NSString *path = [[NSBundle mainBundle] pathForResource:#"img" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
// CGRect pageRect = CGRectMake(0, 0, 1600, 2400);
CGRect pageRect = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self;
tiledLayer.tileSize = CGSizeMake(256.0, 256.0);
tiledLayer.levelsOfDetail = 5; // was 1000, which makes no sense. Each level of detail is a power of 2.
tiledLayer.levelsOfDetailBias = 5; // was 1000, which also makes no sense.
// Do not change the tiledLayer.frame.
tiledLayer.bounds = pageRect; // I think you meant bounds instead of frame.
// Flip the image vertically.
tiledLayer.transform = CATransform3DMakeScale(1.0f, -1.0F, 1.0f);
myContentView = [[UIView alloc] initWithFrame:CGRectMake(500,400,image.size.width,image.size.height)];
[myContentView.layer addSublayer:tiledLayer];
// UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(10, 10, 300, 400)];
scrollView.backgroundColor = [UIColor whiteColor];
// [scrollView setContentSize:CGSizeMake(image.size.width,image.size.height)];
[scrollView setContentSize:image.size];
scrollView.maximumZoomScale = 32; // = 2 ^ 5
scrollView.delegate = self;
// scrollView.contentSize = pageRect.size;
[scrollView addSubview:myContentView];
[self.view addSubview:scrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return myContentView;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"img" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
CGRect imageRect = CGRectMake (0.0, 0.0, image.size.width, image.size.height);
CGContextDrawImage (context, imageRect, [image CGImage]);
}
The easier way is to just downscale the image until it fits (I think devices support up to 2044x2044 or so). You can create subimages of a CGImage with CGImageCreateWithImageInRect()
Here is the problem... I am using CA Tiled Layer to display a large jpg. The view loads okay, and when I go to scroll around, it works fine. However, as soon as I zoom in or out once, it scrolls to the top left (to the anchor point) and will not scroll at all. The zooming works fine, but I just cannot scroll.
Here is my code:
#import <QuartzCore/QuartzCore.h>
#import "PracticeViewController.h"
#implementation practiceViewController
//#synthesize image;
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"H-5" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
CGRect pageRect = CGRectMake(0, 0, image.size.width, image.size.height);
CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.anchorPoint = CGPointMake(0.0f, 1.0f);
tiledLayer.delegate = self;
tiledLayer.tileSize = CGSizeMake(1000, 1000);
tiledLayer.levelsOfDetail = 6;
tiledLayer.levelsOfDetailBias = 0;
tiledLayer.bounds = pageRect;
tiledLayer.transform = CATransform3DMakeScale(1.0f, -1.0f, 0.3f);
myContentView = [[UIView alloc] initWithFrame:self.view.bounds];
[myContentView.layer addSublayer:tiledLayer];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.delegate = self;
scrollView.contentSize = pageRect.size;
scrollView.minimumZoomScale = .2;
scrollView.maximumZoomScale = 1;
[scrollView addSubview:myContentView];
[self.view addSubview:scrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return myContentView;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"H-5" ofType:#"jpg"];
NSData *data = [NSData dataWithContentsOfFile:path];
image = [UIImage imageWithData:data];
CGRect imageRect = CGRectMake (0.0, 0.0, image.size.width, image.size.height);
CGContextDrawImage (ctx, imageRect, [image CGImage]);
}
#end
See the above listed question