1from Quartz import * 2import Utilities 3 4import sys 5 6def scalePatternPhase(phase): 7 # Adjust the pattern phase if scaling to export as bits. This is equivalent to scaling base 8 # space by the scaling factor. 9 patternScaling = Utilities.getScalingFactor() 10 if patternScaling != 1.0: 11 phase = CGSizeApplyAffineTransform(phase, 12 CGAffineTransformMakeScale(patternScaling, patternScaling)) 13 14 return phase 15 16def scalePatternMatrix(patternTransform): 17 # Scale the pattern by the scaling factor when exporting to bits. This is equivalent to 18 # scaling base space by the scaling factor. 19 patternScaling = Utilities.getScalingFactor() 20 if patternScaling != 1.0: 21 patternTransform = CGAffineTransformConcat(patternTransform, 22 CGAffineTransformMakeScale(patternScaling, patternScaling)) 23 24 return patternTransform 25 26 27def myDrawRedBlackCheckerBoardPattern(info, patternCellContext): 28 # This pattern proc draws a red and a black rectangle 29 # patch representing the minimum cell needed to paint a 30 # checkerboard with that pattern. 31 # 32 # Each 'cell' of the checkerboard is 2 units on a side. 33 # 34 # This code uses CGColorRefs which are available in Panther 35 # and later only. Patterns are available in all versions of 36 # Mac OS X but this code uses CGColorRefs for convenience 37 # and efficiency. 38 39 # Paint a black checkerboard box. 40 CGContextSetFillColorWithColor(patternCellContext, 41 Utilities.getRGBOpaqueBlackColor()) 42 # This is a 1x1 unit rect whose origin is at 0,0 in pattern space. 43 CGContextFillRect(patternCellContext, CGRectMake(0.0, 0.0, 1.0, 1.0)) 44 # This is a 1x1 unit rect whose origin is at 1,1 in pattern space. 45 CGContextFillRect(patternCellContext, CGRectMake(1.0, 1.0, 1.0, 1.0)) 46 47 # Paint a red checkerboard box. 48 CGContextSetFillColorWithColor(patternCellContext, 49 Utilities.getRGBOpaqueRedColor()) 50 # This is a 1x1 unit rect whose origin is at 1,0 in pattern space, 51 # that is, immediately to the right of first black checkerboard box. 52 CGContextFillRect(patternCellContext, CGRectMake(1.0, 0.0, 1.0, 1.0)) 53 # This is a 1x1 unit rect whose origin is at 0,1 in pattern space, 54 # that is, immediately above the first black checkerboard box. 55 CGContextFillRect(patternCellContext, CGRectMake(0.0, 1.0, 1.0, 1.0)) 56 57def createRedBlackCheckerBoardPattern(patternTransform): 58 pattern = CGPatternCreate(None, 59 # The pattern cell origin is at (0,0) with a 60 # width of 2 units and a height of 2 units. 61 CGRectMake(0, 0, 2, 2), 62 # Use the pattern transform supplied to this routine. 63 scalePatternMatrix(patternTransform), 64 # In pattern space the xStep is 2 units to the next cell in x 65 # and the yStep is 2 units to the next row of cells in y. 66 2, 2, 67 # This value is a good choice for this type of pattern and it 68 # avoids seams between tiles. 69 kCGPatternTilingConstantSpacingMinimalDistortion, 70 # This pattern has intrinsic color. 71 True, 72 ( 73 myDrawRedBlackCheckerBoardPattern, 74 None 75 )) 76 return pattern 77 78def doRedBlackCheckerboard(context): 79 dash = [4] 80 pattern = createRedBlackCheckerBoardPattern( 81 CGAffineTransformMakeScale(20, 20)) 82 if pattern is None: 83 print >>sys.stderr, "Couldn't create pattern!" 84 return 85 86 # Create the pattern color space. Since the pattern 87 # itself has intrinsic color, the 'baseColorSpace' parameter 88 # to CGColorSpaceCreatePattern must be None. 89 patternColorSpace = CGColorSpaceCreatePattern(None) 90 CGContextSetFillColorSpace(context, patternColorSpace) 91 92 # The pattern has intrinsic color so the color components array 93 # passed to CGContextSetFillPattern is just the alpha value used 94 # to composite the pattern cell. 95 96 # Paint the pattern with alpha = 1. 97 color = [1.0] 98 99 # Set the fill color to the checkerboard pattern. 100 CGContextSetFillPattern(context, pattern, color) 101 102 # Fill a 100x100 unit rect at (20,20). 103 CGContextFillRect(context, CGRectMake(20, 20, 100, 100)) 104 105 # Save the graphics state before changing the stroke color. 106 CGContextSaveGState(context) 107 if 1: 108 # Set the stroke color space and color to the pattern. 109 CGContextSetStrokeColorSpace(context, patternColorSpace) 110 CGContextSetStrokePattern(context, pattern, color) 111 112 # Stroke an ellipse with the pattern. 113 CGContextSetLineWidth(context, 8) 114 CGContextBeginPath(context) 115 Utilities.myCGContextAddEllipseInRect(context, CGRectMake(120, 20, 50, 100)) 116 CGContextStrokePath(context) 117 118 # Restore to the graphics state without the 119 # pattern stroke color. 120 CGContextRestoreGState(context) 121 122 # Now draw text. 123 CGContextSetTextMatrix(context, CGAffineTransformIdentity) 124 # Choose the font with the PostScript name "Times-Roman", 125 # size 80 points, with the encoding MacRoman encoding. 126 CGContextSelectFont(context, "Times-Roman", 80, kCGEncodingMacRoman) 127 128 # Using the fill text drawing mode. 129 CGContextSetTextDrawingMode(context, kCGTextFill) 130 131 # Draw text with the pattern. 132 CGContextShowTextAtPoint(context, 20, 120, "Text", 4) 133 134 # Rectangle 1, filled. 135 CGContextFillRect(context, CGRectMake(200, 20, 90, 90)) 136 137 # Rectangle 2, filled and stroked with a dash. 138 CGContextSetLineWidth(context, 2) 139 CGContextSetLineDash(context, 0, dash, 1) 140 CGContextBeginPath(context) 141 CGContextAddRect(context, CGRectMake(200, 70, 90, 90)) 142 CGContextDrawPath(context, kCGPathFillStroke) 143 144def doPatternMatrix(context): 145 basePatternMatrix = CGAffineTransformMakeScale(20, 20) 146 pattern = createRedBlackCheckerBoardPattern(basePatternMatrix) 147 if pattern is None: 148 print >>sys.stderr, "Couldn't create pattern!" 149 return 150 151 # Create the pattern color space. Since the pattern 152 # itself has intrinsic color, the 'baseColorSpace' parameter 153 # to CGColorSpaceCreatePattern must be None. 154 patternColorSpace = CGColorSpaceCreatePattern(None) 155 156 CGContextSetFillColorSpace(context, patternColorSpace) 157 del patternColorSpace 158 159 CGContextTranslateCTM(context, 40, 40) 160 CGContextSetPatternPhase(context, scalePatternPhase(CGSize(40, 40))) 161 162 # The pattern has intrinsic color so the color components array 163 # passed to CGContextSetFillPattern is the alpha value used 164 # to composite the pattern cell. 165 166 # Paint the pattern first with alpha = 1. 167 color = [1] 168 CGContextSetFillPattern(context, pattern, color) 169 170 # Rectangle 1. 171 CGContextFillRect(context, CGRectMake(0, 0, 100, 100)) 172 173 CGContextSaveGState(context) 174 if 1: 175 # Rectangle 2. 176 # Paint the pattern with 65% alpha. 177 color = [0.65] 178 CGContextSetFillPattern(context, pattern, color) 179 # Rotate 45 degrees about the point (150, 50). 180 CGContextTranslateCTM(context, 150.0, 50.0) 181 CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45.0)) 182 CGContextTranslateCTM(context, -50.0, -50.0) 183 # Rectangle 2. Patterns do not translate, scale or 184 # rotate with the CTM. You can see that the pattern 185 # tile of this filled rectangle is that of Rectangle 186 # 1. 187 CGContextFillRect(context, CGRectMake(0, 0, 100, 100)) 188 # Release the pattern. 189 del pattern 190 CGContextRestoreGState(context) 191 192 CGContextSaveGState(context) 193 if 1: 194 # Rectangle 3. The pattern is rotated with the object. 195 # Rotate 45 degrees about the point 250, 50. 196 t = CGAffineTransformMakeTranslation(250.0, 50.0) 197 t = CGAffineTransformRotate(t, Utilities.DEGREES_TO_RADIANS(45.0)) 198 # Translate back to -50, -50. 199 t = CGAffineTransformTranslate(t, -50.0, -50.0) 200 CGContextConcatCTM(context, t) 201 # Make a new pattern that is equivalent to 202 # the old pattern but transformed to current user 203 # space. The order of transformations is crucial. 204 # This ordering is equivalent to using the same pattern 205 # matrix as before but transforming base space by t. 206 patTransform = CGAffineTransformConcat(basePatternMatrix, t) 207 pattern = createRedBlackCheckerBoardPattern(patTransform) 208 color = [1] 209 CGContextSetFillPattern(context, pattern, color) 210 # Release the pattern. 211 del pattern 212 CGContextFillRect(context, CGRectMake(0, 0, 100, 100)) 213 CGContextRestoreGState(context) 214 215 CGContextSaveGState(context) 216 if 1: 217 # Rectangle 4. The pattern is scaled with the object. 218 # Translate and scale. 219 t = CGAffineTransformMakeTranslation(320, 0) 220 t = CGAffineTransformScale(t, 2, 2) 221 CGContextConcatCTM(context, t) 222 # Make a new pattern that is equivalent to 223 # the old pattern but transformed to current user 224 # space. The order of transformations is crucial. 225 # This ordering is equivalent to using the same pattern 226 # matrix as before but transforming base space by t. 227 patTransform = CGAffineTransformConcat(basePatternMatrix, t) 228 pattern = createRedBlackCheckerBoardPattern(patTransform) 229 color = [1] 230 CGContextSetFillPattern(context, pattern, color) 231 # Release the pattern. 232 del pattern 233 CGContextFillRect(context, CGRectMake(0, 0, 100, 100)) 234 CGContextRestoreGState(context) 235 236 237def doPatternPhase(context): 238 pattern = createRedBlackCheckerBoardPattern( 239 CGAffineTransformMakeScale(20, 20)) 240 if pattern is None: 241 print >>sys.stderr, "Couldn't create pattern!" 242 return 243 244 # Create the pattern color space for a colored pattern. 245 patternColorSpace = CGColorSpaceCreatePattern(None) 246 CGContextSetFillColorSpace(context, patternColorSpace) 247 248 # Paint the pattern with alpha = 1. 249 color = (1,) 250 CGContextSetFillPattern(context, pattern, color) 251 252 # Rectangle 1 253 CGContextFillRect(context, CGRectMake(20, 150, 100, 100)) 254 255 # Rectangle 2 256 CGContextFillRect(context, CGRectMake(130, 150, 100, 100)) 257 258 # Rectangle 3 259 # Set the pattern phase so that the pattern origin 260 # is at the lower-left of the shape. 261 CGContextSetPatternPhase(context, scalePatternPhase( CGSizeMake(20, 20) )) 262 CGContextFillRect(context, CGRectMake(20, 20, 100, 100)) 263 264 # Rectangle 4 265 # Set the pattern phase so that the pattern origin 266 # is at the lower-left corner of the shape. 267 CGContextSetPatternPhase(context, scalePatternPhase( CGSizeMake(130, 20) )) 268 CGContextTranslateCTM(context, 130, 20) 269 CGContextFillRect(context, CGRectMake(0, 0, 100, 100)) 270 271def drawRotatedRect(c, p): 272 r = CGRectMake(0, 0, 1, 1) 273 CGContextSaveGState(c) 274 if 1: 275 CGContextTranslateCTM(c, p.x, p.y) 276 CGContextRotateCTM(c, Utilities.DEGREES_TO_RADIANS(45)) 277 CGContextTranslateCTM(c, -r.size.width/2, -r.size.height/2) 278 CGContextFillRect(c, r) 279 CGContextRestoreGState(c) 280 281def myStencilPatternProc(info, patternCellContext): 282 drawRotatedRect(patternCellContext, CGPointMake(1, 1)) 283 drawRotatedRect(patternCellContext, CGPointMake(1.75, 1)) 284 285def createStencilPattern(patternTransform): 286 pattern = CGPatternCreate(None, 287 # The pattern cell origin is at (0,0) with a 288 # width of 2.5 units and a height of 2 units. This 289 # pattern cell has transparent areas since 290 # the pattern proc only marks a portion of the cell. 291 CGRectMake(0, 0, 2.5, 2), 292 # Use the pattern transform supplied to this routine. 293 scalePatternMatrix(patternTransform), 294 # Use the width and height of the pattern cell for 295 # the xStep and yStep. 296 2.5, 2, 297 # This value is a good choice for this type of pattern and it 298 # avoids seams between tiles. 299 kCGPatternTilingConstantSpacingMinimalDistortion, 300 # This pattern does not have intrinsic color. 301 False, # Must be False for a stencil pattern. 302 ( 303 myStencilPatternProc, 304 None, 305 )) 306 return pattern 307 308def doStencilPattern(context): 309 pattern = createStencilPattern(CGAffineTransformMakeScale(20, 20)) 310 if pattern is None: 311 print >>sys.stderr, "Couldn't create pattern!" 312 return 313 314 # Create the pattern color space. This pattern is a stencil 315 # pattern so when the code sets the pattern it also sets the 316 # color it will paint the pattern with. In order to 317 # set the pattern color space in this case we also have 318 # to say what underlying color space should be used when 319 # the pattern proc is called. 320 baseColorSpace = Utilities.getTheCalibratedRGBColorSpace() 321 patternColorSpace = CGColorSpaceCreatePattern(baseColorSpace) 322 323 CGContextSetFillColorSpace(context, patternColorSpace) 324 # This code is finished with the pattern color space and can release 325 # it because Quartz retains it while it is the current color space. 326 del patternColorSpace 327 328 # The pattern has no intrinsic color so the color components array 329 # passed to CGContextSetFillPattern contains the colors to paint 330 # the pattern with in the baseColorSpace. In the case here, 331 # first paint the pattern with opaque blue. 332 color = (0.11, 0.208, 0.451, 1.0) 333 CGContextSetFillPattern(context, pattern, color) 334 335 # Rectangle 1. 336 CGContextSetPatternPhase(context, scalePatternPhase( CGSizeMake(20, 160) )) 337 CGContextBeginPath(context) 338 CGContextAddRect(context, CGRectMake(20, 160, 105, 80)) 339 CGContextDrawPath(context, kCGPathFillStroke) 340 341 # Rectangle 2. 342 # Set the pattern color so the stencil pattern 343 # is painted in a yellow shade. 344 color = (1.0, 0.816, 0.0, 1.0) 345 CGContextSetFillPattern(context, pattern, color) 346 # Set the pattern phase to the origin of the next object. 347 CGContextSetPatternPhase(context, scalePatternPhase( CGSizeMake(140, 160) )) 348 CGContextBeginPath(context) 349 CGContextAddRect(context, CGRectMake(140, 160, 105, 80)) 350 CGContextDrawPath(context, kCGPathFillStroke) 351 352 CGContextSaveGState(context) 353 if 1: 354 CGContextSetFillColorWithColor(context, 355 Utilities.getRGBOpaqueBlueColor()) 356 # Fill color is now blue. Paint two blue rectangles 357 # that will be underneath the drawing which follows. 358 CGContextFillRect(context, CGRectMake(20, 40, 105, 80)) 359 CGContextFillRect(context, CGRectMake(140, 40, 105, 80)) 360 CGContextRestoreGState(context) 361 362 # The fill color is again the stencil pattern with 363 # the underlying fill color an opaque yellow. 364 365 # Rectangle 3. 366 # This paints over the blue rect just painted at 20,40 367 # and the blue underneath is visible where the pattern has 368 # transparent areas. 369 CGContextSetPatternPhase(context, scalePatternPhase( CGSizeMake(20, 40) )) 370 CGContextFillRect(context, CGRectMake(20, 40, 105, 80)) 371 372 # Rectangle 4. 373 # Change the alpha value of the underlying color used 374 # to paint the stencil pattern. 375 color = list(color) 376 color[3] = 0.75 377 CGContextSetFillPattern(context, pattern, color) 378 CGContextSetPatternPhase(context, scalePatternPhase( CGSizeMake(140, 40) )) 379 CGContextFillRect(context, CGRectMake(140, 40, 105, 80)) 380 381class MyPDFPatternInfo (object): 382 rect = None 383 pdfDoc = None 384 385def myDrawPDFPattern(info, patternCellContext): 386 # This pattern proc draws the first page of a PDF document to 387 # a destination rect. 388 CGContextSaveGState(patternCellContext) 389 CGContextClipToRect(patternCellContext, info.rect) 390 CGContextDrawPDFDocument(patternCellContext, info.rect, info.pdfDoc, 1) 391 CGContextRestoreGState(patternCellContext) 392 393# Versions of Tiger prior to 10.4.3 have a bug such that use of an xStep that 394# doesn't match the width of pattern bounding box or a yStep that doesn't match the 395# height of the pattern bounding box produces incorrect results when drawn 396# to a bit-based context. Setting TIGERSTEPWORKAROUND works around this bug. 397 398 399TIGERSTEPWORKAROUND=1 400SCALEPATTERN=1 401OPTIMIZEDPERF=0 402 403 404def createPDFPatternPattern(additionalTransformP, url): 405 patternInfoP = MyPDFPatternInfo() 406 407 patternInfoP.pdfDoc = CGPDFDocumentCreateWithURL(url) 408 if patternInfoP.pdfDoc is None: 409 print >>sys.stderr, "Couldn't create PDF document reference!" 410 return 411 412 patternInfoP.rect = CGPDFDocumentGetMediaBox(patternInfoP.pdfDoc, 1) 413 # Set the origin of the media rect for the PDF document to (0,0). 414 patternInfoP.rect.origin = CGPointZero 415 416 if additionalTransformP is not None: 417 patternTransform = additionalTransformP 418 else: 419 patternTransform = CGAffineTransformIdentity 420 421 # To emulate the example from the bitmap context drawing chapter, 422 # the tile offset in each dimension is the tile size in that 423 # dimension, plus 6 units. 424 if SCALEPATTERN: 425 tileOffsetX = 6. + patternInfoP.rect.size.width 426 tileOffsetY = 6. + patternInfoP.rect.size.height 427 else: 428 tileOffsetX = 2. + patternInfoP.rect.size.width 429 tileOffsetY = 2. + patternInfoP.rect.size.height 430 431 # Tiger versions 10.4.0 - 10.4.2 have a bug such that the bounds 432 # width and height is incorrectly used as the xstep,ystep. 433 # To workaround this bug, we can make the bounds rect incorporate 434 # the xstep,ystep since xstep,ystep are larger than the bounds. 435 if OPTIMIZEDPERF or TIGERSTEPWORKAROUND: 436 patternRect = CGRectMake(0, 0, tileOffsetX, tileOffsetY) 437 else: 438 patternRect = patternInfoP.rect 439 440 if OPTIMIZEDPERF: 441 # Produces best performance if bbox == xstep/ystep 442 spacing = kCGPatternTilingConstantSpacing 443 else: 444 spacing = kCGPatternTilingConstantSpacingMinimalDistortion 445 446 pattern = CGPatternCreate(patternInfoP, 447 # The pattern cell size is the size 448 # of the media rect of the PDF document. 449 patternRect, 450 scalePatternMatrix(patternTransform), 451 tileOffsetX, tileOffsetY, 452 # This value is a good choice for this type of pattern and 453 # it avoids seams between tiles. 454 spacing, 455 # This pattern has intrinsic color. 456 True, 457 ( 458 myDrawPDFPattern, 459 None, 460 )) 461 # If the pattern can't be created then release the 462 # pattern resources and info parameter. 463 if pattern is None: 464 patternInfoP = None 465 466 return pattern 467 468 469def drawWithPDFPattern(context, url): 470 if SCALEPATTERN: 471 patternMatrix = CGAffineTransformMakeScale(1.0/3, 1.0/3) 472 else: 473 patternMatrix = CGAffineTransformMakeScale(1, 1) 474 475 # Scale the PDF pattern down to 1/3 its original size. 476 pdfPattern = createPDFPatternPattern(patternMatrix, url) 477 if pdfPattern is None: 478 print >>sys.stderr, "Couldn't create pattern!" 479 return 480 481 # Create the pattern color space. Since the pattern 482 # itself has intrinsic color, the 'baseColorSpace' parameter 483 # to CGColorSpaceCreatePattern must be None. 484 patternColorSpace = CGColorSpaceCreatePattern(None) 485 CGContextSetFillColorSpace(context, patternColorSpace) 486 # Quartz retains the color space so this code 487 # can now release it since it no longer needs it. 488 del patternColorSpace 489 490 # Paint the pattern with an alpha of 1. 491 color = (1,) 492 CGContextSetFillPattern(context, pdfPattern, color) 493 # Quartz retains the pattern so this code 494 # can now release it since it no longer needs it. 495 del pdfPattern 496 497 # Fill a US Letter size rect with the pattern. 498 CGContextFillRect(context, CGRectMake(0, 0, 612, 792)) 499