from UIHandling import * from Quartz import * import math kOurImageFile = "ptlobos.tif" # For best performance make bytesPerRow a multiple of 16 bytes. BEST_BYTE_ALIGNMENT = 16 def COMPUTE_BEST_BYTES_PER_ROW(bpr): return ((bpr + (BEST_BYTE_ALIGNMENT-1)) & ~(BEST_BYTE_ALIGNMENT-1)) def DEGREES_TO_RADIANS(degrees): return degrees * math.pi / 180 _colorSpace = None def myGetGenericRGBSpace(): global _colorSpace if _colorSpace is None: _colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB) return _colorSpace _blue = None def myGetBlueColor(): global _blue if _blue is None: _blue = CGColorCreate(myGetGenericRGBSpace(), (0, 0, 1, 1)) return _blue _green = None def myGetGreenColor(): global _green if _green is None: _green = CGColorCreate(myGetGenericRGBSpace(), (0, 1, 0, 1)) return _green _red = None def myGetRedColor(): global _red if _red is None: _red = CGColorCreate(myGetGenericRGBSpace(), (1, 0, 0, 1)) return _red _ourImageURL = None def doDrawImageFile(context, doclip): global _ourImageURL if _ourImageURL is None: mainBundle = CFBundleGetMainBundle() if mainBundle: _ourImageURL = CFBundleCopyResourceURL(mainBundle, kOurImageFile, None, None) else: print "Can't get the app bundle!" if _ourImageURL: if doclip: clipImageToEllipse(context, _ourImageURL) else: drawCGImage(context, _ourImageURL) else: print "Couldn't create the URL for our Image file!" def myDispatchDrawing(context, drawingType): if drawingType == kCommandStrokedAndFilledRects: drawStrokedAndFilledRects(context) elif drawingType == kCommandAlphaRects: drawAlphaRects(context) elif drawingType == kCommandSimpleClip: doDrawImageFile(context, True) elif drawingType == kCommandDrawImageFile: doDrawImageFile(context, False) elif drawingType == kCommandDoUncachedDrawing: drawUncachedForLayer(context) elif drawingType == kCommandDoCGLayer: drawSimpleCGLayer(context) def drawStrokedAndFilledRects(context): ourRect = CGRectMake(40, 40, 130, 100) # Set the fill color to an opaque blue. CGContextSetFillColorWithColor(context, myGetBlueColor()) # Fill the rect. CGContextFillRect(context, ourRect) # Set the stroke color to an opaque green. CGContextSetStrokeColorWithColor(context, myGetGreenColor()) # Stroke the rect with a line width of 10 units. CGContextStrokeRectWithWidth(context, ourRect, 10) # Save the current graphics state. CGContextSaveGState(context) # Translate the coordinate system origin to the right # by 200 units. CGContextTranslateCTM(context, 200, 0) # Stroke the rect with a line width of 10 units. CGContextStrokeRectWithWidth(context, ourRect, 10) # Fill the rect. CGContextFillRect(context, ourRect) # Restore the graphics state to the previously saved # graphics state. This restores all graphics state # parameters to those in effect during the last call # to CGContextSaveGState. In this example that restores # the coordinate system to that in effect prior to the # call to CGContextTranslateCTM. CGContextRestoreGState(context) # Create a mutable path object that represents 'rect'. # Note that this is for demonstrating how to create a simple # CGPath object. The Quartz function CGPathAddRect would normally # be a better choice for adding a rect to a CGPath object. def createRectPath(rect): path = CGPathCreateMutable() # Start a new subpath. CGPathMoveToPoint(path, None, rect.origin.x, rect.origin.y) # ***** Segment 1 ***** CGPathAddLineToPoint(path, None, rect.origin.x + rect.size.width, rect.origin.y) # ***** Segment 2 ***** CGPathAddLineToPoint(path, None, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height) # ***** Segment 3 ***** CGPathAddLineToPoint(path, None, rect.origin.x, rect.origin.y + rect.size.height) # ***** Segment 4 is created by closing the path ***** CGPathCloseSubpath(path) return path def drawAlphaRects(context): ourRect = CGRectMake(0, 0, 130, 100) numRects = 6 rotateAngle = 2*math.pi/numRects tintAdjust = 1.0/numRects # Create the path object representing our rectangle. This # example is for demonstrating the use of a CGPath object. # For a simple rectangular shape, you'd typically use # CGContextFillRect or CGContextStrokeRect instead of this # approach. path = createRectPath(ourRect) # Move the origin of coordinates to a location that allows # the drawing to be within the window. CGContextTranslateCTM(context, 2*ourRect.size.width, 2*ourRect.size.height) # Set the fill color to a red color. CGContextSetFillColorWithColor(context, myGetRedColor()) tint = 1.0 while 0 < tint: # Set the global alpha to the tint value. CGContextSetAlpha(context, tint) # For a CGPath object that is a simple rect, # this is equivalent to CGContextFillRect. CGContextBeginPath(context) CGContextAddPath(context, path) CGContextFillPath(context) # These transformations are cummulative. CGContextRotateCTM(context, rotateAngle) tint -= tintAdjust def drawCGImage(context, url): # Create a CGImageSource object from 'url'. imageSource = CGImageSourceCreateWithURL(url, None) # Create a CGImage object from the first image in the file. Image # indexes are 0 based. image = CGImageSourceCreateImageAtIndex(imageSource, 0, None) # Create a rectangle that has its origin at (100, 100) with the width # and height of the image itself. imageRect = CGRectMake(100, 100, CGImageGetWidth(image), CGImageGetHeight(image)) # Draw the image into the rect. CGContextDrawImage(context, imageRect, image) def clipImageToEllipse(context, url): # Create a CGImageSource object from 'url'. imageSource = CGImageSourceCreateWithURL(url, None) # Create a CGImage object from the first image in the file. Image # indexes are 0 based. image = CGImageSourceCreateImageAtIndex( imageSource, 0, None ) # Create a rectangle that has its origin at (100, 100) with the width # and height of the image itself. imageRect = CGRectMake(100, 100, CGImageGetWidth(image), CGImageGetHeight(image)) CGContextBeginPath(context) # Create an elliptical path corresponding to the image width and height. CGContextAddEllipseInRect(context, imageRect) # Clip to the current path. CGContextClip(context) # Draw the image into the rect, clipped by the ellipse. CGContextDrawImage(context, imageRect, image) 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 = 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 = 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. CGContextClearRect(bitmapContext, CGRectMake(0, 0, width, height)) # Scale the coordinate system so that 72 units are dpi pixels. 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 = CGBitmapContextCreateImage(bitmapContext) # Return the CGImage object this code created from the drawing. return image def myExportCGDrawingAsPNG(url, drawingCommand): dpi = 300 # Create an RGBA image from the Quartz drawing that corresponds to drawingCommand. image = createRGBAImageFromQuartzDrawing(dpi, drawingCommand) # Create a CGImageDestination object will write PNG data to URL. # We specify that this object will hold 1 image. imageDestination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, None) properties = { kCGImagePropertyDPIWidth: dpi, kCGImagePropertyDPIHeight: dpi, } # Add the image to the destination, characterizing the image with # the properties dictionary. CGImageDestinationAddImage(imageDestination, image, properties) # When all the images (only 1 in this example) are added to the destination, # finalize the CGImageDestination object. CGImageDestinationFinalize(imageDestination) def createCachedContent(c): # The cached content will be 50x50 units. width = height = 50 # Create the layer to draw into. layer = CGLayerCreateWithContext(c, CGSizeMake(width, height), None) # Get the CG context corresponding to the layer. layerContext = CGLayerGetContext(layer) # Cache some very simple drawing just as an example. CGContextFillRect(layerContext, CGRectMake(0, 0, width, height) ) # The layer now contains cached drawing so return it. return layer def drawSimpleCGLayer(context): # Create a CGLayer object that represents some drawing. layer = createCachedContent(context) # Get the size of the layer created. s = CGLayerGetSize(layer); # Position the drawing to an appropriate location. CGContextTranslateCTM(context, 40, 100) # Paint 4 columns of layer objects. for i in range(4): # Draw the layer at the point that varies as the code loops. CGContextDrawLayerAtPoint(context, CGPointMake(2*(i+1)*s.width, 0), layer) # The equivalent drawing as doSimpleCGLayer but without creating # a CGLayer object and caching that drawing to a layer. def drawUncachedForLayer(context): r = CGRectMake(0, 0, 50, 50) CGContextTranslateCTM(context, 40, 100) for i in range(4): # Adjust the origin as the code loops. Recall that # transformations are cummulative. CGContextTranslateCTM( context, 2*CGRectGetWidth(r), 0 ) CGContextFillRect(context, r) # Do the uncached drawing. # Create a PDF document at 'url' from the drawing represented by drawingCommand. def myCreatePDFDocument(url, drawingCommand): # mediaRect represents the media box for the PDF document the code is # creating. The size here is that of a US Letter size sheet. mediaRect = CGRectMake(0, 0, 8.5*72, 11*72) # Create a CGContext object to capture the drawing as a PDF document located # at 'url'. pdfContext, mediaRect = CGPDFContextCreateWithURL(url, mediaRect, None) # Start capturing drawing on a page. mediaRect = CGContextBeginPage(pdfContext, mediaRect) # Perform drawing for the first page. myDispatchDrawing(pdfContext, drawingCommand) # Tell the PDF context that drawing for the current page is finished. CGContextEndPage(pdfContext) # If there were more pages they would be captured as: # # mediaRect = CGContextBeginPage(pdfContext, None) # # DrawingForPage2(pdfContext) # # CGContextEndPage(pdfContext) # # mediaRect = CGContextBeginPage(pdfContext, None) #