caracteristicas camara argentina apple app iphone ios uiimage quartz-2d drawrect

iphone - camara - La manera más eficiente de dibujar parte de una imagen en iOS



iphone 6 (5)

En última instancia, sería más rápido, con mucha menos creación de imágenes desde atlas de sprites, si pudieras configurar no solo la imagen para un UIImageView, sino también la compensación de la parte superior izquierda para mostrar dentro de ese UIImage. Quizás esto es posible.

Mientras tanto, creé estas útiles funciones en una clase de utilidad que uso en mis aplicaciones. Crea un UIImage de parte de otro UIImage, con opciones para rotar, escalar y voltear utilizando los valores de UIImageOrientation estándar para especificar.

Mi aplicación crea muchos UIImages durante la inicialización, y esto necesariamente lleva tiempo. Pero algunas imágenes no son necesarias hasta que se selecciona una determinada pestaña. Para dar la apariencia de una carga más rápida, podría crearlos en un hilo separado generado al inicio, luego simplemente espere hasta que esté listo si se selecciona esa pestaña.

+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture { return [ChordCalcController imageByCropping:imageToCrop toRect:aperture withOrientation:UIImageOrientationUp]; } // Draw a full image into a crop-sized area and offset to produce a cropped, rotated image + (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture withOrientation:(UIImageOrientation)orientation { // convert y coordinate to origin bottom-left CGFloat orgY = aperture.origin.y + aperture.size.height - imageToCrop.size.height, orgX = -aperture.origin.x, scaleX = 1.0, scaleY = 1.0, rot = 0.0; CGSize size; switch (orientation) { case UIImageOrientationRight: case UIImageOrientationRightMirrored: case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: size = CGSizeMake(aperture.size.height, aperture.size.width); break; case UIImageOrientationDown: case UIImageOrientationDownMirrored: case UIImageOrientationUp: case UIImageOrientationUpMirrored: size = aperture.size; break; default: assert(NO); return nil; } switch (orientation) { case UIImageOrientationRight: rot = 1.0 * M_PI / 2.0; orgY -= aperture.size.height; break; case UIImageOrientationRightMirrored: rot = 1.0 * M_PI / 2.0; scaleY = -1.0; break; case UIImageOrientationDown: scaleX = scaleY = -1.0; orgX -= aperture.size.width; orgY -= aperture.size.height; break; case UIImageOrientationDownMirrored: orgY -= aperture.size.height; scaleY = -1.0; break; case UIImageOrientationLeft: rot = 3.0 * M_PI / 2.0; orgX -= aperture.size.height; break; case UIImageOrientationLeftMirrored: rot = 3.0 * M_PI / 2.0; orgY -= aperture.size.height; orgX -= aperture.size.width; scaleY = -1.0; break; case UIImageOrientationUp: break; case UIImageOrientationUpMirrored: orgX -= aperture.size.width; scaleX = -1.0; break; } // set the draw rect to pan the image to the right spot CGRect drawRect = CGRectMake(orgX, orgY, imageToCrop.size.width, imageToCrop.size.height); // create a context for the new image UIGraphicsBeginImageContextWithOptions(size, NO, imageToCrop.scale); CGContextRef gc = UIGraphicsGetCurrentContext(); // apply rotation and scaling CGContextRotateCTM(gc, rot); CGContextScaleCTM(gc, scaleX, scaleY); // draw the image to our clipped context using the offset rect CGContextDrawImage(gc, drawRect, imageToCrop.CGImage); // 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; }

Dado un UIImage y un CGRect , ¿cuál es la forma más eficiente (en memoria y tiempo) para dibujar la parte de la imagen correspondiente al CGRect (sin escalar)?

Como referencia, así es como lo hago actualmente:

- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGRect frameRect = CGRectMake(frameOrigin.x + rect.origin.x, frameOrigin.y + rect.origin.y, rect.size.width, rect.size.height); CGImageRef imageRef = CGImageCreateWithImageInRect(image_.CGImage, frameRect); CGContextTranslateCTM(context, 0, rect.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, rect, imageRef); CGImageRelease(imageRef); }

Desafortunadamente, esto parece extremadamente lento con imágenes de tamaño medio y una frecuencia alta de setNeedsDisplay . Jugar con el marco de clipToBounds y clipToBounds produce mejores resultados (con menos flexibilidad).


En lugar de crear una nueva imagen (que es costosa porque asigna memoria), ¿qué hay de usar CGContextClipToRect ?


La forma muy simple de mover una imagen grande dentro de UIImageView es la siguiente.

Permítanos tener la imagen de tamaño (100, 400) que representa 4 estados de alguna imagen uno debajo de otro. Queremos mostrar la segunda imagen teniendo offsetY = 100 en UIImageView cuadrado de tamaño (100, 100). La solucion es:

UIImageView *iView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; CGRect contentFrame = CGRectMake(0, 0.25, 1, 0.25); iView.layer.contentsRect = contentFrame; iView.image = [UIImage imageNamed:@"NAME"];

Aquí contentFrame es el marco normalizado relativo al tamaño real del UIImage . Entonces, "0" significa que comenzamos la parte visible de la imagen desde el borde izquierdo, "0.25" significa que tenemos un desplazamiento vertical 100, "1" significa que queremos mostrar el ancho completo de la imagen y, finalmente, "0.25" significa que queremos mostrar solo 1/4 parte de la imagen en altura.

Por lo tanto, en las coordenadas de imagen locales mostramos el siguiente marco

CGRect visibleAbsoluteFrame = CGRectMake(0*100, 0.25*400, 1*100, 0.25*400) or CGRectMake(0, 100, 100, 100);


La manera más rápida es usar una máscara de imagen: una imagen que tiene el mismo tamaño que la imagen para enmascarar pero con un cierto patrón de píxeles que indica qué parte de la imagen se enmascara cuando se renderiza ->

// maskImage is used to block off the portion that you do not want rendered // note that rect is not actually used because the image mask defines the rect that is rendered -(void) drawRect:(CGRect)rect maskImage:(UIImage*)maskImage { UIGraphicsBeginImageContext(image_.size); [maskImage drawInRect:image_.bounds]; maskImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); CGImageRef maskRef = maskImage.CGImage; CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef), CGImageGetBitsPerComponent(maskRef), CGImageGetBitsPerPixel(maskRef), CGImageGetBytesPerRow(maskRef), CGImageGetDataProvider(maskRef), NULL, false); CGImageRef maskedImageRef = CGImageCreateWithMask([image_ CGImage], mask); image_ = [UIImage imageWithCGImage:maskedImageRef scale:1.0f orientation:image_.imageOrientation]; CGImageRelease(mask); CGImageRelease(maskedImageRef); }


Supuse que estás haciendo esto para mostrar parte de una imagen en la pantalla, porque mencionaste UIImageView . Y los problemas de optimización siempre necesitan una definición específica.

Confíe en Apple para cosas de UI regulares

En realidad, UIImageView con clipsToBounds es una de las formas más rápidas y simples de archivar tu objetivo si tu objetivo es simplemente recortar una región rectangular de una imagen (no demasiado grande). Además, no es necesario enviar setNeedsDisplay mensaje setNeedsDisplay .

O puede intentar colocar el UIImageView dentro de una UIView vacía y establecer el recorte en la vista de contenedor. Con esta técnica, puede transformar su imagen libremente configurando la propiedad de transform en 2D (escalado, rotación, traslación).

Si necesita una transformación en 3D, todavía puede usar CALayer con la propiedad masksToBounds , pero usar CALayer le dará muy poco rendimiento adicional, generalmente no considerable.

De todos modos, debe conocer todos los detalles de bajo nivel para usarlos adecuadamente para la optimización.

