簡體   English   中英

顯示生成的屏幕截圖UIImage未在UIImageView中顯示(僅限設備)

[英]Displaying a screen shot generated UIImage is not displaying in UIImageView (for device only)

我正在嘗試將OpenGL緩沖區(當前顯示在視圖中)保存到設備的照片庫中。 下面的代碼片段在模擬器上運行正常。 但對於實際設備而言,它正在崩潰。 我相信我創建從屏幕捕獲的UIImage的方式可能存在問題。

  • 此操作通過IBAction事件句柄方法啟動。
  • 我用來保存圖像的功能是UIImageWriteToSavedPhotosAlbum(我最近把它更改為ALAssetsLibrary的writeImageToSavedPhotosAlbum)。
  • 我確保我的應用程序有權訪問照片庫。
  • 我還確保我的CGImageRed是全局定義的(在文件頂部定義),而我的UIImage是(非原子,保留)屬性。

有人可以幫我解決這個問題嗎? 我想要一個從glReadPixels數據生成的有效UIImage引用。

以下是相關的代碼段(調用保存到照片庫):

-(void)TakeImageBufferSnapshot:(CGSize)dimensions
{
   NSLog(@"TakeSnapShot 1 : (%f, %f)", dimensions.width, dimensions.height);
   NSInteger size = dimensions.width * dimensions.height * 4;
   GLubyte *buffer = (GLubyte *) malloc(size);
   glReadPixels(0, 0, dimensions.width, dimensions.height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
   GLubyte *buffer2 = (GLubyte *) malloc(size);
   int height = (int)dimensions.height - 1;
   int width = (int)dimensions.width;

   for(int y = 0; y < dimensions.height; y++)
   {
       for(int x = 0; x < dimensions.width * 4; x++)
       {
           buffer2[(height - 1 - y) * width * 4 + x] = buffer[y * 4 * width + x];
       }
   }

   NSLog(@"TakeSnapShot 2");

   // make data provider with data.
   CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, size, NULL);

   if (buffer) free(buffer);
   if (buffer2) free(buffer2);

   // prep the ingredients
   int bitsPerComponent = 8;
   int bitsPerPixel = 32;
   int bytesPerRow = 4 * self.view.bounds.size.width;
   CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
   CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
   CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

   NSLog(@"TakeSnapShot 3");

   // make the cgimage
   g_savePhotoImageRef = CGImageCreate(dimensions.width, dimensions.height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

   NSLog(@"TakeSnapShot 4");

   // then make the uiimage from that
   self.savePhotoImage = [UIImage imageWithCGImage:g_savePhotoImageRef];

   CGColorSpaceRelease(colorSpaceRef);
   CGDataProviderRelease(provider);
}

-(void)SaveToPhotoAlbum
{
   ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
   NSLog(@"Authorization status: %d", status);

   if (status == ALAuthorizationStatusAuthorized)
   {
       [self TakeImageBufferSnapshot:self.view.bounds.size];

       // UPDATED - DO NOT proceed to save to album below.
       // Instead, set the created image to a UIImageView IBOutlet.
       // On the simulator this shows the screen/buffer captured image (as expected) -
       // but on the device (ipad) this doesnt show anything and the app crashes.
       self.testImageView.image = self.savePhotoImage;
       return;

       NSLog(@"Saving to photo album...");
       UIImageWriteToSavedPhotosAlbum(self.savePhotoImage,
                                   self,
                                       @selector(photoAlbumImageSave:didFinishSavingWithError:contextInfo:),
                                   nil);
   }
   else
   {
       UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Access is denied"
                                                    message:@"Allow access to your Photos library to save this image."
                                                   delegate:nil
                                          cancelButtonTitle:@"Close"
                                          otherButtonTitles:nil, nil];
       [alert show];
   }
}

- (void)photoAlbumImageSave:(UIImage *)image didFinishSavingWithError:(NSError *)error     contextInfo:(void *)context
{
   self.savePhotoImage = nil;
   CGImageRelease(g_savePhotoImageRef);

   if (error)
   {
       NSLog(@"Error saving photo to albums: %@", error.description);
   }
   else
   {
       NSLog(@"Saved to albums!");
   }
}

