2009-08-30 20 views

答えて

1

EDIT:以下のdemianturnerノートでレイヤーをレンダリングする必要はなくなりました。上位レベルの[UIView drawViewHierarchyInRect:]を使用することができます。それ以外;これは同じように動作するはずです。

EAGLViewは一種の見解であり、その基礎となるCAEAGLLayerは単なる一種のレイヤーです。つまり、ビュー/レイヤーをUIImageに変換するためのstandard approachが機能します。 (リンク質問がのUIWebViewは問題ではないという事実;。それはちょうどビューのさらに別のようなものだ)

+3

返信ありがとうRob、しかし私は "標準私それはうまくいきませんでした。私のEAGLViewは、それに読み込まれたテクスチャを正しく表示していますが、私は標準的なアプローチを使用してそれから抽出できるUIImageは、空の白い色です。これは、EAGLViewがIBにどのように現れるかです。これは、実際には、EAGLViewがUIViewの一種であることを見て、奇妙です。私は多分glReadPixelsまたは何かを代わりに使用する必要があると思いますか?私はAppleサンプルコードGLImageProcessingの例からEAGLViewを使って作業しています。 – RexOnRoids

+0

ええと...私は本当に速く話しました。 OpenGLはQuartzと同じ方法でレンダリングしません。申し訳ありません。私はこのスレッドがあなたの問題に対処すると信じています。すべてのメッセージを読む。彼は途中でいくつかのバグを解決します。私はあなたがすでにCGContextを扱う方法を知っていて、そこからCGImageを得ることを前提としています(そしてそこからのUIImage)。 http://lists.apple.com/archives/mac-opengl/2006//jan/msg00085.html –

+0

これらの回答は古く、不必要に長引いています。このソリューションはUIKitコードが4行しか必要ではありません。https://developer.apple.com/library/content/qa/qa1817/_index.html – demianturner

6
-(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]; 

    CGImageRelease(imageRef); 
    CGDataProviderRelease(provider); 
    CGColorSpaceRelease(colorSpaceRef); 
    free(buffer2); 

    return myImage; 
} 
+0

上記のコードにはいくつかのリークがありますので、代わりにBramの答えで提供されるより効率的なコードを使用することを強くお勧めします。 –

+2

今後の開発者がこのコードをコピーして自分のアプリケーションに貼り付ける場合に備えて、Bramのコードからより重要なメモリリークの修正を取り入れました。 –

+0

リークを修正していただきありがとうございます! –

9

