-1

Is there any way to Convert NSView Contain many Subviews with background color into NSImage ? Sample code will be great

    NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[_collageView bounds]];
        [_collageView unlockFocus];
    //    NSData *exportedData = [rep representationUsingType:NSJPEGFileType properties:nil];
          NSData *exportedData = [rep representationUsingType:NSPNGFileType properties:nil];


        NSSavePanel *savepanel = [NSSavePanel savePanel];
    savepanel.title = @"Save chart";
    [savepanel setAllowedFileTypes:[NSArray arrayWithObject:@"png"]];
    [savepanel beginSheetModalForWindow:self.view.window completionHandler:^(NSInteger result)
     {
         if (NSFileHandlingPanelOKButton == result)
         {
             NSURL* fileURL = [savepanel URL];
             if ([fileURL.pathExtension isEqualToString:@""])
                 fileURL = [fileURL URLByAppendingPathExtension:@"png"];

             [exportedData writeToURL:fileURL atomically:YES];
         }
     }];

i also used the following code to

 [_collageView lockFocus];
    NSBitmapImageRep *bits = [[NSBitmapImageRep alloc]initWithFocusedViewRect: [_collageView bounds]];
    [_collageView unlockFocus];
    NSImage *image1 = [[NSImage alloc] init];
    [image1 addRepresentation: bits];
    [self.ImageView setImage:image1];