*更新*我想我已經設法縮小了我的問題范圍。 我開始做試驗和錯誤,我在評論出代碼行后運行應用程序(在設備上),以縮小范圍。 看起來我的TakeImageBufferSnapshot函數可能有問題,它接受屏幕緩沖區(使用glReadPixels)並創建CGImageRef。 現在,當我嘗試創建一個UIImage時(使用[UIImage imageWithCGImage:]方法,這似乎是應用程序崩潰的原因。如果我評論這一行,似乎沒有問題(除了事實,我沒有UIImage參考)。

我基本上需要一個有效的UIImage參考,以便我可以將它保存到照片庫(使用測試圖像似乎工作得很好)。

首先,我應該指出glReadPixels()行為可能與您期望的不同。 如果在調用-presentRenderbuffer:之后嘗試使用它來從屏幕上讀取,則結果是未定義的。 例如,在iOS 6.0+上,它會返回黑色圖像。 您需要在將內容呈現給屏幕之前使用glReadPixels() (我的建議),或者為OpenGL ES上下文啟用保留的后備(這會產生不利的性能影響)。

其次,不需要兩個緩沖區。 您可以直接捕獲到一個並使用它來創建CGImageRef。

對於您的核心問題,問題是您在CGImageRef / UIImage仍然依賴它時釋放原始圖像字節緩沖區。 這會從你的UIImage下方拉出地毯,並導致你看到的圖像損壞/崩潰。 為了解決這個問題,您需要在CGDataProvider的重新分配時觸發一個回調函數。 這就是我在GPUImage框架中執行此操作的方法:

rawImagePixels = (GLubyte *)malloc(totalBytesForImage);
glReadPixels(0, 0, (int)currentFBOSize.width, (int)currentFBOSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
dataProvider = CGDataProviderCreateWithData(NULL, rawImagePixels, totalBytesForImage, dataProviderReleaseCallback);
cgImageFromBytes = CGImageCreate((int)currentFBOSize.width, (int)currentFBOSize.height, 8, 32, 4 * (int)currentFBOSize.width, defaultRGBColorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaLast, dataProvider, NULL, NO, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);

回調函數采用以下形式:

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

僅當包含CGImageRef(和擴展名CGDataProvider)的UIImage被釋放時,才會調用此函數。 在此之前,包含圖像字節的緩沖區仍然存在。

作為一個功能示例,您可以在GPUImage中檢查我是如何做到這一點的。 看一下GPUImageFilter類,了解如何從OpenGL ES框架中提取圖像,包括使用紋理緩存而不是glReadPixels()的更快方法。

好吧 - 從我的經驗來看,你現在不能只抓住緩沖區中的像素,你需要重新建立正確的上下文,在最終發布上下文之前繪制並抓住那些

=>這主要適用於設備和ios6

EAGLContext* previousContext = [EAGLContext currentContext];
[EAGLContext setCurrentContext: self.context];

    [self fillBuffer:sender];

    //GRAB the pixels here

    [EAGLContext setCurrentContext:previousContext];

或者(這就是我如何做)創建一個新的FrameBuffer,填充THAT並從THERE中獲取像素

GLuint rttFramebuffer;
glGenFramebuffers(1, &rttFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, rttFramebuffer);

[self fillBuffer:self.displayLink];

size_t size = viewportHeight * viewportWidth * 4;
GLubyte *pixels = malloc(size*sizeof(GLubyte));
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glReadPixels(0, 0, viewportWidth, viewportHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

// Restore the original framebuffer binding
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glDeleteFramebuffers(1, &rttFramebuffer);

size_t bitsPerComponent = 8;
size_t bitsPerPixel = 32;
size_t bytesPerRow = viewportWidth * bitsPerPixel / bitsPerComponent;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, size, ImageProviderReleaseData);
CGImageRef cgImage = CGImageCreate(viewportWidth, viewportHeight, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, provider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);

UIImage *image = [UIImage imageWithCGImage:cgImage scale:self.contentScaleFactor orientation:UIImageOrientationDownMirrored];
CGImageRelease(cgImage);
CGColorSpaceRelease(colorSpace);

編輯:刪除了對presentBuffer的調用

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM