Is there a way to render a PDF page into bitmap context without losing quality?
Here is my code:
-(UIImage *)imageForPage:(int)pageNumber sized:(CGSize)size{
CGPDFDocumentRef _document = [self CreatePDFDocumentRef:filePath];
CGPDFPageRef page = CGPDFDocumentGetPage (_document, pageNumber);
CGRect pageRect = CGPDFPageGetBoxRect(page,kCGPDFTrimBox);
CGSize pageSize = pageRect.size;
pageSize = MEDSizeScaleAspectFit(pageSize, size);
UIGraphicsBeginImageContextWithOptions(pageSize, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
CGContextTranslateCTM(context, 0.0, pageSize.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSaveGState(context);
CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFTrimBox, CGRectMake(0, 0, pageSize.width, pageSize.height), 0, true);
CGContextConcatCTM(context, pdfTransform);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGPDFDocumentRelease (_document);
return resultingImage;
}
CGSize MEDSizeScaleAspectFit(CGSize size, CGSize maxSize) {
CGFloat originalAspectRatio = size.width / size.height;
CGFloat maxAspectRatio = maxSize.width / maxSize.height;
CGSize newSize = maxSize;
// The largest dimension will be the `maxSize`, and then we need to scale
// the other dimension down relative to it, while maintaining the aspect
// ratio.
if (originalAspectRatio > maxAspectRatio) {
newSize.height = maxSize.width / originalAspectRatio;
} else {
newSize.width = maxSize.height * originalAspectRatio;
}
return newSize;
}
Make sure the image you are creating is as close to equal to the size it will be displayed. That will create an image that is the most visually accurate to display.
Related
I'm trying to get cropped image from photo. I have the following code that crop image that was got from ios face detection sdk:
- (UIImage *)scaleAndRotateImage:(CIFeature *)ciFeature image:(UIImage *)image {
static int kMaxResolution = 640;
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;
UIImageOrientation orient = image.imageOrientation;
switch(orient) {
case UIImageOrientationUp:
transform = CGAffineTransformIdentity;
break;
default:
[NSException raise:NSInternalInconsistencyException
format:#"Invalid image orientation"];
}
UIGraphicsBeginImageContext(bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
CGContextScaleCTM(context, -scaleRatio, scaleRatio);
CGContextTranslateCTM(context, -height, 0);
} else {
CGContextScaleCTM(context, scaleRatio, -scaleRatio);
CGContextTranslateCTM(context, 0, -height);
}
CGContextConcatCTM(context, transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImage;
}
But how can I crop only face from the image (Like face image in box) because for now it changes original resolution of image?
If you want to crop your image with more conveniently and more user-defined, I recommend you to use the RSKImageCropper, its github location is here:https://github.com/ruslanskorb/RSKImageCropper
Because you want to crop the face's position, so the system's default method is not satisfy your requirement, you can learn from here:https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/CameraAndPhotoLib_TopicsForIOS/Articles/PickinganItemfromthePhotoLibrary.html#//apple_ref/doc/uid/TP40010408-SW1
The Core Graphic's crop image method:CGImageCreateWithImageInRect, is not easy to confirm the rect you want, because you for get the position of the face you should do a lot of work, the RSKImageCropper may be your convenient choise.
I have a PNG (52x52) image file,if I show it in a UIImageView (16x16) , it is showed good.
But if I try to use CGContextDrawImage to draw, the quality is very bad.
Please see below detail code:
resizeImage is used to resize the image (copy from apple site).
drawStonesPng is do draw the image and called form CALayer:: drawInContext
- (UIImage*)resizeImage:(UIImage*)image toWidth:(NSInteger)width height:(NSInteger)height
{
// Create a graphics context with the target size
// On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration
// On iOS prior to 4, fall back to use UIGraphicsBeginImageContext
CGSize size = CGSizeMake(width, height);
if (NULL != UIGraphicsBeginImageContextWithOptions)
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
else
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
// Flip the context because UIKit coordinate system is upside down to Quartz coordinate system
CGContextTranslateCTM(context, 0.0, height);
CGContextScaleCTM(context, 1.0, -1.0);
// Draw the original image to the context
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextDrawImage(context, CGRectMake(0.0, 0.0, width, height), image.CGImage);
// Retrieve the UIImage from the current context
UIImage *imageOut = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imageOut;
}
-(void)drawStonesPng:(CGContextRef)ctx{
_cellWidth=16;
float x1,y1;
float f1=0.48;
// self.contentsScale = [UIScreen mainScreen].scale;
UIImage* resizedImageBlack = [self resizeImage:[UIImage imageNamed:#"blackstone52"] toWidth:_cellWidth*f1*2 height:_cellWidth*f1*2];
UIImage* resizedImageBlackShadow = [self resizeImage:[UIImage imageNamed:#"blackshadow"] toWidth:_cellWidth*f1*2 height:_cellWidth*f1*2];
CGImageRef imgBlack = [resizedImageBlack CGImage];
CGImageRef imgBlackShadow = [resizedImageBlackShadow CGImage];
CGFloat width = CGImageGetWidth(imgBlack), height = CGImageGetHeight(imgBlack);
// CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
// CGContextSetShouldAntialias(ctx, true);
for(int y=0; y<_boardSize; y++) {
for(int x=0; x<_boardSize; x++) {
STONE_T stone = [MyGoController getStoneType:x y:y];
if(STONE_INVALID==stone){
CGContextClosePath(ctx);
return;
}
if(stone==STONE_BLACK){
x1=(x+1)*_cellWidth-_cellWidth*f1;
y1=(y+1)*_cellWidth-_cellWidth*f1;
// CGFloat scale = _cellWidth*0.9/width;
// NSLog(#"Scale: %f\nWidth: %f\nHeight: %f", scale, width, height);
// CGContextTranslateCTM(ctx, 0, height / scale);
// CGContextScaleCTM(ctx, 1.0, -1.0);
CGFloat scale = [[UIScreen mainScreen] scale];
self.contentsScale =[[UIScreen mainScreen] scale];
NSLog(#"Scale: %f\nWidth: %f\nHeight: %f", scale, width, height);
CGContextTranslateCTM(ctx, 0, width / scale);
CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx, CGRectMake(x1+1, y1+1, width/scale,width/scale), imgBlackShadow);
CGContextDrawImage(ctx, CGRectMake(x1, y1, width/scale,width/scale), imgBlack);
}
}
}
resizedImageBlack = [self resizeImage:[UIImage imageNamed:#"whitestone52"] toWidth:_cellWidth*f1*2 height:_cellWidth*f1*2];
resizedImageBlackShadow = [self resizeImage:[UIImage imageNamed:#"whiteshadow"] toWidth:_cellWidth*f1*2 height:_cellWidth*f1*2];
imgBlack = [resizedImageBlack CGImage];
imgBlackShadow = [resizedImageBlackShadow CGImage];
//draw white stones
for(int y=0; y<_boardSize; y++) {
for(int x=0; x<_boardSize; x++) {
STONE_T stone = [MyGoController getStoneType:x y:y];
if(stone==STONE_WHITE){
x1=(x+1)*_cellWidth-_cellWidth*f1;
y1=(y+1)*_cellWidth-_cellWidth*f1;
// CGFloat scale = _cellWidth*0.9/width;
// NSLog(#"Scale: %f\nWidth: %f\nHeight: %f", scale, width, height);
// CGContextTranslateCTM(ctx, 0, height / scale);
// CGContextScaleCTM(ctx, 1.0, -1.0);
CGContextDrawImage(ctx, CGRectMake(x1+1, y1+1, _cellWidth*f1*2,_cellWidth*f1*2), imgBlackShadow);
CGContextDrawImage(ctx, CGRectMake(x1, y1, _cellWidth*f1*2,_cellWidth*f1*2), imgBlack);
}
}
}
}
You need to set the content scale of the layer you're using :
CALayer theLayer = ....;
theLayer.contentsScale = [UIScreen mainScreen].scale
How to draw smaller image than size of context without using CGAffineTransform?
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
// center image
CGContextTranslateCTM(context, placement.origin.x, placement.origin.y);
[imageView.layer renderInContext:context];
The problem is that if imageView.image.size is bigger than size it will be drawn centered, but cut at borders (bigger image than drawing area). I want the image to fit, so would like to decrease size of imageView.image.size. Is there any way to achieve this using renderInContext?
Via [imageView.layer renderInContext:context]; you can't change the size (or i didn't find a way). The only option to do so, is to create now image with drawImage (on requested size), allocate new UIImageView and draw its layer.
UIImage *resizedImage = [self createNewImageFrom:currentImage withSize:size];
UIImageView *imageView = [[UIImageView alloc] initWithImage:resizedImage];
// ...
[imageView.layer renderInContext:context];
- (UIImage*)createNewImageFrom:(UIImage*)image withSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0f, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.width, size.height), image.CGImage);
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
Why do you want to do this without transforming the context? It's the exact right tool for the job!
The simplest way here would be to call CGContextScaleCTM to apply a scale factor to the context. That scale factor will effect all drawing to the context, including the call to -renderIntoContext:.
this is version for scaled size, just replace scale with size.width and size.height for any target size.
for UIView:
-(UIImage*)convertViewToImage:(UIView*)v withScale:(float)scale{
//create bitmap context with scaled size
CGSize s = v.bounds.size;
s.width *= scale;
s.height *= scale;
UIGraphicsBeginImageContextWithOptions(s, NO, [UIScreen mainScreen].scale);
//scale CTM to render and resotre CTM
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextScaleCTM(context, scale, scale);
[v.layer renderInContext: context];
CGContextRestoreGState(context);
UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
for UIScrollView
-(UIImage*)convertScrollContentToImage:(UIScrollView*)sv withScale:(float)scale{
//create bitmap context with scaled size
CGSize s = sv.contentSize;
s.width *= scale;
s.height *= scale;
UIGraphicsBeginImageContextWithOptions(s, NO, [UIScreen mainScreen].scale);
{
//set scroll.frame.size to contentSize, and contentOffset to 0
CGPoint savedContentOffset = sv.contentOffset;
CGRect savedFrame = sv.frame;
sv.contentOffset = CGPointZero;
sv.frame = CGRectMake(0, 0, sv.contentSize.width, sv.contentSize.height);
//scale CTM to render and resotre CTM
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextScaleCTM(context, scale, scale);
[sv.layer renderInContext: context];
CGContextRestoreGState(context);
//set scroll.frame.size and ContentOffset back
sv.contentOffset = savedContentOffset;
sv.frame = savedFrame;
}
UIImage*image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
I wrote a small function to zoom by 2x. But it is giving EXC_BAD_ACCESS error while I run it. Below is the code
- (CGImageRef)CGImageScale2x:(CGImageRef)imgRef
{
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGRect imgRect = CGRectMake(0, 0, width, height);
CGAffineTransform transform = CGAffineTransformMakeScale(2.0, 2.0);
CGRect scaledRect = CGRectApplyAffineTransform(imgRect, transform);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL,
scaledRect.size.width,
scaledRect.size.height,
8,
0,
colorSpace,
kCGImageAlphaPremultipliedFirst);
CGContextSetAllowsAntialiasing(bmContext, FALSE);
CGContextSetInterpolationQuality(bmContext, kCGInterpolationNone);
CGColorSpaceRelease(colorSpace);
CGContextScaleCTM(bmContext, 2.0, 2.0);
CGContextDrawImage(bmContext, CGRectMake(0, 0,
scaledRect.size.width,
scaledRect.size.height),
imgRef);
CGImageRef scaledImage = CGBitmapContextCreateImage(bmContext);
CFRelease(bmContext);
[(id)scaledImage autorelease];
return scaledImage;
}
I am new to iOS. Please help.
Thanks
CGImageRef can't be autoreleased, it's Core Foundation type. Try to use CGImageRelease(scaledImage) instead.
I want to get the thumbnail from the jpg file and display as a UIImage. The display style is fit out mode, such as the UIViewContentModeScaleAspectFill. below is the sample code, but it is too complicate.
-(UIImage*)scaleToSize:(CGSize)size
{
CGFloat width = CGImageGetWidth(self.CGImage);
CGFloat height = CGImageGetHeight(self.CGImage);
NSLog(#"size w=%f, h=%f, image w=%f, h=%f", size.width, size.height, width, height);
float verticalRadio = size.height*1.0/height;
float horizontalRadio = size.width*1.0/width;
float radio = 1;
if(verticalRadio>1 && horizontalRadio>1)
{
radio = verticalRadio > horizontalRadio ? horizontalRadio : verticalRadio;
}
else
{
radio = verticalRadio < horizontalRadio ? verticalRadio : horizontalRadio;
}
width = width*radio;
height = height*radio;
NSLog(#"width=%f, height=%f", width, height);
int xPos = (size.width - width)/2;
int yPos = (size.height-height)/2;
NSLog(#"xpos=%d, ypos=%d", xPos, yPos);
CGSize sz = CGSizeMake(width, height);
UIGraphicsBeginImageContext(sz);
//
[self drawInRect:CGRectMake(0, 0, width, height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect rt = CGRectMake(-xPos, -yPos, size.width, size.height);
UIImage* thub = [scaledImage getSubImage:rt];
scaledImage = nil;
return thub;
}
- (UIImage *)getSubImage:(CGRect) rect{
CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));
NSLog(#"small bounds x=%f, y=%f, w=%f, h=%f", smallBounds.origin.x, smallBounds.origin.y, smallBounds.size.width, smallBounds.size.height);
UIGraphicsBeginImageContext(smallBounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, smallBounds, subImageRef);
UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
UIGraphicsEndImageContext();
return smallImg;
}
I have update the method:
-(UIImage*)getSubImage:(CGSize)size
{
CGFloat width = CGImageGetWidth(self.CGImage);
CGFloat height = CGImageGetHeight(self.CGImage);
float verticalRadio = size.height*1.0/height;
float horizontalRadio = size.width*1.0/width;
float radio = 1;
radio = verticalRadio < horizontalRadio ? horizontalRadio : verticalRadio;
CGRect displayRect = CGRectMake((size.width - width*radio)/2.0f,
(size.height-height*radio)/2.0f,
width*radio,
height*radio);
// create a bitmap context and then set the context to current using
UIGraphicsBeginImageContext(size);
//clip
UIRectClip(displayRect);
//draw the bitmap in rect
[self drawInRect:displayRect];
// from context to get a UIIMage
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// move context out of buffer
UIGraphicsEndImageContext();
NSLog(#"getSubImage ---->");
// return.
return scaledImage;
}