¿Por qué es esa una de las formas más rápidas?

UIView es solo una capa delgada sobre CALayer que se implementa sobre OpenGL que es una interfaz virtualmente directa para la GPU . Esto significa que UIKit está siendo acelerado por GPU.

Entonces, si los usa adecuadamente (es decir, dentro de las limitaciones diseñadas), funcionará tan bien como la implementación de OpenGL . Si usa solo unas pocas imágenes para mostrar, obtendrá un rendimiento aceptable con la implementación de UIView , ya que puede obtener la aceleración total del OpenGL subyacente (lo que significa la aceleración de la GPU).

De todos modos, si necesitas una optimización extrema para cientos de sprites animados con sombreadores de píxeles finamente ajustados como en una aplicación de juego, debes usar OpenGL directamente, porque a CALayer faltan muchas opciones para optimizar en niveles inferiores. De todos modos, al menos para la optimización de las cosas de la interfaz de usuario, es increíblemente difícil ser mejor que Apple.

¿Por qué tu método es más lento que UIImageView ?

Lo que debes saber es todo acerca de la aceleración de GPU. En todas las computadoras recientes, el rendimiento de gráficos rápidos se logra solo con GPU. Entonces, el punto es si el método que está utilizando se implementa sobre la GPU o no.

CGImage métodos de dibujo IMO, CGImage no se implementan con GPU. Creo que leí mencionar sobre esto en la documentación de Apple, pero no recuerdo dónde. Entonces no estoy seguro de esto De todos modos, creo que CGImage está implementado en la CPU porque,

  1. Su API parece diseñada para CPU, como la interfaz de edición de mapas de bits y el dibujo de texto. No se ajustan muy bien a una interfaz de GPU.
  2. La interfaz de contexto de mapa de bits permite el acceso directo a la memoria. Eso significa que su almacenamiento de back-end está ubicado en la memoria de la CPU. Tal vez algo diferente en la arquitectura de memoria unificada (y también con Metal API), pero de todos modos, la intención de diseño inicial de CGImage debería ser para CPU.
  3. Muchos lanzaron recientemente otras API de Apple que mencionan la aceleración de GPU explícitamente. Eso significa que sus API antiguas no lo eran. Si no hay una mención especial, generalmente se realiza en la CPU de forma predeterminada.

Entonces parece que se hace en la CPU. Las operaciones de gráficos realizadas en la CPU son mucho más lentas que en la GPU.

Simplemente recortar una imagen y componer las capas de la imagen son operaciones muy simples y baratas para la GPU (en comparación con la CPU), por lo que puede esperar que la biblioteca UIKit lo utilice porque todo el UIKit se implementa sobre OpenGL.

Acerca de las limitaciones

Debido a que la optimización es un tipo de trabajo sobre microgestión, los números específicos y los pequeños hechos son muy importantes. ¿Cuál es el tamaño mediano ? OpenGL en iOS generalmente limita el tamaño máximo de la textura a 1024x1024 píxeles (tal vez más grande en versiones recientes). Si su imagen es más grande que esto, no funcionará, o el rendimiento se degradará mucho (creo que UIImageView está optimizado para imágenes dentro de los límites).

Si necesita visualizar imágenes de gran CATiledLayer con recorte, debe usar otra optimización como CATiledLayer y esa es una historia totalmente diferente.

Y no vaya a OpenGL a menos que desee conocer todos los detalles de OpenGL. Necesita una comprensión total sobre gráficos de bajo nivel y al menos 100 códigos más.

Acerca de algún futuro

Aunque no es muy probable que suceda, pero los CGImage (o cualquier otra cosa) no necesitan estar bloqueados solo en la CPU. No olvides verificar la tecnología básica de la API que estás usando. Aún así, los productos de GPU son monstruos muy diferentes de la CPU, por lo que los chicos de API generalmente los mencionan explícita y claramente.