Показаны сообщения с ярлыком графический контекст. Показать все сообщения
Показаны сообщения с ярлыком графический контекст. Показать все сообщения

среда, 8 января 2014 г.

Правильный разоворот изображения в IOS

Не так давно натолкнулся на проблему правильно повернуть картинку на нужный угол. Чаще всего это 90,180 градусов. Причем проблема заключалась не только в том, что бы развернуть картинку для пользователя на экране IPhone, а и сохранить изменения в графическом контексте, что бы запомнить эту трансформацию. Перебрав кучу методов я остановился на более простом и менее нагрузочном для системе методе. Данный способ не обрежет картинку не по сторонам ни по высоте или ширине, что немаловажно.

И так приступим. Подразумевается что у вас уже есть проект с размещенным на ViewVontroller-е вашей UIImageView куда вы будете показывать картинку до и полсе разворота. Поэтому я опишу лишь методы по работе с самим изображением.
Итак в методе viewDidLoad я подгружаю картинку из проекта
UIImage *img = [UIImage imageNamed:@"test.png"];
mainimage.image = img;
где mainimage это ваш UIImageView
Теперь релазуем два метода для переворота картинки вправо и влево:
-(void) RotateToLeft{
mainimage.image = [self imageByRotatingImage:img fromImageOrientation:UIImageOrientationLeft];
}
-(void)RotateToRight{
mainimage.image = [self imageByRotatingImage:img fromImageOrientation:UIImageOrientationRight];
}
Конечно вы можете написать эти методы как -(IBAction) и привязать вызов к кнопкам, но это не слишком сложно, что бы тратить на это время в этой статье :)
Итак мы видим, что в обоих случаях вызывается один и тот же метод -(UIImage*)imageByRotatingImage:(UIImage*)initImage fromImageOrientation:(UIImageOrientation)orientation только с разным параметром вконце. Он то как раз и указывает на ориентацию изображения в которую необходимо повернуть  картинку. Взял я его гдето на просторах интернетов и немножко окрасивил для понятного использования. Перед просмотром листинга кода метода хочу сказать, что перед его вызовом подразумевается, что текущая картинка имеет ориентацию UIImageOrientationUp.
Итак сам метод:
-(UIImage*)imageByRotatingImage:(UIImage*)initImage fromImageOrientation:(UIImageOrientation)orientation
{
CGImageRef imgRef = initImage.CGImage;
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);
CGAffineTransform transform = CGAffineTransformIdentity;
CGRect bounds = CGRectMake(0, 0, width, height);
CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
CGFloat boundHeight;
UIImageOrientation orient = orientation;
switch(orient) {
case UIImageOrientationUp: //EXIF = 1
return initImage; //если прислать в качестве параметра orientation – UIImageOrientationUp метод вернёт саму картинку целой и невредмой
break;
case UIImageOrientationUpMirrored: //EXIF = 2  – Зеркальное отображение
transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
break;
case UIImageOrientationDown: //EXIF = 3 – перевернет вертикально
transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationDownMirrored: //EXIF = 4 – Вертикальное зеркальное отображение
transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
break;
case UIImageOrientationLeftMirrored: //EXIF = 5
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
transform = CGAffineTransformScale(transform, -1.0, 1.0);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationLeft: //EXIF = 6 – Перевернуть влево -90 градусов
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
break;
case UIImageOrientationRightMirrored: //EXIF = 7
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeScale(-1.0, 1.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
case UIImageOrientationRight: //EXIF = 8 – Вправо тоесть +90 градусов по часовой стрелке
boundHeight = bounds.size.height;
bounds.size.height = bounds.size.width;
bounds.size.width = boundHeight;
transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
transform = CGAffineTransformRotate(transform, M_PI / 2.0);
break;
default:
[NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];
}
// Вот тут создается графический контекст
CGContextRef    context = NULL;
void *          bitmapData;
int             bitmapByteCount;
int             bitmapBytesPerRow;
bitmapBytesPerRow   = (bounds.size.width * 4);
bitmapByteCount     = (bitmapBytesPerRow * bounds.size.height);
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
return nil;
}
CGColorSpaceRef colorspace = CGImageGetColorSpace(imgRef);
context = CGBitmapContextCreate (bitmapData,bounds.size.width,bounds.size.height,8,bitmapBytesPerRow,
colorspace,kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorspace);
if (context == NULL)
// если неполучилось создать контекст по какой либо причине отдатим nil
return nil;
CGContextScaleCTM(context, -1.0, -1.0);
CGContextTranslateCTM(context, -bounds.size.width, -bounds.size.height);
CGContextConcatCTM(context, transform);
//Отрисовка изображения в графический контекст в памяти
CGContextDrawImage(context, CGRectMake(0,0,width, height), imgRef);
CGImageRef imgRef2 = CGBitmapContextCreateImage(context);
CGContextRelease(context);
free(bitmapData);
UIImage * image = [UIImage imageWithCGImage:imgRef2 scale:initImage.scale orientation:UIImageOrientationUp];
NSData *newimageData = UIImageJPEGRepresentation(image,1.0);
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:newimageData  forKey:@"chosed_image_data"];
CGImageRelease(imgRef2);
return image;
}
Во время выполнения этого метода может быть некая задержка в приложении, так как идёт выделение памяти и устройство в отличии от эмулятора может слегка притормозить свою активность, поэтому нужно быть готовым к этому. Итак мы имеем универсальный метод по развороту картинки на 90 и 180  градусов в любую сторону, а также возможность зеркально отобразить изображения. При этом сохраняя его в контексте. Чем это лучше чем просто трансформация в виде анимации? Потому, что после например анимированного траснформа поворота мы получим картинку повернутую на 90 градусов, но в контексте она останется в прежнем состоянии. И при повторном выполнении трансформации мы получим тот же результат – картинку развернутую на 90 градусов, а не на 180 как задумывалось.