but not working

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • What does "not working" mean? Have you debugged the code? Are any of the variables `nil`? Does the image look wrong? Your first code block is missing a `[_collageView lockFocus];` at the beginning, is this a copy&paste error or is the line really missing? Are you aware there is a [different way](http://stackoverflow.com/questions/3251261/how-do-i-take-a-screenshot-of-an-nsview)? – DarkDust Dec 18 '14 at 19:20
  • Yes i did all the thing ,first code creating black image.... and Preview of 2nd code also creating black image .....i am new in OSx development this is my first project thats why... – IOSDeveloper Dec 18 '14 at 19:28
  • …and the second code? And what about the missing `lockFocus` in the first code? – DarkDust Dec 18 '14 at 19:28
  • 2nd code also creating black image and yes that was the copy paste error – IOSDeveloper Dec 18 '14 at 19:30
  • I tried all ways but not able to Export NSView as NSImage – IOSDeveloper Dec 18 '14 at 19:31
  • Alright, since this was the [problem of another user](http://stackoverflow.com/a/8891319/400056) I have to ask this: is your view drawing any colors _besides_ black? The default background color is going to be black, and if your view draws black lines/text you won't see anything. – DarkDust Dec 18 '14 at 19:35
  • Always creating a transparent view i tried to export view with random color but still creating blank transparent image...i think these is something wrong with the code or not able to convert NSView which is a subview on main window – IOSDeveloper Dec 18 '14 at 19:39
  • You neglected to mention that the views in question were layer-backed views: http://stackoverflow.com/questions/1978319/how-do-i-render-a-view-which-contains-core-animation-layers-to-a-bitmap – NSGod Dec 19 '14 at 16:37

3 Answers3

2

Your approach copies the bitmap from the windows server. This is the rendered content of the view. Typically you do not want to do that.

It is a better way to lock the focus on an NSImage, which builds up the graphic contexts and then draw into that image using -drawRect: or whatever drawing methods you have. You can add subviews by simply iterating them.

// perhaps in a category of NSView
- (void)drawRecursively
{
    [self drawRect:self.bounds];
    for (NSView *subview in self.subviews)
    {
        [subview drawRecursively];
    }
}

To start that

NSImage *image = [[NSImage alloc] initWithSize:theView.bounds.size];
[image lockFocus]; // Set-up the context for the image
[theView drawRecursively];
[image unlockFocus];

Typped in Safari.

Edit: Before going to the subviews, you have to perform a transformation for them. Forgot that.

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
0

I have actually implemented the drawing of a view hierarchy offscreen myself. It is non-trivial, even for a very simple view hierarchy where all views are subclasses NSControl and have -(BOOL) isFlipped { return YES; }

Here's one small portion of that code, which will give you an idea of the pain of doing this:

- (void)drawRect:(NSRect)iDirtyBigRect
{
#ifdef DISABLE_PSEUDOVIEW
   debugRectsBeingDrawn();
   return;
#endif

   if ( !drawableShape )
   {
      if ( [[[self window] contentView] wantsLayer] )
      {
         CGRect cgRect = NSRectToCGRect([self bounds]);
         drawableShape = HIShapeCreateMutableWithRect( &cgRect );
      }
      else
      {
         DUMP( @"our superview should have set up our drawableShape!" );
         [self setNeedsDisplay:YES];
         return;
      }
   }
   else if ( iDirtyBigRect.size.width <= 0 )
   {
      // Our drawable shape is empty.
      // This can happen for several valid reasons:
      // 1) dirtyShape is fully masked by opaqueShape
      // 2) for some reason, we were called to obey needsDisplay when not needed
      DLog( @"nothing to draw %@", self );
      return; // must return, otherwise we risk drawing outside our bounds
   }

   //NSArray *hardSubviews = [self subviews];
   for ( NSView *pseudoSubview in pseudoSubviews )
   {
      if ( ![pseudoSubview isHidden] && 
          [pseudoSubview window] == offscreenWindow ) // only draw pseudo view if it's in offscreen window
      {
         NSRect rectToDraw = [pseudoSubview frame];
//         if ( rectToDraw.size.width == 0 )
//         {
//            DLog( @"clipped to superview %@", pseudoSubview );
//         }
         CGRect cgRect = NSRectToCGRect(rectToDraw);
         if ( HIShapeIntersectsRect(drawableShape, &cgRect) )
         {         
            // the magic: transform to subview coordinates
            NSAffineTransform* xform = [NSAffineTransform transform];

            [xform translateXBy:rectToDraw.origin.x yBy:rectToDraw.origin.y];
            if ( [pseudoSubview isFlipped] != [self isFlipped] )
            {
               if ( ![pseudoSubview isKindOfClass: [NSColorWell class]] )
               {
                  DUMP( @"hmmm flippedness different %d %d", [pseudoSubview isFlipped], [self isFlipped] );
               }
               [xform translateXBy:0.0 yBy:rectToDraw.size.height];
               [xform scaleXBy:1.0 yBy:-1.0];
            }
            [xform concat];

            HIMutableShapeRef newShape = HIShapeCreateMutableWithRect( &cgRect );
            HIShapeIntersect( newShape, drawableShape, newShape ); // clip to subview frame
            HIShapeOffset( newShape, -cgRect.origin.x, -cgRect.origin.y ); // translate to subview coords
            CGRect dirtyRect;
            HIShapeGetBounds(newShape, &dirtyRect);
//            if ( dirtyRect.size.width <= 0 )
//            {
//               DUMP( @"why?" );
//            }

            if ( [pseudoSubview isKindOfClass:[PseudoView class]] )
            {
               //DLog( @"drawing Pseudo %@ dirtyRect %@ ", pseudoSubview, NSStringFromRect(NSRectFromCGRect(dirtyRect)));

               PseudoView *pView = (PseudoView *)pseudoSubview;
               if ( pView->drawableShape )
               {
                  CFRelease( pView->drawableShape );
                  pView->drawableShape = NULL;
               }
               // create subview personal drawableShape from my drawable shape
               pView->drawableShape = newShape;
               if ( [pseudoSubview isOpaque] )
                  gDrawDirtyPixels += dirtyRect.size.width * dirtyRect.size.height;

               if ( dirtyRect.size.width > 0 )
                  [pseudoSubview drawRect: NSRectFromCGRect(dirtyRect)];

            }
            else
            {
               //DLog( @"drawing non-Pseudo %@ rectToDraw %@ ", pseudoSubview, NSStringFromRect( rectToDraw ));

               UInt64 t1 = CpuCycles();
               CFRelease( newShape );
               // sacrifice efficiency to avoid bugs...
               // always draw the entire view.
               [pseudoSubview drawRect:[pseudoSubview bounds]]; // NSRectFromCGRect(dirtyRect)]; // [pseudoSubview bounds]];
               UnitTestQuartz( [pseudoSubview bounds] );
               // NSLog( @"drawRect %@ %@ sUnitTestQuartzToggle %f\n -- superv %@\n -- self %@", pseudoSubview, NSStringFromRect(rectToDraw), sUnitTestQuartzToggle*100, [pseudoSubview superview],  self );
               UInt64 t2 = CpuCycles();
               int diff = (int)(t2-t1);
               gDrawControl += diff;
            }


            [xform invert]; // restore
            [xform concat];
         }
      }
   }

   //if ( [self isKindOfClass:[PseudoRootView class]] )
   //   [offscreenWindow setViewsNeedDisplay:NO];
}
Keith Knauber
  • 752
  • 6
  • 13
0

I am able to solve my problem with the following Code

  // Setup the image to render
        NSRect imgRect = _collageView.frame;
        NSSize imgSize = imgRect.size;

        NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
                                                                        pixelsWide:imgSize.width
                                                                        pixelsHigh:imgSize.height
                                                                     bitsPerSample:8
                                                                   samplesPerPixel:4
                                                                          hasAlpha:YES
                                                                          isPlanar:NO
                                                                    colorSpaceName:NSDeviceRGBColorSpace
                                                                      bitmapFormat:NSAlphaFirstBitmapFormat
                                                                       bytesPerRow:0
                                                                      bitsPerPixel:0];

        NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep];
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:context];
        // Render
        CGContextRef zCgContextRef = (CGContextRef) [context graphicsPort];
        [[_collageView layer] renderInContext:zCgContextRef];

        // Get the Data for the image
        NSData *exportedData = [rep representationUsingType:NSJPEGFileType properties:nil];


        // Start the savepanel
        NSSavePanel *savepanel = [NSSavePanel savePanel];
        savepanel.title = @"Save chart";

        [savepanel setAllowedFileTypes:[NSArray arrayWithObject:@"jpg"]];
        [savepanel beginSheetModalForWindow:self.view.window completionHandler:^(NSInteger result)
         {
             if (NSFileHandlingPanelOKButton == result)
             {
                 NSURL* fileURL = [savepanel URL];

                 if ([fileURL.pathExtension isEqualToString:@""])
                     fileURL = [fileURL URLByAppendingPathExtension:@"jpg"];

                 [exportedData writeToURL:fileURL atomically:YES];
             }
         }];