Quartz 2D.pdf
(
201 KB
)
Pobierz
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
6/18/12
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
Quartz 2D drawing
Relevant resources
Documentation
Quartz 2D Programming Guide
Web
"coregraphics" tagged questions on Stack Overflow, sorted by most votes
"quartzgraphics" tagged questions on Stack Overflow, sorted by most votes
"quartz2d" tagged questions on Stack Overflow, sorted by most votes
Sample code Apple
QuartzDemo
Reflection
Sample code other
Core Plot framework
Sample code class
QuartzExamples
Quartz 2D
You can do a lot with the default user interface elements and their display properties, but sometimes you need to
create custom elements, do some 2D drawing onscreen, or even generate PDFs within your application. For this,
you will need to use the Quartz 2D API. Quartz 2D resides within the Core Graphics, so the two names are often
synonymous. In fact, many of the API functions we will discuss have the CG prefix for Core Graphics.
The Quartz API is procedural C (Carbon), rather than the
ObjectiveC
you're used to with higherlevel Cocoa. This
can lead to code that is more difficult to read and understand, so I often find myself referring to "cheat sheets" of
standard components.
One confusing item about Quartz drawing is the coordinate system. On the iPhone, the origin, (0,0), is in the
upperleft corner of the screen or view. On the Mac, and in the standard Quartz coordinate space, the origin is the
bottomleft corner of the screen or view. This is further complicated by the fact that UIViews flip their underlying
layers, inverting the standard Quartz coordinate space.
Graphics contexts
When you draw using Quartz functions, you need to supply a context to draw into. A context is a canvas on which
all of your drawing will be layered, in the order that you draw onto it. There are three primary types of contexts:
display contexts, PDF contexts, and bitmap contexts.
Display contexts
As we covered in the discussion about
views and controllers
, there is only one place within a UIView where you can
draw to the screen: within the
-drawRect:
method. You can only get a valid display context within this method. To
retrieve the current context, you use code like the following:
CGContextRef context = UIGraphicsGetCurrentContext();
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
1/7
6/18/12
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
The context is a
CGContextRef
, a pointer to a Core Foundation opaque type. Core Foundation opaque types are
similar to
ObjectiveC
classes in the way that they are managed, memorywise. Core Foundation
objects
created
using functions that have "create" in their name must be released at some point in your code using
CFRelease()
or
one of the more typespecific release functions. In this case, the context is not created or retained, so we don't need
to release it.
PDF contexts
In addition to drawing to the screen, you can use the same vectorbased Quartz drawing commands to create PDF
documents within your application. Setting up a PDF context can be done in one of two ways: setting it to
immediately write to a file, or configuring the context to write to data in memory. I prefer the latter approach,
because it lets you do things like attach NSData to an email message.
This is an example of creating a PDF contained within an NSData object:
NSMutableData *pdfData = [[NSMutableData alloc] init];
CGDataConsumerRef dataConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)pdfData);
const CGRect mediaBox = CGRectMake(0.0f, 0.0f, drawingWidth, drawingHeight);
CGContextRef pdfContext = CGPDFContextCreate(dataConsumer, &mediaBox, NULL);
UIGraphicsPushContext(pdfContext);
CGContextBeginPage(pdfContext, &mediaBox);
// Draw your content here
CGContextEndPage(pdfContext);
CGPDFContextClose(pdfContext);
UIGraphicsPopContext();
CGContextRelease(pdfContext);
CGDataConsumerRelease(dataConsumer);
There are a few things going on here. First, we create an NSMutableData instance and set it to be a data consumer
(a destination for the PDF context to write to). Because Core Graphics uses Core Foundation types, and not Cocoa
classes,
CGDataConsumerCreateWithCFData()
requires a
CFMutableDataRef
argument. We are able to simply cast
the NSMutableData class we created as this type, because NSData is a tollfree bridged class. What that means is
that it can be used in either Cocoa methods or Core Foundation functions without conversion between types.
After that, we set the page size of the PDF context (in points) and create a PDF context, using the data consumer we
set up before. We then make this the active context for drawing by using
UIGraphicsPushContext()
.
In this case, we are only creating a single page in the PDF we're drawing, so we begin the page, draw, then end the
page. If you wanted to do multiple pages, you could repeat this for each page.
Note that in order to avoid memory leaks, we release the context and data consumer when done with them. We use
typespecific release functions for these two
objects
we've created.
Another note is that all of this drawing will be done in the Quartz coordinate space, so if you've set up your drawing
routines to display correctly in an iPhone view, it will be flipped here. To counteract this flipping, you can place the
following within your drawing code (after
UIGraphicsPushContext()
):
CGContextTranslateCTM(context, 0.0f, self.frame.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
These are transforms, which I'll explain later.
In addition to saving to PDF contexts, you can also load PDFs for display. This is done using the
CGPDFDocument type. Apple provides sample code for loading PDF documents in the
Quartz 2D Programming
Guide
section "PDF Document Creation, Viewing, and Transforming".
Bitmap contexts
You can also draw directly to an image context, which is slightly simpler to set up than a PDF one:
UIGraphicsBeginImageContext(drawingSize);
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
2/7
6/18/12
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
CGContextRef context = UIGraphicsGetCurrentContext();
// Do your drawing here
UIImage *contextImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
In this case, we create an image context of the same size as our image, draw in it, and then extract an autoreleased
UIImage instance from the context. We can then use this image in a number of different ways, including creating a
JPEG out of it:
NSData *jpegData = UIImageJPEGRepresentation(contextImage, 0.9);
which returns an NSData instance containing the data for a JPEG impression at a 90% quality setting.
This does the same, only with PNG data:
NSData *pngData = UIImagePNGRepresentation(contextImage);
Colors and colorspaces
When dealing with colors in your Quartz drawing, it is possible to set the current colors for a context using the
commands
CGContextSetRGBStrokeColor(context, redComponent, greenComponent, blueComponent, alphaComponent);
CGContextSetRGBFillColor(context, redComponent, greenComponent, blueComponent, alphaComponent);
where the various color components are normalized to 1.0 (that is, they are floating point values that go from 0.0 to
1.0). Anecdotal evidence and limited testing indicates that setting colors in this fashion is slower than setting
colors based on preexisting color
objects
.
To create color
objects
, you first need to create a colorspace. I tend to use a shared instance for this, using code like
the following:
+ (CGColorSpaceRef)genericRGBSpace;
{
static CGColorSpaceRef space = NULL;
if (space == NULL)
{
space = CGColorSpaceCreateDeviceRGB();
}
return space;
}
I also like caching commonly used colors for performance. Again, shared instance code for this looks something
like the following:
+ (CGColorRef)redColor;
{
static CGColorRef red = NULL;
if (red == NULL)
{
CGFloat values[4] = {1.0, 0.0, 0.0, 1.0};
red = CGColorCreate([self genericRGBSpace], values);
}
return red;
}
With these kinds of class methods, you can use the faster CGContextSetStrokeColorWithColor() methods, and the
like:
CGContextSetStrokeColorWithColor(context, [MyClass redColor]);
CGContextSetFillColorWithColor(context, [MyClass redColor]);
Graphic states
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
3/7
6/18/12
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
By changing the fill and stroke color of a context, you are changing its state. Everything drawn after this point will
have that state applied to it (in this case, all paths will have a red stroke and fill). There are several other state
properties you can change. These include the shadow color, clipping path, and transform.
To store a graphics state to be retrieved later, you use the following:
CGContextSaveGState(context);
Then when you want to retrieve the previous graphics state, you use:
CGContextRestoreGState(context);
This lets you temporarily set graphics properties for a context, then be able to quickly return to the old values.
Shadow color
You can set it so that any paths drawn in your context have a shadow by using code like the following:
CGContextSetShadowWithColor( context, shadowOffset, shadowRadius, [shadowColor CGColor] );
where
shadowOffset
is a CGSize struct that contains values for how much to offset the shadow right and up, and
shadowRadius
is the radius to extend the shadow out from the path. The shadow can easily be made a glow by
using a nongreyscale color and by using a (o,o) offset on the shadow.
Again, everything drawn after this in the context will have the shadow or glow drawn for it.
Clipping path
There are times when you might want to clip your drawing to within a particular region. This is most common
when dealing with gradients. To do this, first create a path (see below for that) and then call
CGContextClip(context);
This will clip all drawing to within the path you have defined. Again, this is useful for drawing gradients like you'd
see in glossy highlights.
Masking
Similar to clipping, you can mask part of your drawing based on an image's alpha channel. You use code like to
following for this:
CGContextClipToMask(context, maskRect, alphaImage);
Transform
Transforms let you move, scale, rotate, and otherwise manipulate your drawing. The context has a current
transformation matrix (CTM), which contains within it all of the manipulations you've made to the context's
geometry. You can directly manipulate the current transformation matrix using code like the following:
CGContextTranslateCTM (context, shiftRight, shiftUp);
CGContextScaleCTM(context, 0.5f, 0.5f);
CGContextRotateCTM(context, M_PI / 4.0f);
These commands shift the drawing after this point in the context, scale it, and rotate it, respectively. Note that the
order in which you apply these commands matters, and altering that order can produce vastly different drawings.
You can also create 2D affine transforms, then apply the transforms all at once. We were introduced to these
transforms when we covered
views
. You can create a new affine transform based on an operation using
CGAffineTransformMakeTranslation()
,
CGAffineTransformMakeRotation()
, etc. These transforms can then be further
manipulated using
CGAffineTransformTranslate()
,
CGAffineTransformRotate()
, etc. An example of this is as follows:
CGAffineTransform affineTransform = CGAffineTransformMakeTranslation(10.0f, 20.0f);
affineTransform = CGAffineTransformRotate(affineTransform, M_PI / 4.0f);
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
4/7
6/18/12
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
This transform now contains the matrix required to translate drawing by (10.0, 20.0), then rotate it by 45 degrees.
To apply this transform to the context, you would use
CGContextConcatCTM(context, affineTransform);
As mentioned before, these transforms operate in the same way when you set them to the
transform
property of a
UIView.
You can also apply a transform to a CGPoint, CGSize, or CGRect struct using
CGPointApplyAffineTransform()
,
CGSizeApplyAffineTransform()
, and
CGRectApplyAffineTransform()
, respectively.
Drawing paths
Paths are vector elements drawn within a Quartz context. They can be simple or arbitrarily complex, with a stroke
and / or fill.
Another peculiarity about Quartz drawing comes in how paths are pixelaligned. When dealing with
views
and
layers, you align the origin of the view to integral (nonfractional) values in X and Y to ensure sharp rendering.
Quartz instead aligns elements midway between pixels. This means that you need to draw on halfpixel
coordinates (1.5, 2.5) in order to end up with sharply drawn paths, etc.
You can start a path with
CGContextBeginPath(context);
and end it with
CGContextClosePath(context);
To draw that path you use
CGContextDrawPath(context, kCGPathFillStroke);
where the last argument is the drawing mode:
kCGPathFill
,
kCGPathEOFill
,
kCGPathStroke
,
kCGPathFillStroke
, or
kCGPathEOFillStroke
. These control whether just the stroke, just the fill, both, or an alternate fill mode is used to
draw the path.
You can also create paths for use outside of drawing using a
CGMutablePathRef
type. The following is an example
of this:
CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, xCoordinate1, yCoordinate1);
CGPathAddCurveToPoint(curvedPath, NULL, xCoordinate2, yCoordinate2, xCoordinate2, yCoordinate1, xCoordinate2,
yCoordinate2);
// Use the path for something
CGPathRelease(curvedPath);
We will see a use for this when we cover
Core Animation
CAKeyframeAnimations.
Lines
Lines are drawn by first moving the path to the start point of the line, then adding a line segment to a point:
CGContextBeginPath(context);
CGContextMoveToPoint(context, 10.5f, 10.5f);
CGContextAddLineToPoint(context, 20.5f, 20.5f);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
The preceding code would draw just a line from (10.5, 10.5) to (20.5, 20.5).
For lines, there are a few additional context state elements of interest:
www.sunsetlakesoftware.com/sites/default/files/Spring2010CourseNotes/quartz 2d drawing.html
5/7
Plik z chomika:
piotrbard
Inne pliki z tego folderu:
Objective-C_for_Absolute_Beginners.pdf
(14232 KB)
Blocks Programming Topics - Apple (2011).pdf
(239 KB)
Wrox Professional iPhone and iPad Database Application Programming.pdf
(71970 KB)
Apress - Pro Core Data for iOS.pdf
(7871 KB)
AppleScript Language Guide.pdf
(2230 KB)
Inne foldery tego chomika:
AppleMagazine
Filmy
iATKOS ML2 10.8.2 (Build 12C54)
iMagazine
Inne
Zgłoś jeśli
naruszono regulamin