• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.10/pyobjc-45/2.5/pyobjc/pyobjc-framework-Quartz/Examples/Programming with Quartz/BasicDrawing/
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