I hope to understand internals of CoreFoundation CGColor object with this research. I could find a sample definition of CGColor structure from free quartz project which seems to match the IOS declaration(relying on my researchs).
typedef struct CGColor {
CFRuntimeBase obj;
CFTypeID colorID;
CGColorSpaceRef colorSpace;
CGPatternRef pattern;
size_t numberOfComponents;
CGFloat *components;
} *CGColorRef;
(colorID field is named as nextID by free quartz but i think its intended as a unique identifier for the color by IOS so its not a kind of next identifier.)
A globally thread safe unique value is hold which is incremented by 1 for each CGColor object created and assigned to the colorID member. Only the undocumented CGColorGetIdentifier() function returns this value. (I have a guess about monotonically increasing id value, it may improve performance while translating between device to calibrated color lookups or vice versa.)
I have checked CoreGraphics and its resource libraries. I have found that only ripc_GetColor (libRIP.A.dylib) function calls the CGColorGetIdentifier() function.
Call stack for CGColorGetIdentifier;(with hope of helping to make inferences about colorID)
0 com.apple.CoreGraphics CGColorGetIdentifier + 0
1 libRIP.A.dylib ripc_GetColor + 112
2 libRIP.A.dylib ripc_DrawGlyphs + 1740
3 com.apple.CoreGraphics CGContextDelegateDrawGlyphs + 108
4 com.apple.CoreGraphics drawGlyphs + 284
5 com.apple.CoreGraphics CGContextShowGlyphsWithAdvances + 208
For the current color graphics context operation, ripc_GetColor() computes some transformations for the current stroke/fill color and it caches these transformations with the reference and colorID of this color.
So, for the next graphics context operation, ripc_GetColor() compares the previously cached and current reference and colorID values to skip color transformations which were already cached for the last graphics context operation.
We know that the reference(memory address) of a released object could be used while creating another object. So just checking the reference will not be enough that same color object is valid but we need to compare contents or some kind of hash value. So, we could use the unique identifier values for this purpose.
However, an identifier could be in use for a single object and its reference, so it is enough to compare only ids. But, both refs and ids are used. I don't think that engineers overlooked such a simple and crucial thing.
So, i try to find out the necessity of comparing both ids and refs while comparing just ids would be enough.
Is it left over from a previous approach so could not be abandoned totally?
If I understand correctly, you're asking why someone might implement a cache as
void DoSomethingWith(CGColorRef c)
{
static CGColorRef cached_c = NULL;
static CFTypeID cached_colorID;
if (c == cached_c && c->colorID == cached_colorID) ...
instead of merely
void DoSomethingWith(CGColorRef c)
{
static CFTypeID cached_colorID = 0;
if (c->colorID == cached_colorID) ...
? Well, two obvious reasons are
Random access memory isn't random access. Dereferencing c
is probably a slow operation (a cache miss wastes many nanoseconds), so if we can save those nanoseconds 90% of the time by prepending a cheap pointer comparison, let's do it.
How do you initialize cached_colorID
? In the first implementation above, if we assume that the user respects the API contract and always passes in a non-null c
, then once we know c
== cached_c
, then we also know that cached_c != NULL
and therefore we have a meaningful value in cached_colorID
. In the second implementation, if the first c
the user passes in happens to have c->colorID == 0
, then we'll incorrectly believe that we've seen it before, and madcap hijinks ensue.
I don't know if either of these is the reason Apple did what you're looking at... but they seem like solid possibilities, don't they?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With