ここQuakeboyのコードのクリーンアップバージョンです。 私はiPadでそれをテストし、うまく動作します。 改善は、次のとおりのmemcpy

  • と網膜ディスプレイと
  • 作品(ポイントスケール2)
  • 置換ネストされたループは、メモリ
  • UIImageのに節約をリーククリーンアップEAGLView任意のサイズを有する

    • 作品ボーナスとしてphotoalbum。あなたのEAGLViewでの方法として

    これを使用して:

    -(void)snapUIImage 
    { 
        int s = 1; 
        UIScreen* screen = [ UIScreen mainScreen ]; 
        if ([ screen respondsToSelector:@selector(scale) ]) 
         s = (int) [ screen scale ]; 
    
        const int w = self.frame.size.width; 
        const int h = self.frame.size.height; 
        const NSInteger myDataLength = w * h * 4 * s * s; 
        // allocate array and read pixels into it. 
        GLubyte *buffer = (GLubyte *) malloc(myDataLength); 
        glReadPixels(0, 0, w*s, h*s, 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 < h*s; y++) 
        { 
         memcpy(buffer2 + (h*s - 1 - y) * w * 4 * s, buffer + (y * 4 * w * s), w * 4 * s); 
        } 
        free(buffer); // work with the flipped buffer, so get rid of the original one. 
    
        // make data provider with data. 
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL); 
        // prep the ingredients 
        int bitsPerComponent = 8; 
        int bitsPerPixel = 32; 
        int bytesPerRow = 4 * w * s; 
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; 
        CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; 
        // make the cgimage 
        CGImageRef imageRef = CGImageCreate(w*s, h*s, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); 
        // then make the uiimage from that 
        UIImage *myImage = [ UIImage imageWithCGImage:imageRef scale:s orientation:UIImageOrientationUp ]; 
        UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil); 
        CGImageRelease(imageRef); 
        CGDataProviderRelease(provider); 
        CGColorSpaceRelease(colorSpaceRef); 
        free(buffer2); 
    } 
    
  • +0

    上記のコードでは、色空間とデータプロバイダの一致するリリースが不足していたため、メソッドの最後に漏れを防ぐためにそれらを追加しました。 –

    +0

    また、 'buffer2'は実際にリークを避けるために手動で解放する必要があります。これも修正されました。 –

    +9

    私はこれを試しましたが、常に描画なしの黒い画像を作成します。何が間違っているのでしょうか? – Brodie

    6

    私は私のために正常に動作するために、ここで他の回答を得ることができませんでした。

    数日後、私はついにこの問題に対する解決策を得ました。 EAGLViewからUIImageを生成するコードがAppleによって提供されています。その後、UIkitが上下逆になっているので、画像を上下に反転させるだけです。

    Apple提供の方法 - 画像にしたいビューの内側になるように修正されました。

    -(UIImage *) drawableToCGImage 
    { 
    GLint backingWidth2, backingHeight2; 
    //Bind the color renderbuffer used to render the OpenGL ES view 
    // If your application only creates a single color renderbuffer which is already bound at this point, 
    // this call is redundant, but it is needed if you're dealing with multiple renderbuffers. 
    // Note, replace "_colorRenderbuffer" with the actual name of the renderbuffer object defined in your class. 
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); 
    
    // Get the size of the backing CAEAGLLayer 
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth2); 
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight2); 
    
    NSInteger x = 0, y = 0, width2 = backingWidth2, height2 = backingHeight2; 
    NSInteger dataLength = width2 * height2 * 4; 
    GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte)); 
    
    // Read pixel data from the framebuffer 
    glPixelStorei(GL_PACK_ALIGNMENT, 4); 
    glReadPixels(x, y, width2, height2, GL_RGBA, GL_UNSIGNED_BYTE, data); 
    
    // Create a CGImage with the pixel data 
    // If your OpenGL ES content is opaque, use kCGImageAlphaNoneSkipLast to ignore the alpha channel 
    // otherwise, use kCGImageAlphaPremultipliedLast 
    CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL); 
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); 
    CGImageRef iref = CGImageCreate(width2, height2, 8, 32, width2 * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, 
               ref, NULL, true, kCGRenderingIntentDefault); 
    
    // OpenGL ES measures data in PIXELS 
    // Create a graphics context with the target size measured in POINTS 
    NSInteger widthInPoints, heightInPoints; 
    if (NULL != UIGraphicsBeginImageContextWithOptions) { 
        // On iOS 4 and later, use UIGraphicsBeginImageContextWithOptions to take the scale into consideration 
        // Set the scale parameter to your OpenGL ES view's contentScaleFactor 
        // so that you get a high-resolution snapshot when its value is greater than 1.0 
        CGFloat scale = self.contentScaleFactor; 
        widthInPoints = width2/scale; 
        heightInPoints = height2/scale; 
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale); 
    } 
    else { 
        // On iOS prior to 4, fall back to use UIGraphicsBeginImageContext 
        widthInPoints = width2; 
        heightInPoints = height2; 
        UIGraphicsBeginImageContext(CGSizeMake(widthInPoints, heightInPoints)); 
    } 
    
    CGContextRef cgcontext = UIGraphicsGetCurrentContext(); 
    
    // UIKit coordinate system is upside down to GL/Quartz coordinate system 
    // Flip the CGImage by rendering it to the flipped bitmap context 
    // The size of the destination area is measured in POINTS 
    CGContextSetBlendMode(cgcontext, kCGBlendModeCopy); 
    CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref); 
    
    // Retrieve the UIImage from the current context 
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
    
    UIGraphicsEndImageContext(); 
    
    // Clean up 
    free(data); 
    CFRelease(ref); 
    CFRelease(colorspace); 
    CGImageRelease(iref); 
    
    return image; 
    

    }

    そして相続画像

    - (UIImage *) flipImageVertically:(UIImage *)originalImage { 
    UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage]; 
    UIGraphicsBeginImageContext(tempImageView.frame.size); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGAffineTransform flipVertical = CGAffineTransformMake(
                     1, 0, 0, -1, 0, tempImageView.frame.size.height 
                     ); 
    CGContextConcatCTM(context, flipVertical); 
    
    [tempImageView.layer renderInContext:context]; 
    
    UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    //[tempImageView release]; 
    
    return flippedImage; 
    

    }

    そして、ここで私が参照する第一の方法を発見したアップルのdevのページへのリンクですを反転させる方法。 http://developer.apple.com/library/ios/#qa/qa1704/_index.html

    +0

    ネイト、この質問(これを複製したもの)をここに入れて、人々がOpenGLからさまざまな方法でキャプチャする方法を知ってもらうESシーン。 –

    +0

    @ネイト:EAGLViewに書き込まれたコンテンツがあるかどうかを知る方法はありますか?私はまた、上記のように動作したビューのスクリーンショットを撮ろうとしていますが、ユーザーがスクリーンショットを撮ったり、スクリーンをクリアしてスクリーンショットを撮ったりすると、空のスクリーンがキャプチャされてしまいます。 –

    0

    CGDataProviderCreateWithDataリリースを行う必要があり、データを、リリースごとにコールバックが付属しています:

    void releaseBufferData(void *info, const void *data, size_t size) 
    { 
        free((void*)data); 
    } 
    

    その後、他の例のように、ではなく、ここでは無料のデータにこれを実行します。

    GLubyte *bufferData = (GLubyte *) malloc(bufferDataSize); 
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bufferData, bufferDataSize, releaseBufferData); 
    .... 
    CGDataProviderRelease(provider); 
    

    代わりに、リリースコールバックの代わりにCGDataProviderCreateWithCFDataを使用してください。

    詳細については
    GLubyte *bufferData = (GLubyte *) malloc(bufferDataSize); 
    NSData *data = [NSData dataWithBytes:bufferData length:bufferDataSize]; 
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); 
    .... 
    CGDataProviderRelease(provider); 
    free(bufferData); // Remember to free it 
    

    、これは議論し確認してください。

    What's the right memory management pattern for buffer->CGImageRef->UIImage?

    +0

    これは正しいです。 Core Graphicsコンテキストでデータを解放するためのコールバックのみを使用します。 –

    0

    のブラッドラーソンのこの上記のコードでは、あなたのEAGLView.mあなたがしなければならない

    - (id)initWithCoder:(NSCoder*)coder{ 
        self = [super initWithCoder:coder]; 
        if (self) { 
         CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; 
         eaglLayer.opaque = TRUE; 
         eaglLayer.drawableProperties = 
          [NSDictionary dictionaryWithObjectsAndKeys: 
           [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, 
           kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; 
        } 
        return self; 
    } 
    

    を編集する必要が変更numberWithBoolYES

    関連する問題