def createRGBAImageFromQuartzDrawing(dpi, drawingCommand): # For generating RGBA data from drawing. Use a Letter size page as the # image dimensions. Typically this size would be the minimum necessary to # capture the drawing of interest. We want 8 bits per component and for # RGBA data there are 4 components. width = 8.5 * dpi height = 11 * dpi bitsPerComponent = 8 numComps = 4 # Compute the minimum number of bytes in a given scanline. bytesPerRow = width * bitsPerComponent / 8 * numComps # This bitmapInfo value specifies that we want the format where alpha is # premultiplied and is the last of the components. We use this to produce # RGBA data. bitmapInfo = Quartz.kCGImageAlphaPremultipliedLast # Round to nearest multiple of BEST_BYTE_ALIGNMENT for optimal performance. bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow) # Allocate the data for the bitmap. data = array.array("c", "\0" * bytesPerRow * height) # Create the bitmap context. Characterize the bitmap data with the # Generic RGB color space. bitmapContext = Quartz.CGBitmapContextCreate( data, width, height, bitsPerComponent, bytesPerRow, myGetGenericRGBSpace(), bitmapInfo, ) # Clear the destination bitmap so that it is completely transparent before # performing any drawing. This is appropriate for exporting PNG data or # other data formats that capture alpha data. If the destination output # format doesn't support alpha then a better choice would be to paint # to white. Quartz.CGContextClearRect(bitmapContext, Quartz.CGRectMake(0, 0, width, height)) # Scale the coordinate system so that 72 units are dpi pixels. Quartz.CGContextScaleCTM(bitmapContext, dpi / 72, dpi / 72) # Perform the requested drawing. myDispatchDrawing(bitmapContext, drawingCommand) # Create a CGImage object from the drawing performed to the bitmapContext. image = Quartz.CGBitmapContextCreateImage(bitmapContext) # Return the CGImage object this code created from the drawing. return image
def testFunctions(self): bytes_val = array.array("B", (0 for i in range(100 * 80 * 4))) self.assertIsInstance(bytes_val, array.array) self.assertEqual(len(bytes_val), 100 * 80 * 4) ctx = Quartz.CGBitmapContextCreate( bytes_val, 100, 80, 8, 400, Quartz.CGColorSpaceCreateDeviceRGB(), Quartz.kCGImageAlphaPremultipliedLast, ) self.assertIsInstance(ctx, Quartz.CGContextRef) buf = Quartz.CGBitmapContextGetData(ctx) self.assertIsInstance(buf, objc.varlist) self.assertIsInstance(buf[0], bytes) self.assertEqual(Quartz.CGBitmapContextGetWidth(ctx), 100) self.assertEqual(Quartz.CGBitmapContextGetHeight(ctx), 80) self.assertEqual(Quartz.CGBitmapContextGetBitsPerComponent(ctx), 8) self.assertEqual(Quartz.CGBitmapContextGetBitsPerPixel(ctx), 32) self.assertEqual(Quartz.CGBitmapContextGetBytesPerRow(ctx), 400) v = Quartz.CGBitmapContextGetColorSpace(ctx) self.assertIsInstance(v, Quartz.CGColorSpaceRef) v = Quartz.CGBitmapContextGetAlphaInfo(ctx) self.assertIsInstance(v, int) v = Quartz.CGBitmapContextGetBitmapInfo(ctx) self.assertIsInstance(v, int) img = Quartz.CGBitmapContextCreateImage(ctx) self.assertIsInstance(img, Quartz.CGImageRef)
Quartz.kCGPDFMediaBox) x = Quartz.CGRectGetWidth(mediaBox) y = Quartz.CGRectGetHeight(mediaBox) x *= scale y *= scale r = Quartz.CGRectMake(0, 0, x, y) # Create a Bitmap Context, draw a white background and add the PDF writeContext = Quartz.CGBitmapContextCreate( None, int(x), int(y), 8, 0, cs, transparency) Quartz.CGContextSaveGState(writeContext) Quartz.CGContextScaleCTM(writeContext, scale, scale) Quartz.CGContextSetFillColorWithColor(writeContext, whiteColor) Quartz.CGContextFillRect(writeContext, r) Quartz.CGContextDrawPDFPage(writeContext, page) Quartz.CGContextRestoreGState(writeContext) # Convert to an "Image" image = Quartz.CGBitmapContextCreateImage(writeContext) # Create unique filename per page outFile = folderName + "/" + prefix + " %03d.png" % i url = Quartz.CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, outFile, len(outFile), False) # kUTTypeJPEG, kUTTypeTIFF, kUTTypePNG type = kUTTypePNG # See the full range of image properties on Apple's developer pages. options = { Quartz.kCGImagePropertyDPIHeight: resolution, Quartz.kCGImagePropertyDPIWidth: resolution } writeImage(image, url, type, options) del page
def screenshot(self, path, region = None): #https://pythonhosted.org/pyobjc/examples/Quartz/Core%20Graphics/CGRotation/index.html try: # record how long it takes to take screenshot start = time.time() # Set to capture entire screen, including multiple monitors if region is None: region = CG.CGRectInfinite # Create CGImage, composite image of windows in region image = CG.CGWindowListCreateImage( region, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault ) scr = NSScreen.screens() xmin = 0 ymin = 0 for s in scr: if s.frame().origin.x < xmin: xmin = s.frame().origin.x if s.frame().origin.y < ymin: ymin = s.frame().origin.y nativeHeight = CGImageGetHeight(image)*1.0 nativeWidth = CGImageGetWidth(image)*1.0 nativeRatio = nativeWidth/nativeHeight prefHeight = NSUserDefaultsController.sharedUserDefaultsController().values().valueForKey_('imageSize') height = int(prefHeight/scr[0].frame().size.height*nativeHeight) width = int(nativeRatio * height) heightScaleFactor = height/nativeHeight widthScaleFactor = width/nativeWidth mouseLoc = NSEvent.mouseLocation() x = int(mouseLoc.x) y = int(mouseLoc.y) w = 16 h = 24 scale_x = int((x-xmin) * widthScaleFactor) scale_y = int((y-h+5-ymin) * heightScaleFactor) scale_w = w*widthScaleFactor scale_h = h*heightScaleFactor #Allocate image data and create context for drawing image imageData = LaunchServices.objc.allocateBuffer(int(4 * width * height)) bitmapContext = Quartz.CGBitmapContextCreate( imageData, # image data we just allocated... width, height, 8, # 8 bits per component 4 * width, # bytes per pixel times number of pixels wide Quartz.CGImageGetColorSpace(image), # use the same colorspace as the original image Quartz.kCGImageAlphaPremultipliedFirst # use premultiplied alpha ) #Draw image on context at new scale rect = CG.CGRectMake(0.0,0.0,width,height) Quartz.CGContextDrawImage(bitmapContext, rect, image) # Add Mouse cursor to the screenshot cursorPath = "../Resources/cursor.png" cursorPathStr = NSString.stringByExpandingTildeInPath(cursorPath) cursorURL = NSURL.fileURLWithPath_(cursorPathStr) # Create a CGImageSource object from 'url'. cursorImageSource = Quartz.CGImageSourceCreateWithURL(cursorURL, None) # Create a CGImage object from the first image in the file. Image # indexes are 0 based. cursorOverlay = Quartz.CGImageSourceCreateImageAtIndex(cursorImageSource, 0, None) Quartz.CGContextDrawImage(bitmapContext, CG.CGRectMake(scale_x, scale_y, scale_w, scale_h), cursorOverlay) #Recreate image from context imageOut = Quartz.CGBitmapContextCreateImage(bitmapContext) #Image properties dictionary dpi = 72 # FIXME: Should query this from somewhere, e.g for retina display properties = { Quartz.kCGImagePropertyDPIWidth: dpi, Quartz.kCGImagePropertyDPIHeight: dpi, Quartz.kCGImageDestinationLossyCompressionQuality: 0.6, } #Convert path to url for saving image pathWithCursor = path[0:-4] + "_" + str(x) + "_" + str(y) + '.jpg' pathStr = NSString.stringByExpandingTildeInPath(pathWithCursor) url = NSURL.fileURLWithPath_(pathStr) #Set image destination (where it will be saved) dest = Quartz.CGImageDestinationCreateWithURL( url, LaunchServices.kUTTypeJPEG, # file type 1, # 1 image in file None ) # Add the image to the destination, with certain properties Quartz.CGImageDestinationAddImage(dest, imageOut, properties) # finalize the CGImageDestination object. Quartz.CGImageDestinationFinalize(dest) #For testing how long it takes to take screenshot stop = time.time() print 'took ' + str(height) + 'px image in ' + str(stop-start)[:5] + ' seconds' except KeyboardInterrupt: print "Keyboard interrupt" AppHelper.stopEventLoop() except errno.ENOSPC: NSLog("No space left on storage device. Turning off Selfspy recording.") self.delegate.toggleLogging_(self) except: NSLog("couldn't save image")
def screenshot(self, path, region = None): #https://pythonhosted.org/pyobjc/examples/Quartz/Core%20Graphics/CGRotation/index.html try: # record how long it takes to take screenshot start = time.time() scr = NSScreen.screens() # Trying to capture mouse cursor # Quartz.CGDisplayShowCursor(Quartz.CGMainDisplayID()) # Quartz.CGAssociateMouseAndMouseCursorPosition(True) # Set to capture entire screen, including multiple monitors if region is None: region = CG.CGRectInfinite # Create CGImage, composite image of windows in region image = None image = CG.CGWindowListCreateImage( region, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault ) xmin = 0 ymin = 0 for s in scr: if s.frame().origin.x < xmin: xmin = s.frame().origin.x if s.frame().origin.y < ymin: ymin = s.frame().origin.y nativeHeight = CGImageGetHeight(image)*1.0 nativeWidth = CGImageGetWidth(image)*1.0 nativeRatio = nativeWidth/nativeHeight prefHeight = NSUserDefaultsController.sharedUserDefaultsController().values().valueForKey_('imageSize') height = int(prefHeight) #int(prefHeight/scr[0].frame().size.height*nativeHeight) width = int(nativeRatio * height) # Computes the scale factor between the user resolution and the native screen resolution resolutionScaleFactor = scr[0].frame().size.height / nativeHeight # Computes the scale factor between the image size in the preferences and the user resolution prefScaleFactor = height / scr[0].frame().size.height mouseLoc = NSEvent.mouseLocation() x = int(mouseLoc.x) y = int(mouseLoc.y) w = 16 h = 24 scale_x = int((x-xmin) * prefScaleFactor) scale_y = int((y-h+5-ymin) * prefScaleFactor) #int((y-h+5-ymin) * heightScaleFactor) scale_w = w*prefScaleFactor scale_h = h*prefScaleFactor #Allocate image data and create context for drawing image imageData = None imageData = LaunchServices.objc.allocateBuffer(int(100)) imageData = LaunchServices.objc.allocateBuffer(int(4 * width * height)) bitmapContext = None bitmapContext = Quartz.CGBitmapContextCreate( imageData, # image data we just allocated... width, height, 8, # 8 bits per component 4 * width, # bytes per pixel times number of pixels wide Quartz.CGImageGetColorSpace(image), # use the same colorspace as the original image Quartz.kCGImageAlphaPremultipliedFirst # use premultiplied alpha ) #Draw image on context at new scale rect = CG.CGRectMake(0.0,0.0,width,height) Quartz.CGContextDrawImage(bitmapContext, rect, image) # Add Mouse cursor to the screenshot cursorPath = "../Resources/cursor.png" cursorPathStr = NSString.stringByExpandingTildeInPath(cursorPath) cursorURL = NSURL.fileURLWithPath_(cursorPathStr) # Create a CGImageSource object from 'url'. cursorImageSource = None cursorImageSource = Quartz.CGImageSourceCreateWithURL(cursorURL, None) # Create a CGImage object from the first image in the file. Image # indexes are 0 based. cursorOverlay = None cursorOverlay = Quartz.CGImageSourceCreateImageAtIndex(cursorImageSource, 0, None) Quartz.CGContextDrawImage(bitmapContext, CG.CGRectMake(scale_x, scale_y, scale_w, scale_h), cursorOverlay) #Recreate image from context imageOut = Quartz.CGBitmapContextCreateImage(bitmapContext) #Image properties dictionary dpi = 72 # FIXME: Should query this from somewhere, e.g for retina display properties = { Quartz.kCGImagePropertyDPIWidth: dpi, Quartz.kCGImagePropertyDPIHeight: dpi, Quartz.kCGImageDestinationLossyCompressionQuality: 0.6, } # Getting id of current window and application try: activeAppName = self.workspace.activeApplication()['NSApplicationName'] except: activeAppName = "" print "failed NSApplicationName" active_app_id = self.getProcessIDFromName(activeAppName) options = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) for window in windowList: # window_name = str(window.get('kCGWindowName', u'').encode('ascii', 'replace')) window_name = self.getWindowName(window) owner = window['kCGWindowOwnerName'] if (activeAppName == owner and window_name != ''): break active_window_id = self.getWindowIDFromName(window_name) # Done with getting id of current window and application #Convert path to url for saving image pathWithCursor = path[0:-4] + "_" + str(x) + "_" + str(y) if (active_app_id != None and active_app_id != '') : pathWithCursor = pathWithCursor + "_app" + str(active_app_id) if (active_window_id != None and active_window_id != '') : pathWithCursor = pathWithCursor + "_win" + str(active_window_id) pathWithCursor = pathWithCursor + '.jpg' # print pathWithCursor pathStr = NSString.stringByExpandingTildeInPath(pathWithCursor) url = NSURL.fileURLWithPath_(pathStr) #Set image destination (where it will be saved) dest = Quartz.CGImageDestinationCreateWithURL( url, LaunchServices.kUTTypeJPEG, # file type 1, # 1 image in file None ) # Add the image to the destination, with certain properties Quartz.CGImageDestinationAddImage(dest, imageOut, properties) # finalize the CGImageDestination object. Quartz.CGImageDestinationFinalize(dest) #For testing how long it takes to take screenshot stop = time.time() # print 'took ' + str(height) + 'px image in ' + str(stop-start)[:5] + ' seconds' except KeyboardInterrupt: print "Keyboard interrupt" AppHelper.stopEventLoop() except errno.ENOSPC: NSLog("No space left on storage device. Turning off Selfspy recording.") self.delegate.toggleLogging_(self) except: NSLog("couldn't save image")
def IISaveImage(image, url, width, height): result = False # If there is no image, no destination, or the width/height is 0, then fail early. assert ((image is not None) and (url is not None) and (width != 0.0) and (height != 0.0)) # Try to create a jpeg image destination at the url given to us imageDest = Quartz.CGImageDestinationCreateWithURL( url, LaunchServices.kUTTypeJPEG, 1, None) if imageDest is not None: # And if we can, then we can start building our final image. # We begin by creating a CGBitmapContext to host our desintation image. # Allocate enough space to hold our pixels imageData = objc.allocateBuffer(int(4 * width * height)) # Create the bitmap context bitmapContext = Quartz.CGBitmapContextCreate( imageData, # image data we just allocated... width, # width height, # height 8, # 8 bits per component 4 * width, # bytes per pixel times number of pixels wide Quartz.CGImageGetColorSpace( image.fImageRef ), # use the same colorspace as the original image Quartz.kCGImageAlphaPremultipliedFirst, ) # use premultiplied alpha # Check that all that went well if bitmapContext is not None: # Now, we draw the image to the bitmap context IIDrawImageTransformed(image, bitmapContext, Quartz.CGRectMake(0.0, 0.0, width, height)) # We have now gotten our image data to the bitmap context, and correspondingly # into imageData. If we wanted to, we could look at any of the pixels of the image # and manipulate them in any way that we desire, but for this case, we're just # going to ask ImageIO to write this out to disk. # Obtain a CGImageRef from the bitmap context for ImageIO imageIOImage = Quartz.CGBitmapContextCreateImage(bitmapContext) # Check if we have additional properties from the original image if image.fProperties is not None: # If we do, then we want to inspect the orientation property. # If it exists and is not the default orientation, then we # want to replace that orientation in the destination file orientation = IIGetImageOrientation(image) if orientation != 1: # If the orientation in the original image was not the default, # then we need to replace that key in a duplicate of that dictionary # and then pass that dictionary to ImageIO when adding the image. prop = CFDictionaryCreateMutableCopy( None, 0, image.fProperties) orientation = 1 prop[Quartz.kCGImagePropertyOrientation] = orientation # And add the image with the new properties Quartz.CGImageDestinationAddImage(imageDest, imageIOImage, prop) else: # Otherwise, the image was already in the default orientation and we can # just save it with the original properties. Quartz.CGImageDestinationAddImage(imageDest, imageIOImage, image.fProperties) else: # If we don't, then just add the image without properties Quartz.CGImageDestinationAddImage(imageDest, imageIOImage, None) del bitmapContext # Finalize the image destination result = Quartz.CGImageDestinationFinalize(imageDest) del imageDest return result
mediaBox = CG.CGPDFPageGetBoxRect(page, CG.kCGPDFMediaBox) x = CG.CGRectGetWidth(mediaBox) y = CG.CGRectGetHeight(mediaBox) x *= scale y *= scale r = CG.CGRectMake(0,0,x, y) # Create a Bitmap Context, draw a white background and add the PDF ctx = CG.CGBitmapContextCreate(None, int(x), int(y), 8, 0, cs, transparency) CG.CGContextSaveGState (ctx) CG.CGContextScaleCTM(ctx, scale,scale) CG.CGContextSetFillColorWithColor(ctx, whiteColor) CG.CGContextFillRect(ctx, r) CG.CGContextDrawPDFPage(ctx, page) CG.CGContextRestoreGState(ctx) # Convert to an "Image" image = CG.CGBitmapContextCreateImage(ctx) # Create unique filename per page outFile = shortName +"//" + prefix + " %03d.tif"%i url = CG.CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, outFile, len(outFile), False) # kUTTypeJPEG, kUTTypeTIFF, kUTTypePNG type = kUTTypeTIFF # For some reason, this doesn't seem to be passed to the TIFF file. options = { CG.kCGImagePropertyTIFFDictionary: 'TIFFDictionary', CG.kCGImagePropertyTIFFXResolution: 300, CG.kCGImagePropertyTIFFYResolution: 300, } writeImage (image, url, type, options) del page #CGContextRelease(ctx) # Not needed apparently. Causes crash.