CG, Contexts, Colors, Insanity

Discussion in 'iOS Programming' started by KnightWRX, Feb 22, 2012.

  1. KnightWRX macrumors Pentium


    Jan 28, 2009
    Quebec, Canada
    Now, I'm not quite so familiar with Quartz 2D and don't quite get this, if someone can explain it, it would be appreciated.

    The following code didn't work, except to display white text :

    UIFont * usedfont = ...
    	CGFloat color4f [] = ...
    	CGRect currentimage = ...;
    	CGColorSpaceRef colorSpace;
    	CGContextRef context;
    	colorSpace = CGColorSpaceCreateDeviceRGB();
    	context = CGBitmapContextCreate(imagePixels, 
    									width * bytesPerChannel,
    									kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    	CGContextTranslateCTM(context, 0.0f, height);
    	CGContextScaleCTM(context, 1.0f, -1.0f);
    	[B]CGContextSetFillColor(context, color4f);[/B]
    	[string drawInRect: currentimage withFont: usedfont lineBreakMode: UILineBreakModeWordWrap];
    Now, the bolded line, I simply changed it to the following, no other code changed in the function (I deleted a bunch of initialization here for readability) and everything suddenly works (text renders in any color I specify and pass to the function to initialize color4f) :

        CGContextSetFillColorWithColor(context, CGColorCreate(colorSpace, color4f));

    The difference I see is that I pass the actual colorSpace I made earlier to the function, but since I created the context with that color space anyhow, shouldn't CGContextSetFillColor() be able to get it from there and create its own CGColorRef object ?

    What's even the purpose of CGContextSetFillColor() if it doesn't work ?
  2. xStep macrumors 68000

    Jan 28, 2003
    Less lost in L.A.
    I don't know, but you left out a critical item, the setup of color4f which may influence the response to you. Perhaps CGColorCreate is clamping the values to the nearest correct values.
  3. PhoneyDeveloper macrumors 68040


    Sep 2, 2008
    Your code will be simplified if you use the various UI convenience routines to create a context and colors. Use UIColors.CGColor etc.
  4. KnightWRX thread starter macrumors Pentium


    Jan 28, 2009
    Quebec, Canada
    It's the same for both, that code didn't change nor did the values passed to it. Basically, I was passing it 0.0, 0.0, 0.0, 1.0 according to the debugger, both times.

    I'd hope Apple uses the same code to manpilation the components. :confused: (CGContextSetFillColor() takes the same CGFloat components[] parameter than CGColorCreate() does, hence why I was able to just insert it as is).

    But if it's going to help solve this question, here it is :

    CGFloat color4f [] = { (color.components[COMPONENT_RED] / 0xff), (color.components[COMPONENT_GREEN] / 0xff), (color.components[COMPONENT_BLUE] / 0xff), (color.components[COMPONENT_ALPHA] / 0xff) };
    color is defined as the following :

    typedef union
    	unsigned char components[4];
    	uint32_t pixel;
    } MyColorType
    The constants are properly defined to point to the right channel yes (since it works with CGCreateColor after all ;) )
  5. chown33, Feb 23, 2012
    Last edited: Feb 23, 2012

    chown33 macrumors 604

    Aug 9, 2009
    Sailing beyond the sunset
    In this expression:
    (color.components[COMPONENT_RED] / 0xff)
    The type of color.components[COMPONENT_RED] is char (one of the integer types, as distinct from floating-point or pointer types), and the type of 0xff is an integer compile-time constant. So the expression is evaluated using integer arithmetic.

    Unfortunately for you, you need the expression to be evaluated in floating-point arithmetic, otherwise all values of char that are less than 0xff will produce a net result of 0. To understand why, think about what the runtime result would be for two integer constants:
    int i = 35 / 0xff;
    It should be clear that all values of the numerator below 0xff will result in 0. If the color you want is black, fine. But think about what effect that has on alpha.

    The fix is to change your compile-time expression term from 0xff to 255.0. With a decimal point and a 0 fractional part, it's now a floating-point expression, and the compile-time operands are all promoted to floating-point (same as runtime).
  6. KnightWRX thread starter macrumors Pentium


    Jan 28, 2009
    Quebec, Canada
    Sure, but then everything else will break as I will no longer have direct access to my imagePixel[] array. I use it as a good old framebuffer, the goal of writing it the way I am is to replicate as close as possible the experience of writing graphics code for a time long forgotten.

    I already feel dirty enough with UIFont and NSString's drawInRect.

    You're right, though it still doesn't explain why CGContextSetFillColor() didn't work for black text :

    White : R: 1.000000 G: 1.000000 B: 1.000000 A: 1.000000
    [B]Red : R: 0.000000 G: 0.000000 B: 0.000000 A: 1.000000[/B]
    Black : R: 0.000000 G: 0.000000 B: 0.000000 A: 1.000000
    Red obviously would have looked black if I had used it as a test case (all my test cases were white and black, with white working and black not working).

    Casting the color.components[] to float value also works.
  7. KnightWRX thread starter macrumors Pentium


    Jan 28, 2009
    Quebec, Canada
    chown33, you actually raised another point without making it. 0xff is nice as long as chars are 8 bit long. The portable way though would be to use the standard library's CHAR_MAX constant in order to always get a proper result.

    So I edited the initialization to the following :

    CGFloat color4f [] = { (color.components[COMPONENT_RED] / (CGFloat) CHAR_MAX), (color.components[COMPONENT_GREEN] / (CGFloat) CHAR_MAX), (color.components[COMPONENT_BLUE] / (CGFloat) CHAR_MAX), (color.components[COMPONENT_ALPHA] / (CGFloat) CHAR_MAX) };
    Now it's portable and returning proper floating point values between 0.000000 and 1.000000 for each channel. I actually tested this time with colors other than white and black (never actually did, which is why I never noticed that while the result of the division was a floating point, the conversion was done after the actual math had taken place and the decimals had been dropped).

    However, the code still renders the color properly using CGContextSetFillColorWithColor() but not with CGContextSetFillColor() even though I pass both those functions the current context (the one used to draw the string and the one created for my bitmap I'm rendering) and the same color4f array... :confused:

    No one has any idea why ?

Share This Page