Пропорциональное изменение размеров изображений в IOS IPhone

Статья посвящена теме об пропорциональном изменении размеров фотографий в вашем приложении для IPone IPad. Так как в большинстве приложений по работе  с фотографиями рано или поздно разработчик сталкивается с задачей уменьшить или увеличить размер фото согласно заданным величинам ширины или величины. И уменьшение необходимо сделать пропорционально например ширине как это зачастую бывает. Так как если просто накинуть пару сотен пикселов ширине или высоте картинки полученное изображение может иметь искажённый вид, например растянуто по высоте. Это может и сойдет если работа идет с объектами типа дерево или куст, но никак не удовлетворит заказчика если вы работаете с фотографиями людей. А также возможно вам просто необходимо уменьшить размеры фото что бы не тратить драгоценный мобильный трафик при передаче фотографии друзьям.

Итак сама реализация:
Как обычно предполагаю что у вас уже есть проект с созданным в storyboard UIImageView для работы с изображениями. viewDidLoad я подгружаю картинку из проекта
UIImage *img = [UIImage imageNamed:@"test.png"];
mainimage.image = img;
где mainimage это ваш UIImageView
Теперь напишем метод в который можно привязать к кнопке, по нажатию на котороую мы уменьшаем реальный размер фото пропорционально по ширине до 640px.
-(IBAction) scale:(id)sender{
// создаем объект класса CGSize что бы задать пропорции нового фото
// первый параметр это ширина второй высота, но играет роль только размер для ширины так как метод ориентирован именно на пропорциональное уменьшение/увеличение по ширине
CGSize sz = CGSizeMake(640, 640);
UIImage *img = [UIImage imageNamed:@"test.png"];
UIImage *newimage = [self ScaleImgPropoWidth:img scaledToSize:sz];
mainimage.image = newimage;
}
Как вы видите реализация скрыта в методе ScaleImgPropoWidth, давайте посмотрим на реализацию метода:
- (UIImage *)ScaleImgPropoWidth:(UIImage *)image scaledToSize:(CGSize)newSize {
double ratio;
double delta;
CGPoint offset;
//проверка на то если новый размер картинки больше или равен старому то вернуть ту же картинку
if(newSize.width>=image.size.width){
return image;
}//если данная проверка вам ненужна вы можете её убрать, напрмиер если хотите в любом случае кропить размер или же при увеличении размеров заменить условие на if(newSize.width<=image.size.width)
ratio = newSize.width / image.size.width;
delta = (ratio*image.size.height – ratio*image.size.width);
UIImage *scaledImage =
[UIImage imageWithCGImage:[image CGImage]
scale:(image.scale / ratio)
orientation:(image.imageOrientation)];
CGRect clipRect = CGRectMake(0, 0,
scaledImage.size.width,
scaledImage.size.height);
CGSize sz = CGSizeMake(newSize.width, scaledImage.size.height);
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
} else {
UIGraphicsBeginImageContext(sz);
}
UIRectClip(clipRect);
[image drawInRect:clipRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Как вы могли заметить преобразовании резамера происходит в графическом контексте, то есть это не просто трансформация изображения.