【问题标题】:GLPaint save imageGLPaint 保存图像
【发布时间】:2009-06-03 19:33:17
【问题描述】:

我正在尝试在 iPhone 上开发一个复杂的绘画应用程序。我目前正在使用 Quartz 进行绘图(例如 CGContext...)。不幸的是,Quartz 开销对于我正在做的绘图类型来说太慢了,我正在使用 GLPaint 示例作为参考点移植到 OpenGL 调用。

有没有办法从 EAGLview 类中获取 UIImage/CGImage(相当于 Quartz 的 UIGraphicsGetImageFromCurrentImageContext)?基本上我需要保存GLPaint应用程序绘制的图片。

【问题讨论】:

    标签: iphone objective-c opengl-es quartz-graphics


    【解决方案1】:
    -(UIImage *) saveImageFromGLView
    {
        NSInteger myDataLength = 320 * 480 * 4;
        // allocate array and read pixels into it.
        GLubyte *buffer = (GLubyte *) malloc(myDataLength);
        glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
        // gl renders "upside down" so swap top to bottom into new array.
        // there's gotta be a better way, but this works.
        GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
        for(int y = 0; y <480; y++)
        {
            for(int x = 0; x <320 * 4; x++)
            {
                buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
            }
        }
        // make data provider with data.
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
        // prep the ingredients
        int bitsPerComponent = 8;
        int bitsPerPixel = 32;
        int bytesPerRow = 4 * 320;
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
        CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
        // make the cgimage
        CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
        // then make the uiimage from that
        UIImage *myImage = [UIImage imageWithCGImage:imageRef];
        return myImage;
    }
    

    【讨论】:

    • 非常感谢 Quakeboy!它有效,为我节省了很多时间! :)
    • 使用这段代码,我得到的只是一张全黑的图像。有什么想法吗?
    • @Brodie,你在使用多个渲染目标吗?
    • 不 - 我在这里发现了我的问题stackoverflow.com/questions/9857912/…
    • 这段代码产生了一些严重的内存泄漏(在 iPad 上价值 6MB)。你需要释放这里的缓冲区来修复它:free(buffer), free(buffer2)。
    【解决方案2】:

    与@Quakeboy 的答案相同,但传入视图以便动态确定大小(我将其用于我的通用应用程序):

    - (UIImage *)saveImageFromGLView:(UIView *)glView {
        int width = glView.frame.size.width;
        int height = glView.frame.size.height;
    
        NSInteger myDataLength = width * height * 4;
        // allocate array and read pixels into it.
        GLubyte *buffer = (GLubyte *) malloc(myDataLength);
        glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
        // gl renders "upside down" so swap top to bottom into new array.
        // there's gotta be a better way, but this works.
        GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
        for(int y = 0; y < height; y++)
        {
            for(int x = 0; x < width * 4; x++)
            {
                buffer2[((height - 1) - y) * width * 4 + x] = buffer[y * 4 * width + x];
            }
        }
        // make data provider with data.
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
        // prep the ingredients
        int bitsPerComponent = 8;
        int bitsPerPixel = 32;
        int bytesPerRow = 4 * width;
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
        CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
        // make the cgimage
        CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
        // then make the uiimage from that
        UIImage *myImage = [UIImage imageWithCGImage:imageRef];
        return myImage;
    }
    

    【讨论】:

    • 当我浏览 Quakeboy 的回复时,我已经在寻找如何使其通用的方法。你打败了我 :) 双方都做得很好!
    • 酷!再问一个问题,如果先设置背景为红色,然后还要保存那个,怎么办?通过上述方式,图像仅显示背景为黑色,而不是红色。谢谢
    【解决方案3】:

    这绝对是可能的。诀窍是使用 glReadPixels 将图像数据从 OpenGL 帧缓冲区中提取到您可以使用的内存中。一旦有了指向图像数据的指针,就可以使用 CGDataProviderCreateWithData 和 CGI​​mageCreate 从数据中创建 CGImage。我正在开发一个基于 OpenGL 的绘图应用程序,该应用程序大量使用了这种技术!

    【讨论】:

      【解决方案4】:

      此代码不会像上述解决方案那样泄漏内存,并且会考虑动态视图大小以及视网膜与标准显示器:

      -(BOOL)iPhoneRetina{
          return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0))?YES:NO;
      }
      
      void releasePixels(void *info, const void *data, size_t size) {
          free((void*)data);
      }
      
      -(UIImage *) glToUIImage{
      
          int imageWidth, imageHeight;
      
          int scale = [self iPhoneRetina]?2:1;
      
          imageWidth = self.frame.size.width*scale;
          imageHeight = self.frame.size.height*scale;
      
          NSInteger myDataLength = imageWidth * imageHeight * 4;
      
          // allocate array and read pixels into it.
          GLubyte *buffer = (GLubyte *) malloc(myDataLength);
          glReadPixels(0, 0, imageWidth, imageHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
      
          // make data provider with data.
          CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, myDataLength, releasePixels);
      
          // prep the ingredients
          int bitsPerComponent = 8;
          int bitsPerPixel = 32;
          int bytesPerRow = 4 * imageWidth;
          CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
          CGBitmapInfo bitmapInfo =  kCGImageAlphaPremultipliedLast;
          CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
      
          // make the cgimage
      
          CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
      
          UIImage *myImage = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationDownMirrored]; //Render image flipped, since OpenGL's data is mirrored
      
          CGImageRelease(imageRef);
          CGColorSpaceRelease(colorSpaceRef);
      
          CGDataProviderRelease(provider);
      
          return myImage;
      }
      

      其他的泄漏内存是因为 CGDataProviderCreateWithData 的最后一个参数应该是一个释放内存的函数,而且他们也省略了 CGRelease 函数。

      【讨论】:

        【解决方案5】:
        void SaveScreenImage()
        
        {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        CGImageRef cgImage = UIGetScreenImage();
        void *imageBytes = NULL;
        if (cgImage == NULL) {
        CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
        imageBytes = malloc(320 * 480 * 4);
        CGContextRef context = CGBitmapContextCreate(imageBytes, 320, 480, 8, 320 * 4, colorspace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big);
            CGColorSpaceRelease(colorspace);
        for (UIWindow *window in [[UIApplication sharedApplication] windows]) {
                CGRect bounds = [window bounds];
                CALayer *layer = [window layer];
                CGContextSaveGState(context);
                if ([layer contentsAreFlipped]) {
                    CGContextTranslateCTM(context, 0.0f, bounds.size.height);
                    CGContextScaleCTM(context, 1.0f, -1.0f);
                }
        [layer renderInContext:(CGContextRef)context];
                CGContextRestoreGState(context);
            }
            cgImage = CGBitmapContextCreateImage(context);
            CGContextRelease(context);
        }
        UIImage *image=[UIImage imageWithCGImage:cgImage];
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); 
        [pool release];
        }
        

        此代码将保存为您在屏幕上看到的内容。但它可能是私有 api。

        【讨论】:

        • UIGetScreenImage() 现已弃用!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-09
        • 1970-01-01
        • 2013-12-20
        • 2011-01-19
        • 1970-01-01
        • 1970-01-01
        • 2022-12-22
        相关资源
        最近更新 更多