BasicDrawing¶
A PyObjC Example without documentation
Sources¶
AppDrawing.py¶
import BitmapContext
import Cocoa
import ColorAndGState
import CoordinateSystem
import DrawingBasics
import EPSPrinting
import ImageMasking
import Images
import PathDrawing
import PatternDrawing
import QuartzTextDrawing
import Shadings
import ShadowsAndTransparencyLayers
import UIHandling
import Utilities
# Defines
kCatPDF = "Kitty.pdf"
kPDFForBlendMode = "blendmode.pdf"
kOurJPEG = "Poot.jpg"
kQTImage = "ptlobos.tif"
kOurSubstituteJPG = "LyingOnDeckNoProfile.JPG"
kOurEPS = "imageturkey.eps"
RAW_IMAGE_WIDTH = 400
RAW_IMAGE_HEIGHT = 300
kRawColorImage = "image-400x300x24.raw"
kOtherColorImage = "otherimage-400x300x24.raw"
MASKING_IMAGE_WIDTH = 400
MASKING_IMAGE_HEIGHT = 259
kMaskingImage = "400x259x8.bw.raw"
noOffScreen = 0
bitmapOffScreen = 1
layerOffScreen = 2
# Cache info for GetURL
_mainBundle = None
_urlMap = {}
def GetURL(name):
"""
Returns the CFURLRef for an embedded resource, or None of that cannot be found.
"""
global _mainBundle
if _mainBundle is None:
_mainBundle = Utilities.getAppBundle()
mainBundle = _mainBundle
if mainBundle is not None:
if name in _urlMap:
return _urlMap[name]
url = Cocoa.CFBundleCopyResourceURL(mainBundle, name, None, None)
_urlMap[name] = url
else:
print("Can't get the app bundle!")
return
if url is None:
print(f"Couldn't get URL for {name!r}")
return url
#
# Helper functions for drawing
#
def callPDFDrawProc(context, proc, pdfFile):
ourPDFurl = GetURL(pdfFile)
if ourPDFurl:
proc(context, ourPDFurl)
def doDrawJPEGFile(context):
ourJPEGurl = GetURL(kOurJPEG)
if ourJPEGurl is not None:
Images.drawJPEGImage(context, ourJPEGurl)
def doRawImageFileWithURL(context):
url = GetURL(kRawColorImage)
if url is not None:
Images.drawImageFromURL(
context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, 8, True
)
# 8 bits per component, isColor = True
def doRawImageFileWithCallbacks(context):
url = GetURL(kRawColorImage)
if url is not None:
Images.doImageWithCallbacksCreatedFromURL(
context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, 8, True
)
# 8 bits per component, isColor = True
def doDrawImageWithCGImageSource(context):
url = GetURL(kOurJPEG)
if url is not None:
Images.drawImageWithCGImageDataSource(context, url)
def doIncrementalImage(context):
url = GetURL(kOurJPEG)
if url is not None:
Images.doIncrementalImageWithURL(context, url)
def doQTImage(context):
url = GetURL(kQTImage)
if url is not None:
Images.drawQTImageWithQuartz(context, url)
def doJPEGDocumentWithMultipleProfiles(context):
url = GetURL(kOurSubstituteJPG)
if url is not None:
Images.drawJPEGDocumentWithMultipleProfiles(context, url)
def doMaskImageWithMask(context):
theImageToMaskURL = GetURL(kOtherColorImage)
theMaskingImageURL = GetURL(kMaskingImage)
if theImageToMaskURL is not None and theMaskingImageURL is not None:
ImageMasking.doMaskImageWithMaskFromURL(
context,
theImageToMaskURL,
RAW_IMAGE_WIDTH,
RAW_IMAGE_HEIGHT,
8,
theMaskingImageURL,
MASKING_IMAGE_WIDTH,
MASKING_IMAGE_HEIGHT,
)
def doMaskImageWithGrayImage(context):
theImageToMaskURL = GetURL(kOtherColorImage)
theMaskingImageURL = GetURL(kMaskingImage)
if theImageToMaskURL is not None and theMaskingImageURL is not None:
ImageMasking.doMaskImageWithGrayImageFromURL(
context,
theImageToMaskURL,
RAW_IMAGE_WIDTH,
RAW_IMAGE_HEIGHT,
8,
theMaskingImageURL,
MASKING_IMAGE_WIDTH,
MASKING_IMAGE_HEIGHT,
)
def doImageMaskedWithColor(context):
url = GetURL(kOtherColorImage)
if url is not None:
ImageMasking.doMaskImageWithColorFromURL(
context, url, RAW_IMAGE_WIDTH, RAW_IMAGE_HEIGHT, True
)
def exportImageMaskedWithImage(context):
theImageToMaskURL = GetURL(kOtherColorImage)
theMaskingImageURL = GetURL(kMaskingImage)
if theImageToMaskURL is not None and theMaskingImageURL is not None:
ImageMasking.exportImageWithMaskFromURLWithDestination(
context,
theImageToMaskURL,
RAW_IMAGE_WIDTH,
RAW_IMAGE_HEIGHT,
8,
theMaskingImageURL,
MASKING_IMAGE_WIDTH,
MASKING_IMAGE_HEIGHT,
)
def doClipMask(context):
theMaskingImageURL = GetURL(kMaskingImage)
if theMaskingImageURL is not None:
ImageMasking.drawWithClippingMask(
context, theMaskingImageURL, MASKING_IMAGE_WIDTH, MASKING_IMAGE_HEIGHT
)
def tilePDFDocument(context, offscreenType):
url = GetURL(kCatPDF)
if url is not None:
if offscreenType == noOffScreen:
BitmapContext.TilePDFNoBuffer(context, url)
elif offscreenType == bitmapOffScreen:
BitmapContext.TilePDFWithOffscreenBitmap(context, url)
else:
BitmapContext.TilePDFWithCGLayer(context, url)
def doCompatibleEPSDrawing(context):
ourEPSurl = GetURL(kOurEPS)
if ourEPSurl is not None:
EPSPrinting.drawEPSDataImage(context, ourEPSurl)
def DispatchDrawing(context, drawingType):
"""Drawing dispatcher"""
if drawingType == UIHandling.kHICommandSimpleRect:
DrawingBasics.doSimpleRect(context)
elif drawingType == UIHandling.kHICommandStrokedRect:
DrawingBasics.doStrokedRect(context)
elif drawingType == UIHandling.kHICommandStrokedAndFilledRect:
DrawingBasics.doStrokedAndFilledRect(context)
elif drawingType == UIHandling.kHICommandPathRects:
DrawingBasics.doPathRects(context)
elif drawingType == UIHandling.kHICommandAlphaRects:
DrawingBasics.doAlphaRects(context)
elif drawingType == UIHandling.kHICommandDashed:
DrawingBasics.doDashedLines(context)
elif drawingType == UIHandling.kHICommandSimpleClip:
DrawingBasics.doClippedCircle(context)
elif drawingType == UIHandling.kHICommandPDFDoc:
callPDFDrawProc(context, DrawingBasics.doPDFDocument, kCatPDF)
elif drawingType == UIHandling.kHICommandRotatedEllipses:
CoordinateSystem.doRotatedEllipses(context)
elif drawingType == UIHandling.kHICommandDrawSkewCoordinates:
CoordinateSystem.drawSkewedCoordinateSystem(context)
elif drawingType == UIHandling.kHICommandBezierEgg:
PathDrawing.doEgg(context)
elif drawingType == UIHandling.kHICommandRoundedRects:
PathDrawing.doRoundedRects(context)
elif drawingType == UIHandling.kHICommandStrokeWithCTM:
PathDrawing.doStrokeWithCTM(context)
elif drawingType == UIHandling.kHICommandRotatedEllipsesWithCGPath:
PathDrawing.doRotatedEllipsesWithCGPath(context)
elif drawingType == UIHandling.kHICommandPixelAligned:
PathDrawing.doPixelAlignedFillAndStroke(context)
elif drawingType == UIHandling.kHICommandDeviceFillAndStrokeColor:
ColorAndGState.doColorSpaceFillAndStroke(context)
elif drawingType == UIHandling.kHICommandCLUTDrawGraphics:
ColorAndGState.doIndexedColorDrawGraphics(context)
elif drawingType == UIHandling.kHICommandDrawWithGlobalAlpha:
ColorAndGState.drawWithGlobalAlpha(context)
elif drawingType == UIHandling.kHICommandDrawWithBlendMode:
callPDFDrawProc(
context, ColorAndGState.drawWithColorBlendMode, kPDFForBlendMode
)
elif drawingType == UIHandling.kHICommandDrawWithColorRefs:
ColorAndGState.drawWithColorRefs(context)
elif drawingType == UIHandling.kHICommandFunctionsHaveOwnGSave:
ColorAndGState.doClippedEllipse(context)
elif drawingType == UIHandling.kHICommandDrawJPEGImage:
doDrawJPEGFile(context)
elif drawingType == UIHandling.kHICommandColorImageFromFile:
doRawImageFileWithURL(context)
elif drawingType == UIHandling.kHICommandColorImageFromData:
Images.doColorRampImage(context)
elif drawingType == UIHandling.kHICommandColorImageFromCallbacks:
doRawImageFileWithCallbacks(context)
elif drawingType == UIHandling.kHICommandGrayRamp:
Images.doGrayRamp(context)
elif drawingType == UIHandling.kHICommandDrawWithCGImageSource:
doDrawImageWithCGImageSource(context)
elif drawingType == UIHandling.kHICommandDrawWithCGImageSourceIncremental:
doIncrementalImage(context)
elif drawingType == UIHandling.kHICommandDrawWithQuickTime:
doQTImage(context)
elif drawingType == UIHandling.kHICommandSubstituteImageProfile:
doJPEGDocumentWithMultipleProfiles(context)
elif drawingType == UIHandling.kHICommandDoSubImage:
Images.doColorRampSubImage(context)
elif drawingType == UIHandling.kHICommandExportWithQuickTime:
Images.exportColorRampImageWithQT(context)
elif drawingType == UIHandling.kHICommandMaskTurkeyImage:
ImageMasking.doOneBitMaskImages(context)
elif drawingType == UIHandling.kHICommandImageMaskedWithMask:
doMaskImageWithMask(context)
elif drawingType == UIHandling.kHICommandImageMaskedWithGrayImage:
doMaskImageWithGrayImage(context)
elif drawingType == UIHandling.kHICommandMaskImageWithColor:
doImageMaskedWithColor(context)
elif drawingType == UIHandling.kHICommandClipToMask:
doClipMask(context)
elif drawingType == UIHandling.kHICommandExportWithCGImageDestination:
exportImageMaskedWithImage(context)
elif drawingType == UIHandling.kHICommandSimpleCGLayer:
BitmapContext.doSimpleCGLayer(context)
elif drawingType == UIHandling.kHICommandAlphaOnlyContext:
BitmapContext.doAlphaOnlyContext(context)
elif drawingType == UIHandling.kHICommandDrawNoOffScreenImage:
tilePDFDocument(context, noOffScreen)
elif drawingType == UIHandling.kHICommandDrawOffScreenImage:
tilePDFDocument(context, bitmapOffScreen)
elif drawingType == UIHandling.kHICommandDrawWithLayer:
tilePDFDocument(context, layerOffScreen)
elif drawingType == UIHandling.kHICommandQuartzRomanText:
QuartzTextDrawing.drawQuartzRomanText(context)
elif drawingType == UIHandling.kHICommandQuartzTextModes:
QuartzTextDrawing.drawQuartzTextWithTextModes(context)
elif drawingType == UIHandling.kHICommandQuartzTextMatrix:
QuartzTextDrawing.drawQuartzTextWithTextMatrix(context)
elif drawingType == UIHandling.kHICommandSimplePattern:
PatternDrawing.doRedBlackCheckerboard(context)
elif drawingType == UIHandling.kHICommandPatternPhase:
PatternDrawing.doPatternPhase(context)
elif drawingType == UIHandling.kHICommandPatternMatrix:
PatternDrawing.doPatternMatrix(context)
elif drawingType == UIHandling.kHICommandUncoloredPattern:
PatternDrawing.doStencilPattern(context)
elif drawingType == UIHandling.kHICommandDrawWithPDFPattern:
callPDFDrawProc(context, PatternDrawing.drawWithPDFPattern, kCatPDF)
elif drawingType == UIHandling.kHICommandSimpleShadow:
ShadowsAndTransparencyLayers.drawSimpleShadow(context)
elif drawingType == UIHandling.kHICommandShadowScaling:
ShadowsAndTransparencyLayers.doShadowScaling(context)
elif drawingType == UIHandling.kHICommandShadowProblems:
ShadowsAndTransparencyLayers.showComplexShadowIssues(context)
elif drawingType == UIHandling.kHICommandComplexShadow:
ShadowsAndTransparencyLayers.showComplexShadow(context)
elif drawingType == UIHandling.kHICommandMultipleShapeComposite:
ShadowsAndTransparencyLayers.doLayerCompositing(context)
elif drawingType == UIHandling.kHICommandFillAndStrokeWithShadow:
ShadowsAndTransparencyLayers.drawFillAndStrokeWithShadow(context)
elif drawingType == UIHandling.kHICommandPDFDocumentShadow:
callPDFDrawProc(
context, ShadowsAndTransparencyLayers.shadowPDFDocument, kCatPDF
)
elif drawingType == UIHandling.kHICommandSimpleAxialShading:
Shadings.doSimpleAxialShading(context)
elif drawingType == UIHandling.kHICommandExampleAxialShadings:
Shadings.doExampleAxialShading(context)
elif drawingType == UIHandling.kHICommandSimpleRadialShading:
Shadings.doSimpleRadialShading(context)
elif drawingType == UIHandling.kHICommandExampleRadialShadings:
Shadings.doExampleRadialShadings(context)
elif drawingType == UIHandling.kHICommandEllipseShading:
Shadings.doEllipseShading(context)
elif drawingType == UIHandling.kHICommandDoCompatibleEPS:
doCompatibleEPSDrawing(context)
BitmapContext.py¶
import AppDrawing
import DrawingBasics
import LaunchServices
import Quartz
import Utilities
# import Carbon.QT
BEST_BYTE_ALIGNMENT = 16
def COMPUTE_BEST_BYTES_PER_ROW(bpr):
return (int(bpr) + BEST_BYTE_ALIGNMENT - 1) & ~(BEST_BYTE_ALIGNMENT - 1)
# We need to ensure that the raster data stays alive until we clean up
# the context, store it here.
_rasterDataForContext = {}
def createRGBBitmapContext(
width, height, wantDisplayColorSpace, needsTransparentBitmap
):
# This routine allocates data for a pixel array that contains width*height
# pixels where each pixel is 4 bytes. The format is 8-bit ARGB or XRGB, depending on
# whether needsTransparentBitmap is true. In order to get the recommended
# pixel alignment, the bytesPerRow is rounded up to the nearest multiple
# of BEST_BYTE_ALIGNMENT bytes.
# Minimum bytes per row is 4 bytes per sample * number of samples.
bytesPerRow = width * 4
# Round to nearest multiple of BEST_BYTE_ALIGNMENT.
bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow)
# Allocate the data for the raster. The total amount of data is bytesPerRow
# times the number of rows. The function 'calloc' is used so that the
# memory is initialized to 0.
try:
rasterData = bytearray(int(bytesPerRow * height))
except MemoryError:
return None
# The wantDisplayColorSpace argument passed to the function determines
# whether or not to use the display color space or the generic calibrated
# RGB color space. The needsTransparentBitmap argument determines whether
# create a context that records alpha or not.
if wantDisplayColorSpace:
cs = Utilities.getTheDisplayColorSpace()
else:
cs = Utilities.getTheCalibratedRGBColorSpace()
if needsTransparentBitmap:
transparency = Quartz.kCGImageAlphaPremultipliedFirst
else:
transparency = Quartz.kCGImageAlphaPremultipliedFirst
context = Quartz.CGBitmapContextCreate(
rasterData, width, height, 8, bytesPerRow, cs, transparency
)
if context is None:
return None
_rasterDataForContext[context] = rasterData
# Either clear the rect or paint with opaque white, depending on
# the needs of the caller.
if needsTransparentBitmap:
# Clear the context bits so they are transparent.
Quartz.CGContextClearRect(context, Quartz.CGRectMake(0, 0, width, height))
else:
# Since the drawing destination is opaque, first paint
# the context bits to white.
Quartz.CGContextSaveGState(context)
Quartz.CGContextSetFillColorWithColor(
context, Utilities.getRGBOpaqueWhiteColor()
)
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, width, height))
Quartz.CGContextRestoreGState(context)
return context
def myCGContextGetBitmapInfo(c):
if hasattr(Quartz, "CGBitmapContextGetBitmapInfo"):
return Quartz.CGBitmapContextGetBitmapInfo(c)
else:
return Quartz.CGBitmapContextGetAlphaInfo(c)
# createImageFromBitmapContext creates a CGImageRef
# from a bitmap context. Calling this routine
# transfers 'ownership' of the raster data
# in the bitmap context, to the image. If the
# image can't be created, this routine frees
# the memory associated with the raster.
def createImageFromBitmapContext(c):
rasterData = _rasterDataForContext[c]
# We own the data, hence remove from the mapping
del _rasterDataForContext[c]
imageDataSize = Quartz.CGBitmapContextGetBytesPerRow(
c
) * Quartz.CGBitmapContextGetHeight(c)
if rasterData is None:
print("Context is not a bitmap context!")
# Create the data provider from the image data
dataProvider = Quartz.CGDataProviderCreateWithData(
None, rasterData, imageDataSize, None
)
if dataProvider is None:
print("Couldn't create data provider!")
return None
# Now create the image. The parameters for the image closely match
# the parameters of the bitmap context. This code uses a NULL
# decode array and shouldInterpolate is true.
image = Quartz.CGImageCreate(
Quartz.CGBitmapContextGetWidth(c),
Quartz.CGBitmapContextGetHeight(c),
Quartz.CGBitmapContextGetBitsPerComponent(c),
Quartz.CGBitmapContextGetBitsPerPixel(c),
Quartz.CGBitmapContextGetBytesPerRow(c),
Quartz.CGBitmapContextGetColorSpace(c),
myCGContextGetBitmapInfo(c),
dataProvider,
None,
True,
Quartz.kCGRenderingIntentDefault,
)
if image is None:
print("Couldn't create image!")
return None
return image
def exportCGImageToFileWithQT(image, url, outputFormat, dpi):
"""
Export an image using QuickTime API's.
This function can't possibly work, as the relevant APIs aren't available
through MacPython. The code below is mostly there in case someone fixes
the MacPython QuickTime bindings.
"""
# XXX: Finish
return
if outputFormat.lower() == LaunchServices.kUTTypeTIFF.lower():
imageExportType = LaunchServices.kQTFileTypeTIFF
elif outputFormat.lower() == LaunchServices.kUTTypePNG.lower():
imageExportType = LaunchServices.kQTFileTypePNG
elif outputFormat.lower() == LaunchServices.kUTTypeJPEG.lower():
imageExportType = LaunchServices.kQTFileTypeJPEG
else:
print(f"Requested image export format {outputFormat!r} unsupported")
return
result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
url, 0, None, None
)
if result == 0:
result, graphicsExporter = Quartz.OpenADefaultComponent(
Quartz.GraphicsExporterComponentType,
imageExportType,
graphicsExporter, # noqa: F821
)
if result == 0:
result = Quartz.GraphicsExportSetInputCGImage(graphicsExporter, image)
if result == 0:
result = Quartz.GraphicsExportSetResolution(
graphicsExporter,
Quartz.FloatToFixed(dpi),
Quartz.FloatToFixed(dpi),
)
if result == 0:
result = Quartz.GraphicsExportSetOutputDataReference(
graphicsExporter, dataRef, dataRefType
)
if result == 0:
result, sizeWritten = Quartz.GraphicsExportDoExport(
graphicsExporter, None
)
Quartz.CloseComponent(graphicsExporter)
if dataRef:
Quartz.DisposeHandle(dataRef)
if result:
print("QT export got bad result = %d!" % (result,))
def exportCGImageToFileWithDestination(image, url, outputFormat, dpi):
# Create an image destination at the supplied URL that
# corresponds to the output image format. The destination will
# only contain 1 image.
imageDestination = Quartz.CGImageDestinationCreateWithURL(
url, outputFormat, 1, None
)
if imageDestination is None:
print("Couldn't create image destination!")
return
# Create an options dictionary with the X&Y resolution of the image
options = {
Quartz.kCGImagePropertyDPIWidth: dpi,
Quartz.kCGImagePropertyDPIHeight: dpi,
}
# Add the image with the options dictionary to the destination.
Quartz.CGImageDestinationAddImage(imageDestination, image, options)
# When all the images are added to the destination, finalize it.
Quartz.CGImageDestinationFinalize(imageDestination)
def MakeImageDocument(url, imageType, exportInfo):
# First make a bitmap context for a US Letter size
# raster at the requested resolution.
dpi = exportInfo.dpi
width = int(8.5 * dpi)
height = int(11 * dpi)
# For JPEG output type the bitmap should not be transparent. If other types are added that
# do not support transparency, this code should be updated to check for those types as well.
needTransparentBitmap = imageType.lower() != LaunchServices.kUTTypeJPEG.lower()
# Create an RGB Bitmap context using the generic calibrated RGB color space
# instead of the display color space.
useDisplayColorSpace = False
c = createRGBBitmapContext(
width, height, useDisplayColorSpace, needTransparentBitmap
)
if c is None:
print("Couldn't make destination bitmap context")
return -1
# Scale the coordinate system based on the resolution in dots per inch.
Quartz.CGContextScaleCTM(c, dpi / 72, dpi / 72)
# Set the font smoothing parameter to false since it's better to
# draw any text without special LCD text rendering when creating
# rendered data for export.
if hasattr(Quartz, "CGContextSetShouldSmoothFonts"):
Quartz.CGContextSetShouldSmoothFonts(c, False)
# Set the scaling factor for shadows. This is a hack so that
# drawing code that needs to know the scaling factor can
# obtain it. Better would be that DispatchDrawing and the code
# it calls would take this scaling factor as a parameter.
Utilities.setScalingFactor(dpi / 72)
# Draw into that raster...
AppDrawing.DispatchDrawing(c, exportInfo.command)
# Set the scaling factor back to 1.0.
Utilities.setScalingFactor(1.0)
# Create an image from the raster data. Calling
# createImageFromBitmapContext gives up ownership
# of the raster data used by the context.
image = createImageFromBitmapContext(c)
# Release the context now that the image is created.
del c
if image is None:
# Users of this code should update this to be an error code they find useful.
return -1
# Now export the image.
if exportInfo.useQTForExport:
exportCGImageToFileWithQT(image, url, imageType, exportInfo.dpi)
else:
exportCGImageToFileWithDestination(image, url, imageType, exportInfo.dpi)
def MakeTIFFDocument(url, exportInfo):
return MakeImageDocument(url, LaunchServices.kUTTypeTIFF, exportInfo)
def MakePNGDocument(url, exportInfo):
return MakeImageDocument(url, LaunchServices.kUTTypePNG, exportInfo)
def MakeJPEGDocument(url, exportInfo):
return MakeImageDocument(url, LaunchServices.kUTTypeJPEG, exportInfo)
def createCGLayerForDrawing(c):
rect = Quartz.CGRectMake(0, 0, 50, 50)
# Make the layer the size of the rectangle that
# this code draws into the layer.
layerSize = rect.size
# Create the layer to draw into.
layer = Quartz.CGLayerCreateWithContext(c, layerSize, None)
if layer is None:
return None
# Get the context corresponding to the layer.
layerContext = Quartz.CGLayerGetContext(layer)
if layerContext is None:
return None
# $ Set the fill color to opaque black.
Quartz.CGContextSetFillColorWithColor(
layerContext, Utilities.getRGBOpaqueBlackColor()
)
# Draw the content into the layer.
Quartz.CGContextFillRect(layerContext, rect)
# Now the layer has the contents needed.
return layer
def doSimpleCGLayer(context):
# Create the layer.
layer = createCGLayerForDrawing(context)
if layer is None:
print("Couldn't create layer!")
return
# Get the size of the layer created.
s = Quartz.CGLayerGetSize(layer)
# Clip to a rect that corresponds to
# a grid of 8x8 layer objects.
Quartz.CGContextClipToRect(
context, Quartz.CGRectMake(0, 0, 8 * s.width, 8 * s.height)
)
# Paint 8 rows of layer objects.
for j in range(8):
Quartz.CGContextSaveGState(context)
# Paint 4 columns of layer objects, moving
# across the drawing canvas by skipping a
# square on the grid each time across.
for _ in range(4):
# Draw the layer at the current origin.
Quartz.CGContextDrawLayerAtPoint(context, Quartz.CGPointZero, layer)
# Translate across two layer widths.
Quartz.CGContextTranslateCTM(context, 2 * s.width, 0)
Quartz.CGContextRestoreGState(context)
# Translate to the left one layer width on
# even loop counts and to the right one
# layer width on odd loop counts. Each
# time through the outer loop, translate up
# one layer height.
if j % 2:
Quartz.CGContextTranslateCTM(context, s.width, s.height)
else:
Quartz.CGContextTranslateCTM(context, -s.width, s.height)
def createAlphaOnlyContext(width, height):
# This routine allocates data for a pixel array that contains
# width*height pixels, each pixel is 1 byte. The format is
# 8 bits per pixel, where the data is the alpha value of the pixel.
# Minimum bytes per row is 1 byte per sample * number of samples.
bytesPerRow = width
# Round to nearest multiple of BEST_BYTE_ALIGNMENT.
bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow)
# Allocate the data for the raster. The total amount of data is bytesPerRow
# // times the number of rows. The function 'calloc' is used so that the
# // memory is initialized to 0.
try:
rasterData = bytearray(bytesPerRow * height)
except MemoryError:
return None
# This type of context is only available in Panther and later, otherwise
# this fails and returns a NULL context. The color space for an alpha
# // only context is NULL and the BitmapInfo value is kCGImageAlphaOnly.
context = Quartz.CGBitmapContextCreate(
rasterData, width, height, 8, bytesPerRow, None, Quartz.kCGImageAlphaOnly
)
if context is None:
print("Couldn't create the context!")
return None
_rasterDataForContext[context] = rasterData
# Clear the context bits so they are initially transparent.
Quartz.CGContextClearRect(context, Quartz.CGRectMake(0, 0, width, height))
return context
# createMaskFromAlphaOnlyContext creates a CGImageRef
# from an alpha-only bitmap context. Calling this routine
# transfers 'ownership' of the raster data in the bitmap
# context, to the image. If the image can't be created, this
# routine frees the memory associated with the raster.
def createMaskFromAlphaOnlyContext(alphaContext):
rasterData = _rasterDataForContext[alphaContext]
# We own the data, hence remove from the mapping
del _rasterDataForContext[alphaContext]
imageDataSize = Quartz.CGBitmapContextGetBytesPerRow(
alphaContext
) * Quartz.CGBitmapContextGetHeight(alphaContext)
invertDecode = [1.0, 0.0]
# Create the data provider from the image data.
dataProvider = Quartz.CGDataProviderCreateWithData(
None, rasterData, imageDataSize, None
)
if dataProvider is None:
print("Couldn't create data provider!")
return None
mask = Quartz.CGImageMaskCreate(
Quartz.CGBitmapContextGetWidth(alphaContext),
Quartz.CGBitmapContextGetHeight(alphaContext),
Quartz.CGBitmapContextGetBitsPerComponent(alphaContext),
Quartz.CGBitmapContextGetBitsPerPixel(alphaContext),
Quartz.CGBitmapContextGetBytesPerRow(alphaContext),
dataProvider,
# The decode is an inverted decode since a mask has the opposite
# sense than alpha, i.e. 0 in a mask paints 100% and 1 in a mask
# paints nothing.
invertDecode,
True,
)
if mask is None:
print("Couldn't create image mask!")
return None
return mask
def doAlphaOnlyContext(context):
# This code is going to capture the alpha coverage
# of the drawing done by the doAlphaRects routine.
# The value passed here as the width and height is
# the size of the bounding rectangle of that drawing.
width = 520
height = 400
alphaContext = createAlphaOnlyContext(width, height)
if context is None:
print("Couldn't create the alpha-only context!")
return
# Draw the content to the alpha-only context, capturing
# the alpha coverage. The doAlphaRects routine paints
# a series of translucent red rectangles.
DrawingBasics.doAlphaRects(alphaContext)
# Finished drawing to the context and now the raster contains
# the alpha data captured from the drawing. Create
# the mask from the data in the context.
mask = createMaskFromAlphaOnlyContext(alphaContext)
# This code is now finished with the context so it can
# release it.
del alphaContext
if mask is None:
return
# Set the fill color space.
Quartz.CGContextSetFillColorSpace(
context, Utilities.getTheCalibratedRGBColorSpace()
)
opaqueBlue = (0.11, 0.208, 0.451, 1.0)
# Set the painting color to opaque blue.
Quartz.CGContextSetFillColor(context, opaqueBlue)
# Draw the mask, painting the mask with blue. This colorizes
# the image to blue and it is as if we painted the
# alpha rects with blue instead of red.
Quartz.CGContextDrawImage(context, Quartz.CGRectMake(0, 0, width, height), mask)
_pdfDoc = None
_pdfURL = None
_width = 0
_height = 0
def getThePDFDoc(url):
"""
This function caches a CGPDFDocumentRef for
the most recently requested PDF document.
"""
global _pdfDoc, _pdfURL, _width, _height
if url is None:
return None, 0, 0
# See whether to update the cached PDF document.
if _pdfDoc is None or url != _pdfURL:
# Release any cached document or URL.
_pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
if _pdfDoc is not None:
pdfMediaRect = Quartz.CGPDFDocumentGetMediaBox(_pdfDoc, 1)
_width = pdfMediaRect.size.width
_height = pdfMediaRect.size.height
# Keep the URL of the PDF file being cached.
_pdfURL = url
else:
_pdfURL = None
if _pdfDoc is not None:
return _pdfDoc, _width, _height
else:
return None, 0, 0
# Defining this scales the content down by 1/3.
DOSCALING = True
def TilePDFNoBuffer(context, url):
# The amount of area to tile should really be based on the
# window/document. Here it is hard coded to a US Letter
# size document. This may draw too many or too few tiles
# for the area actually being filled.
fillwidth = 612.0
fillheight = 792.0
extraOffset = 6.0
pdfDoc, tileX, tileY = getThePDFDoc(url)
if pdfDoc is None:
print("Couldn't get the PDF document!")
return
if DOSCALING:
# Make the tiles 1/3 the size of the PDF document.
tileX /= 3
tileY /= 3
extraOffset /= 3
# Space the tiles by the tile width and height
# plus extraOffset units in each dimension.
tileOffsetX = extraOffset + tileX
tileOffsetY = extraOffset + tileY
# Tile the PDF document.
for h in range(0, int(fillheight), int(tileOffsetY)):
for w in range(0, int(fillwidth), int(tileOffsetX)):
Quartz.CGContextDrawPDFDocument(
context, Quartz.CGRectMake(w, h, tileX, tileY), pdfDoc, 1
)
def TilePDFWithOffscreenBitmap(context, url):
# Again this should really be computed based on
# the area intended to be tiled.
fillwidth = 612.0
fillheight = 792.0
extraOffset = 6.0
pdfDoc, tileX, tileY = getThePDFDoc(url)
if pdfDoc is None:
print("Couldn't get the PDF document")
return
if DOSCALING:
# Make the tiles 1/3 the size of the PDF document.
tileX /= 3
tileY /= 3
extraOffset /= 3
# Space the tiles by the tile width and height
# plus extraOffset units in each dimension.
tileOffsetX = extraOffset + tileX
tileOffsetY = extraOffset + tileY
# Since the bitmap context is for use with the display
# and should capture alpha, these are the values
# to pass to createRGBBitmapContext.
useDisplayColorSpace = True
needTransparentBitmap = True
bitmapContext = createRGBBitmapContext(
tileX, tileY, useDisplayColorSpace, needTransparentBitmap
)
if bitmapContext is None:
print("Couldn't create bitmap context!")
return
# Draw the PDF document one time into the bitmap context.
Quartz.CGContextDrawPDFDocument(
bitmapContext, Quartz.CGRectMake(0, 0, tileX, tileY), pdfDoc, 1
)
# Create an image from the raster data. Calling
# createImageFromBitmapContext gives up ownership
# of the raster data used by the context.
image = createImageFromBitmapContext(bitmapContext)
# Release the context now that the image is created.
del bitmapContext
if image is None:
return
# Now tile the image.
for h in range(0, int(fillheight), int(tileOffsetY)):
for w in range(0, int(fillwidth), int(tileOffsetX)):
Quartz.CGContextDrawImage(
context, Quartz.CGRectMake(w, h, tileX, tileY), image
)
def createLayerWithImageForContext(c, url):
layerSize = Quartz.CGSize()
pdfDoc, layerSize.width, layerSize.height = getThePDFDoc(url)
if pdfDoc is None:
return None
if DOSCALING:
# Make the layer 1/3 the size of the PDF document.
layerSize.width /= 3
layerSize.height /= 3
# Create the layer to draw into.
layer = Quartz.CGLayerCreateWithContext(c, layerSize, None)
if layer is None:
return None
# Get the context corresponding to the layer. Note
# that this is a 'Get' function so the code must
# not release the context.
layerContext = Quartz.CGLayerGetContext(layer)
if layerContext is None:
return None
# Draw the PDF document into the layer.
Quartz.CGContextDrawPDFDocument(
layerContext,
Quartz.CGRectMake(0, 0, layerSize.width, layerSize.height),
pdfDoc,
1,
)
# Now the layer has the contents needed.
return layer
def TilePDFWithCGLayer(context, url):
# Again this should really be computed based on
# the area intended to be tiled.
fillwidth = 612.0
fillheight = 792.0
layer = createLayerWithImageForContext(context, url)
if layer is None:
print("Couldn't create the layer!")
return
# Compute the tile size and offset.
s = Quartz.CGLayerGetSize(layer)
tileX = s.width
tileY = s.height
if DOSCALING:
# Space the tiles by the tile width and height
# plus an extra 2 units in each dimension.
tileOffsetX = 2.0 + tileX
tileOffsetY = 2.0 + tileY
else:
# Add 6 units to the offset in each direction
# if there is no scaling of the source PDF document.
tileOffsetX = 6.0 + tileX
tileOffsetY = 6.0 + tileY
# Now draw the contents of the layer to the context.
# The layer is drawn at its true size (the size of
# the tile) with its origin located at the corner
# of each tile.
for h in range(0, int(fillheight), int(tileOffsetY)):
for w in range(0, int(fillwidth), int(tileOffsetX)):
Quartz.CGContextDrawLayerAtPoint(context, Quartz.CGPointMake(w, h), layer)
ColorAndGState.py¶
import array
import Quartz
import Utilities
def doColorSpaceFillAndStroke(context):
theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
opaqueRed = (0.663, 0.0, 0.031, 1.0) # red,green,blue,alpha
aBlue = (0.482, 0.62, 0.871, 1.0) # red,green,blue,alpha
# Set the fill color space to be the generic calibrated RGB color space.
Quartz.CGContextSetFillColorSpace(context, theColorSpace)
# Set the fill color to opaque red. The number of elements in the
# array passed to this function must be the number of color
# components in the current fill color space plus 1 for alpha.
Quartz.CGContextSetFillColor(context, opaqueRed)
# Set the stroke color space to be the generic calibrated RGB color space.
Quartz.CGContextSetStrokeColorSpace(context, theColorSpace)
# Set the stroke color to opaque blue. The number of elements
# in the array passed to this function must be the number of color
# components in the current stroke color space plus 1 for alpha.
Quartz.CGContextSetStrokeColor(context, aBlue)
Quartz.CGContextSetLineWidth(context, 8.0)
# Rectangle 1.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(20.0, 20.0, 100.0, 100.0))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# Continue to use the stroke colorspace already set
# but change the stroke alpha value to a semitransparent blue.
aBlue = list(aBlue)
aBlue[3] = 0.5
Quartz.CGContextSetStrokeColor(context, aBlue)
# Rectangle 2.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(140.0, 20.0, 100.0, 100.0))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# Don't release the color space since this routine
# didn't create it.
_opaqueRedColor = None
_opaqueBlueColor = None
_transparentBlueColor = None
def drawWithColorRefs(context):
global _opaqueRedColor
global _opaqueBlueColor
global _transparentBlueColor
# Initialize the CGColorRefs if necessary
if _opaqueRedColor is None:
# Initialize the color array to an opaque red
# in the generic calibrated RGB color space.
color = (0.663, 0.0, 0.031, 1.0)
theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
# Create a CGColorRef for opaque red.
_opaqueRedColor = Quartz.CGColorCreate(theColorSpace, color)
# Make the color array correspond to an opaque blue color.
color = (0.482, 0.62, 0.87, 1.0)
# Create another Quartz.CGColorRef for opaque blue.
_opaqueBlueColor = Quartz.CGColorCreate(theColorSpace, color)
# Create a new CGColorRef from the opaqueBlue CGColorRef
# but with a different alpha value.
_transparentBlueColor = Quartz.CGColorCreateCopyWithAlpha(_opaqueBlueColor, 0.5)
if (
_opaqueRedColor is None
or _opaqueBlueColor is None
or _transparentBlueColor is None
):
print("Couldn't create one of the CGColorRefs!!!")
return
# Set the fill color to the opaque red CGColor object.
Quartz.CGContextSetFillColorWithColor(context, _opaqueRedColor)
# Set the stroke color to the opaque blue CGColor object.
Quartz.CGContextSetStrokeColorWithColor(context, _opaqueBlueColor)
Quartz.CGContextSetLineWidth(context, 8.0)
# Draw the first rectangle.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(20.0, 20.0, 100.0, 100.0))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# Set the stroke color to be that of the transparent blue
# CGColor object.
Quartz.CGContextSetStrokeColorWithColor(context, _transparentBlueColor)
# Draw a second rectangle to the right of the first one.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(140.0, 20.0, 100.0, 100.0))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
def doIndexedColorDrawGraphics(context):
theBaseRGBSpace = Utilities.getTheCalibratedRGBColorSpace()
lookupTable = array.array("B", (0,) * 6)
opaqueRed = (0, 1) # index, alpha
aBlue = (1, 1) # index, alpha
# Set the first 3 values in the lookup table to a red of
# 169/255 = 0.663, no green, and blue = 8/255 = 0.031. This makes
# the first entry in the lookup table a shade of red.
lookupTable[0] = 169
lookupTable[1] = 0
lookupTable[2] = 8
# Set the second 3 values in the lookup table to a red value
# of 123/255 = 0.482, a green value of 158/255 = 0.62, and
# a blue value of 222/255 = 0.871. This makes the second entry
# in the lookup table a shade of blue.
lookupTable[3] = 123
lookupTable[4] = 158
lookupTable[5] = 222
# Create the indexed color space with this color lookup table,
# using the RGB color space as the base color space and a 2 element
# color lookup table to characterize the indexed color space.
theIndexedSpace = Quartz.CGColorSpaceCreateIndexed(theBaseRGBSpace, 1, lookupTable)
if theIndexedSpace is not None:
Quartz.CGContextSetStrokeColorSpace(context, theIndexedSpace)
Quartz.CGContextSetFillColorSpace(context, theIndexedSpace)
# Set the stroke color to an opaque blue.
Quartz.CGContextSetStrokeColor(context, aBlue)
# Set the fill color to an opaque red.
Quartz.CGContextSetFillColor(context, opaqueRed)
Quartz.CGContextSetLineWidth(context, 8.0)
# Draw the first rectangle.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(20.0, 20.0, 100.0, 100.0))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# Continue to use the stroke colorspace already set
# but change the stroke alpha value to a semitransparent value
# while leaving the index value unchanged.
aBlue = list(aBlue)
aBlue[1] = 0.5
Quartz.CGContextSetStrokeColor(context, aBlue)
# Draw another rectangle to the right of the first one.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(140.0, 20.0, 100.0, 100.0))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
else:
print("Couldn't make the indexed color space!")
def drawWithGlobalAlpha(context):
rect = Quartz.CGRectMake(40.0, 210.0, 100.0, 100.0)
color = [1.0, 0.0, 0.0, 1.0] # opaque red
# Set the fill color space to that returned by getTheCalibratedRGBColorSpace.
Quartz.CGContextSetFillColorSpace(
context, Utilities.getTheCalibratedRGBColorSpace()
)
Quartz.CGContextSetFillColor(context, color)
for _ in range(2):
Quartz.CGContextSaveGState(context)
# Paint the leftmost rect on this row with 100% opaque red.
Quartz.CGContextFillRect(context, rect)
Quartz.CGContextTranslateCTM(context, rect.size.width + 70.0, 0.0)
# Set the alpha value of this rgba color to 0.5.
color[3] = 0.5
# Use the new color as the fill color in the graphics state.
Quartz.CGContextSetFillColor(context, color)
# Paint the center rect on this row with 50% opaque red.
Quartz.CGContextFillRect(context, rect)
Quartz.CGContextTranslateCTM(context, rect.size.width + 70.0, 0.0)
# Set the alpha value of this rgba color to 0.25.
color[3] = 0.25
# Use the new color as the fill color in the graphics state.
Quartz.CGContextSetFillColor(context, color)
# Paint the rightmost rect on this row with 25% opaque red.
Quartz.CGContextFillRect(context, rect)
Quartz.CGContextRestoreGState(context)
# After restoring the graphics state, the fill color is set to
# that prior to calling CGContextSaveGState, that is, opaque
# red. The coordinate system is also restored.
# Now set the context global alpha value to 50% opaque.
Quartz.CGContextSetAlpha(context, 0.5)
# Translate down for a second row of rectangles.
Quartz.CGContextTranslateCTM(context, 0.0, -(rect.size.height + 70.0))
# Reset the alpha value of the color array to fully opaque.
color[3] = 1.0
def drawWithColorBlendMode(context, url):
# A pleasant green color.
green = [0.584, 0.871, 0.318, 1.0]
# Create a CGPDFDocument object from the URL.
pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
if pdfDoc is None:
print("Couldn't create CGPDFDocument from URL!")
return
# Obtain the media box for page 1 of the PDF document.
pdfRect = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
# Set the origin of the rectangle to (0,0).
pdfRect.origin.x = pdfRect.origin.y = 0
# Graphic 1, the left portion of the figure.
Quartz.CGContextTranslateCTM(context, 20, 10 + Quartz.CGRectGetHeight(pdfRect) / 2)
# Draw the PDF document.
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
# Set the fill color space to that returned by getTheCalibratedRGBColorSpace.
Quartz.CGContextSetFillColorSpace(
context, Utilities.getTheCalibratedRGBColorSpace()
)
# Set the fill color to green.
Quartz.CGContextSetFillColor(context, green)
# Graphic 2, the top-right portion of the figure.
Quartz.CGContextTranslateCTM(
context,
Quartz.CGRectGetWidth(pdfRect) + 10,
Quartz.CGRectGetHeight(pdfRect) / 2 + 10,
)
# Draw the PDF document again.
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
# Make a fill rectangle that is the same size as the PDF document
# but inset each side by 80 units in x and 20 units in y.
insetRect = Quartz.CGRectInset(pdfRect, 80, 20)
# Fill the rectangle with green. Because the fill color is opaque and
# the blend mode is Normal, this obscures the drawing underneath.
Quartz.CGContextFillRect(context, insetRect)
# Graphic 3, the bottom-right portion of the figure.
Quartz.CGContextTranslateCTM(context, 0, -(10 + Quartz.CGRectGetHeight(pdfRect)))
# Draw the PDF document again.
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
# Set the blend mode to kCGBlendModeColor which will
# colorize the destination with subsequent drawing.
Quartz.CGContextSetBlendMode(context, Quartz.kCGBlendModeColor)
# Draw the rectangle on top of the PDF document. The portion of the
# background that is covered by the rectangle is colorized
# with the fill color.
Quartz.CGContextFillRect(context, insetRect)
def createEllipsePath(context, center, ellipseSize):
Quartz.CGContextSaveGState(context)
# Translate the coordinate origin to the center point.
Quartz.CGContextTranslateCTM(context, center.x, center.y)
# Scale the coordinate system to half the width and height
# of the ellipse.
Quartz.CGContextScaleCTM(context, ellipseSize.width / 2, ellipseSize.height / 2)
Quartz.CGContextBeginPath(context)
# Add a circular arc to the path, centered at the origin and
# with a radius of 1.0. This radius, together with the
# scaling above for the width and height, produces an ellipse
# of the correct size.
Quartz.CGContextAddArc(
context, 0.0, 0.0, 1.0, 0.0, Utilities.DEGREES_TO_RADIANS(360.0), 0.0
)
# Close the path so that this path is suitable for stroking,
# should that be desired.
Quartz.CGContextClosePath(context)
Quartz.CGContextRestoreGState(context)
_opaqueBrownColor = None
_opaqueOrangeColor = None
def doClippedEllipse(context):
global _opaqueBrownColor, _opaqueOrangeColor
theCenterPoint = Quartz.CGPoint(120.0, 120.0)
theEllipseSize = Quartz.CGSize(100.0, 200.0)
dash = [2.0]
# Initialize the CGColorRefs if necessary.
if _opaqueBrownColor is None:
# The initial value of the color array is an
# opaque brown in an RGB color space.
color = [0.325, 0.208, 0.157, 1.0]
theColorSpace = Utilities.getTheCalibratedRGBColorSpace()
# Create a CGColorRef for opaque brown.
_opaqueBrownColor = Quartz.CGColorCreate(theColorSpace, color)
# Make the color array correspond to an opaque orange.
color = [0.965, 0.584, 0.059, 1.0]
# Create another CGColorRef for opaque orange.
_opaqueOrangeColor = Quartz.CGColorCreate(theColorSpace, color)
# Draw two ellipses centered about the same point, one
# rotated 45 degrees from the other.
Quartz.CGContextSaveGState(context)
# Ellipse 1
createEllipsePath(context, theCenterPoint, theEllipseSize)
Quartz.CGContextSetFillColorWithColor(context, _opaqueBrownColor)
Quartz.CGContextFillPath(context)
# Translate and rotate about the center point of the ellipse.
Quartz.CGContextTranslateCTM(context, theCenterPoint.x, theCenterPoint.y)
# Rotate by 45 degrees.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
# Ellipse 2
# CGPointZero is a pre-defined Quartz point corresponding to
# the coordinate (0,0).
createEllipsePath(context, Quartz.CGPointZero, theEllipseSize)
Quartz.CGContextSetFillColorWithColor(context, _opaqueOrangeColor)
Quartz.CGContextFillPath(context)
Quartz.CGContextRestoreGState(context)
Quartz.CGContextTranslateCTM(context, 170.0, 0.0)
# Now use the first ellipse as a clipping area prior to
# painting the second ellipse.
Quartz.CGContextSaveGState(context)
# Ellipse 3
createEllipsePath(context, theCenterPoint, theEllipseSize)
Quartz.CGContextSetStrokeColorWithColor(context, _opaqueBrownColor)
Quartz.CGContextSetLineDash(context, 0, dash, 1)
# Stroke the path with a dash.
Quartz.CGContextStrokePath(context)
# Ellipse 4
createEllipsePath(context, theCenterPoint, theEllipseSize)
# Clip to the elliptical path.
Quartz.CGContextClip(context)
Quartz.CGContextTranslateCTM(context, theCenterPoint.x, theCenterPoint.y)
# Rotate by 45 degrees.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
# Ellipse 5
createEllipsePath(context, Quartz.CGPointZero, theEllipseSize)
Quartz.CGContextSetFillColorWithColor(context, _opaqueOrangeColor)
Quartz.CGContextFillPath(context)
Quartz.CGContextRestoreGState(context)
CoordinateSystem.py¶
import math
import Quartz
import Utilities
def doRotatedEllipses(context):
totreps = 144
tint = 1.0
tintIncrement = 1.0 / totreps
# Create a new transform consisting of a 45 degrees rotation.
theTransform = Quartz.CGAffineTransformMakeRotation(math.pi / 4)
# Apply a scale to the transform just created.
theTransform = Quartz.CGAffineTransformScale(theTransform, 1, 2)
# Place the first ellipse at a good location.
Quartz.CGContextTranslateCTM(context, 100.0, 100.0)
for _ in range(totreps):
# Make a snapshot the coordinate system.
Quartz.CGContextSaveGState(context)
# Set up the coordinate system for the rotated ellipse.
Quartz.CGContextConcatCTM(context, theTransform)
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddArc(context, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, 0)
# Set the fill color for this instance of the ellipse.
Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, 1.0)
Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)
# Restore the coordinate system to that of the snapshot.
Quartz.CGContextRestoreGState(context)
# Compute the next tint color.
tint -= tintIncrement
# Move over by 1 unit in x for the next ellipse.
Quartz.CGContextTranslateCTM(context, 1.0, 0.0)
def drawSkewedCoordinateSystem(context):
# alpha is 22.5 degrees and beta is 15 degrees.
alpha = math.pi / 8
beta = math.pi / 12
# Create a rectangle that is 72 units on a side
# with its origin at (0,0).
r = Quartz.CGRectMake(0, 0, 72, 72)
Quartz.CGContextTranslateCTM(context, 144, 144)
# Draw the coordinate axes untransformed.
Utilities.drawCoordinateAxes(context)
# Fill the rectangle.
Quartz.CGContextFillRect(context, r)
# Create an affine transform that skews the coordinate system,
# skewing the x-axis by alpha radians and the y-axis by beta radians.
skew = Quartz.CGAffineTransformMake(1, math.tan(alpha), math.tan(beta), 1, 0, 0)
# Apply that transform to the context coordinate system.
Quartz.CGContextConcatCTM(context, skew)
# Set the fill and stroke color to a dark blue.
Quartz.CGContextSetRGBStrokeColor(context, 0.11, 0.208, 0.451, 1)
Quartz.CGContextSetRGBFillColor(context, 0.11, 0.208, 0.451, 1)
# Draw the coordinate axes again, now transformed.
Utilities.drawCoordinateAxes(context)
# Set the fill color again but with a partially transparent alpha.
Quartz.CGContextSetRGBFillColor(context, 0.11, 0.208, 0.451, 0.7)
# Fill the rectangle in the transformed coordinate system.
Quartz.CGContextFillRect(context, r)
DataProvidersAndConsumers.py¶
import os
import sys
import Cocoa
import Quartz
def createDataProviderFromPathName(path):
# Create a CFURL for the supplied file system path.
url = Cocoa.CFURLCreateWithFileSystemPath(
None, path, Cocoa.kCFURLPOSIXPathStyle, False
)
if url is None:
print("Couldn't create url!")
return None
# Create a Quartz data provider for the URL.
dataProvider = Quartz.CGDataProviderCreateWithURL(url)
if dataProvider is None:
print("Couldn't create data provider!")
return None
return dataProvider
def createRGBRampDataProvider():
width = 256
height = 256
imageDataSize = width * height * 3
dataP = bytearray(imageDataSize)
# Build an image that is RGB 24 bits per sample. This is a ramp
# where the red component value increases in red from left to
# right and the green component increases from top to bottom.
#
idx = 0
for g in range(height):
for r in range(width):
dataP[idx] = chr(r) if sys.version_info[0] == 2 else r
dataP[idx + 1] = chr(g) if sys.version_info[0] == 2 else g
dataP[idx + 2] = "\0" if sys.version_info[0] == 2 else 0
idx += 3
# Once this data provider is created, the data associated
# with dataP MUST be available until Quartz calls the data
# releaser function 'rgbReleaseRampData'.
dataProvider = Quartz.CGDataProviderCreateWithData(None, dataP, imageDataSize, None)
return dataProvider
class MyImageDataInfo:
fp = None
totalBytesRead = 0
skippedBytes = 0
numRewinds = 0
def getBytesSequentialAccessDP(data, buffer, count):
buf = data.fp.read(count)
buffer[: len(buf)] = buf
data.totalBytesRead += len(buf)
return len(buf), buffer
def skipBytesSequentialAccessDP(data, count):
try:
data.fp.seek(count, os.SEEK_CUR)
data.skippedBytes += count
except OSError as msg:
print("Couldn't seek %d bytes because of %s" % (count, msg))
def rewindSequentialAccessDP(data):
# Rewind the beginning of the data.
data.fp.seek(0, 0)
data.numRewinds += 1
def releaseSequentialAccessDP(data):
if data is not None:
print(
"read %d bytes, skipped %d bytes, rewind called %d times"
% (data.totalBytesRead, data.skippedBytes, data.numRewinds)
)
data.fp.close()
def createSequentialAccessDPForURL(url):
success, pathString = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
pathString = pathString.rstrip(b"\0")
if not success:
print("Couldn't get the path name C string!")
return None
fp = open(pathString, "rb")
if fp is None:
print(f"Couldn't open path to file {pathString}!")
return None
imageDataInfoP = MyImageDataInfo()
imageDataInfoP.fp = fp
provider = Quartz.CGDataProviderCreate(
imageDataInfoP,
(
getBytesSequentialAccessDP,
skipBytesSequentialAccessDP,
rewindSequentialAccessDP,
releaseSequentialAccessDP,
),
)
if provider is None:
print("Couldn't create data provider!")
# Release the info data and cleanup.
releaseSequentialAccessDP(imageDataInfoP)
return None
return provider
def getBytesGrayRampDirectAccess(info, buffer, offset, count):
# This computes a linear gray ramp that is 256 samples wide and
# 1 sample high. The ith byte in the image is the sample
# value i. This produces a gray ramp that goes from 0 (black) to
# FF (white).
idx = 0
# This data provider provides 256 bytes total. If Quartz
# requests more data than is available, only return
# the available data.
if (offset + count) > 256:
count = 256 - offset
for i in range(offset, offset + count):
buffer[idx] = chr(i) if sys.version_info[0] == 2 else i
idx += 1
return count, buffer
def createGrayRampDirectAccessDP():
provider = Quartz.CGDataProviderCreateDirectAccess(
None, 256, (None, None, getBytesGrayRampDirectAccess, None)
)
if provider is None:
print("Couldn't create data provider!")
return None
return provider
# This only builds on Tiger and later.
def myCGDataProviderCreateWithCFData(data):
# If the CFData object passed in is None, this code returns
# a None data provider.
if data is None:
return None
# Test to see if the Quartz version is available and if so, use it.
# XXX: force the replacement code to be used
# if hasattr(Quartz, 'CGDataProviderCreateWithCFData'):
# return CGDataProviderCreateWithCFData(data)
dataSize = Cocoa.CFDataGetLength(data)
provider = Quartz.CGDataProviderCreateWithData(data, data, dataSize, None)
return provider
def createDataConsumerFromPathName(path):
# Create a CFURL for the supplied file system path.
url = Cocoa.CFURLCreateWithFileSystemPath(
None, path, Cocoa.kCFURLPOSIXPathStyle, False
)
if url is None:
print("Couldn't create url!")
return None
# Create a Quartz data provider for the URL.
dataConsumer = Quartz.CGDataConsumerCreateWithURL(url)
if dataConsumer is None:
print("Couldn't create data consumer!")
return None
return dataConsumer
def myCFDataConsumerPutBytes(data, buffer, count):
# Append 'count' bytes from 'buffer' to the CFData
# object 'data'.
Cocoa.CFDataAppendBytes(data, buffer, count)
return count
# This only builds on Tiger and later.
def myCGDataConsumerCreateWithCFData(data):
# If the CFData object passed in is None, this code returns
# a None data consumer.
if data is None:
return None
# Test to see if the Quartz version is available.
# XXX: force the replacement code to be used:
# if hasattr(Quartz, 'CGDataConsumerCreateWithCFData'):
# return CGDataConsumerCreateWithCFData(data)
consumer = Quartz.CGDataConsumerCreate(data, (myCFDataConsumerPutBytes, None))
return consumer
DrawingBasics.py¶
import math
import Quartz
def doSimpleRect(context):
# Set the fill color to opaque red.
Quartz.CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0)
# Set up the rectangle for drawing.
ourRect = Quartz.CGRectMake(20.0, 20.0, 130.0, 100.0)
# Draw the filled rectangle.
Quartz.CGContextFillRect(context, ourRect)
def doStrokedRect(context):
# Set the stroke color to a light opaque blue.
Quartz.CGContextSetRGBStrokeColor(context, 0.482, 0.62, 0.871, 1.0)
# Set up the rectangle for drawing.
ourRect = Quartz.CGRectMake(20.0, 20.0, 130.0, 100.0)
# Draw the stroked rectangle with a line width of 3.
Quartz.CGContextStrokeRectWithWidth(context, ourRect, 3.0)
def doStrokedAndFilledRect(context):
# Define a rectangle to use for drawing.
ourRect = Quartz.CGRectMake(20.0, 220.0, 130.0, 100.0)
# ***** Rectangle 1 *****
# Set the fill color to a light opaque blue.
Quartz.CGContextSetRGBFillColor(context, 0.482, 0.62, 0.871, 1.0)
# Set the stroke color to an opaque green.
Quartz.CGContextSetRGBStrokeColor(context, 0.404, 0.808, 0.239, 1.0)
# Fill the rect.
Quartz.CGContextFillRect(context, ourRect)
# ***** Rectangle 2 *****
# Move the rectangle's origin to the right by 200 units.
ourRect.origin.x += 200.0
# Stroke the rectangle with a line width of 10.
Quartz.CGContextStrokeRectWithWidth(context, ourRect, 10.0)
# ***** Rectangle 3 *****
# Move the rectangle's origin to the left by 200 units
# and down by 200 units.
ourRect.origin.x -= 200.0
ourRect.origin.y -= 200.0
# Fill then stroke the rect with a line width of 10.
Quartz.CGContextFillRect(context, ourRect)
Quartz.CGContextStrokeRectWithWidth(context, ourRect, 10.0)
# ***** Rectangle 4 *****
# Move the rectangle's origin to the right by 200 units.
ourRect.origin.x += 200.0
# Stroke then fill the rect.
Quartz.CGContextStrokeRectWithWidth(context, ourRect, 10.0)
Quartz.CGContextFillRect(context, ourRect)
def createRectPath(context, rect):
# Create a path using the coordinates of the rect passed in.
Quartz.CGContextBeginPath(context)
Quartz.CGContextMoveToPoint(context, rect.origin.x, rect.origin.y)
# ***** Segment 1 *****
Quartz.CGContextAddLineToPoint(
context, rect.origin.x + rect.size.width, rect.origin.y
)
# ***** Segment 2 *****
Quartz.CGContextAddLineToPoint(
context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height
)
# ***** Segment 3 *****
Quartz.CGContextAddLineToPoint(
context, rect.origin.x, rect.origin.y + rect.size.height
)
# ***** Segment 4 is created by closing the path *****
Quartz.CGContextClosePath(context)
def doPathRects(context):
# Define a rectangle to use for drawing.
ourRect = Quartz.CGRectMake(20.0, 20.0, 130.0, 100.0)
# ***** Rectangle 1 *****
# Create the rect path.
createRectPath(context, ourRect)
# Set the fill color to a light opaque blue.
Quartz.CGContextSetRGBFillColor(context, 0.482, 0.62, 0.871, 1.0)
# Fill the path.
Quartz.CGContextDrawPath(context, Quartz.kCGPathFill) # Clears the path.
# ***** Rectangle 2 *****
# Translate the coordinate system 200 units to the right.
Quartz.CGContextTranslateCTM(context, 200.0, 0.0)
# Set the stroke color to an opaque green.
Quartz.CGContextSetRGBStrokeColor(context, 0.404, 0.808, 0.239, 1.0)
createRectPath(context, ourRect)
# Set the line width to 10 units.
Quartz.CGContextSetLineWidth(context, 10.0)
# Stroke the path.
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke) # Clears the path.
# ***** Rectangle 3 *****
# Translate the coordinate system
# 200 units to the left and 200 units down.
Quartz.CGContextTranslateCTM(context, -200.0, -200.0)
createRectPath(context, ourRect)
# Quartz.CGContextSetLineWidth(context, 10.0) # This is redundant.
# Fill, then stroke the path.
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke) # Clears the path.
# ***** Rectangle 4 *****
# Translate the coordinate system 200 units to the right.
Quartz.CGContextTranslateCTM(context, 200.0, 0.0)
createRectPath(context, ourRect)
# Stroke the path.
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke) # Clears the path.
# Create the path again.
createRectPath(context, ourRect)
# Fill the path.
Quartz.CGContextDrawPath(context, Quartz.kCGPathFill) # Clears the path.
def doAlphaRects(context):
# ***** Part 1 *****
ourRect = Quartz.CGRectMake(0.0, 0.0, 130.0, 100.0)
numRects = 6
rotateAngle = 2 * math.pi / numRects
tintAdjust = 1.0 / numRects
# ***** Part 2 *****
Quartz.CGContextTranslateCTM(
context, 2 * ourRect.size.width, 2 * ourRect.size.height
)
# ***** Part 3 *****
tint = 1.0
for _ in range(numRects):
Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, tint)
Quartz.CGContextFillRect(context, ourRect)
# These transformations are cumulative.
Quartz.CGContextRotateCTM(context, rotateAngle)
tint -= tintAdjust
def drawStrokedLine(context, start, end):
Quartz.CGContextBeginPath(context)
Quartz.CGContextMoveToPoint(context, start.x, start.y)
Quartz.CGContextAddLineToPoint(context, end.x, end.y)
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
def doDashedLines(context):
lengths = (12.0, 6.0, 5.0, 6.0, 5.0, 6.0)
start = Quartz.CGPoint(20.0, 270.0)
end = Quartz.CGPoint(300.0, 270.0)
# ***** Line 1 solid line *****
Quartz.CGContextSetLineWidth(context, 5.0)
drawStrokedLine(context, start, end)
# ***** Line 2 long dashes *****
Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
Quartz.CGContextSetLineDash(context, 0.0, lengths, 2)
drawStrokedLine(context, start, end)
# ***** Line 3 long short pattern *****
Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
Quartz.CGContextSetLineDash(context, 0.0, lengths, 4)
drawStrokedLine(context, start, end)
# ***** Line 4 long short short pattern *****
Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
Quartz.CGContextSetLineDash(context, 0.0, lengths, 6)
drawStrokedLine(context, start, end)
# ***** Line 5 short short long pattern *****
Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
Quartz.CGContextSetLineDash(context, lengths[0] + lengths[1], lengths, 6)
drawStrokedLine(context, start, end)
# ***** Line 6 solid line *****
Quartz.CGContextTranslateCTM(context, 0.0, -50.0)
# Reset dash to solid line.
Quartz.CGContextSetLineDash(context, 0, None, 0)
drawStrokedLine(context, start, end)
def doClippedCircle(context):
circleCenter = Quartz.CGPoint(150.0, 150.0)
circleRadius = 100.0
startingAngle = 0.0
endingAngle = 2 * math.pi
ourRect = Quartz.CGRectMake(65.0, 65.0, 170.0, 170.0)
# ***** Filled Circle *****
Quartz.CGContextSetRGBFillColor(context, 0.663, 0.0, 0.031, 1.0)
Quartz.CGContextBeginPath(context)
# Construct the circle path counterclockwise.
Quartz.CGContextAddArc(
context,
circleCenter.x,
circleCenter.y,
circleRadius,
startingAngle,
endingAngle,
0,
)
Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)
# ***** Stroked Square *****
Quartz.CGContextStrokeRect(context, ourRect)
# Translate so that the next drawing doesn't overlap what
# has already been drawn.
Quartz.CGContextTranslateCTM(context, ourRect.size.width + circleRadius + 5.0, 0)
# Create a rectangular path and clip to that path.
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, ourRect)
Quartz.CGContextClip(context)
# ***** Clipped Circle *****
Quartz.CGContextBeginPath(context)
# Construct the circle path counterclockwise.
Quartz.CGContextAddArc(
context,
circleCenter.x,
circleCenter.y,
circleRadius,
startingAngle,
endingAngle,
0,
)
Quartz.CGContextDrawPath(context, Quartz.kCGPathFill)
def doPDFDocument(context, url):
pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
if pdfDoc is not None:
Quartz.CGContextScaleCTM(context, 0.5, 0.5)
# The media box is the bounding box of the PDF document.
pdfRect = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1) # page 1
# Set the destination rect origin to the Quartz origin.
pdfRect.origin.x = pdfRect.origin.y = 0.0
# Draw page 1 of the PDF document.
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
Quartz.CGContextTranslateCTM(context, pdfRect.size.width * 1.2, 0)
# Scale non-uniformly making the y coordinate scale 1.5 times
# the x coordinate scale.
Quartz.CGContextScaleCTM(context, 1, 1.5)
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
Quartz.CGContextTranslateCTM(
context, pdfRect.size.width * 1.2, pdfRect.size.height
)
# Flip the y coordinate axis horizontally about the x axis.
Quartz.CGContextScaleCTM(context, 1, -1)
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
else:
print("Can't create PDF document for URL!")
EPSPrinting.py¶
import BitmapContext
import Cocoa
import objc
import Quartz
import Utilities
# We're using a function that isn't made available through a wrapper, just
# load it manually:
if not hasattr(Quartz, "PMCGImageCreateWithEPSDataProvider"):
functions = [("PMCGImageCreateWithEPSDataProvider", b"@@@")]
import AppKit
d = {}
objc.loadBundleFunctions(AppKit.__bundle__, d, functions)
if "PMCGImageCreateWithEPSDataProvider" in d:
PMCGImageCreateWithEPSDataProvider = d["PMCGImageCreateWithEPSDataProvider"]
else:
print("PMCGImageCreateWithEPSDataProvider doesn't exist")
def getEPSBBox(epspath):
try:
fp = open(epspath)
except OSError:
return Quartz.CGRectZero
try:
# This is a VERY poor man's EPS DSC parser, here just so that
# this sample code can handle simple EPS files. It is
# simple but very inefficient. In addition it does not ensure
# that the DSC comments are at the beginning of a line,
# nor does it handle (atend) style comments at all.
# It will simply find the first occurrence of a
# %%BoundingBox comment and if it is of the typical
# form, it will obtain the bounding box data.
#
for ln in fp:
if ln.startswith("%%BoundingBox:"):
fields = ln.split()[1:]
if len(fields) >= 4:
llx = int(fields[0])
lly = int(fields[1])
urx = int(fields[2])
ury = int(fields[3])
return Quartz.CGRectMake(llx, lly, urx - llx, ury - lly)
finally:
fp.close()
return Quartz.CGRectZero
def createEPSPreviewImage(url):
# The CGImage used as the preview needs to have the
# same width and height as the EPS data it will
# be associated with. This sample code doesn't attempt
# to use any preview image associated with the EPS
# data but instead simply draws a box of an appropriate
# size. Your code would most likely create an image
# that reflects a PICT or TIFF preview present in the
# EPS data.
result, path = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
if not result:
print("Couldn't get the path for EPS file!")
return None
path = path.rstrip(b"\0")
epsRect = getEPSBBox(path)
# Check whether the EPS bounding box is empty.
if epsRect == Quartz.CGRectZero:
print("Couldn't find BoundingBox comment!")
return None
wantDisplayColorSpace = False
needsTransparentBitmap = True
# Create a bitmap context to draw to in order to
# create the preview image. Use the routine
# createRGBBitmapContext from the earlier chapter.
bitmapContext = BitmapContext.createRGBBitmapContext(
epsRect.size.width,
epsRect.size.height,
wantDisplayColorSpace,
needsTransparentBitmap,
)
if bitmapContext is None:
print("Couldn't create bitmap context")
return None
epsRect.origin.x = epsRect.origin.y = 0
# Draw the contents of the preview. The preview consists
# of two lines and a stroke around the bounding box. One
# of the two lines is drawn from the lower-left corner to
# the upper-right corner of the bounding box and the other
# line is from the lower-right corner to the upper-left
# corner of the bounding box.
Quartz.CGContextBeginPath(bitmapContext)
Quartz.CGContextMoveToPoint(bitmapContext, 0, 0)
Quartz.CGContextAddLineToPoint(
bitmapContext, epsRect.size.width, epsRect.size.height
)
Quartz.CGContextMoveToPoint(bitmapContext, epsRect.size.width, 0)
Quartz.CGContextAddLineToPoint(bitmapContext, 0, epsRect.size.height)
Quartz.CGContextStrokePath(bitmapContext)
# Stroke the bounding rectangle, inset so that the stroke is
# completely contained in the EPS bounding rect.
Quartz.CGContextStrokeRect(bitmapContext, Quartz.CGRectInset(epsRect, 0.5, 0.5))
# Now create an image from the bitmap raster data. This image
# has a data provider that releases the image raster data when
# the image is released. Use the createImageFromBitmapContext
# from Chapter 12. Calling createImageFromBitmapContext
# gives up ownership of the raster data used by the context.
epsPreviewImage = BitmapContext.createImageFromBitmapContext(bitmapContext)
if epsPreviewImage is None:
print("Couldn't create preview image!")
return None
return epsPreviewImage
# This technique of handling EPS data is available in
# macOS v10.1 and later and is one alternative method
# of supporting EPS data during printing as compared to
# converting EPS data to PDF data using CGPSConverter which
# is only available in Panther and later.
def createCGEPSImage(url):
previewImage = createEPSPreviewImage(url)
if previewImage is None:
print("Couldn't create EPS preview!")
return None
# It is important that the data provider supplying the
# EPS data conform to the Quartz guidelines for data providers
# and is able to provide the data until the data releaser function
# is called. If you have a custom data provider, you need
# to follow these guidelines since your data provider
# is not necessarily called before you release the image
# that uses the provider.
epsDataProvider = Quartz.CGDataProviderCreateWithURL(url)
if epsDataProvider is None:
print("Couldn't create EPS data provider!")
return None
# Create the hybrid CGImage that contains the preview image
# and the EPS data. Note that the data provider isn't
# called during image creation but at some later point in time.
epsImage = PMCGImageCreateWithEPSDataProvider(epsDataProvider, previewImage)
# The preview image and data provider are no longer needed
# because Quartz retains them and this code doesn't
# require them further.
del previewImage
del epsDataProvider
if epsImage is None:
print("Couldn't create EPS hybrid image!")
return None
return epsImage
def drawEPSDataImage(context, url):
# Create the a CGImage that has EPS data associated with it.
epsDataImage = createCGEPSImage(url)
if epsDataImage is None:
return
# Create a destination rectangle at the location
# to draw the EPS document. The size of the rect is scaled
# down to 1/2 the size of the EPS graphic.
destinationRect = Quartz.CGRectMake(
100,
100,
Quartz.CGImageGetWidth(epsDataImage),
Quartz.CGImageGetHeight(epsDataImage),
)
# Draw the image to the destination. When the EPS
# data associated with the image is sent to a PostScript
# printer, the EPS bounding box is mapped to this
# destination rectangle, translated and scaled as necessary.
Quartz.CGContextDrawImage(context, destinationRect, epsDataImage)
# Draw the image a second time. This time the image is
# rotated by 45 degrees and scaled by an additional scaling factor
# of 0.5 in the x dimension. The center point of this image coincides
# with the center point of the earlier drawing.
Quartz.CGContextTranslateCTM(
context,
destinationRect.origin.x + destinationRect.size.width / 2,
destinationRect.origin.y + destinationRect.size.height / 2,
)
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
Quartz.CGContextScaleCTM(context, 0.5, 1)
Quartz.CGContextTranslateCTM(
context,
-(destinationRect.origin.x + destinationRect.size.width / 2),
-(destinationRect.origin.y + destinationRect.size.height / 2),
)
Quartz.CGContextDrawImage(context, destinationRect, epsDataImage)
FrameworkTextDrawing.py¶
import Cocoa
import objc
import Quartz
import QuartzTextDrawing
import Utilities
from objc import super # noqa: A004
def getTextString():
# These unicode values are the characters: Q, u, a, r, t, z,
# eighthnote, floral heart, black chess queen, and two CJK characters.
# Note: Create an NSString, because we'll use NSString-specific API's, otherwise
# we could just have used a python unicode object
return Cocoa.NSString.stringWithString_(
"\u0051\u0075\u0061\u0072\u0074\u007A\u266A\u2766\u265B\u3042\u304E"
)
doPointDrawing = 1
def drawNSStringWithAttributes():
textString = getTextString()
if doPointDrawing:
context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
# Text Line 1. Draw with default attributes.
p = Cocoa.NSMakePoint(20.0, 400.0)
# Draw text with default text attributes. The point supplied is
# not the text baseline but rather the lower-left corner of the box
# which bounds the text.
textString.drawAtPoint_withAttributes_(p, None)
if doPointDrawing:
Utilities.drawPoint(context, p)
# Text Line 2. Draw with a specific font and color.
# Position the text 50 units below the previous text.
p.y -= 50
# Set attributes to use when drawing the string.
stringAttributes = {
# Use the font with the PostScript name "Times-Roman" at 40 point.
Cocoa.NSFontAttributeName: Cocoa.NSFont.fontWithName_size_("Times-Roman", 40),
# Set the color attribute to an opaque red.
Cocoa.NSForegroundColorAttributeName: Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_( # noqa: B950
0.663, 0, 0.031, 1.0
),
}
# Draw the text.
textString.drawAtPoint_withAttributes_(p, stringAttributes)
if doPointDrawing:
Utilities.drawPoint(context, p)
# Text Line 3. Draw stroked text.
# Position the text 50 units below the previous text.
p.y -= 50
# Panther and later support stroke attributes. A positive value
# of the stroke width attribute produces text that is stroked rather
# than filled.
stringAttributes[Cocoa.NSStrokeWidthAttributeName] = 3.0
textString.drawAtPoint_withAttributes_(p, stringAttributes)
if doPointDrawing:
Utilities.drawPoint(context, p)
# Text Line 4. Draw with fill and stroke.
p.y -= 50
# Panther and later support stroke attributes. A negative value
# of the stroke width attribute results in text that is both filled
# and stroked.
stringAttributes[Cocoa.NSStrokeWidthAttributeName] = -3.0
# Set the stroke color attribute to black.
stringAttributes[Cocoa.NSStrokeColorAttributeName] = (
Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_(0, 0, 0, 1.0)
)
textString.drawAtPoint_withAttributes_(p, stringAttributes)
if doPointDrawing:
Utilities.drawPoint(context, p)
# Text Line 5. Draw at baseline.
# Tiger and later support the drawWithRect method which allows
# string text drawing from a point on the text baseline.
p.y -= 50
rect = Cocoa.NSRect(origin=p, size=Cocoa.NSSize(0, 0))
textString.drawWithRect_options_attributes_(
rect, Cocoa.NSStringDrawingDisableScreenFontSubstitution, stringAttributes
)
if doPointDrawing:
Utilities.drawPoint(context, p)
_myLayout = None
_textStorage = None
_myTextRange = None
def drawWithNSLayout():
global _myLayout, _textStorage, _myTextRange
if _myLayout is None:
# Initialize the text storage with the string to draw.
_textStorage = Cocoa.NSTextStorage.alloc().initWithString_(getTextString())
# Initialize the layout manager to use with the text storage.
_myLayout = Cocoa.NSLayoutManager.alloc().init()
# Allocate and initialize a text container object.
textContainer = Cocoa.NSTextContainer.alloc().init()
# Add the text container to the layout.
_myLayout.addTextContainer_(textContainer)
# Release the text container since the layout retains it and
# this code no longer needs it.
del textContainer
# Add the layout to the text storage.
_textStorage.addLayoutManager_(_myLayout)
# Set attributes to use when drawing the string.
stringAttributes = {
# Use the font with the PostScript name "Times-Roman" at 40 point.
Cocoa.NSFontAttributeName: Cocoa.NSFont.fontWithName_size_(
"Times-Roman", 40
),
# Set the text color attribute to an opaque red.
Cocoa.NSForegroundColorAttributeName: Cocoa.NSColor.colorWithCalibratedRed_green_blue_alpha_( # noqa: B950
0.663, 0, 0.031, 1.0
),
}
# Create the range of text for the entire length of text
# in the textStorage object.
_myTextRange = Cocoa.NSMakeRange(0, _textStorage.length())
# Set the attributes on the entire range of text.
_textStorage.setAttributes_range_(stringAttributes, _myTextRange)
# Set the point for drawing the layout.
p = Cocoa.NSMakePoint(20.0, 400.0)
# Draw the text range at the point.
_myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)
if doPointDrawing:
context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
Utilities.drawPoint(context, p)
# The interface to the NSLayoutManager subclass.
class MyNSLayoutManager(Cocoa.NSLayoutManager):
# The extra instance variables for this subclass.
_textMode = objc.ivar()
_fColor = objc.ivar()
_sColor = objc.ivar()
_yStartPosition = objc.ivar()
_lineWidth = objc.ivar()
_clippingDrawProc = objc.ivar()
_clippingInfo = objc.ivar()
# Public methods to set the special attributes
# of the MyNSLayoutManager instance.
def setTextMode_(self, textMode):
self._textMode = textMode
def setFillColor_(self, color):
self._fColor = color
def setStrokeColor_(self, color):
self._sColor = color
def setTextLineWidth_(self, width):
self._lineWidth = width
def setClippingDrawProc_withInfo_(self, clippingDrawProc, info):
self._clippingDrawProc = clippingDrawProc
self._clippingInfo = info
def init(self):
self = super().init()
if self is None:
return None
# Initialize the custom instance variables.
self._textMode = Quartz.kCGTextFill
self._fColor = None
self._sColor = None
self._yStartPosition = 0
self._lineWidth = 1
self._clippingDrawProc = None
self._clippingInfo = None
return self
# This code overrides this method to record the y coordinate
# to use as the True baseline for the text drawing.
def drawGlyphsForGlyphRange_atPoint_(self, glyphsToShow, origin):
self._yStartPosition = origin.y
super().drawGlyphsForGlyphRange_atPoint_(glyphsToShow, origin)
# This is the rendering method of NSLayoutManager that the
# code overrides to perform its custom rendering.
def showPackedGlyphs_length_glyphRange_atPoint_font_color_printAdjustment_(
self, glyphs, glyphLen, glyphRange, point, font, color, printingAdjustment
):
# Obtain the destination drawing context.
context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
# Adjust start position y value based on the adjusted y coordinate.
# This ensures the text baseline is at the starting position
# passed to drawGlyphsForGlyphRange. This technique won't work
# for super, subscripts, or underlines but that's OK for this example.
point.y = self._yStartPosition
# The Quartz graphics state should be preserved by showPackedGlyphs.
Quartz.CGContextSaveGState(context)
# Set the desired text drawing mode.
Quartz.CGContextSetTextDrawingMode(context, self._textMode)
# Set the fill color if needed.
if (
self._textMode == Quartz.kCGTextFill
or self.textMode == Quartz.kCGTextFillStroke
or self._textMode == Quartz.kCGTextFillClip
or self._textMode == Quartz.kCGTextFillStrokeClip
):
if self._fColor is not None:
Quartz.CGContextSetFillColorWithColor(context, self._fColor)
# Set the line width and the stroke color if needed.
if (
self._textMode == Quartz.kCGTextStroke
or self._textMode == Quartz.kCGTextFillStroke
or self._textMode == Quartz.kCGTextStrokeClip
or self._textMode == Quartz.kCGTextFillStrokeClip
):
Quartz.CGContextSetLineWidth(context, self._lineWidth)
if self._sColor is not None:
Quartz.CGContextSetStrokeColorWithColor(context, self._sColor)
# Now draw the text. Check whether to adjust for printing widths
# and if needed adjust extra character spacing accordingly.
if printingAdjustment.width != 0.0:
# If printingAdjustment width is non-zero then the text
# needs to be adjusted. printingAdjustment is the per character
# adjustment required for this piece of text. Because
# the Quartz text character spacing set is transformed by
# the text matrix, this code needs to factor out that effect
# prior to setting it. Cocoa sets the text matrix to account
# for the point size of the font so we factor that out of the
# per character width supplied here.
charAdjust = printingAdjustment.width / font.pointSize()
Quartz.CGContextSetCharacterSpacing(context, charAdjust)
else:
Quartz.CGContextSetCharacterSpacing(context, 0.0)
# Draw the glyphs. The total number of glyphs is the length
# of the glyphs string passed to showPackedGlyphs, divided by 2
# since there are two bytes per glyph.
Quartz.CGContextShowGlyphsAtPoint(
context, point.x, point.y, glyphs, glyphLen / 2
)
# If the text drawing mode requires clipping and there is
# a custom clipping proc, call it. This allows drawing through
# clipped text before the graphics state is restored.
if (
self._textMode == Quartz.kCGTextClip
or self._textMode == Quartz.kCGTextFillClip
or self._textMode == Quartz.kCGTextStrokeClip
or self._textMode == Quartz.kCGTextFillStrokeClip
) and self._clippingDrawProc is not None:
self._clippingDrawProc(context, point.x, point.y, self._clippingInfo)
Quartz.CGContextRestoreGState(context)
def MyClipProc(c, x, y, info):
Quartz.CGContextTranslateCTM(c, x, y)
Quartz.CGContextSetStrokeColorWithColor(c, Utilities.getRGBOpaqueBlackColor())
# Draw a grid of lines through the clip.
QuartzTextDrawing.drawGridLines(c)
_myLayout2 = None
_textStorage2 = None
_myTextRange2 = None
def drawWithCustomNSLayout():
global _myLayout2, _textStorage2, _myTextRange2
if _myLayout2 is None:
textContainer = Cocoa.NSTextContainer.alloc().init()
_textStorage2 = Cocoa.NSTextStorage.alloc().initWithString_(getTextString())
# Create an instance of the MyNSLayoutManager subclass of NSLayoutManager.
_myLayout2 = MyNSLayoutManager.alloc().init()
_myLayout2.addTextContainer_(textContainer)
# The layout retains the text container so this code can release it.
del textContainer
_textStorage2.addLayoutManager_(_myLayout2)
# Set attributes to use when drawing the string.
stringAttributes = {
# Use the font with the PostScript name "Times-Roman" at 40 point.
Cocoa.NSFontAttributeName: Cocoa.NSFont.fontWithName_size_(
"Times-Roman", 40
)
}
# Create the range.
_myTextRange2 = Cocoa.NSMakeRange(0, _textStorage2.length())
# Set the attributes on the entire range of text.
_textStorage2.setAttributes_range_(stringAttributes, _myTextRange2)
p = Cocoa.NSMakePoint(20.0, 400.0)
# Set the custom attributes of the layout subclass so that
# the text will be filled with black.
_myLayout2.setTextMode_(Quartz.kCGTextFill)
_myLayout2.setFillColor_(Utilities.getRGBOpaqueBlackColor())
# Draw text line 1.
_myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
if doPointDrawing:
context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
Utilities.drawPoint(context, p)
# Set the custom attributes of the layout subclass so that
# the text will be stroked with black.
_myLayout2.setTextMode_(Quartz.kCGTextStroke)
_myLayout2.setStrokeColor_(Utilities.getRGBOpaqueBlackColor())
_myLayout2.setTextLineWidth_(2)
# Draw text line 2.
p.y -= 50
_myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
if doPointDrawing:
Utilities.drawPoint(context, p)
p.y -= 50
# Set the custom attributes of the layout subclass so that
# the text will be filled and stroked and the fill color
# will be red. Since the stroke color hasn't changed it
# will be stroked with black.
_myLayout2.setTextMode_(Quartz.kCGTextFillStroke)
_myLayout2.setFillColor_(Utilities.getRGBOpaqueRedColor())
# Draw text line 3.
_myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
if doPointDrawing:
Utilities.drawPoint(context, p)
p.y -= 50
# Set the custom attributes of the layout subclass so that
# the text will be filled, stroked, then clipped.
_myLayout2.setTextMode_(Quartz.kCGTextFillStrokeClip)
# Set the clipping proc to MyClipProc which requires
# no info data.
_myLayout2.setClippingDrawProc_withInfo_(MyClipProc, None)
# Draw text line 4.
_myLayout2.drawGlyphsForGlyphRange_atPoint_(_myTextRange2, p)
if doPointDrawing:
Utilities.drawPoint(context, p)
# Set the clipping proc to None for future drawing.
_myLayout2.setClippingDrawProc_withInfo_(None, None)
FrameworkUtilities.py¶
import AppDrawing # noqa: F401
from PDFHandling import cfDataCreatePDFDocumentFromCommand
import Cocoa
def myCreatePDFDataFromPasteBoard():
# Obtain the pasteboard to examine.
pboard = Cocoa.NSPasteboard.generalPasteboard()
# Scan the types of data on the pasteboard and return the first type available that is
# listed in the array supplied. Here the array of requested types contains only one type,
# that of PDF data. If your application could handle more types, you would list them in
# the creation of this array.
pboard_type = pboard.availableTypeFromArray_([Cocoa.NSPDFPboardType])
# If the string is non-nil, there was data of one of our requested types
# on the pasteboard that can be obtained.
if pboard_type is not None:
# Test that the type is the PDF data type. This code is not strictly necessary
# for this example since we only said we could handle PDF data, but is appropriate
# if you can handle more types than just PDF.
if pboard_type == Cocoa.NSPDFPboardType:
# Get the PDF data from the pasteboard.
pdfData = pboard.dataForType_(pboard_type)
if pdfData is not None:
return pdfData
else:
Cocoa.NSLog("Couldn't get PDF data from pasteboard!")
else:
Cocoa.NSLog("Pasteboard doesn't contain PDF data!")
return None
def addPDFDataToPasteBoard(command):
pdfData = cfDataCreatePDFDocumentFromCommand(command)
if pdfData is not None:
pboard = Cocoa.NSPasteboard.generalPasteboard()
pboard.declareTypes_owner_(Cocoa.NSPDFPboardType, None)
pboard.setData_forType_(pdfData, Cocoa.NSPDFPboardType)
ImageMasking.py¶
import sys
import Cocoa
# import DataProvidersAndConsumers
import Images
import Quartz
import Utilities
def exportImageWithMaskFromURLWithDestination(
context,
imageURL,
imagewidth,
imageheight,
bitsPerComponent,
theMaskingImageURL,
maskwidth,
maskheight,
):
imageBitsPerPixel = bitsPerComponent * 3
bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
shouldInterpolate = True
imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
colorspace = Utilities.getTheCalibratedRGBColorSpace()
image = Quartz.CGImageCreate(
imagewidth,
imageheight,
bitsPerComponent,
imageBitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del imageDataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
# Draw the image.
Quartz.CGContextDrawImage(context, imageRect, image)
# Now the mask.
maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
if maskDataProvider is None:
print("Couldn't create Image Data provider!")
return
mask = Quartz.CGImageMaskCreate(
maskwidth,
maskheight,
bitsPerComponent,
bitsPerComponent,
maskwidth,
maskDataProvider,
None,
shouldInterpolate,
)
del maskDataProvider
if mask is None:
print("Couldn't create CGImageRef for mask data!")
return
# Draw the mask below the image.
maskRect = Quartz.CGRectMake(0.0, 0.0, maskwidth, maskheight)
Quartz.CGContextDrawImage(context, maskRect, mask)
# Create a new CGImage object, the image, masked with mask.
imageMaskedWithImage = Quartz.CGImageCreateWithMask(image, mask)
# Once the new image is created, we can release the image
# and the mask which make it up. Quartz retains what it needs
# for the new masked image.
del image
del mask
if imageMaskedWithImage is None:
print("Couldn't create image masked with mask!")
return
imageRect = Quartz.CGRectMake(imagewidth, imageheight / 2, imagewidth, imageheight)
# Draw the masked image to the right of the image and its mask.
Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithImage)
# Of course this is a total hack.
outPath = b"/tmp/imageout.png"
exportURL = Cocoa.CFURLCreateFromFileSystemRepresentation(
None, outPath, len(outPath), False
)
if exportURL is not None:
Images.exportCGImageToPNGFileWithDestination(imageMaskedWithImage, exportURL)
if sys.version_info[0] == 2:
def make_bytes(values):
return "".join(map(chr, values))
else:
def make_bytes(values):
return bytes(values)
_data = make_bytes(
(
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0xF8,
0xE7,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x00,
0x40,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0x00,
0x00,
0x00,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x00,
0x00,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x06,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x01,
0xC0,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x0F,
0xF8,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x0F,
0xFF,
0xF8,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0xFC,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x7F,
0xFF,
0xFC,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x00,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x03,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x03,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0x80,
0x01,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x1F,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x1F,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x1F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x1F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x1F,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFE,
0x40,
0x00,
0x0F,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFC,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFC,
0x00,
0x05,
0xEF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xF0,
0x00,
0x3F,
0x00,
0x03,
0xFC,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xE0,
0x00,
0x7C,
0x00,
0x00,
0x78,
0x1F,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xC0,
0x00,
0x38,
0x00,
0x00,
0x78,
0x3C,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x78,
0x00,
0x00,
0x70,
0x18,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x78,
0x1F,
0x00,
0x30,
0x00,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x7C,
0x3F,
0x00,
0x18,
0x00,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x38,
0x00,
0x00,
0x03,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x80,
0x00,
0x3C,
0x00,
0x0C,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x3C,
0x20,
0x1C,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x04,
0x00,
0x3C,
0x00,
0x3C,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x70,
0xBF,
0x86,
0x3C,
0x1F,
0xFC,
0x0B,
0xFF,
0xFF,
0xFF,
0xFF,
0xA0,
0x11,
0xF0,
0x0E,
0x3C,
0x1F,
0xFE,
0x8B,
0xFF,
0xFF,
0xFF,
0xFF,
0xA0,
0x19,
0xF0,
0x0C,
0x3C,
0x0F,
0xFF,
0x0B,
0xFF,
0xFF,
0xFF,
0xFF,
0xB0,
0x1D,
0xFE,
0x1C,
0x7E,
0x0F,
0xFF,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xB8,
0x1C,
0xFF,
0x3C,
0xFE,
0x03,
0xFE,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x1E,
0x7F,
0xF8,
0xDE,
0x00,
0x7C,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x1E,
0x7F,
0xF1,
0xDF,
0x30,
0x03,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x1F,
0x3F,
0xE3,
0x9F,
0x10,
0x3F,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x0F,
0xFF,
0x83,
0xDF,
0x80,
0x1F,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x03,
0xFC,
0x03,
0xDF,
0x81,
0x8F,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x07,
0xFE,
0x1F,
0x8F,
0x00,
0x07,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x07,
0xFE,
0x3C,
0x06,
0x00,
0x01,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x03,
0xFC,
0x7C,
0x00,
0x00,
0x01,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x01,
0xF8,
0x7F,
0x00,
0x00,
0x01,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x01,
0xF8,
0xFF,
0xE0,
0x30,
0x01,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0xF1,
0xEF,
0xF9,
0xE0,
0x03,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x80,
0xF1,
0xFF,
0xFF,
0x80,
0x0F,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x03,
0xE2,
0xFF,
0xFE,
0x00,
0x1F,
0x87,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x83,
0xF0,
0x00,
0x00,
0x1C,
0x3F,
0x87,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC3,
0xF0,
0x00,
0x01,
0xF8,
0x0F,
0x87,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC3,
0xF0,
0x03,
0xFF,
0xF0,
0x5F,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC1,
0xFF,
0xC7,
0xFF,
0xE0,
0x7F,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE1,
0xFF,
0xF1,
0xFF,
0x80,
0x2F,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE1,
0xFF,
0xF8,
0x0F,
0xC0,
0x06,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF4,
0xFF,
0xFE,
0x0F,
0xF8,
0x44,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF4,
0xFF,
0xFF,
0xFF,
0xF8,
0x64,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF9,
0xFF,
0xFF,
0xFF,
0x3C,
0xE4,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFD,
0x9F,
0xFF,
0xFC,
0x1F,
0xC0,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFD,
0x1F,
0xFF,
0xFC,
0x03,
0xC0,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x01,
0xFF,
0xFF,
0xFF,
0xC0,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0xFF,
0xFF,
0xFF,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0xFF,
0xFF,
0xFF,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x80,
0x7F,
0xFF,
0xFF,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x80,
0x1F,
0xFF,
0xFF,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0xC0,
0x0F,
0xFF,
0xFF,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x07,
0xFF,
0xFF,
0x00,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x03,
0xFF,
0xFF,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x70,
0x01,
0xFF,
0xFC,
0x00,
0x17,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x78,
0x00,
0x7F,
0xF0,
0x00,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0xFC,
0x00,
0x00,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x03,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x1F,
0xFF,
0x80,
0x00,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x7F,
0x7F,
0xC0,
0x00,
0x00,
0x00,
0x07,
0xFF,
0xFF,
)
)
def getMaskData1():
return _data
_data2 = make_bytes(
(
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x00,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x00,
0x01,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x00,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x00,
0x01,
0x80,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x00,
0x03,
0xC0,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x00,
0x0B,
0xE0,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x00,
0x07,
0xF0,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0x00,
0x1F,
0xF4,
0x83,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x00,
0x3F,
0xE4,
0x03,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x00,
0x3F,
0xE4,
0x43,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x00,
0x3F,
0xE4,
0x4B,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x02,
0xFF,
0xE4,
0x5B,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x02,
0xFF,
0xE0,
0x5B,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x07,
0xC1,
0xFF,
0xE0,
0x59,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x18,
0x00,
0x7F,
0xF0,
0xFE,
0x00,
0x79,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x18,
0x00,
0x78,
0x0F,
0xFE,
0x04,
0xE1,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x18,
0x00,
0xB0,
0x47,
0xFF,
0xFF,
0xE1,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x10,
0x00,
0xC4,
0x69,
0xFF,
0xFF,
0xC3,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x10,
0x01,
0xFF,
0xE1,
0xFC,
0x07,
0xC1,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x00,
0x01,
0xFF,
0xF8,
0x78,
0x01,
0xC5,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x04,
0x01,
0xFF,
0xF0,
0x78,
0x01,
0xC5,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x0C,
0x00,
0xFF,
0xF8,
0x7E,
0x3F,
0xC1,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x0C,
0x00,
0x7F,
0xF0,
0x18,
0xFF,
0xC3,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x07,
0x00,
0x7F,
0xF4,
0x1F,
0xFF,
0xE3,
0xFF,
0xFF,
0xFF,
0xFF,
0xFC,
0x23,
0x00,
0x7F,
0xE0,
0x3E,
0xFF,
0xE3,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x11,
0x00,
0x7F,
0xEC,
0x5F,
0xBF,
0xE3,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x01,
0x00,
0x3F,
0xCE,
0x7E,
0x3F,
0xE3,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x08,
0x00,
0x7F,
0x80,
0x2E,
0x3F,
0xE3,
0xFF,
0xFF,
0xFF,
0xFF,
0xF8,
0x06,
0x00,
0x7F,
0x00,
0x6E,
0x3F,
0xEB,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x7E,
0x0D,
0xFE,
0xFF,
0xEB,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x3C,
0x00,
0xFE,
0x3F,
0xEB,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x50,
0x00,
0xFE,
0x3F,
0xCB,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x01,
0x98,
0xFF,
0x3F,
0xCB,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0xC7,
0xE1,
0xFF,
0x3F,
0x8B,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x40,
0xFF,
0xFF,
0xBF,
0x8B,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0xE0,
0x1F,
0xFF,
0xDF,
0x0B,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0xE8,
0x63,
0xFF,
0xDF,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x02,
0xFC,
0xF9,
0xFF,
0xEF,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xF0,
0x00,
0x03,
0xFE,
0x7F,
0xF8,
0x1E,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x03,
0x7E,
0x0F,
0xF9,
0xBE,
0x05,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x01,
0x7F,
0xC1,
0xF3,
0xFC,
0x05,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x01,
0x3D,
0xF8,
0x0F,
0x7C,
0x05,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x01,
0xBC,
0x7F,
0xFF,
0xF8,
0x02,
0xFF,
0xFF,
0xFF,
0xFF,
0xE0,
0x00,
0x00,
0xBE,
0xFF,
0xFF,
0xF8,
0x02,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x1D,
0xFF,
0xFF,
0xF0,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x0F,
0xF7,
0xFF,
0xE0,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0xC0,
0x00,
0x00,
0x0F,
0xF3,
0xFF,
0xE0,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x03,
0xF3,
0xFF,
0xC0,
0x00,
0x7F,
0xFF,
0xFF,
0xFF,
0x80,
0x00,
0x00,
0x01,
0xF7,
0xFF,
0x80,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x3F,
0xFF,
0x00,
0x00,
0x3F,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0x00,
0x20,
0x3F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0x00,
0x10,
0x3F,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x00,
0x00,
0x1F,
0xFF,
0x00,
0x10,
0x3F,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0x02,
0x00,
0x0F,
0xFF,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xFC,
0x00,
0x00,
0x04,
0x00,
0x1F,
0xFE,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x07,
0x81,
0x7F,
0xFE,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x03,
0xDF,
0xFF,
0xFE,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xF8,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xF0,
0x00,
0x00,
0x07,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x1F,
0xFF,
0xFF,
0xF0,
0x00,
0x40,
0x07,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xF0,
0x00,
0xC0,
0x03,
0xFF,
0xFF,
0xFE,
0x00,
0x00,
0x0F,
0xFF,
0xFF,
0xE0,
0x00,
0xE0,
0x07,
0xFF,
0xFF,
0xFE,
0x81,
0x00,
0x07,
0xFF,
0xFF,
0xC0,
0x01,
0xE0,
0x07,
0xFF,
0xFF,
0xFE,
0x01,
0x00,
0x07,
0xFF,
0xFF,
0x80,
0x0F,
0xF0,
0x03,
0xFF,
0xFF,
0xFE,
0x83,
0x80,
0x03,
0xFF,
0xFF,
0x00,
0x1F,
0xF0,
0x13,
0xFF,
0xFF,
0xFE,
0x03,
0xE0,
0x01,
0xFF,
0xFC,
0x03,
0x3F,
0xF0,
0x21,
0xFF,
0xFF,
0xFE,
0x03,
0xFC,
0x00,
0x3F,
0xF0,
0x3F,
0x3F,
0xF8,
0x3B,
0xFF,
0xFF,
0xFE,
0x03,
0xFE,
0xC0,
0x0F,
0xE3,
0xFB,
0x7F,
0xF8,
0x3B,
0xFF,
0xFF,
0xFF,
0x07,
0xFF,
0xFF,
0x07,
0x9F,
0xFB,
0x7F,
0xFC,
0x79,
0xFF,
0xFF,
0xFF,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x7F,
0xFC,
0x39,
0xFF,
0xFF,
0xFF,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x7F,
0xFE,
0x3F,
0xFF,
0xFF,
0xFE,
0x07,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x7F,
0xFE,
0x1F,
0xFF,
0xFF,
0xFE,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x7F,
0xFE,
0x1F,
0x7F,
0xFF,
0xFE,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x7F,
0xFF,
0x1F,
0xFE,
0xFF,
0xFC,
0x0F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x0F,
0xFF,
0xFF,
0xFF,
0x1F,
0xFF,
0xEF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0x87,
0xFF,
0xFF,
0xFE,
0x1F,
0xFF,
0xEF,
0xFF,
0xFF,
0xFF,
0xBF,
0xFF,
0x82,
0xFF,
0xFF,
0xFC,
0x3F,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xBF,
0xFF,
0x83,
0xFF,
0xFF,
0xFC,
0x3F,
0xFF,
0xBF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xC1,
0xFF,
0xFF,
0xF8,
0x7F,
0xFF,
0xBF,
0xFF,
0xFF,
0xFF,
0xBF,
0xFF,
0xE0,
0xFF,
0xFF,
0xF0,
0xFF,
0xFF,
0xBF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
)
)
def getMaskData2():
return _data2
def doOneBitMaskImages(context):
bitsPerComponent = 1
bitsPerPixel = 1
width = 96
height = 96
bytesPerRow = 12
imageDataSize = bytesPerRow * height
shouldInterpolate = True
lightBlue = [0.482, 0.62, 0.871, 1.0]
black = [0.0, 0.0, 0.0, 1.0]
darkRed = [0.663, 0.0, 0.031, 1.0]
darkGreen = [0.404, 0.808, 0.239, 1.0]
darkBlue = [0.11, 0.208, 0.451, 1.0]
purple = [0.69, 0.486, 0.722, 1.0]
darkOrange = [0.965, 0.584, 0.059, 1.0]
# A decode array contains two elements for each component. In this
# case, an image mask has one component so the array consists of
# two values. When using this decode array, a sample value of 0
# is mapped into the value 1, and the maximum sample value is
# mapped into the value 0. This inverts the sense of the mask data.
decode = (1, 0)
# Create a Quartz data provider for the image data. Because this
# data is static data, we don't need to release it so the data
# release function is None.
data = getMaskData1()
dataProvider = Quartz.CGDataProviderCreateWithData(None, data, imageDataSize, None)
if dataProvider is None:
print("Couldn't create Mask1 Data provider!")
return
# Create a mask from the data.
mask1 = Quartz.CGImageMaskCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
dataProvider,
None,
shouldInterpolate,
)
# Create the same mask but with a decode array that
# inverts the sense of the mask.
invertedmask1 = Quartz.CGImageMaskCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
dataProvider,
decode,
shouldInterpolate,
)
# Release the data provider now that this code no longer needs it.
del dataProvider
if mask1 is None or invertedmask1 is None:
if mask1 is None:
print("Couldn't create CGImageRef for the mask data 1!")
if invertedmask1 is None:
print("Couldn't create CGImageRef for the inverted mask data 1!")
return
# Get the pointer to the data for the second mask.
data = getMaskData2()
dataProvider = Quartz.CGDataProviderCreateWithData(None, data, imageDataSize, None)
if dataProvider is None:
print("Couldn't create Mask2 Data provider!")
return
mask2 = Quartz.CGImageMaskCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
dataProvider,
None,
shouldInterpolate,
)
# Create the same mask but with a decode array that
# inverts the sense of the mask.
invertedmask2 = Quartz.CGImageMaskCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
dataProvider,
decode,
shouldInterpolate,
)
# Release the data provider now that this code no longer needs it.
del dataProvider
if mask2 is None or invertedmask2 is None:
if mask2 is None:
print("Couldn't create CGImageRef for the mask data 2!")
if invertedmask2 is None:
print("Couldn't create CGImageRef for the inverted mask data 2!")
return
Quartz.CGContextScaleCTM(context, 1.5, 1.5)
colorSpace = Utilities.getTheCalibratedRGBColorSpace()
Quartz.CGContextSetFillColorSpace(context, colorSpace)
# Set the fill color to a light blue.
Quartz.CGContextSetFillColor(context, lightBlue)
# Paint part of the background.
backRect = Quartz.CGRectMake(width / 2, height / 2, width * 3, height)
Quartz.CGContextFillRect(context, backRect)
imageRect = Quartz.CGRectMake(0.0, height, width, height)
Quartz.CGContextSaveGState(context)
# Set the fill color to opaque black.
Quartz.CGContextSetFillColor(context, black)
# Mask 1.
Quartz.CGContextDrawImage(context, imageRect, mask1)
Quartz.CGContextTranslateCTM(context, width, 0)
# Set the fill color to opaque red.
Quartz.CGContextSetFillColor(context, darkRed)
# Mask 2.
Quartz.CGContextDrawImage(context, imageRect, mask2)
Quartz.CGContextTranslateCTM(context, width, 0)
# Set the fill color to dark orange.
Quartz.CGContextSetFillColor(context, darkOrange)
# Mask 3.
Quartz.CGContextDrawImage(context, imageRect, mask1)
Quartz.CGContextTranslateCTM(context, width, 0)
# Make the orange 50% transparent.
darkOrange[3] = 0.5
Quartz.CGContextSetFillColor(context, darkOrange)
# Mask 4.
Quartz.CGContextDrawImage(context, imageRect, mask2)
Quartz.CGContextRestoreGState(context)
# Translate down the page. The cast is necessary
# since height is typed as size_t which is unsigned.
Quartz.CGContextTranslateCTM(context, 0, -height)
# Set the fill color to an opaque green.
Quartz.CGContextSetFillColor(context, darkGreen)
# Mask 5.
Quartz.CGContextDrawImage(context, imageRect, invertedmask2)
Quartz.CGContextTranslateCTM(context, width, 0)
# Set the fill color to a dark blue.
Quartz.CGContextSetFillColor(context, darkBlue)
# Mask 6.
Quartz.CGContextDrawImage(context, imageRect, invertedmask1)
Quartz.CGContextTranslateCTM(context, width, 0)
# Set the fill color to purple.
Quartz.CGContextSetFillColor(context, purple)
# Mask 7.
Quartz.CGContextDrawImage(context, imageRect, invertedmask2)
Quartz.CGContextTranslateCTM(context, width, 0)
# Make the purple 50% transparent.
purple[3] = 0.5
Quartz.CGContextSetFillColor(context, purple)
# Mask 8.
Quartz.CGContextDrawImage(context, imageRect, invertedmask1)
def doMaskImageWithMaskFromURL(
context,
imageURL,
imagewidth,
imageheight,
bitsPerComponent,
theMaskingImageURL,
maskwidth,
maskheight,
):
imageBitsPerPixel = bitsPerComponent * 3
bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
shouldInterpolate = True
imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
colorspace = Utilities.getTheCalibratedRGBColorSpace()
image = Quartz.CGImageCreate(
imagewidth,
imageheight,
bitsPerComponent,
imageBitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del imageDataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
# Draw the image.
Quartz.CGContextDrawImage(context, imageRect, image)
# Now the mask.
maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
if maskDataProvider is None:
print("Couldn't create Image Data provider!")
return
mask = Quartz.CGImageMaskCreate(
maskwidth,
maskheight,
bitsPerComponent,
bitsPerComponent,
maskwidth,
maskDataProvider,
None,
shouldInterpolate,
)
del maskDataProvider
if mask is None:
print("Couldn't create CGImageRef for mask data!")
return
# Draw the mask below the image. The current fill color (black)
# is painted through the mask.
maskRect = Quartz.CGRectMake(0.0, 0.0, maskwidth, maskheight)
Quartz.CGContextDrawImage(context, maskRect, mask)
# Create a new CGImage object, the image, masked with mask.
imageMaskedWithImage = Quartz.CGImageCreateWithMask(image, mask)
# Once the new image is created, the code can release the image
# and the mask which make it up. Quartz retains what it needs
# for the new masked image 'imageMaskedWithImage'.
del image
del mask
if imageMaskedWithImage is None:
print("Couldn't create image masked with mask!")
return
imageRect = Quartz.CGRectMake(
imagewidth + 10, imageheight / 2, imagewidth, imageheight
)
# Draw the masked image to the right of the image and its mask.
Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithImage)
def doMaskImageWithGrayImageFromURL(
context,
imageURL,
imagewidth,
imageheight,
bitsPerComponent,
theMaskingImageURL,
maskwidth,
maskheight,
):
imageBitsPerPixel = bitsPerComponent * 3
bytesPerRow = ((imagewidth * imageBitsPerPixel) + 7) / 8
shouldInterpolate = True
imageDataProvider = Quartz.CGDataProviderCreateWithURL(imageURL)
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
colorspace = Utilities.getTheCalibratedRGBColorSpace()
image = Quartz.CGImageCreate(
imagewidth,
imageheight,
bitsPerComponent,
imageBitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del imageDataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
imageRect = Quartz.CGRectMake(0.0, imageheight, imagewidth, imageheight)
# Draw the image.
Quartz.CGContextDrawImage(context, imageRect, image)
# Now the mask.
maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
if maskDataProvider is None:
print("Couldn't create Image Data provider!")
return
# The color space for the image MUST be DeviceGray for it to
# be used as a masking image with CGImageCreateWithMask.
deviceGraySpace = Quartz.CGColorSpaceCreateDeviceGray()
mask = Quartz.CGImageCreate(
maskwidth,
maskheight,
bitsPerComponent,
bitsPerComponent,
maskwidth,
deviceGraySpace,
Quartz.kCGImageAlphaNone,
maskDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
# Release the color space since it is no longer needed.
del deviceGraySpace
del maskDataProvider
if mask is None:
print("Couldn't create CGImageRef for gray image data!")
return
# Draw the mask below the image. The current fill color (black)
# is painted through the mask.
maskRect = Quartz.CGRectMake(0.0, 0.0, maskwidth, maskheight)
Quartz.CGContextDrawImage(context, maskRect, mask)
# Create a new CGImage object, the image, masked with mask.
imageMaskedWithImage = Quartz.CGImageCreateWithMask(image, mask)
# Once the new image is created, the code can release the image
# and the mask which make it up. Quartz retains what it needs
# for the new masked image 'imageMaskedWithImage'.
del image
del mask
if imageMaskedWithImage is None:
print("Couldn't create image masked with mask!")
return
imageRect = Quartz.CGRectMake(
imagewidth + 10, imageheight / 2, imagewidth, imageheight
)
# Draw the masked image to the right of the image and its mask.
Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithImage)
# Be sure and release the masked image.
del imageMaskedWithImage
def doMaskImageWithColorFromURL(context, url, width, height, isColor):
# This routine treats color images as RGB.
bitsPerComponent = 8
if isColor:
bitsPerPixel = bitsPerComponent * 3
else:
bitsPerPixel = bitsPerComponent
bytesPerRow = ((width * bitsPerPixel) + 7) / 8
shouldInterpolate = True
# This is a range of dark gray to black colors for an 8 bit per component
# image in a gray or RGB color space. The entries are image sample
# values of 0-0x1F for the first color component, 0-0x1F for the
# second color component, and so on. For image sample values where
# all components fall within the ranges in maskingColors, the sample
# value is masked and therefore unpainted.
maskingColors = (0x00, 0x1F, 0x00, 0x1F, 0x00, 0x1F)
backColor = (1.0, 0.0, 0.0, 1.0) # Opaque red.
# Create a Quartz data provider from the supplied URL.
dataProvider = Quartz.CGDataProviderCreateWithURL(url)
if dataProvider is None:
print("Couldn't create Image data provider!")
return
# Create an image of the specified width, height and bits per pixel
# from the URL.
if isColor:
colorspace = Utilities.getTheCalibratedRGBColorSpace()
else:
colorspace = Utilities.getTheCalibratedGrayColorSpace()
image = Quartz.CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
dataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del dataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
imageRect = Quartz.CGRectMake(10.0, 10.0, width, height)
# Quartz.CGContextScaleCTM(context, 0.33, 0.33)
# Set the color space and the color, then
# paint a red rectangle behind the image.
Quartz.CGContextSetFillColorSpace(context, colorspace)
Quartz.CGContextSetFillColor(context, backColor)
Quartz.CGContextFillRect(context, imageRect)
# Draw the image into the rectangle.
Quartz.CGContextDrawImage(context, imageRect, image)
# Create a new image from the original one, masking out a range
# of the blackest sample values.
imageMaskedWithColor = Quartz.CGImageCreateWithMaskingColors(image, maskingColors)
# Release the original image; it is no longer needed.
del image
if imageMaskedWithColor is None:
print("Couldn't create CGImageRef for masking color!")
return
# Paint the rectangle behind the next image with red.
imageRect = Quartz.CGRectMake(30.0 + width, 10.0, width, height)
Quartz.CGContextFillRect(context, imageRect)
# Draw the image. Image sample values in the range of
# the masking color are unpainted, allowing the background
# to show through.
Quartz.CGContextDrawImage(context, imageRect, imageMaskedWithColor)
if 1: # Set to 1 for code in the book.
def drawWithClippingMask(context, theMaskingImageURL, imagewidth, imageheight):
# An array of CGColor objects.
colors = (
Utilities.getRGBOpaqueDarkGreenColor(),
Utilities.getRGBOpaqueDarkBlueColor(),
Utilities.getRGBOpaqueBlueColor(),
Utilities.getRGBOpaqueRedColor(),
)
imageBitsPerComponent = 8
bytesPerRow = imagewidth
shouldInterpolate = True
decode = (1, 0)
# Create the data.
dataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
if dataProvider is None:
print("Couldn't create Image data provider!")
return
cs = Quartz.CGColorSpaceCreateDeviceGray()
image = Quartz.CGImageCreate(
imagewidth,
imageheight,
imageBitsPerComponent,
imageBitsPerComponent,
bytesPerRow,
cs,
Quartz.kCGImageAlphaNone,
dataProvider,
decode,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del cs
del dataProvider
if image is None:
print("Couldn't create Image!")
return
imageRect = Quartz.CGRectMake(0, 0, imagewidth * 2 / 3, imageheight * 2 / 3)
# Position for drawing the image at the left side of the figure.
Quartz.CGContextTranslateCTM(context, 50, 50)
# Draw the image.
Quartz.CGContextDrawImage(context, imageRect, image)
# Position to the right of the image just painted.
Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(imageRect) + 25, 0)
# Clip to the image.
Quartz.CGContextClipToMask(context, imageRect, image)
# Release the image since this code no longer needs it.
del image
# Make a rect that has a width and height 1/3 that of the image.
rect = Quartz.CGRectMake(
0,
0,
Quartz.CGRectGetWidth(imageRect) / 3,
Quartz.CGRectGetHeight(imageRect) / 3,
)
Quartz.CGContextTranslateCTM(context, 0, 2 * Quartz.CGRectGetHeight(rect))
# Draw a 3 x 3 grid of rectangles, setting the color for each rectangle
# by cycling through the array of CGColor objects in the 'colors' array.
for j in range(3):
Quartz.CGContextSaveGState(context)
for i in range(3):
# Draw a row of rectangles.
# Set the fill color using one of the CGColor objects in the
# colors array.
Quartz.CGContextSetFillColorWithColor(context, colors[(i + j) % 4])
Quartz.CGContextFillRect(context, rect)
Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(rect), 0)
Quartz.CGContextRestoreGState(context)
# Position to draw the next row.
Quartz.CGContextTranslateCTM(context, 0, -Quartz.CGRectGetHeight(rect))
else:
# This code works just fine to screen but when drawing to a PDF
# or printing context the masked drawing is completely masked out
# due to a bug in Quartz prior to Tiger 10.4.3.
def drawWithClippingMask(context, theMaskingImageURL, maskwidth, maskheight):
# An array of Quartz.CGColor objects.
colors = (
Utilities.getRGBOpaqueDarkGreenColor(),
Utilities.getRGBOpaqueDarkBlueColor(),
Utilities.getRGBOpaqueBlueColor(),
Utilities.getRGBOpaqueRedColor(),
)
maskBitsPerComponent = 8
# bytesPerRow = ((maskwidth * maskBitsPerComponent) + 7) / 8
shouldInterpolate = True
maskDataProvider = Quartz.CGDataProviderCreateWithURL(theMaskingImageURL)
if maskDataProvider is None:
print("Couldn't create Image Mask provider!")
return
mask = Quartz.CGImageMaskCreate(
maskwidth,
maskheight,
maskBitsPerComponent,
maskBitsPerComponent,
maskwidth,
maskDataProvider,
None,
shouldInterpolate,
)
del maskDataProvider
if mask is None:
print("Couldn't create Image Mask!")
return
maskRect = Quartz.CGRectMake(0, 0, maskwidth / 3, maskheight / 3)
# Position for drawing the mask at the left side of the figure.
Quartz.CGContextTranslateCTM(context, 50, 50)
# Set the context fill color to a Quartz.CGColor object that is black.
Quartz.CGContextSetFillColorWithColor(
context, Utilities.getRGBOpaqueBlackColor()
)
# Draw the mask. It is painted with with the black fill color.
Quartz.CGContextDrawImage(context, maskRect, mask)
# Position to the right of the mask just painted.
Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(maskRect) + 25, 0)
# Clip to the mask.
Quartz.CGContextClipToMask(context, maskRect, mask)
# Release the mask since this code no longer needs it.
del mask
# Make a rect that has a width and height 1/3 that of the image mask.
rect = Quartz.CGRectMake(
0,
0,
Quartz.CGRectGetWidth(maskRect) / 3,
Quartz.CGRectGetHeight(maskRect) / 3,
)
Quartz.CGContextTranslateCTM(context, 0, 2 * Quartz.CGRectGetHeight(rect))
# Draw a 3 x 3 grid of rectangles, setting the color for each rectangle
# by cycling through the array of CGColor objects in the 'colors' array.
for j in range(3):
Quartz.CGContextSaveGState(context)
for i in range(3):
# Draw a row of rectangles.
# Set the fill color using one of the CGColor objects in the
# colors array.
Quartz.CGContextSetFillColorWithColor(context, colors[(i + j) % 4])
Quartz.CGContextFillRect(context, rect)
Quartz.CGContextTranslateCTM(context, Quartz.CGRectGetWidth(rect), 0)
Quartz.CGContextRestoreGState(context)
# Position to draw the next row.
Quartz.CGContextTranslateCTM(context, 0, -Quartz.CGRectGetHeight(rect))
Images.py¶
import Cocoa
import DataProvidersAndConsumers
import Quartz
import Utilities
from LaunchServices import kUTTypePNG
def drawJPEGImage(context, url):
# Create a Quartz data provider for the supplied URL.
jpgProvider = Quartz.CGDataProviderCreateWithURL(url)
if jpgProvider is None:
print("Couldn't create JPEG Data provider!")
return
# Create the CGImageRef for the JPEG image from the data provider.
jpgImage = Quartz.CGImageCreateWithJPEGDataProvider(
jpgProvider, None, True, Quartz.kCGRenderingIntentDefault
)
# CGImageCreateWithJPEGDataProvider retains the data provider.
# Since this code created the data provider and this code no
# longer needs it, it must release it.
del jpgProvider
if jpgImage is None:
print("Couldn't create CGImageRef for JPEG data!")
return
# Make a rectangle that has its origin at (0,0) and
# has a width and height that is 1/4 the native width
# and height of the image.
jpgRect = Quartz.CGRectMake(
0.0,
0.0,
Quartz.CGImageGetWidth(jpgImage) / 4,
Quartz.CGImageGetHeight(jpgImage) / 4,
)
# Draw the image into the rectangle.
# This is Image 1.
Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
Quartz.CGContextSaveGState(context)
# Translate to the top-right corner of the image just drawn.
Quartz.CGContextTranslateCTM(context, jpgRect.size.width, jpgRect.size.height)
# Rotate by -90 degrees.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-90))
# Translate in -x by the width of the drawing.
Quartz.CGContextTranslateCTM(context, -jpgRect.size.width, 0)
# Draw the image into the same rectangle as before.
# This is Image 2.
Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
Quartz.CGContextRestoreGState(context)
Quartz.CGContextSaveGState(context)
# Translate so that the next drawing of the image appears
# below and to the right of the image just drawn.
Quartz.CGContextTranslateCTM(
context, jpgRect.size.width + jpgRect.size.height, jpgRect.size.height
)
# Scale the y axis by a negative value and flip the image.
Quartz.CGContextScaleCTM(context, 0.75, -1.0)
# This is Image 3.
Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
Quartz.CGContextRestoreGState(context)
# Adjust the position of the rectangle so that its origin is
# to the right and above where Image 3 was drawn. Adjust the
# size of the rectangle so that it is 1/4 the image width
# and 1/6 the image height.
jpgRect = Quartz.CGRectMake(
1.75 * jpgRect.size.width + jpgRect.size.height,
jpgRect.size.height,
Quartz.CGImageGetWidth(jpgImage) / 4,
Quartz.CGImageGetHeight(jpgImage) / 6,
)
# This is Image 4.
Quartz.CGContextDrawImage(context, jpgRect, jpgImage)
def drawImageFromURL(context, url, width, height, bitsPerComponent, isRGB):
# This routine treats color images as RGB
if isRGB:
bitsPerPixel = bitsPerComponent * 3
else:
bitsPerPixel = bitsPerComponent
bytesPerRow = (width * bitsPerPixel + 7) / 8
shouldInterpolate = True
# Create a Quartz data provider from the supplied URL.
dataProvider = Quartz.CGDataProviderCreateWithURL(url)
if dataProvider is None:
print("Couldn't create Image data provider!")
return
# Get a Quartz color space object appropriate for the image type.
if isRGB:
colorspace = Utilities.getTheCalibratedRGBColorSpace()
else:
colorspace = Utilities.getTheCalibratedGrayColorSpace()
# Create an image of the width, height, and bitsPerComponent with
# no alpha data, the default decode array, with interpolation,
# and the default rendering intent for images. This code is
# intended for Gray images of the format GGGGG... or RGB images
# of the format RGBRGBRGB... .
image = Quartz.CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
dataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
# Quartz retains the data provider with the image and since this
# code does not create any more images with the data provider, it
# can release it.
del dataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
# Create a rectangle into which the code will draw the image.
imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
# Draw the image into the rectangle.
Quartz.CGContextDrawImage(context, imageRect, image)
def doColorRampImage(context):
width = 256
height = 256
bitsPerComponent = 8
bitsPerPixel = 24
bytesPerRow = width * 3
shouldInterpolate = True
imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
colorspace = Utilities.getTheCalibratedRGBColorSpace()
image = Quartz.CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
# No longer need the data provider.
del imageDataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
# Draw the image.
Quartz.CGContextDrawImage(context, imageRect, image)
def doImageWithCallbacksCreatedFromURL(
context, url, width, height, bitsPerComponent, isRGB
):
if isRGB:
bitsPerPixel = bitsPerComponent * 3
else:
bitsPerPixel = bitsPerComponent
bytesPerRow = ((width * bitsPerPixel) + 7) / 8
shouldInterpolate = True
dataProvider = DataProvidersAndConsumers.createSequentialAccessDPForURL(url)
if dataProvider is None:
print("Couldn't create Image Data provider!")
return
# Create a Quartz color space object appropriate for the image type.
# These user written functions create the color space object
# and that reference must be released by this code.
if isRGB:
colorspace = Utilities.getTheCalibratedRGBColorSpace()
else:
colorspace = Utilities.getTheCalibratedGrayColorSpace()
image = Quartz.CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
dataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del dataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
imageRect = Quartz.CGRectMake(0.0, 0.0, width, height)
# Draw the image into the rectangle.
Quartz.CGContextDrawImage(context, imageRect, image)
def doGrayRamp(context):
width = 256
height = 1
bitsPerComponent = 8
bitsPerPixel = 8
bytesPerRow = width
shouldInterpolate = True
dataProvider = DataProvidersAndConsumers.createGrayRampDirectAccessDP()
if dataProvider is None:
print("Couldn't create Gray Ramp provider!")
return
colorspace = Utilities.getTheCalibratedGrayColorSpace()
image = Quartz.CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
dataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del dataProvider
if image is None:
print("Couldn't create CGImageRef for image data!")
return
imageRect = Quartz.CGRectMake(0.0, 0.0, 256, 256)
# Drawing the image that is 256 samples wide and
# 1 scanline high into a rectangle that is 256 x 256 units
# on a side causes Quartz to stretch the image to fill
# the destination rectangle.
Quartz.CGContextDrawImage(context, imageRect, image)
# This routine examines the CGImageSource at index 0 to
# determine if the first image is a floating point image and
# if it is, it returns an options dictionary suitable for
# passing to CGImageSourceCreateImageAtIndex in order to create
# a CGImageRef that contains full dynamic range floating point data.
def createFloatingPointImageOptions(imageSource):
# Allow the image to be a floating point image.
# Without this, Quartz would return integer pixel data, even for
# floating point images. Typically you don't need floating point data
# but in some special cases you might want it.
options = {Quartz.kCGImageSourceShouldAllowFloat: True}
isFloat = False
# Obtain the properties for the first image
# in the image source. This is a 'Copy' function
# so the code owns a reference to the
# dictionary returned.
properties = Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource, 0, options)
if properties is not None:
# Get the value for the kCGImagePropertyIsFloat if it exists
# and if the value is a CFBoolean then get the corresponding
# Boolean result.
if Quartz.kCGImagePropertyIsFloat in properties:
isFloat = bool(properties[Quartz.kCGImagePropertyIsFloat])
if not isFloat:
return None
return options
def myCreateImageUsingImageSource(url):
# Set to zero, indicating the property was unavailable.
xdpi = ydpi = 0
# Create the image source from the URL.
imageSource = Quartz.CGImageSourceCreateWithURL(url, None)
if imageSource is None:
print("Couldn't create image source from URL!")
return (None, xdpi, ydpi)
if False:
options = createFloatingPointImageOptions(imageSource)
if options is not None:
print("image IS a floating point image")
else:
print("image IS NOT a floating point image")
else:
options = None
# Obtain the properties dictionary for the first image
# in the image source. This is a copy function so this
# code owns the reference returned and must
# must release it.
properties = Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource, 0, options)
if properties is not None:
# Check for the x and y resolution of the image.
xdpi = properties[Quartz.kCGImagePropertyDPIWidth]
ydpi = properties[Quartz.kCGImagePropertyDPIHeight]
# Create a CGImageRef from the first image in the CGImageSource.
image = Quartz.CGImageSourceCreateImageAtIndex(imageSource, 0, options)
# Release the CGImageSource object since it is no longer needed
# and this code created it. This code uses CFRelease since a
# CGImageSource object is a CoreFoundation object.
del imageSource
del options
if image is None:
print("Couldn't create image from image source!")
return None
return (image, xdpi, ydpi)
def myCreateThumbnailFromImageSource(url):
maxThumbSize = 160
# Create the image source from the URL.
imageSource = Quartz.CGImageSourceCreateWithURL(url, None)
if imageSource is None:
print("Couldn't create image source from URL!")
return None
options = {
# Specify 160 pixels as the maximum width and height of
# the thumbnail for Quartz to create.
Quartz.kCGImageSourceThumbnailMaxPixelSize: maxThumbSize,
# Request that Quartz create a thumbnail image if
# thumbnail data isn't present in the file.
Quartz.kCGImageSourceCreateThumbnailFromImageIfAbsent: True,
}
# Create the thumbnail image for the first image in the
# image source, that at index 0, using the options
# dictionary that the code just created.
thumb = Quartz.CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options)
# Release the options dictionary.
del options
# Release the image source the code created.
del imageSource
if thumb is None:
print("Couldn't create thumbnail from image source!")
return None
return thumb
def imageHasFloatingPointSamples(image):
if hasattr(Quartz, "CGImageGetBitmapInfo"):
return (
Quartz.kCGBitmapFloatComponents & Quartz.CGImageGetBitmapInfo(image)
) != 0
return False
def drawImageWithCGImageDataSource(context, url):
# This code would be better if it created the image source
# once and used the same image source to create the image and its
# thumbnail, but the point here is to simply test the routines
# myCreateImageUsingImageSource and myCreateThumbnailFromImageSource.
image, xdpi, ydpi = myCreateImageUsingImageSource(url)
if image is None:
print("myCreateImageFromImageSource didn't create a CGImage!")
return
print(f"xdpi = {xdpi:2.f}, ydpi = {ydpi:2.f}")
imageRect = Quartz.CGRectMake(
0.0, 0.0, Quartz.CGImageGetWidth(image) / 3, Quartz.CGImageGetHeight(image) / 3
)
Quartz.CGContextDrawImage(context, imageRect, image)
if 0:
isFloatingImage = imageHasFloatingPointSamples(image)
if isFloatingImage:
print("First image IS a floating point image")
else:
print("First image IS NOT a floating point image")
del image
image = myCreateThumbnailFromImageSource(url)
if image is None:
print("myCreateThumbnailFromImageSource didn't create a CGImage!")
return
imageRect = Quartz.CGRectMake(
400.0, 0.0, Quartz.CGImageGetWidth(image), Quartz.CGImageGetHeight(image)
)
Quartz.CGContextDrawImage(context, imageRect, image)
del image
class MyIncrementalData:
data = None
dataSize = 0
repCount = 0
chunkSize = 0
# This is a dummy data accumulation routine used to demonstrate incremental
# loading of an image.
def myCreateAccumulatedDataSoFar(myDataP):
myDataP.repCount += 1
sizeToReturn = myDataP.chunkSize * myDataP.repCount
if sizeToReturn > myDataP.dataSize:
sizeToReturn = myDataP.dataSize
done = sizeToReturn == myDataP.dataSize
data = Cocoa.CFDataCreate(None, myDataP.data, sizeToReturn)
return data, done
def MyDrawIncrementalImage(context, image, fullHeight):
# Obtain the width and height of the image that has been
# accumulated so far.
print("MyDrawIncrementalImage", context, image, fullHeight)
width = Quartz.CGImageGetWidth(image)
height = Quartz.CGImageGetHeight(image)
# Adjust the location of the imageRect so that the origin is
# such that the full image would be located at 0,0 and the partial
# image top-left corner does not move as the image is filled in.
# This is only needed for views where the y axis points up the
# drawing canvas.
imageRect = Quartz.CGRectMake(0, fullHeight - height, width, height)
Quartz.CGContextDrawImage(context, imageRect, image)
def myDrawFirstImageIncrementally(context, myDataP):
height = -1
# Create an incremental image source.
imageSource = Quartz.CGImageSourceCreateIncremental(None)
if imageSource is None:
print("Couldn't create incremental imagesource!")
return
# Loop, gathering the necessary data to find the True
# height of the image.
while 1:
# Fetch the data. The CFData object returned by
# myCreateAccumulatedDataSoFar is used to update the
# image source. When the data is complete, the code
# passes True in the 'done' parameter passed to
# CGImageSourceUpdateData. Once the data is passed
# to CGImageSourceUpdateData, the code can release
# its reference to the data.
# Accumulate the data.
data, done = myCreateAccumulatedDataSoFar(myDataP)
Quartz.CGImageSourceUpdateData(imageSource, data, done)
# Release the data since Quartz retains it and this code
# no longer needs it.
del data
if height < 0:
print("height < 0", height)
# Determine the height of the full image. This is needed in order
# to adjust the location of the drawing of the partial image in
# a context where the y axis has the default Quartz orientation
# pointing up the drawing canvas.
properties = Quartz.CGImageSourceCopyPropertiesAtIndex(imageSource, 0, None)
if properties is not None:
if Quartz.kCGImagePropertyPixelHeight in properties:
height = properties[Quartz.kCGImagePropertyPixelHeight]
del properties
# Once the height is obtained, go ahead and see if Quartz
# has enough data to create a CGImage object.
print("height", height)
if height > 0:
# Now create the CGImageRef from the image source for the
# first image.
image = Quartz.CGImageSourceCreateImageAtIndex(imageSource, 0, None)
if image is not None:
# Draw the image using the height of the full image
# to adjust the location where the image is drawn.
MyDrawIncrementalImage(context, image, height)
# Release the partial image once you've drawn it.
del image
# Potentially you would want to flush the context so
# that drawing to a window would appear, even inside
# this loop. Of course this flush should really be
# done on a timer so that the flush only occurs at
# most every 60th of a second. See Chapter 17 regarding
# timing your usage of CGContextFlush.
Quartz.CGContextFlush(context)
# Obtain the status for the image source for the first image.
status = Quartz.CGImageSourceGetStatusAtIndex(imageSource, 0)
if done: # or status == Quartz.kCGImageStatusComplete:
print(done, status, status == Quartz.kCGImageStatusComplete)
break
def createMyIncrementalDataFromURL(url, myDataP):
myDataP.data = None
myDataP.dataSize = 0
myDataP.repCount = 0
success, pathString = Cocoa.CFURLGetFileSystemRepresentation(url, True, None, 1024)
pathString = pathString.rstrip(b"\0")
if success and len(pathString):
fp = open(pathString, "rb")
myDataP.data = fp.read()
fp.close()
myDataP.dataSize = len(myDataP.data)
if myDataP.dataSize > 0:
myDataP.chunkSize = myDataP.dataSize / 10 # 10 chunks
def doIncrementalImageWithURL(context, url):
myData = MyIncrementalData()
createMyIncrementalDataFromURL(url, myData)
if myData.data is None:
print("couldn't read data from URL!")
myDrawFirstImageIncrementally(context, myData)
del myData
# This code requires QuickTime.framework.
# from Carbon import Qt
def createCGImageWithQuickTimeFromURL(url):
"""
Note: this function doesn't actually worked because the APIs used in here
aren't properly wrapped (yet).
"""
return None
imageRef = None
err = 0
result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
url, 0, None, None
)
if dataRef is not None:
err, gi = Quartz.GetGraphicsImporterForDataRefWithFlags(
dataRef, dataRefType, None, 0
)
if not err and gi:
# Tell the graphics importer that it shouldn't perform
# gamma correction and it should create an image in
# the original source color space rather than matching it to
# a generic calibrated color space.
result = Quartz.GraphicsImportSetFlags(
gi,
(
Quartz.kGraphicsImporterDontDoGammaCorrection
+ Quartz.kGraphicsImporterDontUseColorMatching
),
)
if result == 0:
result, imageRef = Quartz.GraphicsImportCreateCGImage(gi, None, 0)
if result != 0:
print("got a bad result = %d!" % (result,))
Quartz.DisposeHandle(dataRef)
Quartz.CloseComponent(gi)
return imageRef
def drawQTImageWithQuartz(context, url):
image = createCGImageWithQuickTimeFromURL(url)
if image is None:
print("createCGImageWithQuickTimeFromURL didn't create a CGImage!")
return
imageRect = Quartz.CGRectMake(
0.0, 0.0, Quartz.CGImageGetWidth(image), Quartz.CGImageGetHeight(image)
)
Quartz.CGContextDrawImage(context, imageRect, image)
def drawJPEGDocumentWithMultipleProfiles(context, url):
# isDeviceRGBImage = False
# Create a Quartz data provider for the supplied URL.
jpgProvider = Quartz.CGDataProviderCreateWithURL(url)
if jpgProvider is None:
print("Couldn't create JPEG Data provider!")
return
# Create the Quartz.CGImageRef for the JPEG image from the data provider.
jpgImage = Quartz.CGImageCreateWithJPEGDataProvider(
jpgProvider, None, True, Quartz.kCGRenderingIntentDefault
)
del jpgProvider
if jpgImage is None:
print("Couldn't create CGImageRef for JPEG data!")
return
# Get the color space characterizing the image. This is a
# function with 'Get' semantics so the code doesn't own a reference
# to the color space returned and must not release it.
originalColorSpace = Quartz.CGImageGetColorSpace(jpgImage)
if originalColorSpace is None:
print("image is a masking image, not an image with color!")
return
if Quartz.CGColorSpaceGetNumberOfComponents(originalColorSpace) != 3:
print("This example only works with 3 component JPEG images")
return
# Determine if the original color space is DeviceRGB. If that is
# not the case then bail.
# comparisonColorSpace = Quartz.CGColorSpaceCreateDeviceRGB()
# Note that this comparison of color spaces works only on
# Jaguar and later where a CGColorSpaceRef is a
# CoreFoundation object. Otherwise this will crash!
#
# NOTE: 20140109: Disabled the color space comparison because that's not valid
# on recent enough OSX versions.
# isDeviceRGBImage = (comparisonColorSpace == originalColorSpace)
# This code created 'comparisonColorSpace' so it must release it.
# del comparisonColorSpace
# if not isDeviceRGBImage:
# print("The color space for the JPEG image is not DeviceRGB!",
# comparisonColorSpace, originalColorSpace)
# #return
# Might need to adjust this based on the size of the original image.
Quartz.CGContextScaleCTM(context, 0.5, 0.5)
imageRect = Quartz.CGRectMake(
0.0,
Quartz.CGImageGetHeight(jpgImage) / 2,
Quartz.CGImageGetWidth(jpgImage),
Quartz.CGImageGetHeight(jpgImage),
)
# Draw the original image to the left of the other two.
Quartz.CGContextDrawImage(context, imageRect, jpgImage)
# Recharacterize the original image with the generic Calibrated RGB
# color space.
updatedImage1 = Quartz.CGImageCreateCopyWithColorSpace(
jpgImage, Utilities.getTheCalibratedRGBColorSpace()
)
# Release the original image since this code is done with it.
del jpgImage
if updatedImage1 is None:
print("There is no updated image to draw!")
return
# Draw the image characterized by the Generic profile
# to the right of the other image.
imageRect = Quartz.CGRectOffset(imageRect, Quartz.CGRectGetWidth(imageRect) + 10, 0)
Quartz.CGContextDrawImage(context, imageRect, updatedImage1)
# Recharacterize the image but now with a color space
# created with the sRGB profile.
updatedImage2 = Quartz.CGImageCreateCopyWithColorSpace(
updatedImage1, Utilities.getTheSRGBColorSpace()
)
# Release updatedImage1 since this code is done with it.
del updatedImage1
if updatedImage2 is None:
print("There is no second updated image to draw!")
return
# Draw the image characterized by the sRGB profile to the right of
# the image characterized by the generic RGB profile.
imageRect = Quartz.CGRectOffset(imageRect, Quartz.CGRectGetWidth(imageRect) + 10, 0)
Quartz.CGContextDrawImage(context, imageRect, updatedImage2)
def createRedGreenRampImageData(width, height, size):
try:
dataP = bytearray(size)
except MemoryError:
return None
idx = 0
# Build an image that is RGB 24 bits per sample. This is a ramp
# where the red component value increases in red from left to
# right and the green component increases from top to bottom.
for g in range(height):
for r in range(width):
dataP[idx + 0] = r
dataP[idx + 1] = g
dataP[idx + 2] = 0
idx += 3
return dataP
def createRGBRampSubDataProvider(subRect):
bytesPerSample = 3
width = 256
height = 256
bytesPerRow = width * bytesPerSample
startOffsetX = subRect.origin.x
startOffsetY = subRect.origin.y
imageDataSize = bytesPerRow * height
# The first image sample is at
# (startOffsetY*bytesPerRow + startOffsetX*bytesPerSample)
# bytes into the RGB ramp data.
firstByteOffset = startOffsetY * bytesPerRow + startOffsetX * bytesPerSample
# The actual size of the image data provided is the full image size
# minus the amount skipped at the beginning. This is more than the
# total amount of data that is needed for the subimage but it is
# valid and easy to calculate.
totalBytesProvided = imageDataSize - firstByteOffset
# Create the full color ramp.
dataP = createRedGreenRampImageData(width, height, imageDataSize)
if dataP is None:
print("Couldn't create image data!")
return None
# Use the pointer to the first byte as the info parameter since
# that is the pointer to the block to free when done.
dataProvider = Quartz.CGDataProviderCreateWithData(
dataP, dataP[firstByteOffset:], totalBytesProvided, None
)
if dataProvider is None:
return None
return dataProvider
def doColorRampSubImage(context):
# Start 4 scanlines from the top and 16 pixels from the left edge,
# skip the last 40 scanlines of the image and the right
# most 64 pixels.
insetLeft = 16
insetTop = 4
insetRight = 64
insetBottom = 40
fullImageWidth = 256
fullImageHeight = 256
subImageWidth = fullImageWidth - insetLeft - insetRight
subImageHeight = fullImageHeight - insetTop - insetBottom
bitsPerComponent = 8
bitsPerPixel = 24
bytesPerRow = fullImageWidth * 3
shouldInterpolate = True
imageSubRect = Quartz.CGRectMake(insetLeft, insetTop, subImageWidth, subImageHeight)
colorspace = Utilities.getTheCalibratedRGBColorSpace()
if hasattr(Quartz, "CGImageCreateWithImageInRect"):
imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
fullImage = Quartz.CGImageCreate(
fullImageWidth,
fullImageHeight,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
if fullImage is not None:
image = Quartz.CGImageCreateWithImageInRect(fullImage, imageSubRect)
# release the full image since it is no longer required.
del fullImage
# If the image hasn't been created yet, this code uses the
# customized data provider to do so.
if image is None:
imageDataProvider = createRGBRampSubDataProvider(imageSubRect)
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
# By supplying bytesPerRow, the extra data at the end of
# each scanline and the beginning of the next is properly skipped.
image = Quartz.CGImageCreate(
subImageWidth,
subImageHeight,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
# This code no longer needs the data provider.
del imageDataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
# Draw the subimage.
rect = Quartz.CGRectMake(0, 0, subImageWidth, subImageHeight)
Quartz.CGContextDrawImage(context, rect, image)
def exportCGImageToPNGFileWithDestination(image, url):
resolution = 144.0
# Create an image destination at the supplied URL that
# corresponds to the PNG image format.
imageDestination = Quartz.CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, None)
if imageDestination is None:
print("couldn't create image destination!")
return
# Set the keys to be the x and y resolution of the image.
options = {
Quartz.kCGImagePropertyDPIWidth: resolution,
Quartz.kCGImagePropertyDPIHeight: resolution,
}
# Add the image with the options dictionary to the destination.
Quartz.CGImageDestinationAddImage(imageDestination, image, options)
# Release the options dictionary this code created.
del options
# When all the images are added to the destination, finalize it.
Quartz.CGImageDestinationFinalize(imageDestination)
# Release the destination when done with it.
del imageDestination
# This code requires QuickTime.framework
# include <QuickTime/QuickTime.h>
def exportCGImageToJPEGFile(imageRef, url):
# This doesn't actually work due to lame Python Quicktime bindings...
return
result, dataRef, dataRefType = Quartz.QTNewDataReferenceFromCFURL(
url, 0, None, None
)
if result == 0:
result, graphicsExporter = Quartz.OpenADefaultComponent(
Quartz.GraphicsExporterComponentType, Quartz.kQTFileTypeJPEG
)
if result == 0:
result = Quartz.GraphicsExportSetInputCGImage(graphicsExporter, imageRef)
if result == 0:
result = Quartz.GraphicsExportSetOutputDataReference(
graphicsExporter, dataRef, dataRefType
)
if result == 0:
result, sizeWritten = Quartz.GraphicsExportDoExport(
graphicsExporter, None
)
Quartz.CloseComponent(graphicsExporter)
if dataRef is not None:
Quartz.DisposeHandle(dataRef)
if result != 0:
print("Exporting QT image got bad result = %d!" % (result,))
def exportColorRampImageWithQT(context):
width = 256
height = 256
bitsPerComponent = 8
bitsPerPixel = 24
bytesPerRow = width * 3
shouldInterpolate = True
imageDataProvider = DataProvidersAndConsumers.createRGBRampDataProvider()
if imageDataProvider is None:
print("Couldn't create Image Data provider!")
return
colorspace = Utilities.getTheCalibratedRGBColorSpace()
image = Quartz.CGImageCreate(
width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
Quartz.kCGImageAlphaNone,
imageDataProvider,
None,
shouldInterpolate,
Quartz.kCGRenderingIntentDefault,
)
del imageDataProvider
if image is None:
print("Couldn't create CGImageRef for this data!")
return
rect = Quartz.CGRectMake(0.0, 0.0, width, height)
Quartz.CGContextDrawImage(context, rect, image)
# Of course this is a total hack.
outPath = b"/tmp/imageout.jpg"
exportURL = Cocoa.CFURLCreateFromFileSystemRepresentation(
None, outPath, len(outPath), False
)
if exportURL:
exportCGImageToJPEGFile(image, exportURL)
MyAppController.py¶
import BitmapContext
import Cocoa
import objc
import PDFHandling
import Utilities
# Initial defaults
_dpi = 144
_useQT = False
def getURLToExport(suffix):
savePanel = Cocoa.NSSavePanel.savePanel()
initialFileName = f"BasicDrawing.{suffix}"
if (
savePanel.runModalForDirectory_file_(None, initialFileName)
== Cocoa.NSFileHandlingPanelOKButton
):
return savePanel.URL()
return None
class MyAppController(Cocoa.NSObject):
theView = objc.IBOutlet()
currentDPIMenuItem = objc.IBOutlet()
currentExportStyleMenuItem = objc.IBOutlet()
@objc.IBAction
def print_(self, sender):
self.theView.print_(sender)
def updateDPIMenu_(self, sender):
if self.currentDPIMenuItem is not sender:
# Uncheck the previous item.
if self.currentDPIMenuItem is not None:
self.currentDPIMenuItem.setState_(Cocoa.NSOffState)
# Update to the current item.
self.currentDPIMenuItem = sender
# Check new menu item.
self.currentDPIMenuItem.setState_(Cocoa.NSOnState)
def updateExportStyleMenu_(self, sender):
if self.currentExportStyleMenuItem is not sender:
# Uncheck the previous item.
if self.currentExportStyleMenuItem is not None:
self.currentExportStyleMenuItem.setState_(Cocoa.NSOffState)
# Update to the current item.
self.currentExportStyleMenuItem = sender
# Check new menu item.
self.currentExportStyleMenuItem.setState_(Cocoa.NSOnState)
@objc.IBAction
def setExportResolution_(self, sender):
global _dpi
_dpi = sender.tag()
self.updateDPIMenu_(sender)
@objc.IBAction
def setUseQT_(self, sender):
global _useQT
_useQT = True
self.updateExportStyleMenu_(sender)
@objc.IBAction
def setUseCGImageSource_(self, sender):
global _useQT
_useQT = False
self.updateExportStyleMenu_(sender)
def setupExportInfo_(self, exportInfoP):
# Use the printable version of the current command. This produces
# the best results for exporting.
exportInfoP.command = self.theView.currentPrintableCommand()
exportInfoP.fileType = " " # unused
exportInfoP.useQTForExport = _useQT
exportInfoP.dpi = _dpi
@objc.IBAction
def exportAsPDF_(self, sender):
url = getURLToExport("pdf")
if url is not None:
exportInfo = Utilities.ExportInfo()
self.setupExportInfo_(exportInfo)
PDFHandling.MakePDFDocument(url, exportInfo)
@objc.IBAction
def exportAsPNG_(self, sender):
url = getURLToExport("png")
if url is not None:
exportInfo = Utilities.ExportInfo()
self.setupExportInfo_(exportInfo)
BitmapContext.MakePNGDocument(url, exportInfo)
@objc.IBAction
def exportAsTIFF_(self, sender):
url = getURLToExport("tif")
if url is not None:
exportInfo = Utilities.ExportInfo()
self.setupExportInfo_(exportInfo)
BitmapContext.MakeTIFFDocument(url, exportInfo)
@objc.IBAction
def exportAsJPEG_(self, sender):
url = getURLToExport("jpg")
if url is not None:
exportInfo = Utilities.ExportInfo()
self.setupExportInfo_(exportInfo)
BitmapContext.MakeJPEGDocument(url, exportInfo)
def validateMenuItem_(self, menuItem):
if menuItem.tag == _dpi:
self.currentDPIMenuItem = menuItem
menuItem.setState_(True)
elif menuItem.action() == "setUseQT:":
if _useQT:
self.currentDPIMenuItem = menuItem
menuItem.setState_(True)
else:
menuItem.setState_(False)
elif menuItem.action() == "setUseCGImageSource:":
if _useQT:
self.currentDPIMenuItem = menuItem
menuItem.setState_(True)
else:
menuItem.setState_(False)
return True
MyView.py¶
import AppDrawing
import Cocoa
import Quartz
import FrameworkTextDrawing
import FrameworkUtilities
import objc
import UIHandling
import PDFHandling
from objc import super # noqa: A004
# XXX: Why are these global?
_drawingCommand = UIHandling.kHICommandSimpleRect
_pdfDocument = None
class MyView(Cocoa.NSView):
currentMenuItem = objc.IBOutlet()
def initWithFrame_(self, frameRect):
self = super().initWithFrame_(frameRect)
if self is None:
return None
global _pdfDocument
_pdfDocument = None
return self
if False:
def isFlipped(self):
return True
def drawRect_(self, rect):
context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
if _pdfDocument is None:
if _drawingCommand in (
UIHandling.kHICommandDrawNSString,
UIHandling.kHICommandDrawNSLayoutMgr,
UIHandling.kHICommandDrawCustomNSLayoutMgr,
):
if _drawingCommand == UIHandling.kHICommandDrawNSString:
FrameworkTextDrawing.drawNSStringWithAttributes()
elif _drawingCommand == UIHandling.kHICommandDrawNSLayoutMgr:
FrameworkTextDrawing.drawWithNSLayout()
else:
FrameworkTextDrawing.drawWithCustomNSLayout()
else:
AppDrawing.DispatchDrawing(context, _drawingCommand)
else:
mediaRect = Quartz.CGPDFDocumentGetMediaBox(_pdfDocument, 1)
mediaRect.origin.x = mediaRect.origin.y = 0
Quartz.CGContextDrawPDFDocument(context, mediaRect, _pdfDocument, 1)
@objc.IBAction
def setDrawCommand_(self, sender):
global _drawingCommand, _pdfDocument
newCommand = sender.tag()
if _drawingCommand != newCommand:
_drawingCommand = newCommand
# The view needs to be redisplayed since there is a new drawing command.
self.setNeedsDisplay_(True)
# Disable previous menu item.
if self.currentMenuItem is not None:
self.currentMenuItem.setState_(Cocoa.NSOffState)
# Update the current item.
self.currentMenuItem = sender
# Enable new menu item.
self.currentMenuItem.setState_(Cocoa.NSOnState)
# If we were showing a pasted document, let's get rid of it.
if _pdfDocument:
_pdfDocument = None
def currentPrintableCommand(self):
# The best representation for printing or exporting
# when the current command caches using a bitmap context
# or a layer is to not do any caching.
if _drawingCommand in (
UIHandling.kHICommandDrawOffScreenImage,
UIHandling.kHICommandDrawWithLayer,
):
return UIHandling.kHICommandDrawNoOffScreenImage
return _drawingCommand
def print_(self, sender):
global _drawingCommand
savedDrawingCommand = _drawingCommand
# Set the drawing command to be one that is printable.
_drawingCommand = self.currentPrintableCommand()
# Do the printing operation on the view.
Cocoa.NSPrintOperation.printOperationWithView_(self).runOperation()
# Restore that before the printing operation.
_drawingCommand = savedDrawingCommand
def acceptsFirstResponder(self):
return True
@objc.IBAction
def copy_(self, sender):
FrameworkUtilities.addPDFDataToPasteBoard(_drawingCommand)
@objc.IBAction
def paste_(self, sender):
global _pdfDocument
newPDFDocument = PDFHandling.createNewPDFRefFromPasteBoard()
if newPDFDocument is not None:
_pdfDocument = newPDFDocument
# The view needs to be redisplayed since there is
# a new PDF document.
self.setNeedsDisplay_(True)
# Return the number of pages available for printing. For this
# application it is always 1.
def knowsPageRange_(self, aRange):
return True, Cocoa.NSRange(1, 1)
# Return the drawing rectangle for a particular page number.
# For this application it is always the page width and height.
def rectForPage_(self, page):
pi = Cocoa.NSPrintOperation.currentOperation().printInfo()
# Calculate the page height in points.
paperSize = pi.paperSize()
return Cocoa.NSMakeRect(0, 0, paperSize.width, paperSize.height)
def validateMenuItem_(self, menuItem):
if menuItem.tag() == _drawingCommand:
self.currentMenuItem = menuItem
menuItem.setState_(True)
else:
menuItem.setState_(False)
return True
PDFHandling.py¶
import AppDrawing
import Cocoa
import DataProvidersAndConsumers
import FrameworkUtilities
import Quartz
def createNewPDFRefFromPasteBoard():
# Create a reference to the PDF data on the pasteboard.
# The implementation of myCreatePDFDataFromPasteBoard depends
# on the application framework you are using for your application.
#
# myCreatePDFDataFromPasteBoard creates a reference that is owned
# by the calling application.
pasteBoardData = FrameworkUtilities.myCreatePDFDataFromPasteBoard()
if pasteBoardData is None:
print("There is no PDF data on pasteboard!")
return None
# Create a data provider from the pasteboard data.
dataProvider = Quartz.CGDataProviderCreateWithCFData(pasteBoardData)
# Release the pasteboard data since the data provider retains
# it and this code owns a reference but no longer requires it.
del pasteBoardData
if dataProvider is None:
print("Couldn't create data provider.")
return None
pasteBoardPDFDocument = Quartz.CGPDFDocumentCreateWithProvider(dataProvider)
# Release the data provider now that the code is done with it.
del dataProvider
if pasteBoardPDFDocument is None:
print("Couldn't create PDF document from pasteboard data provider.")
return None
return pasteBoardPDFDocument
_pdfDoc = None
def getPasteBoardPDFDoc(reset):
global _pdfDoc
if reset:
# Release any existing document.
_pdfDoc = createNewPDFRefFromPasteBoard()
else:
# If there isn't already one, create it fresh.
if _pdfDoc is None:
_pdfDoc = createNewPDFRefFromPasteBoard()
return _pdfDoc
def drawPasteBoardPDF(context):
pdfDoc = getPasteBoardPDFDoc(False) # Obtain the existing one.
if pdfDoc is None:
print("Quartz couldn't create CGPDFDocumentRef from pasteboard.")
return
# The media box is the bounding box of the PDF document.
pdfRect = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
# page 1
# Make the destination rect origin at the Quartz origin.
pdfRect.origin.x = pdfRect.origin.y = 0.0
Quartz.CGContextDrawPDFDocument(context, pdfRect, pdfDoc, 1)
# page 1
def cfDataCreatePDFDocumentFromCommand(command):
# Media rect for the drawing. In a real application this
# should be the bounding rectangle of the graphics
# that will be the PDF content.
mediaRect = Quartz.CGRectMake(0, 0, 612, 792)
# Create a dictionary to hold the optional information describing the PDF data.
info_dict = {}
# Add the creator and title information to the PDF content.
info_dict[Quartz.kCGPDFContextTitle] = "Pasted From Sample Quartz Application"
info_dict[Quartz.kCGPDFContextCreator] = "Sample Quartz Application"
# Create a mutable CFData object with unlimited capacity.
data = Cocoa.CFDataCreateMutable(None, 0)
if data is None:
print("Couldn't make CFData!")
return None
# Create the data consumer to capture the PDF data.
consumer = DataProvidersAndConsumers.myCGDataConsumerCreateWithCFData(data)
if consumer is None:
print("Couldn't create data consumer!")
return None
pdfContext, mediaRect = Quartz.CGPDFContextCreate(consumer, None, info_dict)
del consumer
del info_dict
if pdfContext is None:
print("Couldn't create pdf context!")
return None
mediaRect = Quartz.CGContextBeginPage(pdfContext)
if 1:
Quartz.CGContextSaveGState(pdfContext)
if 1:
Quartz.CGContextClipToRect(pdfContext, mediaRect)
AppDrawing.DispatchDrawing(pdfContext, command)
Quartz.CGContextRestoreGState(pdfContext)
Quartz.CGContextEndPage(pdfContext)
return data
def MakePDFDocument(url, exportInfo):
# Use this as the media box for the document.
# In a real application this should be the bounding
# rectangle of the graphics that will be the PDF content.
mediaRect = Quartz.CGRectMake(0, 0, 612, 792)
info = {
# Add the title information for this document.
Quartz.kCGPDFContextTitle: "BasicDrawing Sample Graphics",
# Add the author information for this document. This is typically
# the user creating the document.
Quartz.kCGPDFContextAuthor: "David Gelphman and Bunny Laden",
# The creator is the application creating the document.
Quartz.kCGPDFContextCreator: "BasicDrawing Application",
}
if 0:
# Before using the kCGPDFContextCropBox key, check to ensure that it
# is available.
if hasattr(Quartz, "kCFPDFContextCropBox"):
# Prepare the crop box entry. Use this rectangle as the crop box for
# this example.
# XXX:fixme: need to encode as CFData!!!
info[Quartz.kCGPDFContextCropBox] = Quartz.CGRectMake(100, 100, 200, 200)
if url is not None:
pdfContext = Quartz.CGPDFContextCreateWithURL(url, mediaRect, info)
if pdfContext is not None:
Quartz.CGContextBeginPage(pdfContext, mediaRect)
if 1:
Quartz.CGContextSaveGState(pdfContext)
if 1:
Quartz.CGContextClipToRect(pdfContext, mediaRect)
AppDrawing.DispatchDrawing(pdfContext, exportInfo.command)
Quartz.CGContextRestoreGState(pdfContext)
Quartz.CGContextEndPage(pdfContext)
del pdfContext
else:
print("Can't create PDF document!")
PathDrawing.py¶
import math
import Quartz
def doEgg(context):
p0 = Quartz.CGPoint(0, 0)
p1 = Quartz.CGPoint(0, 200)
c1 = Quartz.CGPoint(140, 5)
c2 = Quartz.CGPoint(80, 198)
Quartz.CGContextTranslateCTM(context, 100, 5)
Quartz.CGContextBeginPath(context)
Quartz.CGContextMoveToPoint(context, p0.x, p0.y)
# Create the Bezier path segment for the right side of the egg.
Quartz.CGContextAddCurveToPoint(context, c1.x, c1.y, c2.x, c2.y, p1.x, p1.y)
# Create the Bezier path segment for the left side of the egg.
Quartz.CGContextAddCurveToPoint(context, -c2.x, c2.y, -c1.x, c1.y, p0.x, p0.y)
Quartz.CGContextClosePath(context)
Quartz.CGContextSetLineWidth(context, 2)
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
def addRoundedRectToPath(context, rect, ovalWidth, ovalHeight):
# If either ovalWidth or ovalHeight is 0, draw a regular rectangle.
if ovalWidth == 0 or ovalHeight == 0:
Quartz.CGContextAddRect(context, rect)
else:
Quartz.CGContextSaveGState(context)
if 1:
# Translate to lower-left corner of rectangle.
Quartz.CGContextTranslateCTM(
context, Quartz.CGRectGetMinX(rect), Quartz.CGRectGetMinY(rect)
)
# Scale by the oval width and height so that
# each rounded corner is 0.5 units in radius.
Quartz.CGContextScaleCTM(context, ovalWidth, ovalHeight)
# Unscale the rectangle width by the amount of the X scaling.
fw = Quartz.CGRectGetWidth(rect) / ovalWidth
# Unscale the rectangle height by the amount of the Y scaling.
fh = Quartz.CGRectGetHeight(rect) / ovalHeight
# Start at the right edge of the rect, at the midpoint in Y.
Quartz.CGContextMoveToPoint(context, fw, fh / 2)
# Segment 1
Quartz.CGContextAddArcToPoint(context, fw, fh, fw / 2, fh, 0.5)
# Segment 2
Quartz.CGContextAddArcToPoint(context, 0, fh, 0, fh / 2, 0.5)
# Segment 3
Quartz.CGContextAddArcToPoint(context, 0, 0, fw / 2, 0, 0.5)
# Segment 4
Quartz.CGContextAddArcToPoint(context, fw, 0, fw, fh / 2, 0.5)
# Closing the path adds the last segment.
Quartz.CGContextClosePath(context)
Quartz.CGContextRestoreGState(context)
def doRoundedRects(context):
rect = Quartz.CGRectMake(10, 10, 210, 150)
ovalWidth = 100
ovalHeight = 100
Quartz.CGContextSetLineWidth(context, 2.0)
Quartz.CGContextBeginPath(context)
addRoundedRectToPath(context, rect, ovalWidth, ovalHeight)
Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
def doStrokeWithCTM(context):
Quartz.CGContextTranslateCTM(context, 150.0, 180.0)
Quartz.CGContextSetLineWidth(context, 10)
# Draw ellipse 1 with a uniform stroke.
Quartz.CGContextSaveGState(context)
if 1:
# Scale the CTM so the circular arc will be elliptical.
Quartz.CGContextScaleCTM(context, 2, 1)
Quartz.CGContextBeginPath(context)
# Create an arc that is a circle.
Quartz.CGContextAddArc(context, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, 0)
# Restore the context parameters prior to stroking the path.
# CGContextRestoreGState does not affect the path in the context.
Quartz.CGContextRestoreGState(context)
Quartz.CGContextStrokePath(context)
# *** was 0, -120
Quartz.CGContextTranslateCTM(context, 220.0, 0.0)
# Draw ellipse 2 with non-uniform stroke.
Quartz.CGContextSaveGState(context)
if 1:
# Scale the CTM so the circular arc will be elliptical.
Quartz.CGContextScaleCTM(context, 2, 1)
Quartz.CGContextBeginPath(context)
# Create an arc that is a circle.
Quartz.CGContextAddArc(context, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, 0)
# Stroke the path with the scaled coordinate system in effect.
Quartz.CGContextStrokePath(context)
Quartz.CGContextRestoreGState(context)
def doRotatedEllipsesWithCGPath(context):
totreps = 144
tint = 1.0
tintIncrement = 1.0 / totreps
# Create a new transform consisting of a 45 degree rotation.
theTransform = Quartz.CGAffineTransformMakeRotation(math.pi / 4)
# Apply a scaling transformation to the transform just created.
theTransform = Quartz.CGAffineTransformScale(theTransform, 1, 2)
# Create a mutable CGPath object.
path = Quartz.CGPathCreateMutable()
if path is None:
print("Couldn't create path!")
return
# Add a circular arc to the CGPath object, transformed
# by an affine transform.
Quartz.CGPathAddArc(path, theTransform, 0.0, 0.0, 45.0, 0.0, 2 * math.pi, False)
# Close the CGPath object.
Quartz.CGPathCloseSubpath(path)
# Place the first ellipse at a good location.
Quartz.CGContextTranslateCTM(context, 100, 100)
for _ in range(totreps):
Quartz.CGContextBeginPath(context)
# Add the CGPath object to the current path in the context.
Quartz.CGContextAddPath(context, path)
# Set the fill color for this instance of the ellipse.
Quartz.CGContextSetRGBFillColor(context, tint, 0.0, 0.0, 1.0)
# Filling the path implicitly closes it.
Quartz.CGContextFillPath(context)
# Compute the next tint color.
tint -= tintIncrement
# Move over for the next ellipse.
Quartz.CGContextTranslateCTM(context, 1, 0.0)
def alignPointToUserSpace(context, p):
# Compute the coordinates of the point in device space.
p = Quartz.CGContextConvertPointToDeviceSpace(context, p)
# Ensure that coordinates are at exactly the corner
# of a device pixel.
p.x = math.floor(p.x)
p.y = math.floor(p.y)
# Convert the device aligned coordinate back to user space.
return Quartz.CGContextConvertPointToUserSpace(context, p)
def alignSizeToUserSpace(context, s):
# Compute the size in device space.
s = Quartz.CGContextConvertSizeToDeviceSpace(context, s)
# Ensure that size is an integer multiple of device pixels.
s.width = math.floor(s.width)
s.height = math.floor(s.height)
# Convert back to user space.
return Quartz.CGContextConvertSizeToUserSpace(context, s)
def alignRectToUserSpace(context, r):
# Compute the coordinates of the rectangle in device space.
r = Quartz.CGContextConvertRectToDeviceSpace(context, r)
# Ensure that the x and y coordinates are at a pixel corner.
r.origin.x = math.floor(r.origin.x)
r.origin.y = math.floor(r.origin.y)
# Ensure that the width and height are an integer number of
# device pixels. Note that this produces a width and height
# that is less than or equal to the original width. Another
# approach is to use ceil to ensure that the new rectangle
# encloses the original one.
r.size.width = math.floor(r.size.width)
r.size.height = math.floor(r.size.height)
# Convert back to user space.
return Quartz.CGContextConvertRectToUserSpace(context, r)
def doPixelAlignedFillAndStroke(context):
p1 = Quartz.CGPointMake(16.7, 17.8)
p2 = Quartz.CGPointMake(116.7, 17.8)
r = Quartz.CGRectMake(16.7, 20.8, 100.6, 100.6)
Quartz.CGContextSetLineWidth(context, 2)
Quartz.CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0)
Quartz.CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
# Unaligned drawing.
Quartz.CGContextBeginPath(context)
Quartz.CGContextMoveToPoint(context, p1.x, p1.y)
Quartz.CGContextAddLineToPoint(context, p2.x, p2.y)
Quartz.CGContextStrokePath(context)
Quartz.CGContextFillRect(context, r)
# Translate to the right before drawing along
# aligned coordinates.
Quartz.CGContextTranslateCTM(context, 106, 0)
# Aligned drawing.
# Compute the length of the line in user space.
s = Quartz.CGSizeMake(p2.x - p1.x, p2.y - p1.y)
Quartz.CGContextBeginPath(context)
# Align the starting point to a device
# pixel boundary.
p1 = alignPointToUserSpace(context, p1)
# Establish the starting point of the line.
Quartz.CGContextMoveToPoint(context, p1.x, p1.y)
# Compute the line length as an integer
# number of device pixels.
s = alignSizeToUserSpace(context, s)
Quartz.CGContextAddLineToPoint(context, p1.x + s.width, p1.y + s.height)
Quartz.CGContextStrokePath(context)
# Compute a rect that is aligned to device
# space with a width that is an integer
# number of device pixels.
r = alignRectToUserSpace(context, r)
Quartz.CGContextFillRect(context, r)
PatternDrawing.py¶
import Quartz
import Utilities
def scalePatternPhase(phase):
# Adjust the pattern phase if scaling to export as bits. This is equivalent to scaling base
# space by the scaling factor.
patternScaling = Utilities.getScalingFactor()
if patternScaling != 1.0:
phase = Quartz.CGSizeApplyAffineTransform(
phase, Quartz.CGAffineTransformMakeScale(patternScaling, patternScaling)
)
return phase
def scalePatternMatrix(patternTransform):
# Scale the pattern by the scaling factor when exporting to bits. This is equivalent to
# scaling base space by the scaling factor.
patternScaling = Utilities.getScalingFactor()
if patternScaling != 1.0:
patternTransform = Quartz.CGAffineTransformConcat(
patternTransform,
Quartz.CGAffineTransformMakeScale(patternScaling, patternScaling),
)
return patternTransform
def myDrawRedBlackCheckerBoardPattern(info, patternCellContext):
# This pattern proc draws a red and a black rectangle
# patch representing the minimum cell needed to paint a
# checkerboard with that pattern.
#
# Each 'cell' of the checkerboard is 2 units on a side.
#
# This code uses Quartz.CGColorRefs which are available in Panther
# and later only. Patterns are available in all versions of
# macOS but this code uses Quartz.CGColorRefs for convenience
# and efficiency.
# Paint a black checkerboard box.
Quartz.CGContextSetFillColorWithColor(
patternCellContext, Utilities.getRGBOpaqueBlackColor()
)
# This is a 1x1 unit rect whose origin is at 0,0 in pattern space.
Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(0.0, 0.0, 1.0, 1.0))
# This is a 1x1 unit rect whose origin is at 1,1 in pattern space.
Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(1.0, 1.0, 1.0, 1.0))
# Paint a red checkerboard box.
Quartz.CGContextSetFillColorWithColor(
patternCellContext, Utilities.getRGBOpaqueRedColor()
)
# This is a 1x1 unit rect whose origin is at 1,0 in pattern space,
# that is, immediately to the right of first black checkerboard box.
Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(1.0, 0.0, 1.0, 1.0))
# This is a 1x1 unit rect whose origin is at 0,1 in pattern space,
# that is, immediately above the first black checkerboard box.
Quartz.CGContextFillRect(patternCellContext, Quartz.CGRectMake(0.0, 1.0, 1.0, 1.0))
def createRedBlackCheckerBoardPattern(patternTransform):
pattern = Quartz.CGPatternCreate(
None,
# The pattern cell origin is at (0,0) with a
# width of 2 units and a height of 2 units.
Quartz.CGRectMake(0, 0, 2, 2),
# Use the pattern transform supplied to this routine.
scalePatternMatrix(patternTransform),
# In pattern space the xStep is 2 units to the next cell in x
# and the yStep is 2 units to the next row of cells in y.
2,
2,
# This value is a good choice for this type of pattern and it
# avoids seams between tiles.
Quartz.kCGPatternTilingConstantSpacingMinimalDistortion,
# This pattern has intrinsic color.
True,
myDrawRedBlackCheckerBoardPattern,
)
return pattern
def doRedBlackCheckerboard(context):
dash = [4]
pattern = createRedBlackCheckerBoardPattern(
Quartz.CGAffineTransformMakeScale(20, 20)
)
if pattern is None:
print("Couldn't create pattern!")
return
# Create the pattern color space. Since the pattern
# itself has intrinsic color, the 'baseColorSpace' parameter
# to Quartz.CGColorSpaceCreatePattern must be None.
patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
# The pattern has intrinsic color so the color components array
# passed to CGContextSetFillPattern is just the alpha value used
# to composite the pattern cell.
# Paint the pattern with alpha = 1.
color = [1.0]
# Set the fill color to the checkerboard pattern.
Quartz.CGContextSetFillPattern(context, pattern, color)
# Fill a 100x100 unit rect at (20,20).
Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 20, 100, 100))
# Save the graphics state before changing the stroke color.
Quartz.CGContextSaveGState(context)
if 1:
# Set the stroke color space and color to the pattern.
Quartz.CGContextSetStrokeColorSpace(context, patternColorSpace)
Quartz.CGContextSetStrokePattern(context, pattern, color)
# Stroke an ellipse with the pattern.
Quartz.CGContextSetLineWidth(context, 8)
Quartz.CGContextBeginPath(context)
Utilities.myCGContextAddEllipseInRect(
context, Quartz.CGRectMake(120, 20, 50, 100)
)
Quartz.CGContextStrokePath(context)
# Restore to the graphics state without the
# pattern stroke color.
Quartz.CGContextRestoreGState(context)
# Now draw text.
Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
# Choose the font with the PostScript name "Times-Roman",
# size 80 points, with the encoding MacRoman encoding.
Quartz.CGContextSelectFont(context, b"Times-Roman", 80, Quartz.kCGEncodingMacRoman)
# Using the fill text drawing mode.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFill)
# Draw text with the pattern.
Quartz.CGContextShowTextAtPoint(context, 20, 120, b"Text", 4)
# Rectangle 1, filled.
Quartz.CGContextFillRect(context, Quartz.CGRectMake(200, 20, 90, 90))
# Rectangle 2, filled and stroked with a dash.
Quartz.CGContextSetLineWidth(context, 2)
Quartz.CGContextSetLineDash(context, 0, dash, 1)
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(200, 70, 90, 90))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
def doPatternMatrix(context):
basePatternMatrix = Quartz.CGAffineTransformMakeScale(20, 20)
pattern = createRedBlackCheckerBoardPattern(basePatternMatrix)
if pattern is None:
print("Couldn't create pattern!")
return
# Create the pattern color space. Since the pattern
# itself has intrinsic color, the 'baseColorSpace' parameter
# to Quartz.CGColorSpaceCreatePattern must be None.
patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
del patternColorSpace
Quartz.CGContextTranslateCTM(context, 40, 40)
Quartz.CGContextSetPatternPhase(context, scalePatternPhase(Quartz.CGSize(40, 40)))
# The pattern has intrinsic color so the color components array
# passed to Quartz.CGContextSetFillPattern is the alpha value used
# to composite the pattern cell.
# Paint the pattern first with alpha = 1.
color = [1]
Quartz.CGContextSetFillPattern(context, pattern, color)
# Rectangle 1.
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
Quartz.CGContextSaveGState(context)
if 1:
# Rectangle 2.
# Paint the pattern with 65% alpha.
color = [0.65]
Quartz.CGContextSetFillPattern(context, pattern, color)
# Rotate 45 degrees about the point (150, 50).
Quartz.CGContextTranslateCTM(context, 150.0, 50.0)
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45.0))
Quartz.CGContextTranslateCTM(context, -50.0, -50.0)
# Rectangle 2. Patterns do not translate, scale or
# rotate with the CTM. You can see that the pattern
# tile of this filled rectangle is that of Rectangle
# 1.
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
# Release the pattern.
del pattern
Quartz.CGContextRestoreGState(context)
Quartz.CGContextSaveGState(context)
if 1:
# Rectangle 3. The pattern is rotated with the object.
# Rotate 45 degrees about the point 250, 50.
t = Quartz.CGAffineTransformMakeTranslation(250.0, 50.0)
t = Quartz.CGAffineTransformRotate(t, Utilities.DEGREES_TO_RADIANS(45.0))
# Translate back to -50, -50.
t = Quartz.CGAffineTransformTranslate(t, -50.0, -50.0)
Quartz.CGContextConcatCTM(context, t)
# Make a new pattern that is equivalent to
# the old pattern but transformed to current user
# space. The order of transformations is crucial.
# This ordering is equivalent to using the same pattern
# matrix as before but transforming base space by t.
patTransform = Quartz.CGAffineTransformConcat(basePatternMatrix, t)
pattern = createRedBlackCheckerBoardPattern(patTransform)
color = [1]
Quartz.CGContextSetFillPattern(context, pattern, color)
# Release the pattern.
del pattern
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
Quartz.CGContextRestoreGState(context)
Quartz.CGContextSaveGState(context)
if 1:
# Rectangle 4. The pattern is scaled with the object.
# Translate and scale.
t = Quartz.CGAffineTransformMakeTranslation(320, 0)
t = Quartz.CGAffineTransformScale(t, 2, 2)
Quartz.CGContextConcatCTM(context, t)
# Make a new pattern that is equivalent to
# the old pattern but transformed to current user
# space. The order of transformations is crucial.
# This ordering is equivalent to using the same pattern
# matrix as before but transforming base space by t.
patTransform = Quartz.CGAffineTransformConcat(basePatternMatrix, t)
pattern = createRedBlackCheckerBoardPattern(patTransform)
color = [1]
Quartz.CGContextSetFillPattern(context, pattern, color)
# Release the pattern.
del pattern
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
Quartz.CGContextRestoreGState(context)
def doPatternPhase(context):
pattern = createRedBlackCheckerBoardPattern(
Quartz.CGAffineTransformMakeScale(20, 20)
)
if pattern is None:
print("Couldn't create pattern!")
return
# Create the pattern color space for a colored pattern.
patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
# Paint the pattern with alpha = 1.
color = (1,)
Quartz.CGContextSetFillPattern(context, pattern, color)
# Rectangle 1
Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 150, 100, 100))
# Rectangle 2
Quartz.CGContextFillRect(context, Quartz.CGRectMake(130, 150, 100, 100))
# Rectangle 3
# Set the pattern phase so that the pattern origin
# is at the lower-left of the shape.
Quartz.CGContextSetPatternPhase(
context, scalePatternPhase(Quartz.CGSizeMake(20, 20))
)
Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 20, 100, 100))
# Rectangle 4
# Set the pattern phase so that the pattern origin
# is at the lower-left corner of the shape.
Quartz.CGContextSetPatternPhase(
context, scalePatternPhase(Quartz.CGSizeMake(130, 20))
)
Quartz.CGContextTranslateCTM(context, 130, 20)
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 100, 100))
def drawRotatedRect(c, p):
r = Quartz.CGRectMake(0, 0, 1, 1)
Quartz.CGContextSaveGState(c)
if 1:
Quartz.CGContextTranslateCTM(c, p.x, p.y)
Quartz.CGContextRotateCTM(c, Utilities.DEGREES_TO_RADIANS(45))
Quartz.CGContextTranslateCTM(c, -r.size.width / 2, -r.size.height / 2)
Quartz.CGContextFillRect(c, r)
Quartz.CGContextRestoreGState(c)
def myStencilPatternProc(info, patternCellContext):
drawRotatedRect(patternCellContext, Quartz.CGPointMake(1, 1))
drawRotatedRect(patternCellContext, Quartz.CGPointMake(1.75, 1))
def createStencilPattern(patternTransform):
pattern = Quartz.CGPatternCreate(
None,
# The pattern cell origin is at (0,0) with a
# width of 2.5 units and a height of 2 units. This
# pattern cell has transparent areas since
# the pattern proc only marks a portion of the cell.
Quartz.CGRectMake(0, 0, 2.5, 2),
# Use the pattern transform supplied to this routine.
scalePatternMatrix(patternTransform),
# Use the width and height of the pattern cell for
# the xStep and yStep.
2.5,
2,
# This value is a good choice for this type of pattern and it
# avoids seams between tiles.
Quartz.kCGPatternTilingConstantSpacingMinimalDistortion,
# This pattern does not have intrinsic color.
False, # Must be False for a stencil pattern.
myStencilPatternProc,
)
return pattern
def doStencilPattern(context):
pattern = createStencilPattern(Quartz.CGAffineTransformMakeScale(20, 20))
if pattern is None:
print("Couldn't create pattern!")
return
# Create the pattern color space. This pattern is a stencil
# pattern so when the code sets the pattern it also sets the
# color it will paint the pattern with. In order to
# set the pattern color space in this case we also have
# to say what underlying color space should be used when
# the pattern proc is called.
baseColorSpace = Utilities.getTheCalibratedRGBColorSpace()
patternColorSpace = Quartz.CGColorSpaceCreatePattern(baseColorSpace)
Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
# This code is finished with the pattern color space and can release
# it because Quartz retains it while it is the current color space.
del patternColorSpace
# The pattern has no intrinsic color so the color components array
# passed to CGContextSetFillPattern contains the colors to paint
# the pattern with in the baseColorSpace. In the case here,
# first paint the pattern with opaque blue.
color = (0.11, 0.208, 0.451, 1.0)
Quartz.CGContextSetFillPattern(context, pattern, color)
# Rectangle 1.
Quartz.CGContextSetPatternPhase(
context, scalePatternPhase(Quartz.CGSizeMake(20, 160))
)
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(20, 160, 105, 80))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# Rectangle 2.
# Set the pattern color so the stencil pattern
# is painted in a yellow shade.
color = (1.0, 0.816, 0.0, 1.0)
Quartz.CGContextSetFillPattern(context, pattern, color)
# Set the pattern phase to the origin of the next object.
Quartz.CGContextSetPatternPhase(
context, scalePatternPhase(Quartz.CGSizeMake(140, 160))
)
Quartz.CGContextBeginPath(context)
Quartz.CGContextAddRect(context, Quartz.CGRectMake(140, 160, 105, 80))
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextSetFillColorWithColor(
context, Utilities.getRGBOpaqueBlueColor()
)
# Fill color is now blue. Paint two blue rectangles
# that will be underneath the drawing which follows.
Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 40, 105, 80))
Quartz.CGContextFillRect(context, Quartz.CGRectMake(140, 40, 105, 80))
Quartz.CGContextRestoreGState(context)
# The fill color is again the stencil pattern with
# the underlying fill color an opaque yellow.
# Rectangle 3.
# This paints over the blue rect just painted at 20,40
# and the blue underneath is visible where the pattern has
# transparent areas.
Quartz.CGContextSetPatternPhase(
context, scalePatternPhase(Quartz.CGSizeMake(20, 40))
)
Quartz.CGContextFillRect(context, Quartz.CGRectMake(20, 40, 105, 80))
# Rectangle 4.
# Change the alpha value of the underlying color used
# to paint the stencil pattern.
color = list(color)
color[3] = 0.75
Quartz.CGContextSetFillPattern(context, pattern, color)
Quartz.CGContextSetPatternPhase(
context, scalePatternPhase(Quartz.CGSizeMake(140, 40))
)
Quartz.CGContextFillRect(context, Quartz.CGRectMake(140, 40, 105, 80))
class MyPDFPatternInfo:
rect = None
pdfDoc = None
def myDrawPDFPattern(info, patternCellContext):
# This pattern proc draws the first page of a PDF document to
# a destination rect.
Quartz.CGContextSaveGState(patternCellContext)
Quartz.CGContextClipToRect(patternCellContext, info.rect)
Quartz.CGContextDrawPDFDocument(patternCellContext, info.rect, info.pdfDoc, 1)
Quartz.CGContextRestoreGState(patternCellContext)
# Versions of Tiger prior to 10.4.3 have a bug such that use of an xStep that
# doesn't match the width of pattern bounding box or a yStep that doesn't match the
# height of the pattern bounding box produces incorrect results when drawn
# to a bit-based context. Setting TIGERSTEPWORKAROUND works around this bug.
TIGERSTEPWORKAROUND = 1
SCALEPATTERN = 1
OPTIMIZEDPERF = 0
def createPDFPatternPattern(additionalTransformP, url):
patternInfoP = MyPDFPatternInfo()
patternInfoP.pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
if patternInfoP.pdfDoc is None:
print("Couldn't create PDF document reference!")
return
patternInfoP.rect = Quartz.CGPDFDocumentGetMediaBox(patternInfoP.pdfDoc, 1)
# Set the origin of the media rect for the PDF document to (0,0).
patternInfoP.rect.origin = Quartz.CGPointZero
if additionalTransformP is not None:
patternTransform = additionalTransformP
else:
patternTransform = Quartz.CGAffineTransformIdentity
# To emulate the example from the bitmap context drawing chapter,
# the tile offset in each dimension is the tile size in that
# dimension, plus 6 units.
if SCALEPATTERN:
tileOffsetX = 6.0 + patternInfoP.rect.size.width
tileOffsetY = 6.0 + patternInfoP.rect.size.height
else:
tileOffsetX = 2.0 + patternInfoP.rect.size.width
tileOffsetY = 2.0 + patternInfoP.rect.size.height
# Tiger versions 10.4.0 - 10.4.2 have a bug such that the bounds
# width and height is incorrectly used as the xstep,ystep.
# To workaround this bug, we can make the bounds rect incorporate
# the xstep,ystep since xstep,ystep are larger than the bounds.
if OPTIMIZEDPERF or TIGERSTEPWORKAROUND:
patternRect = Quartz.CGRectMake(0, 0, tileOffsetX, tileOffsetY)
else:
patternRect = patternInfoP.rect
if OPTIMIZEDPERF:
# Produces best performance if bbox == xstep/ystep
spacing = Quartz.kCGPatternTilingConstantSpacing
else:
spacing = Quartz.kCGPatternTilingConstantSpacingMinimalDistortion
pattern = Quartz.CGPatternCreate(
patternInfoP,
# The pattern cell size is the size
# of the media rect of the PDF document.
patternRect,
scalePatternMatrix(patternTransform),
tileOffsetX,
tileOffsetY,
# This value is a good choice for this type of pattern and
# it avoids seams between tiles.
spacing,
# This pattern has intrinsic color.
True,
myDrawPDFPattern,
)
# If the pattern can't be created then release the
# pattern resources and info parameter.
if pattern is None:
patternInfoP = None
return pattern
def drawWithPDFPattern(context, url):
if SCALEPATTERN:
patternMatrix = Quartz.CGAffineTransformMakeScale(1.0 / 3, 1.0 / 3)
else:
patternMatrix = Quartz.CGAffineTransformMakeScale(1, 1)
# Scale the PDF pattern down to 1/3 its original size.
pdfPattern = createPDFPatternPattern(patternMatrix, url)
if pdfPattern is None:
print("Couldn't create pattern!")
return
# Create the pattern color space. Since the pattern
# itself has intrinsic color, the 'baseColorSpace' parameter
# to CGColorSpaceCreatePattern must be None.
patternColorSpace = Quartz.CGColorSpaceCreatePattern(None)
Quartz.CGContextSetFillColorSpace(context, patternColorSpace)
# Quartz retains the color space so this code
# can now release it since it no longer needs it.
del patternColorSpace
# Paint the pattern with an alpha of 1.
color = (1,)
Quartz.CGContextSetFillPattern(context, pdfPattern, color)
# Quartz retains the pattern so this code
# can now release it since it no longer needs it.
del pdfPattern
# Fill a US Letter size rect with the pattern.
Quartz.CGContextFillRect(context, Quartz.CGRectMake(0, 0, 612, 792))
QuartzTextDrawing.py¶
import Quartz
import Utilities
def drawQuartzRomanText(context):
text = b"Quartz"
textlen = len(text)
fontSize = 60
opaqueBlack = [0.0, 0.0, 0.0, 1.0]
opaqueRed = [0.663, 0.0, 0.031, 1.0]
# Set the fill color space. This sets the
# fill painting color to opaque black.
Quartz.CGContextSetFillColorSpace(
context, Utilities.getTheCalibratedRGBColorSpace()
)
# The Cocoa framework calls the draw method with an undefined
# value of the text matrix. It's best to set it to what is needed by
# this code: the identity transform.
Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
# Set the font with the PostScript name "Times-Roman", at
# fontSize points, with the MacRoman encoding.
Quartz.CGContextSelectFont(
context, b"Times-Roman", fontSize, Quartz.kCGEncodingMacRoman
)
# The default text drawing mode is fill. Draw the text at (70, 400).
Quartz.CGContextShowTextAtPoint(context, 70, 400, text, textlen)
# Set the fill color to red.
Quartz.CGContextSetFillColor(context, opaqueRed)
# Draw the next piece of text where the previous one left off.
Quartz.CGContextShowText(context, text, textlen)
for _ in range(3):
# Get the current text pen position.
p = Quartz.CGContextGetTextPosition(context)
# Translate to the current text pen position.
Quartz.CGContextTranslateCTM(context, p.x, p.y)
# Rotate clockwise by 90 degrees for the next
# piece of text.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-90))
# Draw the next piece of text in blac at the origin.
Quartz.CGContextSetFillColor(context, opaqueBlack)
Quartz.CGContextShowTextAtPoint(context, 0, 0, text, textlen)
# Draw the next piece of text where the previous piece
# left off and paint it with red.
Quartz.CGContextSetFillColor(context, opaqueRed)
Quartz.CGContextShowText(context, text, textlen)
def myCGContextStrokeLineSegments(context, s, count):
# CGContextStrokeLineSegments is available only on Tiger and later
# so if it isn't available, use an emulation of
# CGContextStrokeLineSegments. It is better to use the
# built-in CGContextStrokeLineSegments since it has significant
# performance optimizations on some hardware.
if hasattr(Quartz, "CGContextStrokeLineSegments"):
Quartz.CGContextStrokeLineSegments(context, s, count)
else:
Quartz.CGContextBeginPath(context)
for k in range(0, count, 2):
Quartz.CGContextMoveToPoint(context, s[k].x, s[k].y)
Quartz.CGContextAddLineToPoint(context, s[k + 1].x, s[k + 1].y)
Quartz.CGContextStrokePath(context)
_gridLines = []
def drawGridLines(context):
numlines = 60
if not _gridLines:
stepsize = 4.0
val = 0
for _ in range(0, 2 * numlines, 2):
_gridLines.append(Quartz.CGPointMake(val, -60))
_gridLines.append(Quartz.CGPointMake(val, 200))
val += stepsize
val = -20
for _ in range(2 * numlines, 4 * numlines, 2):
_gridLines.append(Quartz.CGPointMake(0, val))
_gridLines.append(Quartz.CGPointMake(400, val))
val += stepsize
myCGContextStrokeLineSegments(context, _gridLines, len(_gridLines))
def drawQuartzTextWithTextModes(context):
fillText = b"Fill "
strokeText = b"Stroke "
fillAndStrokeText = b"FillStroke "
invisibleText = b"Invisible "
clipText = b"ClipText "
fillStrokeClipText = b"FillStrokeClip "
fontSize = 40.0
extraLeading = 5.0
dash = (1, 1)
opaqueRed = (1.0, 0.0, 0.0, 1.0)
# Set the fill and stroke color space. This sets the
# fill and stroke painting color to opaque black.
Quartz.CGContextSetFillColorSpace(
context, Utilities.getTheCalibratedRGBColorSpace()
)
Quartz.CGContextSetStrokeColorSpace(
context, Utilities.getTheCalibratedRGBColorSpace()
)
# The Cocoa framework calls the draw method with an undefined
# value of the text matrix. It's best to set it to what is needed by
# this code: the identity transform.
Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
# Set the font with the PostScript name "Times-Roman", at
# fontSize points, with the MacRoman encoding.
Quartz.CGContextSelectFont(
context, b"Times-Roman", fontSize, Quartz.kCGEncodingMacRoman
)
# ---- Text Line 1 ----
# Default text drawing mode is fill. Draw the text at (10, 400).
Quartz.CGContextShowTextAtPoint(context, 10, 400, fillText, len(fillText))
# Set the fill color to red.
Quartz.CGContextSetFillColor(context, opaqueRed)
Quartz.CGContextSetTextPosition(context, 180, 400)
Quartz.CGContextShowText(context, fillText, len(fillText))
# Translate down for the next line of text.
Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
# ---- Text Line 2 ----
# Now stroke the text by setting the text drawing mode
# to kCGTextStroke. When stroking text, Quartz uses the stroke
# color in the graphics state.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextStroke)
Quartz.CGContextShowTextAtPoint(context, 10, 400, strokeText, len(strokeText))
# When stroking text, the line width and other gstate parameters
# that affect stroking affect text stroking as well.
Quartz.CGContextSetLineWidth(context, 2)
Quartz.CGContextSetLineDash(context, 0, dash, 2)
Quartz.CGContextSetTextPosition(context, 180, 400)
Quartz.CGContextShowText(context, strokeText, len(strokeText))
# Reset the line dash and line width to their defaults.
Quartz.CGContextSetLineDash(context, 0, None, 0)
Quartz.CGContextSetLineWidth(context, 1)
# Translate down for the next line of text.
Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
# ---- Text Line 3 ----
# Set the text drawing mode so that text is both filled and
# stroked. This produces text that is filled with the fill
# color and stroked with the stroke color.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFillStroke)
Quartz.CGContextShowTextAtPoint(
context, 10, 400, fillAndStrokeText, len(fillAndStrokeText)
)
# Now draw again with a thicker stroke width.
Quartz.CGContextSetLineWidth(context, 2)
Quartz.CGContextSetTextPosition(context, 180, 400)
Quartz.CGContextShowText(context, fillAndStrokeText, len(fillAndStrokeText))
Quartz.CGContextSetLineWidth(context, 1)
Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
# ---- Text Line 4 ----
# Set the text drawing mode to invisible so that the next piece of
# text does not appear. Quartz updates the text position as
# if it had been drawn.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextInvisible)
Quartz.CGContextShowTextAtPoint(context, 10, 400, invisibleText, len(invisibleText))
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFill)
Quartz.CGContextSetTextPosition(context, 180, 400)
Quartz.CGContextShowText(context, fillText, len(fillText))
Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
# ---- Text Line 5 ----
Quartz.CGContextSaveGState(context)
if 1:
# Use the text as a clipping path.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextClip)
Quartz.CGContextShowTextAtPoint(context, 10, 400, clipText, len(clipText))
# Position and draw a grid of lines.
Quartz.CGContextTranslateCTM(context, 10, 400)
drawGridLines(context)
Quartz.CGContextRestoreGState(context)
Quartz.CGContextSaveGState(context)
if 1:
# The current text position is that after the last piece
# of text has been drawn. Since CGContextSaveGState/
# CGContextRestoreGState do not affect the text position or
# the text matrix, the text position is that after the last
# text was "drawn", that drawn with the kCGTextClip mode
# above. This is where the next text drawn will go if it
# isn't explicitly positioned.
nextTextPosition = Quartz.CGContextGetTextPosition(context)
# Draw so that the text is filled, stroked, and then used
# the clip subsequent drawing.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextFillStrokeClip)
# Explicitly set the text position.
Quartz.CGContextSetTextPosition(context, 180, 400)
nextTextPosition = Quartz.CGContextGetTextPosition(context)
Quartz.CGContextShowText(context, fillStrokeClipText, len(fillStrokeClipText))
# Adjust the location of the grid lines so that they overlap the
# text just drawn.
Quartz.CGContextTranslateCTM(context, nextTextPosition.x, nextTextPosition.y)
# Draw the grid lines clipped by the text.
drawGridLines(context)
Quartz.CGContextRestoreGState(context)
# showFlippedTextAtPoint is a cover routine for Quartz.CGContextShowText
# that is useful for drawing text in a coordinate system where the y axis
# is flipped relative to the default Quartz coordinate system.
#
# This code assumes that the text matrix is only used to
# flip the text, not to perform scaling or any other
# possible use of the text matrix.
#
# This function preserves the a, b, c, and d components of
# the text matrix across its execution but updates the
# tx, ty components (the text position) to reflect the
# text just drawn. If all the text you draw is flipped, it
# isn't necessary to continually set the text matrix. Instead
# you could simply call CGContextSetTextMatrix once with
# the flipped matrix each time your drawing
# code is called.
def showFlippedTextAtPoint(c, x, y, text, textLen):
t = Quartz.CGAffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)
# Get the existing text matrix.
s = Quartz.CGContextGetTextMatrix(c)
# Set the text matrix to the one that flips in y.
Quartz.CGContextSetTextMatrix(c, t)
# Draw the text at the point.
Quartz.CGContextShowTextAtPoint(c, x, y, text, textLen)
# Get the updated text position.
p = Quartz.CGContextGetTextPosition(c)
# Update the saved text matrix to reflect the updated
# text position.
s.tx = p.x
s.ty = p.y
# Reset to the text matrix in effect when this
# routine was called but with the text position updated.
Quartz.CGContextSetTextMatrix(c, s)
def drawQuartzTextWithTextMatrix(context):
fontSize = 60.0
extraLeading = 10.0
text = b"Quartz "
textlen = len(text)
# The Cocoa framework calls the draw method with an undefined
# value of the text matrix. It's best to set it to what is needed by
# this code. Initially that is the identity transform.
Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
# Set the font with the PostScript name "Times-Roman", at
# fontSize points, with the MacRoman encoding.
Quartz.CGContextSelectFont(
context, b"Times-Roman", fontSize, Quartz.kCGEncodingMacRoman
)
# ---- Text Line 1 ----
# Draw the text at (10, 600).
Quartz.CGContextShowTextAtPoint(context, 10, 600, text, textlen)
# Get the current text position. The text pen is at the trailing
# point from the text just drawn.
textPosition = Quartz.CGContextGetTextPosition(context)
# Set the text matrix to one that flips text in y and sets
# the text position to the user space coordinate (0,0).
t = Quartz.CGAffineTransformMake(1, 0, 0, -1, 0, 0)
Quartz.CGContextSetTextMatrix(context, t)
# Set the text position to the point where the previous text ended.
Quartz.CGContextSetTextPosition(context, textPosition.x, textPosition.y)
# Draw the text at the current text position. It will be drawn
# flipped in y, relative to the text drawn previously.
Quartz.CGContextShowText(context, text, textlen)
# ---- Text Line 2 ----
# Translate down for the next piece of text.
Quartz.CGContextTranslateCTM(context, 0, -(3 * fontSize + extraLeading))
Quartz.CGContextSaveGState(context)
if 1:
# Change the text matrix to {1, 0, 0, 3, 0, 0}, which
# scales text by a factor of 1 in x and 3 in y.
# This scaling doesn't affect any drawing other than text
# drawing since only text drawing is transformed by
# the text matrix.
t = Quartz.CGAffineTransformMake(1, 0, 0, 3, 0, 0)
Quartz.CGContextSetTextMatrix(context, t)
# This text is scaled relative to the previous text
# because of the text matrix scaling.
Quartz.CGContextShowTextAtPoint(context, 10, 600, text, textlen)
# This restores the graphics state to what it was at the time
# of the last Quartz.CGContextSaveGState, but since the text matrix
# isn't part of the Quartz graphics state, it isn't affected.
Quartz.CGContextRestoreGState(context)
# The text matrix isn't affected by Quartz.CGContextSaveGState and
# Quartz.CGContextRestoreGState. You can see this by observing that
# the next text piece appears immediately after the first piece
# and with the same text scaling as that text drawn with the
# text matrix established before we did CGContextRestoreGState.
Quartz.CGContextShowText(context, text, textlen)
# ---- Text Line 3 ----
# Translate down for the next piece of text.
Quartz.CGContextTranslateCTM(context, 0, -(fontSize + extraLeading))
# Reset the text matrix to the identity matrix.
Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
# Now draw text in a flipped coordinate system.
Quartz.CGContextSaveGState(context)
if 1:
# Flip the coordinate system to mimic a coordinate system with the origin
# at the top-left corner of a window. The new origin is at 600 units in
# +y from the old origin and the y axis now increases with positive y
# going down the window.
Quartz.CGContextConcatCTM(
context, Quartz.CGAffineTransformMake(1, 0, 0, -1, 0, 600)
)
# This text will be flipped along with the CTM.
Quartz.CGContextShowTextAtPoint(context, 10, 10, text, textlen)
# Obtain the user space coordinates of the current text position.
textPosition = Quartz.CGContextGetTextPosition(context)
# Draw text at that point but flipped in y.
showFlippedTextAtPoint(context, textPosition.x, textPosition.y, text, textlen)
Quartz.CGContextRestoreGState(context)
Shadings.py¶
import Quartz
import Utilities
def RedBlackRedRampEvaluate(info, input_value, output_value):
# The domain of this function is 0 - 1. For an input value of 0
# this function returns the color to paint at the start point
# of the shading. For an input value of 1 this function returns
# the color to paint at the end point of the shading. This
# is a 1 in, 4 out function where the output values correspond
# to an r,g,b,a color.
#
# For an RGB color space as the shading color space, this
# function evaluates to produce a blend from pure, opaque
# red at the start point to a pure opaque black at the
# midpoint, and back to pure opaque red at the end point.
return (
# The red component evaluates to 1 for an input value of 0
# (the start point of the shading). It smoothly reduces
# to zero at the midpoint of the shading (input value 0.5)
# and increases up to 1 at the endpoint of the shading (input
# value 1.0).
abs(1.0 - input_value[0] * 2),
# The green and blue components are always 0.
0,
0,
# The alpha component is 1 for the entire shading.
1,
)
def createFunctionForRGB(evaluationFunction):
# This is a 1 in, 4 out function for drawing shadings
# in a 3 component (plus alpha) color space. Shadings
# parameterize the endpoints such that the starting point
# represents the function input value 0 and the ending point
# represents the function input value 1.
domain = (0, 1)
# The range is the range for the output colors. For an rgb
# color space the values range from 0-1 for the r,g,b, and a
# components.
function_range = (
# The red component, min and max.
0,
1,
# The green component, min and max.
0,
1,
# The blue component, min and max.
0,
1,
# The alpha component, min and max.
0,
1,
)
# Dimension of domain is 1 and dimension of range is 4.
function = Quartz.CGFunctionCreate(
None, 1, domain, 4, function_range, evaluationFunction
)
if function is None:
print("Couldn't create the CGFunction!")
return None
return function
def doSimpleAxialShading(context):
# This shading paints colors in the calibrated Generic RGB
# color space so it needs a function that evaluates 1 in to 4 out.
axialFunction = createFunctionForRGB(RedBlackRedRampEvaluate)
if axialFunction is None:
return
# Start the shading at the point (20,20) and
# end it at (420,20). The axis of the shading
# is a line from (20,20) to (420,20).
startPoint = Quartz.CGPoint(x=20, y=20)
endPoint = Quartz.CGPoint(x=420, y=20)
# Don't extend this shading.
extendStart = extendEnd = False
shading = Quartz.CGShadingCreateAxial(
Utilities.getTheCalibratedRGBColorSpace(),
startPoint,
endPoint,
axialFunction,
extendStart,
extendEnd,
)
# The shading retains the function and this code
# is done with the function so it should release it.
del axialFunction
if shading is None:
print("Couldn't create the shading!")
return
# Draw the shading. This paints the shading to
# the destination context, clipped by the
# current clipping area.
Quartz.CGContextDrawShading(context, shading)
def RedGreenRampEvaluate(info, input_value, output_value):
# The domain of this function is 0 - 1. For an input value of 0
# this function returns the color to paint at the start point
# of the shading. For an input value of 1 this function returns
# the color to paint at the end point of the shading. This
# is a 1 in, 4 out function where the output values correspond
# to an r,g,b,a color.
#
# For an RGB color space as the shading color space, this
# function evaluates to produce a blend from pure, opaque
# red at the start point to a pure opaque green at the end point.
return (
# The red component starts at 1 and reduces to zero as the input
# goes from 0 (the start point of the shading) and increases
# to 1 (the end point of the shading).
1.0 - input_value[0],
# The green component starts at 0 for an input of 0
# (the start point of the shading) and increases to 1
# for an input value of 1 (the end point of the shading).
input_value[0],
# The blue component is always 0.
0,
# The alpha component is always 1, the shading is always opaque.
1,
)
def doExampleAxialShading(context):
rect = Quartz.CGRectMake(0, 0, 240, 240)
# This shading paints colors in the calibrated Generic RGB
# color space so it needs a function that evaluates 1 in to 4 out.
redGreenFunction = createFunctionForRGB(RedGreenRampEvaluate)
if redGreenFunction is None:
return
# Start the shading at the point (20,20) and
# end it at (220,220). The axis of the shading
# is a diagonal line from (20,20) to (220,220).
startPoint = Quartz.CGPoint(x=20, y=20)
endPoint = Quartz.CGPoint(x=220, y=220)
# Don't extend this shading.
extendStart = extendEnd = False
shading = Quartz.CGShadingCreateAxial(
Utilities.getTheCalibratedRGBColorSpace(),
startPoint,
endPoint,
redGreenFunction,
extendStart,
extendEnd,
)
if shading is None:
print("Couldn't create the shading!")
return
# Position for the first portion of the drawing.
Quartz.CGContextTranslateCTM(context, 40, 260)
# Stroke a black rectangle that will frame the shading.
Quartz.CGContextSetLineWidth(context, 2)
Quartz.CGContextSetStrokeColorWithColor(context, Utilities.getRGBOpaqueBlackColor())
Quartz.CGContextStrokeRect(context, rect)
Quartz.CGContextSaveGState(context)
if 1:
# Clip to the rectangle that was just stroked.
Quartz.CGContextClipToRect(context, rect)
# Draw the shading. This paints the shading to
# the destination context, clipped to rect.
Quartz.CGContextDrawShading(context, shading)
# Release the shading once the code is finished with it.
del shading
# Restore the graphics state so that the rectangular
# clip is no longer present.
Quartz.CGContextRestoreGState(context)
# Prepare for the next shading.
Quartz.CGContextTranslateCTM(context, 0, -250)
# Extend this shading.
extendStart = extendEnd = True
shading = Quartz.CGShadingCreateAxial(
Utilities.getTheCalibratedRGBColorSpace(),
startPoint,
endPoint,
redGreenFunction,
extendStart,
extendEnd,
)
# The shading retains the function and this code
# is done with the function so it should release it.
del redGreenFunction
if shading is None:
print("Couldn't create the shading!")
return
# Stroke with the current stroke color.
Quartz.CGContextStrokeRect(context, rect)
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextClipToRect(context, rect)
# Draw the shading. This paints the shading to
# the destination context, clipped to rect.
Quartz.CGContextDrawShading(context, shading)
Quartz.CGContextRestoreGState(context)
# Now paint some text with a shading.
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextTranslateCTM(context, 260, 0)
Quartz.CGContextSetTextMatrix(context, Quartz.CGAffineTransformIdentity)
# Set the font with the PostScript name "Times-Roman", at
# 80 points, with the MacRoman encoding.
Quartz.CGContextSelectFont(
context, b"Times-Roman", 80, Quartz.kCGEncodingMacRoman
)
# Rotate so that the text characters are rotated
# relative to the page.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
# Set the text drawing mode to clip so that
# the characters in the string are intersected with
# the clipping area.
Quartz.CGContextSetTextDrawingMode(context, Quartz.kCGTextClip)
Quartz.CGContextShowTextAtPoint(context, 30, 0, b"Shading", 7)
# At this point nothing has been painted; the
# glyphs in the word "Shading" have been intersected
# with the previous clipping area to create a new
# clipping area.
# Rotate the coordinate system back so that the
# shading is not rotated relative to the page.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(-45))
# Draw the shading, painting the shading
# to the destination context, clipped by the glyphs.
Quartz.CGContextDrawShading(context, shading)
Quartz.CGContextRestoreGState(context)
# Release the shading once the code is finished with it.
del shading
class MyStartEndColor:
def __init__(self):
self.startColor = [0.0] * 3
self.endColor = [0.0] * 3
def StartColorEndColorEvaluate(info, input_value, output_value):
# The domain of this function is 0 - 1. For an input value of 0
# this function returns the color to paint at the start point
# of the shading. For an input value of 1 this function returns
# the color to paint at the end point of the shading. This
# is a 1 in, 4 out function where the output values correspond
# to an r,g,b,a color.
#
# This function evaluates to produce a blend from startColor to endColor.
#
# Note that the returned results are clipped to the range
# by Quartz so this function doesn't worry about values
# that are outside the range 0-1.
# Weight the starting and ending color components depending
# on what position in the blend the input value specifies.
return (
(info.startColor[0] * (1 - input_value[0]) + info.endColor[0] * input_value[0]),
(info.startColor[1] * (1 - input_value[0]) + info.endColor[1] * input_value[0]),
(info.startColor[2] * (1 - input_value[0]) + info.endColor[2] * input_value[0]),
# The alpha component is always 1, the shading is always opaque.
1,
)
def createFunctionWithStartEndColorRamp(startColor, endColor):
# Use a pointer to a MyStartEndColor as a way of
# parameterizing the color ramp this function produces.
startEndColorP = MyStartEndColor()
# Set up start and end colors in the info structure.
startEndColorP.startColor[0] = startColor[0]
startEndColorP.startColor[1] = startColor[1]
startEndColorP.startColor[2] = startColor[2]
startEndColorP.endColor[0] = endColor[0]
startEndColorP.endColor[1] = endColor[1]
startEndColorP.endColor[2] = endColor[2]
# This is a 1 in, 4 out function for drawing shadings
# in a 3 component (plus alpha) color space. Shadings
# parameterize the endpoints such that the starting point
# represents the function input value 0 and the ending point
# represents the function input value 1.
domain = (0, 1)
# The range is the range for the output colors. For an rgb
# color space the values range from 0-1 for the r,g,b, and a
# components.
function_range = (
# The red component, min and max.
0,
1,
# The green component, min and max.
0,
1,
# The blue component, min and max.
0,
1,
# The alpha component, min and max.
0,
1,
)
# Pass startEndColorP as the info parameter.
function = Quartz.CGFunctionCreate(
startEndColorP, 1, domain, 4, function_range, StartColorEndColorEvaluate
)
if function is None:
print("Couldn't create the CGFunction!")
return None
return function
def doSimpleRadialShading(context):
startColor = [0.663, 0.0, 0.031] # Red.
endColor = [1.0, 0.8, 0.4] # Light yellow.
# This function describes a color ramp where the starting color
# is red and the ending color is blue.
redYellowFunction = createFunctionWithStartEndColorRamp(startColor, endColor)
if redYellowFunction is None:
return
Quartz.CGContextTranslateCTM(context, 120, 120)
# Circles whose origin is the same.
circleACenter = Quartz.CGPoint(x=0, y=0)
circleBCenter = circleACenter
# The starting circle is inside the ending circle.
circleARadius = 50
circleBRadius = 100
# Don't extend the shading.
extendStart = extendEnd = False
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
redYellowFunction,
extendStart,
extendEnd,
)
del redYellowFunction
if shading is None:
print("Couldn't create the shading!")
return
Quartz.CGContextDrawShading(context, shading)
def doExampleRadialShadings(context):
magenta = [1, 0, 1] # Pure magenta.
magenta30 = [0.3, 0, 0.3] # 30% magenta.
black = [0, 0, 0]
red = [1, 0, 0]
green = [0, 1, 0]
blue = [0, 0, 1]
redgreen = [0.66, 1, 0.04] # A red-green shade.
Quartz.CGContextTranslateCTM(context, 120, 550)
# This function describes a color ramp where the starting color
# is a full magenta, the ending color is 30% magenta.
magentaFunction = createFunctionWithStartEndColorRamp(magenta, magenta30)
if magentaFunction is None:
print("Couldn't create the magenta function!")
return
# Shading 1. Circle A is completely inside circle B but with
# different origins. Circle A has radius 0 which produces
# a point source.
# The center of circle A is offset from the origin.
circleACenter = Quartz.CGPoint(x=30, y=40)
# The center of circle B is at the origin.
circleBCenter = Quartz.CGPoint(x=0, y=0)
# A radius of zero produces a point source.
circleARadius = 0
circleBRadius = 100
# Don't extend the shading.
extendStart = extendEnd = False
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
magentaFunction,
extendStart,
extendEnd,
)
# Finished with the magenta function so release it.
del magentaFunction
if shading is None:
print("Couldn't create the shading!")
return
Quartz.CGContextDrawShading(context, shading)
# Finished with the shading so release it.
del shading
# Shading 2. Circle A is completely inside
# circle B but with different origins.
# The starting color is red and the ending color is green.
redGreenFunction = createFunctionWithStartEndColorRamp(red, green)
if redGreenFunction is None:
print("Couldn't create the red-Green function!")
return
circleACenter.x = 55
circleACenter.y = 70
circleBCenter.x = 20
circleBCenter.y = 0
circleARadius = 10
# The outer circle is outside the clipping path so the
# color at the edge of the shape is not
# that at the radius of the outer circle.
circleBRadius = 200
# Extend the end point of this shading.
extendStart = False
extendEnd = True
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
redGreenFunction,
extendStart,
extendEnd,
)
# Finished with this function so release it.
del redGreenFunction
if shading is None:
print("Couldn't create the shading!")
return
# Set a clipping area to bound the extend. This code
# sets a clipping area that corresponds to a circular
# wedge. The starting circle is inside the clipping
# area and the ending circle is outside.
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextTranslateCTM(context, 250, 0)
Quartz.CGContextBeginPath(context)
Quartz.CGContextMoveToPoint(context, 25, 0)
Quartz.CGContextAddArc(
context,
25,
0,
130,
Utilities.DEGREES_TO_RADIANS(30),
Utilities.DEGREES_TO_RADIANS(-30),
0,
)
Quartz.CGContextClip(context)
# Paint the shading.
Quartz.CGContextDrawShading(context, shading)
# Finished with the shading so release it.
del shading
Quartz.CGContextRestoreGState(context)
Quartz.CGContextTranslateCTM(context, -40, -250)
# Shading 3. The starting circle is completely outside
# the ending circle, no extension. The circles
# have the same radii.
circleACenter.x = 0
circleACenter.y = 0
circleBCenter.x = 125
circleBCenter.y = 0
circleARadius = 50
circleBRadius = 50
extendStart = extendEnd = False
# Create a function that paints a red to black ramp.
redBlackFunction = createFunctionWithStartEndColorRamp(red, black)
if redBlackFunction is None:
print("Couldn't create the red-black function!")
return
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
redBlackFunction,
extendStart,
extendEnd,
)
if shading is None:
print("Couldn't create the shading!")
return
Quartz.CGContextDrawShading(context, shading)
# Finished with the shading so release it.
del shading
# Shading 4. The starting circle is completely outside
# the ending circle. The circles have different radii.
circleACenter.x = 120
circleACenter.y = 0
circleBCenter.x = 0
circleBCenter.y = 0
circleARadius = 75
circleBRadius = 30
# Extend at the start and end.
extendStart = extendEnd = True
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
redBlackFunction,
extendStart,
extendEnd,
)
# Finished with this function so release it.
del redBlackFunction
if shading is None:
print("Couldn't create the shading!")
return
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextTranslateCTM(context, 270, 0)
# Clip to an elliptical path so the shading
# does not extend to infinity at the larger end.
Quartz.CGContextBeginPath(context)
Utilities.myCGContextAddEllipseInRect(
context, Quartz.CGRectMake(-200, -200, 450, 400)
)
Quartz.CGContextClip(context)
Quartz.CGContextDrawShading(context, shading)
# Finished with the shading so release it.
del shading
Quartz.CGContextRestoreGState(context)
Quartz.CGContextTranslateCTM(context, 30, -200)
# The starting color is blue, the ending color is a red-green color.
blueGreenFunction = createFunctionWithStartEndColorRamp(blue, redgreen)
if blueGreenFunction is None:
print("Couldn't create the blue-Green function!")
return
# Shading 5. The circles partially overlap and have
# different radii with the larger circle at the start.
circleACenter.x = 0
circleACenter.y = 0
circleBCenter.x = 90
circleBCenter.y = 30
circleARadius = 75
circleBRadius = 45
extendStart = extendEnd = False
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
blueGreenFunction,
extendStart,
extendEnd,
)
if shading is None:
print("Couldn't create the shading!")
return
Quartz.CGContextDrawShading(context, shading)
# Finished with the shading so release it.
del shading
Quartz.CGContextTranslateCTM(context, 200, 0)
# Shading 6. The circles partially overlap and have
# different radii with the larger circle at the end.
circleARadius = 45
circleBRadius = 75
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
blueGreenFunction,
extendStart,
extendEnd,
)
# Finished with this function so release it.
del blueGreenFunction
if shading is None:
print("Couldn't create the shading!")
return
Quartz.CGContextDrawShading(context, shading)
def doEllipseShading(context):
black = [0, 0, 0]
red = [1, 0, 0]
# This function describes a color ramp where the starting color
# is red, the ending color is black.
redBlackFunction = createFunctionWithStartEndColorRamp(red, black)
if redBlackFunction is None:
print("Couldn't create the red-black function!")
return
Quartz.CGContextTranslateCTM(context, 100, 300)
# Shading 1.
# To obtain an elliptical shading requires that user space
# at the time the shading is painted is transformed so that
# the circles which define the radial shading geometry are
# rotated and elliptical. User space will be rotated
# by 45 degrees, then scaled by 1 in x and 2 in y to produce
# the ellipses.
# Compute the transform needed to create the rotated ellipses.
t = Quartz.CGAffineTransformMakeRotation(Utilities.DEGREES_TO_RADIANS(45))
t = Quartz.CGAffineTransformScale(t, 1, 2)
circleACenter = Quartz.CGPoint(x=0, y=0)
circleBCenter = Quartz.CGPoint(x=circleACenter.x + 144, y=circleACenter.y)
circleARadius = 45
circleBRadius = 45
# Don't extend this shading.
extendStart = extendEnd = False
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
redBlackFunction,
extendStart,
extendEnd,
)
if shading is None:
# Couldn't create the shading so release
# the function before returning.
print("Couldn't create the shading!")
return
Quartz.CGContextSaveGState(context)
if 1:
# Transform coordinates for the drawing of the shading.
# This transform produces the rotated elliptical shading.
# This produces the left shading in the figure, the
# one where both the ellipses and the shading are
# rotated relative to default user space.
Quartz.CGContextConcatCTM(context, t)
Quartz.CGContextDrawShading(context, shading)
del shading
Quartz.CGContextRestoreGState(context)
Quartz.CGContextTranslateCTM(context, 300, 10)
# Shading 2.
# Now draw the shading where the shading ellipses are
# rotated but the axis between the origins of
# the ellipses lies parallel to the x axis in default
# user space. This is similar to the shading drawn
# manually in Chapter 5.
#
# To compute the correct origins for the shading,
# the code needs to compute the points that,
# transformed by the matrix t used to paint the shading,
# produce the desired coordinates. We want coordinates
# that are transformed as follows:
#
# P' = P x t
#
# where P' is the point in untransformed user space that
# we want as the origin, P is the point in transformed
# user space that will be transformed by t, the matrix
# which transforms the circles into rotated ellipses.
#
# So we want to calculate P such that P' = P x t .
#
# Notice that if P = P' x Inverse(t) then:
#
# P' = P' x Inverse(t) x t = P' x Identity = P'.
#
# This means that we can calculate the point P
# by computing P' x Inverse(t).
inverseT = Quartz.CGAffineTransformInvert(t)
# Now the code can transform the coordinates through the
# inverse transform to compute the new coordinates. These
# coordinates, when transformed with the transform t,
# produce the original coordinate.
circleACenter = Quartz.CGPointApplyAffineTransform(circleACenter, inverseT)
circleBCenter = Quartz.CGPointApplyAffineTransform(circleBCenter, inverseT)
shading = Quartz.CGShadingCreateRadial(
Utilities.getTheCalibratedRGBColorSpace(),
circleACenter,
circleARadius,
circleBCenter,
circleBRadius,
redBlackFunction,
extendStart,
extendEnd,
)
# The code is finished with the function so release it.
del redBlackFunction
if shading is None:
print("Couldn't create the shading!")
return
# Transform coordinates for the drawing of the shading.
# This transform produces the rotated elliptical shading.
Quartz.CGContextConcatCTM(context, t)
Quartz.CGContextDrawShading(context, shading)
ShadowsAndTransparencyLayers.py¶
import Quartz
import Utilities
def scaleShadowOffset(offset):
shadowScaling = Utilities.getScalingFactor()
# Adjust the shadow offset if scaling to export as bits. This is
# equivalent to scaling base space by the scaling factor.
if shadowScaling != 1.0:
offset = Quartz.CGSizeApplyAffineTransform(
offset, Quartz.CGAffineTransformMakeScale(shadowScaling, shadowScaling)
)
return offset
def createTrianglePath(context):
Quartz.CGContextBeginPath(context)
Quartz.CGContextMoveToPoint(context, 0, 0)
Quartz.CGContextAddLineToPoint(context, 50, 0)
Quartz.CGContextAddLineToPoint(context, 25, 50)
Quartz.CGContextClosePath(context)
def drawSimpleShadow(context):
r = Quartz.CGRectMake(20, 20, 100, 200)
Quartz.CGContextTranslateCTM(context, 20, 300)
# A blur of 0 is a hard edge blur.
blur = 0
# An offset where both components are negative casts a shadow to the
# left and down from the object. The coordinate system for the offset
# is base space, not current user space.
offset = Quartz.CGSize(-7, -7)
offset = scaleShadowOffset(offset)
# Set the shadow in the context.
Quartz.CGContextSetShadow(context, offset, blur)
# Object 1.
# Paint a rectangle.
Quartz.CGContextFillRect(context, r)
# Object 2.
Quartz.CGContextTranslateCTM(context, 150, 0)
# A blur of 3 is a soft blur more
# appropriate for a shadow effect.
blur = 3
Quartz.CGContextSetShadow(context, offset, blur)
# Fill an ellipse to the right of the rect.
Quartz.CGContextBeginPath(context)
Utilities.myCGContextAddEllipseInRect(context, r)
Quartz.CGContextFillPath(context)
# Object 3.
Quartz.CGContextTranslateCTM(context, -130, -140)
# Scale the coordinate system but the shadow is not affected. The offset
# is in the base space of the context. Typically it looks best if the shapes
# have a uniform shadow regardless of how the shapes were created, scaled,
# rotated, or otherwise transformed.
Quartz.CGContextScaleCTM(context, 2, 2)
createTrianglePath(context)
Quartz.CGContextSetStrokeColorWithColor(context, Utilities.getRGBOpaqueRedColor())
Quartz.CGContextSetLineWidth(context, 5)
# Stroking produces a shadow as well.
Quartz.CGContextStrokePath(context)
# Object 4.
Quartz.CGContextTranslateCTM(context, 75, 0)
createTrianglePath(context)
# Cast the shadow to the left and up from
# the shape painted.
offset.width = -5
offset.height = +7
offset = scaleShadowOffset(offset)
# The shadow can be colored. Create a CGColorRef
# that represents a red color with opacity of 0.3333...
shadowColor = Quartz.CGColorCreateCopyWithAlpha(
Utilities.getRGBOpaqueRedColor(), 1.0 / 3.0
)
Quartz.CGContextSetShadowWithColor(context, offset, blur, shadowColor)
Quartz.CGContextStrokePath(context)
# Object 5. Three stroked circles.
Quartz.CGContextTranslateCTM(context, -75, -65)
# Set a black shadow offset at -7,-7.
offset.width = -7
offset.height = -7
offset = scaleShadowOffset(offset)
Quartz.CGContextSetShadow(context, offset, blur)
# Draw a set of three circles side by side.
Quartz.CGContextBeginPath(context)
Quartz.CGContextSetLineWidth(context, 3)
r = Quartz.CGRectMake(30, 20, 20, 20)
Utilities.myCGContextAddEllipseInRect(context, r)
r = Quartz.CGRectOffset(r, 20, 0)
Utilities.myCGContextAddEllipseInRect(context, r)
r = Quartz.CGRectOffset(r, 20, 0)
Utilities.myCGContextAddEllipseInRect(context, r)
Quartz.CGContextStrokePath(context)
def doShadowScaling(context):
offset = Quartz.CGSize(-7, -7)
blur = 3
Quartz.CGContextTranslateCTM(context, 20, 220)
Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
# Object 1
# Draw a triangle filled with black and shadowed with black.
createTrianglePath(context)
Quartz.CGContextFillPath(context)
# Object 2
# Scaling without changing the shadow doesn't impact
# the shadow offset or blur.
t = Quartz.CGAffineTransformMakeScale(2, 2)
Quartz.CGContextConcatCTM(context, t)
Quartz.CGContextTranslateCTM(context, 40, 0)
createTrianglePath(context)
Quartz.CGContextFillPath(context)
# Object 3
# By transforming the offset you can transform the shadow.
# This may be desirable if you are drawing a zoomed view.
offset = Quartz.CGSizeApplyAffineTransform(offset, t)
Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
Quartz.CGContextTranslateCTM(context, 70, 0)
createTrianglePath(context)
Quartz.CGContextFillPath(context)
def drawFillAndStrokeWithShadow(context):
r = Quartz.CGRectMake(60, 60, 100, 100)
offset = Quartz.CGSize(-7, -7)
blur = 3
# Set the shadow.
Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueOrangeColor())
# Draw the graphic on the left.
Quartz.CGContextBeginPath(context)
Utilities.myCGContextAddEllipseInRect(context, r)
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# Draw the graphic on the right.
r = Quartz.CGRectOffset(r, 125, 0)
# Begin the transparency layer.
Quartz.CGContextBeginTransparencyLayer(context, None)
if 1:
Utilities.myCGContextAddEllipseInRect(context, r)
Quartz.CGContextDrawPath(context, Quartz.kCGPathFillStroke)
# End the transparency layer.
Quartz.CGContextEndTransparencyLayer(context)
def drawColoredLogo(context):
r = Quartz.CGRectMake(0, 0, 100, 100)
Quartz.CGContextSaveGState(context)
if 1:
# Position the center of the rectangle on the left.
Quartz.CGContextTranslateCTM(context, 140, 140)
# Rotate so that the rectangles are rotated 45 degrees
# about the current coordinate origin.
Quartz.CGContextRotateCTM(context, Utilities.DEGREES_TO_RADIANS(45))
# Translate so that the center of the rect is at the previous origin.
Quartz.CGContextTranslateCTM(context, -r.size.width / 2, -r.size.height / 2)
# Set the fill color to a purple color.
Quartz.CGContextSetFillColorWithColor(
context, Utilities.getRGBOpaquePurpleColor()
)
# Fill the first rectangle.
Quartz.CGContextFillRect(context, r)
# Position to draw the right-most rectangle.
Quartz.CGContextTranslateCTM(context, 60, -60)
# Set the fill color to a yellow color.
Quartz.CGContextSetFillColorWithColor(
context, Utilities.getRGBOpaqueYellowColor()
)
Quartz.CGContextFillRect(context, r)
# Position for the center rectangle.
Quartz.CGContextTranslateCTM(context, -30, +30)
# Set the stroke color to an orange color.
Quartz.CGContextSetStrokeColorWithColor(
context, Utilities.getRGBOpaqueOrangeColor()
)
# Stroke the rectangle with a linewidth of 12.
Quartz.CGContextStrokeRectWithWidth(context, r, 12)
Quartz.CGContextRestoreGState(context)
def showComplexShadowIssues(context):
offset = Quartz.CGSize(-6, -6)
blur = 3
# Set the shadow.
Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
# Draw the colored logo.
drawColoredLogo(context)
def showComplexShadow(context):
offset = Quartz.CGSize(-6, -6)
blur = 3
# Set the shadow.
Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), blur)
# Begin a transparency layer. A snapshot is made of the graphics state and
# the shadow parameter is temporarily reset to no shadow, the blend mode
# is set to Normal, and the global alpha parameter is set to 1.0.
#
# All drawing that occurs after CGContextBeginTransparencyLayer but before
# CGContextEndTransparencyLayer is collected together and when
# CGContextEndTransparencyLayer is called, Quartz composites the collected
# drawing to the context, using the global alpha, blend mode, and shadow
# that was in effect when CGContextBeginTransparencyLayer was called.
Quartz.CGContextBeginTransparencyLayer(context, None)
# Draw the colored logo.
drawColoredLogo(context)
# Ending the transparency layer causes all drawing in the transparency
# layer to be composited with the global alpha, blend mode, and shadow
# in effect at the time CGContextBeginTransparencyLayer was called. The
# graphics state is restored to that in effect when
# CGContextBeginTransparencyLayer was called.
# This restores the graphics state to that in effect
# at the last call to CGContextBeginTransparencyLayer.
Quartz.CGContextEndTransparencyLayer(context)
def doLayerCompositing(context):
r = Quartz.CGRectMake(40, 50, 142, 180)
# Object 1.
Quartz.CGContextTranslateCTM(context, 20, 20)
Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueGreenColor())
# Draw a green background.
Quartz.CGContextFillRect(context, r)
# Draw the colored logo.
drawColoredLogo(context)
# Object 2.
Quartz.CGContextTranslateCTM(context, 300, 0)
Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueGreenColor())
# Draw a green background.
Quartz.CGContextFillRect(context, r)
# Draw the rectangles with opacity 0.75.
Quartz.CGContextSetAlpha(context, 0.75)
drawColoredLogo(context)
# Object 3.
Quartz.CGContextTranslateCTM(context, 300, 0)
# Set the alpha to 1.0 for drawing the background.
Quartz.CGContextSetAlpha(context, 1.0)
Quartz.CGContextSetFillColorWithColor(context, Utilities.getRGBOpaqueGreenColor())
Quartz.CGContextFillRect(context, r)
# Draw the rectangles with opacity 0.75.
Quartz.CGContextSetAlpha(context, 0.75)
# Begin a transparency layer. Drawing collected in
# this transparency layer will be composited with an
# alpha value of 0.75 when the transparency layer is ended.
Quartz.CGContextBeginTransparencyLayer(context, None)
if 1:
# Draw the colored logo into the transparency layer.
drawColoredLogo(context)
# Ending the transparency layer causes the drawing
# to then be composited with the global alpha value
# in effect when CGContextBeginTransparencyLayer was called.
Quartz.CGContextEndTransparencyLayer(context)
def shadowPDFDocument(context, url):
pdfDoc = Quartz.CGPDFDocumentCreateWithURL(url)
offset = Quartz.CGSize(-7, -7)
if pdfDoc is None:
print("Couldn't create PDF document reference!")
return
r = Quartz.CGPDFDocumentGetMediaBox(pdfDoc, 1)
r.origin.x = 20
r.origin.y = 20
# Set the shadow.
Quartz.CGContextSetShadow(context, scaleShadowOffset(offset), 3)
# On Tiger and later, there is no need to use
# a transparency layer to draw a PDF document as
# a grouped object. On Panther, you can do so
# by using a transparency layer. Drawing collected in
# this transparency layer is drawn with the shadow
# when the layer is ended.
Quartz.CGContextBeginTransparencyLayer(context, None)
if 1:
Quartz.CGContextDrawPDFDocument(context, r, pdfDoc, 1)
Quartz.CGContextEndTransparencyLayer(context)
UIHandling.py¶
# *** Drawing Basics ***
kHICommandSimpleRect = 1000
kHICommandStrokedRect = 1001
kHICommandStrokedAndFilledRect = 1002
kHICommandPathRects = 1003
kHICommandAlphaRects = 1004
kHICommandDashed = 1005
kHICommandSimpleClip = 1006
kHICommandPDFDoc = 1007
# *** Coordinate System ***
kHICommandRotatedEllipses = 1008
kHICommandDrawSkewCoordinates = 1082
# *** Path Drawing ***
kHICommandBezierEgg = 1009
kHICommandRoundedRects = 1010
kHICommandStrokeWithCTM = 1011
kHICommandRotatedEllipsesWithCGPath = 1012
kHICommandPixelAligned = 1099
# *** Color and GState ***
kHICommandDeviceFillAndStrokeColor = 1013
kHICommandCLUTDrawGraphics = 1014
kHICommandDrawWithGlobalAlpha = 1015
kHICommandDrawWithBlendMode = 1068
kHICommandDrawWithColorRefs = 1016
kHICommandFunctionsHaveOwnGSave = 1017
# *** Images ***
kHICommandDrawJPEGImage = 1018
kHICommandColorImageFromFile = 1019
kHICommandColorImageFromData = 1020
kHICommandColorImageFromCallbacks = 1021
kHICommandGrayRamp = 1022
kHICommandDrawWithCGImageSource = 1023
kHICommandDrawWithCGImageSourceIncremental = 1024
kHICommandDrawWithQuickTime = 1025
kHICommandSubstituteImageProfile = 1026
kHICommandDoSubImage = 1027
kHICommandExportWithQuickTime = 1028
# *** Image Masking ***
kHICommandMaskTurkeyImage = 1029
kHICommandImageMaskedWithMask = 1030
kHICommandImageMaskedWithGrayImage = 1031
kHICommandMaskImageWithColor = 1033
kHICommandClipToMask = 1080
kHICommandExportWithCGImageDestination = 1034
# *** Bitmap Graphics Context and CGLayerRef ***
kHICommandSimpleCGLayer = 1090
kHICommandAlphaOnlyContext = 1091
kHICommandDrawNoOffScreenImage = 1035
kHICommandDrawOffScreenImage = 1036
kHICommandDrawWithLayer = 1037
# *** Text ***
kHICommandQuartzRomanText = 1038
kHICommandQuartzTextModes = 1039
kHICommandQuartzTextMatrix = 1040
kHICommandDrawNSString = 1041
kHICommandDrawNSLayoutMgr = 1042
kHICommandDrawCustomNSLayoutMgr = 1043
# *** Advanced Drawing ***
kHICommandSimplePattern = 1050
kHICommandPatternMatrix = 1051
kHICommandPatternPhase = 1052
kHICommandUncoloredPattern = 1053
kHICommandDrawWithPDFPattern = 1054
kHICommandSimpleShadow = 1055
kHICommandShadowScaling = 1056
kHICommandShadowProblems = 1057
kHICommandComplexShadow = 1058
kHICommandMultipleShapeComposite = 1059
kHICommandFillAndStrokeWithShadow = 1085
kHICommandPDFDocumentShadow = 1060
kHICommandSimpleAxialShading = 1061
kHICommandExampleAxialShadings = 1062
kHICommandSimpleRadialShading = 1063
kHICommandExampleRadialShadings = 1064
kHICommandEllipseShading = 1065
# *** EPS drawing ***
kHICommandDoCompatibleEPS = 1066
Utilities.py¶
import math
import Cocoa
import Quartz
class ExportInfo:
command = None
fileType = None
useQTForExport = False
dpi = 1
def DEGREES_TO_RADIANS(degrees):
return degrees * math.pi / 180
gScalingFactor = 1.0
# These routines are used for getting the correct results when
# drawing shadows and patterns with Quartz and exporting the
# results as bits. This is a hack; in principle the scaling
# factor should be passed to the draw proc.
def setScalingFactor(scalingFactor):
global gScalingFactor
if scalingFactor > 0:
gScalingFactor = scalingFactor
def getScalingFactor():
return gScalingFactor
_appBundle = None
def getAppBundle():
global _appBundle
if _appBundle is None:
_appBundle = Cocoa.CFBundleGetMainBundle()
return _appBundle
#
# This version of getTheRGBColorSpace returns
# the DeviceRGB color space.
#
_deviceRGB = None
def getTheRGBColorSpace():
global _deviceRGB
# Set once, the first time this function is called.
if _deviceRGB is None:
_deviceRGB = Quartz.CGColorSpaceCreateDeviceRGB()
return _deviceRGB
_genericRGBColorSpace = None
def getTheCalibratedRGBColorSpace():
global _genericRGBColorSpace
if _genericRGBColorSpace is None:
_genericRGBColorSpace = Quartz.CGColorSpaceCreateWithName(
Quartz.kCGColorSpaceGenericRGB
)
return _genericRGBColorSpace
_genericGrayColorSpace = None
def getTheCalibratedGrayColorSpace():
global _genericGrayColorSpace
if _genericGrayColorSpace is None:
_genericGrayColorSpace = Quartz.CGColorSpaceCreateWithName(
Quartz.kCGColorSpaceGenericGray
)
return _genericGrayColorSpace
_genericSRGBColorSpace = None
def getTheSRGBColorSpace():
# This only works on 10.5 or later
global _genericSRGBColorSpace
if _genericSRGBColorSpace is None:
_genericSRGBColorSpace = Quartz.CGColorSpaceCreateWithName(
Quartz.kCGColorSpaceGenericRGB
) # XXX: should be GenericSRGB
return _genericSRGBColorSpace
def getTheDisplayColorSpace():
# This is a hack, basically here because the C implementation uses APIs that
# aren't wrapped yet.
return getTheRGBColorSpace()
_rgbWhite = None
def getRGBOpaqueWhiteColor():
global _rgbWhite
if _rgbWhite is None:
opaqueWhite = (1.0, 1.0, 1.0, 1.0)
_rgbWhite = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueWhite)
return _rgbWhite
_rgbBlack = None
def getRGBOpaqueBlackColor():
global _rgbBlack
if _rgbBlack is None:
opaqueBlack = (0, 0, 0, 1)
_rgbBlack = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBlack)
return _rgbBlack
_rgbGray = None
def getRGBOpaqueGrayColor():
global _rgbGray
if _rgbGray is None:
opaqueGray = (0.9, 0.9, 0.9, 1)
_rgbGray = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueGray)
return _rgbGray
_rgbRed = None
def getRGBOpaqueRedColor():
global _rgbRed
if _rgbRed is None:
opaqueRed = (0.663, 0, 0.031, 1)
_rgbRed = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueRed)
return _rgbRed
_rgbBlue = None
def getRGBOpaqueBlueColor():
global _rgbBlue
if _rgbBlue is None:
opaqueBlue = (0.482, 0.62, 0.871, 1)
_rgbBlue = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBlue)
return _rgbBlue
_rgbPurple = None
def getRGBOpaquePurpleColor():
global _rgbPurple
if _rgbPurple is None:
opaquePurple = (0.69, 0.486, 0.722, 1)
_rgbPurple = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaquePurple)
return _rgbPurple
_rgbDarkBlue = None
def getRGBOpaqueDarkBlueColor():
global _rgbDarkBlue
if _rgbDarkBlue is None:
opaqueDarkBlue = (0.11, 0.208, 0.451, 1)
_rgbDarkBlue = Quartz.CGColorCreate(
getTheCalibratedRGBColorSpace(), opaqueDarkBlue
)
return _rgbDarkBlue
_rgbBrown = None
def getRGBOpaqueBrownColor():
global _rgbBrown
if _rgbBrown is None:
opaqueBrown = (0.325, 0.208, 0.157, 1)
_rgbBrown = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueBrown)
return _rgbBrown
_rgbOrange = None
def getRGBOpaqueOrangeColor():
global _rgbOrange
if _rgbOrange is None:
opaqueOrange = (0.965, 0.584, 0.059, 1)
_rgbOrange = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueOrange)
return _rgbOrange
_rgbYellow = None
def getRGBOpaqueYellowColor():
global _rgbYellow
if _rgbYellow is None:
opaqueYellow = (1, 0.816, 0, 1)
_rgbYellow = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueYellow)
return _rgbYellow
_rgbGreen = None
def getRGBOpaqueGreenColor():
global _rgbGreen
if _rgbGreen is None:
opaqueGreen = (0.584, 0.871, 0.318, 1)
_rgbGreen = Quartz.CGColorCreate(getTheCalibratedRGBColorSpace(), opaqueGreen)
return _rgbGreen
_rgbDarkGreen = None
def getRGBOpaqueDarkGreenColor():
global _rgbDarkGreen
if _rgbDarkGreen is None:
opaqueDarkGreen = (0.404, 0.808, 0.239, 1)
_rgbDarkGreen = Quartz.CGColorCreate(
getTheCalibratedRGBColorSpace(), opaqueDarkGreen
)
return _rgbDarkGreen
def myCGContextAddEllipseInRect(context, r):
if hasattr(Quartz, "CGContextAddEllipseInRect"):
Quartz.CGContextAddEllipseInRect(context, r)
else:
# This is not a perfect emulation but is correct as long as there is
# not an open subpath already in the current path. In that case the
# CGContextClosePath here would not necessarily produce the desired
# result.
Quartz.CGContextSaveGState(context)
if 1:
# Translate to the center of the ellipse.
Quartz.CGContextTranslateCTM(
context, Quartz.CGRectGetMidX(r), Quartz.CGRectGetMidY(r)
)
# Scale by half the width and height of the rectangle
# bounding the ellipse.
Quartz.CGContextScaleCTM(context, r.size.width / 2, r.size.height / 2)
# Establish a current point at the first point
# on the ellipse. This ensures that there
# is no line segment connecting the previous
# current point to the first point on the subpath.
Quartz.CGContextMoveToPoint(context, 1, 0)
# Circular arc around the ellipse center with
# a radius that, when scaled by the CTM, produces
# the major and minor axes of the ellipse. Since
# CGContextAddEllipseInRect defines the direction
# of the path as clockwise, this routine will
# draw the arc clockwise also.
Quartz.CGContextAddArc(context, 0, 0, 1, 0, 2 * math.pi, 1)
Quartz.CGContextClosePath(context)
Quartz.CGContextRestoreGState(context)
# Routines that are useful for debugging.
def drawPoint(context, p):
Quartz.CGContextSaveGState(context)
if 1:
# Opaque black.
Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 0, 1)
Quartz.CGContextSetLineWidth(context, 5)
Quartz.CGContextSetLineCap(context, Quartz.kCGLineCapRound)
Quartz.CGContextMoveToPoint(context, p.x, p.y)
Quartz.CGContextAddLineToPoint(context, p.x, p.y)
Quartz.CGContextStrokePath(context)
Quartz.CGContextRestoreGState(context)
def printCTM(context):
t = Quartz.CGContextGetCTM(context)
print(f"CurrentCTM is {t!r}")
kTickLength = 5
kTickDistance = 72
kAxesLength = 20 * kTickDistance
def drawCoordinateAxes(context):
tickLength = kTickLength
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextBeginPath(context)
# Paint the x-axis in red.
Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
Quartz.CGContextMoveToPoint(context, -kTickLength, 0.0)
Quartz.CGContextAddLineToPoint(context, kAxesLength, 0.0)
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
# Paint the y-axis in blue.
Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1)
Quartz.CGContextMoveToPoint(context, 0, -kTickLength)
Quartz.CGContextAddLineToPoint(context, 0, kAxesLength)
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
# Paint the x-axis tick marks in red.
Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1)
for _ in range(2):
for t in range(0, kAxesLength, kTickDistance):
Quartz.CGContextMoveToPoint(context, t, -tickLength)
Quartz.CGContextAddLineToPoint(context, t, tickLength)
Quartz.CGContextDrawPath(context, Quartz.kCGPathStroke)
Quartz.CGContextRotateCTM(context, math.pi / 2.0)
# Paint the y-axis tick marks in blue.
Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1)
drawPoint(context, Quartz.CGPointZero)
Quartz.CGContextRestoreGState(context)
def drawDebuggingRect(context, rect):
Quartz.CGContextSaveGState(context)
if 1:
Quartz.CGContextSetLineWidth(context, 4.0)
# Draw opaque red from top-left to bottom-right.
Quartz.CGContextSetRGBStrokeColor(context, 1, 0, 0, 1.0)
Quartz.CGContextMoveToPoint(
context, rect.origin.x, rect.origin.y + rect.size.height
)
Quartz.CGContextAddLineToPoint(
context, rect.origin.x + rect.size.width, rect.origin.y
)
Quartz.CGContextStrokePath(context)
# Draw opaque blue from top-right to bottom-left.
Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 1, 1.0)
Quartz.CGContextMoveToPoint(
context, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height
)
Quartz.CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y)
Quartz.CGContextStrokePath(context)
# Opaque black.
Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 0, 1.0)
Quartz.CGContextStrokeRect(context, rect)
Quartz.CGContextRestoreGState(context)
main.py¶
# Make sure all code is loaded
import AppDrawing # noqa: F401
import BitmapContext # noqa: F401
import ColorAndGState # noqa: F401
import CoordinateSystem # noqa: F401
import DataProvidersAndConsumers # noqa: F401
import DrawingBasics # noqa: F401
import EPSPrinting # noqa: F401
import FrameworkTextDrawing # noqa: F401
import FrameworkUtilities # noqa: F401
import ImageMasking # noqa: F401
import Images # noqa: F401
import MyAppController # noqa: F401
import MyView # noqa: F401
import PathDrawing # noqa: F401
import PatternDrawing # noqa: F401
import PDFHandling # noqa: F401
import QuartzTextDrawing # noqa: F401
import Shadings # noqa: F401
import ShadowsAndTransparencyLayers # noqa: F401
import Utilities # noqa: F401
from PyObjCTools import AppHelper
AppHelper.runEventLoop()
setup.py¶
"""
Script for building the example.
Usage:
python3 setup.py py2app
"""
import os
from setuptools import setup
setup(
name="BasicDrawing",
app=["main.py"],
data_files=["English.lproj"]
+ [os.path.join("GraphicsFiles", fn) for fn in os.listdir("GraphicsFiles")],
setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-Quartz"],
)