1import sys 2 3import DataProvidersAndConsumers 4import Utilities 5 6from Quartz import * 7import Quartz 8 9from LaunchServices import * # kUTType* constants 10 11 12def drawJPEGImage(context, url): 13 # Create a Quartz data provider for the supplied URL. 14 jpgProvider = CGDataProviderCreateWithURL(url) 15 if jpgProvider is None: 16 print >>sys.stderr, "Couldn't create JPEG Data provider!" 17 return 18 19 # Create the CGImageRef for the JPEG image from the data provider. 20 jpgImage = CGImageCreateWithJPEGDataProvider(jpgProvider, None, 21 True, kCGRenderingIntentDefault) 22 23 # CGImageCreateWithJPEGDataProvider retains the data provider. 24 # Since this code created the data provider and this code no 25 # longer needs it, it must release it. 26 del jpgProvider 27 28 if jpgImage is None: 29 print >>sys.stderr, "Couldn't create CGImageRef for JPEG data!" 30 return 31 32 # Make a rectangle that has its origin at (0,0) and 33 # has a width and height that is 1/4 the native width 34 # and height of the image. 35 jpgRect = CGRectMake(0.0, 0.0, 36 CGImageGetWidth(jpgImage)/4, CGImageGetHeight(jpgImage)/4) 37 38 39 # Draw the image into the rectangle. 40 # This is Image 1. 41 CGContextDrawImage(context, jpgRect, jpgImage) 42 43 CGContextSaveGState(context) 44 45 # Translate to the top-right corner of the image just drawn. 46 CGContextTranslateCTM(context, jpgRect.size.width, 47 jpgRect.size.height) 48 # Rotate by -90 degrees. 49 CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-90)) 50 # Translate in -x by the width of the drawing. 51 CGContextTranslateCTM(context, -jpgRect.size.width, 0) 52 53 # Draw the image into the same rectangle as before. 54 # This is Image 2. 55 CGContextDrawImage(context, jpgRect, jpgImage) 56 CGContextRestoreGState(context) 57 58 CGContextSaveGState(context) 59 60 # Translate so that the next drawing of the image appears 61 # below and to the right of the image just drawn. 62 CGContextTranslateCTM(context, 63 jpgRect.size.width+jpgRect.size.height, jpgRect.size.height) 64 # Scale the y axis by a negative value and flip the image. 65 CGContextScaleCTM(context, 0.75, -1.0) 66 # This is Image 3. 67 CGContextDrawImage(context, jpgRect, jpgImage) 68 CGContextRestoreGState(context) 69 70 # Adjust the position of the rectangle so that its origin is 71 # to the right and above where Image 3 was drawn. Adjust the 72 # size of the rectangle so that it is 1/4 the image width 73 # and 1/6 the image height. 74 jpgRect = CGRectMake( 1.75*jpgRect.size.width + jpgRect.size.height, 75 jpgRect.size.height, 76 CGImageGetWidth(jpgImage)/4, 77 CGImageGetHeight(jpgImage)/6) 78 # This is Image 4. 79 CGContextDrawImage(context, jpgRect, jpgImage) 80 81def drawImageFromURL(context, url, width, height, bitsPerComponent, isRGB): 82 # This routine treats color images as RGB 83 if isRGB: 84 bitsPerPixel = bitsPerComponent * 3 85 else: 86 bitsPerPixel = bitsPerComponent 87 88 bytesPerRow = (width * bitsPerPixel + 7)/8 89 shouldInterpolate = True 90 91 # Create a Quartz data provider from the supplied URL. 92 dataProvider = CGDataProviderCreateWithURL(url) 93 if dataProvider is None: 94 print >>sys.stderr, "Couldn't create Image data provider!" 95 return 96 97 # Get a Quartz color space object appropriate for the image type. 98 if isRGB: 99 colorspace = Utilities.getTheCalibratedRGBColorSpace() 100 else: 101 colorspace = Utilities.getTheCalibratedGrayColorSpace() 102 103 # Create an image of the width, height, and bitsPerComponent with 104 # no alpha data, the default decode array, with interpolation, 105 # and the default rendering intent for images. This code is 106 # intended for Gray images of the format GGGGG... or RGB images 107 # of the format RGBRGBRGB... . 108 image = CGImageCreate(width, height, bitsPerComponent, 109 bitsPerPixel, bytesPerRow, colorspace, 110 kCGImageAlphaNone, dataProvider, None, 111 shouldInterpolate, kCGRenderingIntentDefault) 112 # Quartz retains the data provider with the image and since this 113 # code does not create any more images with the data provider, it 114 # can release it. 115 del dataProvider 116 if image is None: 117 print >>sys.stderr, "Couldn't create CGImageRef for this data!" 118 return 119 120 # Create a rectangle into which the code will draw the image. 121 imageRect = CGRectMake(0.0, 0.0, width, height) 122 123 # Draw the image into the rectangle. 124 CGContextDrawImage(context, imageRect, image) 125 126def doColorRampImage(context): 127 width = 256 128 height = 256 129 bitsPerComponent = 8 130 bitsPerPixel = 24 131 bytesPerRow = width * 3 132 shouldInterpolate = True 133 134 imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider() 135 if imageDataProvider is None: 136 print >>sys.stderr, "Couldn't create Image Data provider!" 137 return 138 139 colorspace = Utilities.getTheCalibratedRGBColorSpace() 140 image = CGImageCreate(width, height, bitsPerComponent, 141 bitsPerPixel, bytesPerRow, colorspace, kCGImageAlphaNone, 142 imageDataProvider, None, shouldInterpolate, 143 kCGRenderingIntentDefault) 144 # No longer need the data provider. 145 del imageDataProvider 146 if image is None: 147 print >>sys.stderr, "Couldn't create CGImageRef for this data!" 148 return 149 150 imageRect = CGRectMake(0.0, 0.0, width, height) 151 # Draw the image. 152 CGContextDrawImage(context, imageRect, image) 153 154def doImageWithCallbacksCreatedFromURL(context, url, width, height, 155 bitsPerComponent, isRGB): 156 157 if isRGB: 158 bitsPerPixel = bitsPerComponent * 3 159 else: 160 bitsPerPixel = bitsPerComponent 161 162 bytesPerRow = ((width * bitsPerPixel) + 7)/8 163 shouldInterpolate = True 164 165 dataProvider = DataProvidersAndConsumers.createSequentialAccessDPForURL(url) 166 if dataProvider is None: 167 print >>sys.stderr, "Couldn't create Image Data provider!" 168 return 169 170 # Create a Quartz color space object appropriate for the image type. 171 # These user written functions create the color space object 172 # and that reference must be released by this code. 173 if isRGB: 174 colorspace = Utilities.getTheCalibratedRGBColorSpace() 175 else: 176 colorspace = Utilities.getTheCalibratedGrayColorSpace() 177 178 image = CGImageCreate(width, height, bitsPerComponent, 179 bitsPerPixel, bytesPerRow, colorspace, 180 kCGImageAlphaNone, dataProvider, None, shouldInterpolate, 181 kCGRenderingIntentDefault) 182 del dataProvider 183 if image is None: 184 print >>sys.stder, "Couldn't create CGImageRef for this data!" 185 return 186 187 imageRect = CGRectMake(0.0, 0.0, width, height) 188 189 # Draw the image into the rectangle. 190 CGContextDrawImage(context, imageRect, image) 191 192def doGrayRamp(context): 193 width = 256 194 height = 1 195 bitsPerComponent = 8 196 bitsPerPixel = 8 197 bytesPerRow = width 198 shouldInterpolate = True 199 200 dataProvider = DataProvidersAndConsumers.createGrayRampDirectAccessDP() 201 if dataProvider is None: 202 print >>sys.stderr, "Couldn't create Gray Ramp provider!" 203 return 204 205 colorspace = Utilities.getTheCalibratedGrayColorSpace() 206 image = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, 207 bytesPerRow, colorspace, kCGImageAlphaNone, dataProvider, 208 None, shouldInterpolate, kCGRenderingIntentDefault) 209 del dataProvider 210 if image is None: 211 print >>sys.stderr, "Couldn't create CGImageRef for image data!" 212 return 213 214 imageRect = CGRectMake(0.0, 0.0, 256, 256) 215 # Drawing the image that is 256 samples wide and 216 # 1 scanline high into a rectangle that is 256 x 256 units 217 # on a side causes Quartz to stretch the image to fill 218 # the destination rectangle. 219 CGContextDrawImage(context, imageRect, image) 220 221# This routine examines the CGImageSource at index 0 to 222# determine if the first image is a floating point image and 223# if it is, it returns an options dictionary suitable for 224# passing to CGImageSourceCreateImageAtIndex in order to create 225# a CGImageRef that contains full dynamic range floating point data. 226def createFloatingPointImageOptions(imageSource): 227 # Allow the image to be a floating point image. 228 # Without this, Quartz would return integer pixel data, even for 229 # floating point images. Typically you don't need floating point data 230 # but in some special cases you might want it. 231 options = { 232 kCGImageSourceShouldAllowFloat: True 233 } 234 isFloat = False 235 236 # Obtain the properties for the first image 237 # in the image source. This is a 'Copy' function 238 # so the code owns a reference to the 239 # dictionary returned. 240 properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 241 0, options) 242 if properties is not None: 243 # Get the value for the kCGImagePropertyIsFloat if it exists 244 # and if the value is a CFBoolean then get the corresponding 245 # Boolean result. 246 if kCGImagePropertyIsFloat in properties: 247 isFloat = bool(properties[kCGImagePropertyIsFloat]) 248 249 if not isFloat: 250 return None 251 252 return options 253 254def myCreateImageUsingImageSource(url): 255 # Set to zero, indicating the property was unavailable. 256 xdpi = ydpi = 0 257 258 # Create the image source from the URL. 259 imageSource = CGImageSourceCreateWithURL(url, None) 260 if imageSource is None: 261 print >>sys.stderr, "Couldn't create image source from URL!" 262 return (None, xdpi, ydpi) 263 264 if False: 265 options = createFloatingPointImageOptions(imageSource) 266 if options is not None: 267 print >>sys.stderr, "image IS a floating point image" 268 else: 269 print >>sys.stderr, "image IS NOT a floating point image" 270 else: 271 options = None 272 273 # Obtain the properties dictionary for the first image 274 # in the image source. This is a copy function so this 275 # code owns the reference returned and must 276 # must release it. 277 properties = CGImageSourceCopyPropertiesAtIndex( 278 imageSource, 0, options) 279 if properties is not None: 280 # Check for the x and y resolution of the image. 281 xdpi = properties[kCGImagePropertyDPIWidth] 282 ydpi = properties[kCGImagePropertyDPIHeight] 283 284 # Create a CGImageRef from the first image in the CGImageSource. 285 image = CGImageSourceCreateImageAtIndex(imageSource, 0, options) 286 # Release the CGImageSource object since it is no longer needed 287 # and this code created it. This code uses CFRelease since a 288 # CGImageSource object is a CoreFoundation object. 289 del imageSource 290 del options 291 292 if image is None: 293 print >>sys.stderr, "Couldn't create image from image source!" 294 return None 295 296 return (image, xdpi, ydpi) 297 298def myCreateThumbnailFromImageSource(url): 299 maxThumbSize = 160 300 301 # Create the image source from the URL. 302 imageSource = CGImageSourceCreateWithURL(url, None) 303 if imageSource is None: 304 print >>sys.stderr, "Couldn't create image source from URL!" 305 return None 306 307 options = { 308 # Specify 160 pixels as the maximum width and height of 309 # the thumbnail for Quartz to create. 310 kCGImageSourceThumbnailMaxPixelSize: maxThumbSize, 311 312 # Request that Quartz create a thumbnail image if 313 # thumbnail data isn't present in the file. 314 kCGImageSourceCreateThumbnailFromImageIfAbsent: True, 315 } 316 317 # Create the thumbnail image for the first image in the 318 # image source, that at index 0, using the options 319 # dictionary that the code just created. 320 thumb = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options) 321 322 # Release the options dictionary. 323 del options 324 # Release the image source the code created. 325 del imageSource 326 327 if thumb is None: 328 print >>sys.stderr, "Couldn't create thumbnail from image source!" 329 return None 330 331 return thumb 332 333def imageHasFloatingPointSamples(image): 334 if hasattr(Quartz, CGImageGetBitmapInfo): 335 return (kCGBitmapFloatComponents & CGImageGetBitmapInfo(image)) != 0 336 return False 337 338 339def drawImageWithCGImageDataSource(context, url): 340 # This code would be better if it created the image source 341 # once and used the same image source to create the image and its 342 # thumbnail, but the point here is to simply test the routines 343 # myCreateImageUsingImageSource and myCreateThumbnailFromImageSource. 344 345 image, xdpi, ydpi = myCreateImageUsingImageSource(url) 346 if image is None: 347 print >>sys.stderr, "myCreateImageFromImageSource didn't create a CGImage!" 348 return 349 350 print "xdpi = %2.f, ydpi = %2.f"%(xdpi, ydpi) 351 imageRect = CGRectMake(0.0, 0.0, 352 CGImageGetWidth(image)/3, CGImageGetHeight(image)/3) 353 CGContextDrawImage(context, imageRect, image) 354 355 if 0: 356 isFloatingImage = imageHasFloatingPointSamples(image) 357 if isFloatingImage: 358 print "First image IS a floating point image" 359 else: 360 print "First image IS NOT a floating point image" 361 362 del image 363 364 image = myCreateThumbnailFromImageSource(url) 365 if image is None: 366 print >>sys.stderr, "myCreateThumbnailFromImageSource didn't create a CGImage!" 367 return 368 369 imageRect = CGRectMake(400.0, 0.0, 370 CGImageGetWidth(image), CGImageGetHeight(image)) 371 CGContextDrawImage(context, imageRect, image) 372 373 del image 374 375 376class MyIncrementalData (object): 377 data = None 378 dataSize = 0 379 repCount = 0 380 chunkSize = 0 381 382# This is a dummy data accumulation routine used to demonstrate incremental 383# loading of an image. 384def myCreateAccumulatedDataSoFar(myDataP): 385 myDataP.repCount += 1 386 sizeToReturn = myDataP.chunkSize*myDataP.repCount 387 388 if sizeToReturn > myDataP.dataSize: 389 sizeToReturn = myDataP.dataSize 390 391 done = (sizeToReturn == myDataP.dataSize) 392 data = CFDataCreate(None, myDataP.data, sizeToReturn) 393 return data, done 394 395 396def MyDrawIncrementalImage(context, image, fullHeight): 397 # Obtain the width and height of the image that has been 398 # accumulated so far. 399 width = CGImageGetWidth(image) 400 height = CGImageGetHeight(image) 401 # Adjust the location of the imageRect so that the origin is 402 # such that the full image would be located at 0,0 and the partial 403 # image top-left corner does not move as the image is filled in. 404 # This is only needed for views where the y axis points up the 405 # drawing canvas. 406 imageRect = CGRectMake(0, fullHeight-height, width, height) 407 CGContextDrawImage(context, imageRect, image) 408 409 410def myDrawFirstImageIncrementally(context, myDataP): 411 height = -1 412 # Create an incremental image source. 413 imageSource = CGImageSourceCreateIncremental(None) 414 if imageSource is None: 415 print >>sys.stderr, "Couldn't create incremental imagesource!" 416 return 417 418 419 # Loop, gathering the necessary data to find the True 420 # height of the image. 421 while 1: 422 # Fetch the data. The CFData object returned by 423 # myCreateAccumulatedDataSoFar is used to update the 424 # image source. When the data is complete, the code 425 # passes True in the 'done' parameter passed to 426 # CGImageSourceUpdateData. Once the data is passed 427 # to CGImageSourceUpdateData, the code can release 428 # its reference to the data. 429 430 # Accumulate the data. 431 data, done = myCreateAccumulatedDataSoFar(myDataP) 432 CGImageSourceUpdateData(imageSource, data, done) 433 434 # Release the data since Quartz retains it and this code 435 # no longer needs it. 436 del data 437 438 if height < 0: 439 # Determine the height of the full image. This is needed in order 440 # to adjust the location of the drawing of the partial image in 441 # a context where the y axis has the default Quartz orientation 442 # pointing up the drawing canvas. 443 properties = CGImageSourceCopyPropertiesAtIndex( 444 imageSource, 0, None) 445 if properties is not None: 446 if kCGImagePropertyPixelHeight in properties: 447 height = properties[kCGImagePropertyPixelHeight] 448 del properties 449 450 # Once the height is obtained, go ahead and see if Quartz 451 # has enough data to create a CGImage object. 452 if height > 0: 453 # Now create the CGImageRef from the image source for the 454 # first image. 455 image = CGImageSourceCreateImageAtIndex( 456 imageSource, 0, None) 457 if image is not None: 458 # Draw the image using the height of the full image 459 # to adjust the location where the image is drawn. 460 MyDrawIncrementalImage(context, image, height) 461 # Release the partial image once you've drawn it. 462 del image 463 # Potentially you would want to flush the context so 464 # that drawing to a window would appear, even inside 465 # this loop. Of course this flush should really be 466 # done on a timer so that the flush only occurs at 467 # most every 60th of a second. See Chapter 17 regarding 468 # timing your usage of CGContextFlush. 469 CGContextFlush(context) 470 471 # Obtain the status for the image source for the first image. 472 status = CGImageSourceGetStatusAtIndex(imageSource, 0) 473 474 if done or status == kCGImageStatusComplete: 475 break 476 477def createMyIncrementalDataFromURL(url, myDataP): 478 myDataP.data = None 479 myDataP.dataSize = 0 480 myDataP.repCount = 0 481 482 success, pathString = CFURLGetFileSystemRepresentation(url, True, None, 1024) 483 pathString = pathString.rstrip('\0') 484 485 486 if success and len(pathString): 487 fp = open(pathString, 'rb') 488 myDataP.data = fp.read() 489 fp.close() 490 myDataP.dataSize = len(myDataP.data) 491 492 if myDataP.dataSize > 0: 493 myDataP.chunkSize = myDataP.dataSize/10 # 10 chunks 494 495def doIncrementalImageWithURL(context, url): 496 myData = MyIncrementalData() 497 createMyIncrementalDataFromURL(url, myData) 498 if myData.data is None: 499 print >>sys.stderr, "couldn't read data from URL!" 500 501 myDrawFirstImageIncrementally(context, myData) 502 del myData 503 504# This code requires QuickTime.framework. 505# from Carbon import Qt 506def createCGImageWithQuickTimeFromURL(url): 507 """ 508 Note: this function doesn't actually worked because the APIs used in here 509 aren't properly wrapped (yet). 510 """ 511 return None 512 513 514 515 516 imageRef = None 517 518 err = noErr 519 result, dataRef, dataRefType = QTNewDataReferenceFromCFURL(url, 0, None, None) 520 if dataRef is not None: 521 err, gi = GetGraphicsImporterForDataRefWithFlags(dataRef, 522 dataRefType, None, 0) 523 if not err and gi: 524 # Tell the graphics importer that it shouldn't perform 525 # gamma correction and it should create an image in 526 # the original source color space rather than matching it to 527 # a generic calibrated color space. 528 result = GraphicsImportSetFlags(gi, 529 (kGraphicsImporterDontDoGammaCorrection + 530 kGraphicsImporterDontUseColorMatching) 531 ) 532 if result == 0: 533 result, imageRef = GraphicsImportCreateCGImage(gi,None,0) 534 if result != 0: 535 print >>sys.stderr, "got a bad result = %d!"%(result,) 536 DisposeHandle(dataRef) 537 CloseComponent(gi) 538 539 return imageRef 540 541def drawQTImageWithQuartz(context, url): 542 image = createCGImageWithQuickTimeFromURL(url) 543 if image is None: 544 print >>sys.stderr, "createCGImageWithQuickTimeFromURL didn't create a CGImage!" 545 return 546 547 imageRect = CGRectMake(0.0, 0.0, 548 CGImageGetWidth(image), CGImageGetHeight(image)) 549 CGContextDrawImage(context, imageRect, image) 550 551def drawJPEGDocumentWithMultipleProfiles(context, url): 552 isDeviceRGBImage = False 553 554 # Create a Quartz data provider for the supplied URL. 555 jpgProvider = CGDataProviderCreateWithURL(url) 556 if jpgProvider is None: 557 print >>sys.stderr, "Couldn't create JPEG Data provider!" 558 return 559 560 # Create the CGImageRef for the JPEG image from the data provider. 561 jpgImage = CGImageCreateWithJPEGDataProvider( 562 jpgProvider, None, True, kCGRenderingIntentDefault) 563 del jpgProvider 564 if jpgImage is None: 565 print >>sys.stderr, "Couldn't create CGImageRef for JPEG data!" 566 return 567 568 # Get the color space characterizing the image. This is a 569 # function with 'Get' semantics so the code doesn't own a reference 570 # to the color space returned and must not release it. 571 originalColorSpace = CGImageGetColorSpace(jpgImage) 572 if originalColorSpace is None: 573 print >>sys.stderr, "image is a masking image, not an image with color!" 574 return 575 576 if CGColorSpaceGetNumberOfComponents(originalColorSpace) != 3: 577 print >>sys.stderr, "This example only works with 3 component JPEG images" 578 return 579 580 # Determine if the original color space is DeviceRGB. If that is 581 # not the case then bail. 582 comparisonColorSpace = CGColorSpaceCreateDeviceRGB() 583 584 # Note that this comparison of color spaces works only on 585 # Jaguar and later where a CGColorSpaceRef is a 586 # CoreFoundation object. Otherwise this will crash! 587 isDeviceRGBImage = (comparisonColorSpace == originalColorSpace) 588 589 # This code created 'comparisonColorSpace' so it must release it. 590 del comparisonColorSpace 591 592 if not isDeviceRGBImage: 593 print >>sys.stderr, "The color space for the JPEG image is not DeviceRGB!" 594 return 595 596 # Might need to adjust this based on the size of the original image. 597 CGContextScaleCTM(context, 0.5, 0.5) 598 599 imageRect = CGRectMake(0.0, CGImageGetHeight(jpgImage)/2, 600 CGImageGetWidth(jpgImage), CGImageGetHeight(jpgImage)) 601 602 # Draw the original image to the left of the other two. 603 CGContextDrawImage(context, imageRect, jpgImage) 604 605 # Recharacterize the original image with the generic Calibrated RGB 606 # color space. 607 updatedImage1 = CGImageCreateCopyWithColorSpace(jpgImage, 608 Utilities.getTheCalibratedRGBColorSpace()) 609 # Release the original image since this code is done with it. 610 del jpgImage 611 if updatedImage1 is None: 612 print >>sys.stderr, "There is no updated image to draw!" 613 return 614 615 # Draw the image characterized by the Generic profile 616 # to the right of the other image. 617 imageRect = CGRectOffset(imageRect, CGRectGetWidth(imageRect) + 10, 0) 618 CGContextDrawImage(context, imageRect, updatedImage1) 619 620 # Recharacterize the image but now with a color space 621 # created with the sRGB profile. 622 updatedImage2 = CGImageCreateCopyWithColorSpace(updatedImage1, 623 Utilities.getTheSRGBColorSpace()) 624 # Release updatedImage1 since this code is done with it. 625 del updatedImage1 626 if updatedImage2 is None: 627 print >>sys.stderr, "There is no second updated image to draw!" 628 return 629 630 # Draw the image characterized by the sRGB profile to the right of 631 # the image characterized by the generic RGB profile. 632 imageRect = CGRectOffset(imageRect, CGRectGetWidth(imageRect) + 10, 0) 633 CGContextDrawImage(context, imageRect, updatedImage2) 634 635def createRedGreenRampImageData(width, height, size): 636 try: 637 dataP = objc.allocateBuffer(size) 638 except MemoryError: 639 return None 640 641 idx = 0 642 # Build an image that is RGB 24 bits per sample. This is a ramp 643 # where the red component value increases in red from left to 644 # right and the green component increases from top to bottom. 645 for g in xrange(height): 646 for r in xrange(width): 647 dataP[idx+0] = r 648 dataP[idx+1] = g 649 dataP[idx+2] = 0 650 idx+=3 651 652 return dataP 653 654def createRGBRampSubDataProvider(subRect): 655 bytesPerSample = 3 656 width = 256 657 height = 256 658 bytesPerRow = width*bytesPerSample 659 startOffsetX = subRect.origin.x 660 startOffsetY = subRect.origin.y 661 imageDataSize = bytesPerRow*height 662 663 # The first image sample is at 664 # (startOffsetY*bytesPerRow + startOffsetX*bytesPerSample) 665 # bytes into the RGB ramp data. 666 firstByteOffset = startOffsetY*bytesPerRow + startOffsetX*bytesPerSample 667 668 # The actual size of the image data provided is the full image size 669 # minus the amount skipped at the beginning. This is more than the 670 # total amount of data that is needed for the subimage but it is 671 # valid and easy to calculate. 672 totalBytesProvided = imageDataSize - firstByteOffset 673 674 # Create the full color ramp. 675 dataP = createRedGreenRampImageData(width, height, imageDataSize) 676 if dataP is None: 677 print >>sys.stderr, "Couldn't create image data!" 678 return None 679 680 # Use the pointer to the first byte as the info parameter since 681 # that is the pointer to the block to free when done. 682 dataProvider = CGDataProviderCreateWithData(dataP, 683 buffer(dataP, firstByteOffset), 684 totalBytesProvided, None) 685 686 if dataProvider is None: 687 return None 688 689 return dataProvider 690 691def doColorRampSubImage(context): 692 # Start 4 scanlines from the top and 16 pixels from the left edge, 693 # skip the last 40 scanlines of the image and the right 694 # most 64 pixels. 695 insetLeft = 16 696 insetTop = 4 697 insetRight = 64 698 insetBottom = 40 699 700 fullImageWidth = 256 701 fullImageHeight = 256 702 subImageWidth = fullImageWidth-insetLeft-insetRight 703 subImageHeight = fullImageHeight-insetTop-insetBottom 704 bitsPerComponent = 8 705 bitsPerPixel = 24 706 bytesPerRow = fullImageWidth * 3 707 shouldInterpolate = True 708 709 imageSubRect = CGRectMake( 710 insetLeft, insetTop, subImageWidth, subImageHeight) 711 colorspace = Utilities.getTheCalibratedRGBColorSpace() 712 713 if hasattr(Quartz, 'CGImageCreateWithImageInRect'): 714 imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider() 715 if imageDataProvider is None: 716 print >>sys.stderr, "Couldn't create Image Data provider!" 717 return 718 719 fullImage = CGImageCreate(fullImageWidth, fullImageHeight, 720 bitsPerComponent, bitsPerPixel, 721 bytesPerRow, colorspace, kCGImageAlphaNone, 722 imageDataProvider, None, shouldInterpolate, 723 kCGRenderingIntentDefault) 724 if fullImage is not None: 725 image = CGImageCreateWithImageInRect(fullImage, imageSubRect) 726 # release the full image since it is no longer required. 727 del fullImage 728 729 # If the image hasn't been created yet, this code uses the 730 # customized data provider to do so. 731 if image is None: 732 imageDataProvider = createRGBRampSubDataProvider(imageSubRect) 733 if imageDataProvider is None: 734 print >>sys.stderr, "Couldn't create Image Data provider!" 735 return 736 737 # By supplying bytesPerRow, the extra data at the end of 738 # each scanline and the beginning of the next is properly skipped. 739 image = CGImageCreate(subImageWidth, subImageHeight, 740 bitsPerComponent, bitsPerPixel, 741 bytesPerRow, colorspace, kCGImageAlphaNone, 742 imageDataProvider, None, shouldInterpolate, 743 kCGRenderingIntentDefault) 744 745 # This code no longer needs the data provider. 746 del imageDataProvider 747 748 if image is None: 749 print >>sys.stderr, "Couldn't create CGImageRef for this data!" 750 return 751 752 # Draw the subimage. 753 rect = CGRectMake(0, 0, subImageWidth, subImageHeight) 754 CGContextDrawImage(context, rect, image) 755 756def exportCGImageToPNGFileWithDestination(image, url): 757 resolution = 144. 758 759 # Create an image destination at the supplied URL that 760 # corresponds to the PNG image format. 761 imageDestination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, None) 762 763 if imageDestination is None: 764 print >>sys.stderr, "couldn't create image destination!" 765 return 766 767 # Set the keys to be the x and y resolution of the image. 768 options = { 769 kCGImagePropertyDPIWidth: resolution, 770 kCGImagePropertyDPIHeight: resolution, 771 } 772 773 # Add the image with the options dictionary to the destination. 774 CGImageDestinationAddImage(imageDestination, image, options) 775 776 # Release the options dictionary this code created. 777 del options 778 779 # When all the images are added to the destination, finalize it. 780 CGImageDestinationFinalize(imageDestination) 781 782 # Release the destination when done with it. 783 del imageDestination 784 785 786# This code requires QuickTime.framework 787# include <QuickTime/QuickTime.h> 788def exportCGImageToJPEGFile(imageRef, url): 789 # This doesn't actually work due to lame Python Quicktime bindings... 790 return 791 792 result, dataRef, dataRefType = QTNewDataReferenceFromCFURL( 793 url, 0, None, None) 794 if result == 0: 795 result, graphicsExporter = OpenADefaultComponent(GraphicsExporterComponentType, 796 kQTFileTypeJPEG) 797 if result == 0: 798 result = GraphicsExportSetInputCGImage(graphicsExporter, 799 imageRef) 800 if result == 0: 801 result = GraphicsExportSetOutputDataReference( 802 graphicsExporter, dataRef, dataRefType) 803 if result == 0: 804 result, sizeWritten = GraphicsExportDoExport( 805 graphicsExporter, None) 806 807 CloseComponent(graphicsExporter) 808 809 if dataRef is not None: 810 DisposeHandle(dataRef) 811 812 if result != 0: 813 print >>sys.stderr, "Exporting QT image got bad result = %d!"%(result,) 814 815def exportColorRampImageWithQT(context): 816 width = 256 817 height = 256 818 bitsPerComponent = 8 819 bitsPerPixel = 24 820 bytesPerRow = width * 3 821 shouldInterpolate = True 822 823 imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider() 824 if imageDataProvider is None: 825 print >>sys.stderr, "Couldn't create Image Data provider!" 826 return 827 828 colorspace = Utilities.getTheCalibratedRGBColorSpace() 829 image = CGImageCreate(width, height, bitsPerComponent, 830 bitsPerPixel, bytesPerRow, colorspace, 831 kCGImageAlphaNone, imageDataProvider, 832 None, shouldInterpolate, kCGRenderingIntentDefault) 833 del imageDataProvider 834 if image is None: 835 print >>sys.stderr, "Couldn't create CGImageRef for this data!" 836 return 837 838 rect = CGRectMake(0.0, 0.0, width, height) 839 CGContextDrawImage(context, rect, image) 840 841 # Of course this is a total hack. 842 outPath = "/tmp/imageout.jpg" 843 exportURL = CFURLCreateFromFileSystemRepresentation(None, 844 outPath, len(outPath), False) 845 if exportURL: 846 exportCGImageToJPEGFile(image, exportURL) 847