• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.10/pyobjc-45/2.6/pyobjc/pyobjc-framework-Quartz/Examples/Core Graphics/CGRotation/
1from Quartz import *
2from LaunchServices import * # UT info
3import math
4
5class ImageInfo (object):
6    __slots__ = ('fRotation', 'fScaleX', 'fScaleY', 'fTranslateX', 'fTranslateY', 'fImageRef', 'fProperties', 'fOrientation')
7
8    def __init__(self):
9	self.fRotation = 0.0	    # The rotation about the center of the image (degrees)
10	self.fScaleX = 0.0	    # The scaling of the image along it's X-axis
11	self.fScaleY = 0.0	    # The scaling of the image along it's Y-axis
12	self.fTranslateX = 0.0      # Move the image along the X-axis
13	self.fTranslateY = 0.0	    # Move the image along the Y-axis
14	self.fImageRef = None	    # The image itself
15	self.fProperties = None     # Image properties
16	self.fOrientation = None    # Affine transform that ensures the image displays correctly
17
18# Create a new image from a file at the given url
19# Returns None if unsuccessful.
20def IICreateImage(url):
21    ii = None
22    # Try to create an image source to the image passed to us
23    imageSrc = CGImageSourceCreateWithURL(url, None)
24    if imageSrc is not None:
25        # And if we can, try to obtain the first image available
26        image = CGImageSourceCreateImageAtIndex(imageSrc, 0, None)
27        if image is not None:
28            # and if we could, create the ImageInfo struct with default values
29            ii = ImageInfo()
30            ii.fRotation = 0.0
31            ii.fScaleX = 1.0
32            ii.fScaleY = 1.0
33            ii.fTranslateX = 0.0
34            ii.fTranslateY = 0.0
35            # the ImageInfo struct owns this CGImageRef now, so no need for a retain.
36            ii.fImageRef = image
37            # the ImageInfo struct owns this CFDictionaryRef, so no need for a retain.
38            ii.fProperties = CGImageSourceCopyPropertiesAtIndex(imageSrc, 0, None);
39            # Setup the orientation transformation matrix so that the image will display with the
40            # proper orientation
41            IIGetOrientationTransform(ii)
42
43    return ii
44
45# Transforms the context based on the orientation of the image.
46# This ensures the image always appears correctly when drawn.
47def IIGetOrientationTransform(image):
48    w = CGImageGetWidth(image.fImageRef)
49    h = CGImageGetHeight(image.fImageRef)
50    if image.fProperties is not None:
51        # The Orientations listed here are mirroed from CGImageProperties.h,
52        # listed under the kCGImagePropertyOrientation key.
53        orientation = IIGetImageOrientation(image)
54        if orientation == 1:
55            # 1 = 0th row is at the top, and 0th column is on the left.
56            # Orientation Normal
57            image.fOrientation = CGAffineTransformMake(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
58
59        elif orientation == 2:
60            # 2 = 0th row is at the top, and 0th column is on the right.
61            # Flip Horizontal
62            image.fOrientation = CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, w, 0.0)
63
64        elif orientation == 3:
65            # 3 = 0th row is at the bottom, and 0th column is on the right.
66            # Rotate 180 degrees
67            image.fOrientation = CGAffineTransformMake(-1.0, 0.0, 0.0, -1.0, w, h)
68
69        elif orientation == 4:
70            # 4 = 0th row is at the bottom, and 0th column is on the left.
71            # Flip Vertical
72            image.fOrientation = CGAffineTransformMake(1.0, 0.0, 0, -1.0, 0.0, h)
73
74        elif orientation == 5:
75            # 5 = 0th row is on the left, and 0th column is the top.
76            # Rotate -90 degrees and Flip Vertical
77            image.fOrientation = CGAffineTransformMake(0.0, -1.0, -1.0, 0.0, h, w)
78
79        elif orientation == 6:
80            # 6 = 0th row is on the right, and 0th column is the top.
81            # Rotate 90 degrees
82            image.fOrientation = CGAffineTransformMake(0.0, -1.0, 1.0, 0.0, 0.0, w)
83
84        elif orientation == 7:
85            # 7 = 0th row is on the right, and 0th column is the bottom.
86            # Rotate 90 degrees and Flip Vertical
87            image.fOrientation = CGAffineTransformMake(0.0, 1.0, 1.0, 0.0, 0.0, 0.0)
88
89        elif orientation == 8:
90            # 8 = 0th row is on the left, and 0th column is the bottom.
91            # Rotate -90 degrees
92            image.fOrientation = CGAffineTransformMake(0.0, 1.0,-1.0, 0.0, h, 0.0)
93
94# Gets the orientation of the image from the properties dictionary if available
95# If the kCGImagePropertyOrientation is not available or invalid,
96# then 1, the default orientation, is returned.
97def IIGetImageOrientation(image):
98    result = 1
99    if image.fProperties is not None:
100        orientation = image.fProperties.get(kCGImagePropertyOrientation)
101        if orientation is not None:
102            result = orientation
103
104    return result
105
106# Save the given image to a file at the given url.
107# Returns true if successful, false otherwise.
108def IISaveImage(image, url, width, height):
109    result = False
110
111    # If there is no image, no destination, or the width/height is 0, then fail early.
112    assert (image is not None) and (url is not None) and (width != 0.0) and (height != 0.0)
113
114    # Try to create a jpeg image destination at the url given to us
115    imageDest = CGImageDestinationCreateWithURL(url, kUTTypeJPEG, 1, None)
116    if imageDest is not None:
117        # And if we can, then we can start building our final image.
118        # We begin by creating a CGBitmapContext to host our desintation image.
119
120        # Allocate enough space to hold our pixels
121        imageData = objc.allocateBuffer(int(4 * width * height))
122
123        # Create the bitmap context
124        bitmapContext = CGBitmapContextCreate(
125                imageData, # image data we just allocated...
126                width, # width
127                height, # height
128                8, # 8 bits per component
129                4 * width, # bytes per pixel times number of pixels wide
130                CGImageGetColorSpace(image.fImageRef), # use the same colorspace as the original image
131                kCGImageAlphaPremultipliedFirst) # use premultiplied alpha
132
133        # Check that all that went well
134        if bitmapContext is not None:
135            # Now, we draw the image to the bitmap context
136            IIDrawImageTransformed(image, bitmapContext, CGRectMake(0.0, 0.0, width, height))
137
138            # We have now gotten our image data to the bitmap context, and correspondingly
139            # into imageData. If we wanted to, we could look at any of the pixels of the image
140            # and manipulate them in any way that we desire, but for this case, we're just
141            # going to ask ImageIO to write this out to disk.
142
143            # Obtain a CGImageRef from the bitmap context for ImageIO
144            imageIOImage = CGBitmapContextCreateImage(bitmapContext)
145
146            # Check if we have additional properties from the original image
147            if image.fProperties is not None:
148                # If we do, then we want to inspect the orientation property.
149                # If it exists and is not the default orientation, then we
150                # want to replace that orientation in the destination file
151                orientation = IIGetImageOrientation(image)
152                if orientation != 1:
153                    # If the orientation in the original image was not the default,
154                    # then we need to replace that key in a duplicate of that dictionary
155                    # and then pass that dictionary to ImageIO when adding the image.
156                    prop = CFDictionaryCreateMutableCopy(None, 0, image.fProperties)
157                    orientation = 1;
158                    prop[kCGImagePropertyOrientation] = orientation
159
160                    # And add the image with the new properties
161                    CGImageDestinationAddImage(imageDest, imageIOImage, prop);
162
163                else:
164                    # Otherwise, the image was already in the default orientation and we can
165                    # just save it with the original properties.
166                    CGImageDestinationAddImage(imageDest, imageIOImage, image.fProperties)
167
168            else:
169                # If we don't, then just add the image without properties
170                CGImageDestinationAddImage(imageDest, imageIOImage, None)
171
172            del bitmapContext
173
174        # Finalize the image destination
175        result = CGImageDestinationFinalize(imageDest)
176        del imageDest
177
178    return result
179
180
181# Applies the transformations specified in the ImageInfo struct without drawing the actual image
182def IIApplyTransformation(image, context, bounds):
183    if image is not None:
184        # Whenever you do multiple CTM changes, you have to be very careful with order.
185        # Changing the order of your CTM changes changes the outcome of the drawing operation.
186        # For example, if you scale a context by 2.0 along the x-axis, and then translate
187        # the context by 10.0 along the x-axis, then you will see your drawing will be
188        # in a different position than if you had done the operations in the opposite order.
189
190        # Our intent with this operation is that we want to change the location from which we start drawing
191        # (translation), then rotate our axies so that our image appears at an angle (rotation), and finally
192        # scale our axies so that our image has a different size (scale).
193        # Changing the order of operations will markedly change the results.
194        IITranslateContext(image, context)
195        IIRotateContext(image, context, bounds)
196        IIScaleContext(image, context, bounds)
197
198# Draw the image to the given context centered inside the given bounds
199def IIDrawImage(image, context, bounds):
200    imageRect = NSRect()
201    if image is not None and context is not None:
202        # Setup the image rect so that the image fills it's natural boudaries in the base coordinate system.
203        imageRect.origin.x = 0.0
204        imageRect.origin.y = 0.0
205        imageRect.size.width = CGImageGetWidth(image.fImageRef);
206        imageRect.size.height = CGImageGetHeight(image.fImageRef);
207
208        # Obtain the orientation matrix for this image
209        ctm = image.fOrientation;
210
211        # Before we can apply the orientation matrix, we need to translate the coordinate system
212        # so the center of the rectangle matces the center of the image.
213        if image.fProperties is None or IIGetImageOrientation(image) < 5:
214            # For orientations 1-4, the images are unrotated, so the width and height of the base image
215            # can be used as the width and height of the coordinate translation calculation.
216            CGContextTranslateCTM(
217                context,
218                math.floor((bounds.size.width - imageRect.size.width) / 2.0),
219                math.floor((bounds.size.height - imageRect.size.height) / 2.0))
220
221        else:
222            # For orientations 5-8, the images are rotated 90 or -90 degrees, so we need to use
223            # the image width in place of the height and vice versa.
224            CGContextTranslateCTM(
225                context,
226                floorf((bounds.size.width - imageRect.size.height) / 2.0),
227                floorf((bounds.size.height - imageRect.size.width) / 2.0))
228
229        # Finally, orient the context so that the image draws naturally.
230        CGContextConcatCTM(context, ctm)
231
232        # And draw the image.
233        CGContextDrawImage(context, imageRect, image.fImageRef)
234
235# Rotates the context around the center point of the given bounds
236def IIRotateContext(image, context, bounds):
237    # First we translate the context such that the 0,0 location is at the center of the bounds
238    CGContextTranslateCTM(context, bounds.size.width/2.0, bounds.size.height/2.0)
239
240    # Then we rotate the context, converting our angle from degrees to radians
241    CGContextRotateCTM(context, image.fRotation * math.pi / 180.0)
242
243    # Finally we have to restore the center position
244    CGContextTranslateCTM(context, -bounds.size.width/2.0, -bounds.size.height/2.0)
245
246# Scale the context around the center point of the given bounds
247def IIScaleContext(image, context, bounds):
248    # First we translate the context such that the 0,0 location is at the center of the bounds
249    CGContextTranslateCTM(context, bounds.size.width/2.0, bounds.size.height/2.0)
250
251    # Next we scale the context to the size that we want
252    CGContextScaleCTM(context, image.fScaleX, image.fScaleY);
253
254    # Finally we have to restore the center position
255    CGContextTranslateCTM(context, -bounds.size.width/2.0, -bounds.size.height/2.0)
256
257# Translate the context
258def IITranslateContext(image, context):
259    # Translation is easy, just translate.
260    CGContextTranslateCTM(context, image.fTranslateX, image.fTranslateY)
261
262# Draw the image to the given context centered inside the given bounds with
263# the transformation info. The CTM of the context is unchanged after this call
264def IIDrawImageTransformed(image, context, bounds):
265    # We save the current graphics state so as to not disrupt it for the caller.
266    CGContextSaveGState(context)
267
268    # Apply the transformation
269    IIApplyTransformation(image, context, bounds)
270
271    # Draw the image centered in the context
272    IIDrawImage(image, context, bounds)
273
274    # Restore our original graphics state.
275    CGContextRestoreGState(context)
276
277# Release the ImageInfo struct and other associated data
278# you should not refer to the reference after this call
279# This function is None safe.
280def IIRelease(image):
281    pass
282