Cocoa won't draw CMYK jpeg

  • I tried opening and drawing a CMYK jpeg with my Cocoa app. All I get
    is a large almost all black rectangle. (Some of the image is there
    very faintly, you can detect it using the DigitalColor Meter and
    moving the cursor around.)

    The image is read in by

      NSBitmapImageRep* newRep =
            [NSBitmapImageRep imageRepWithContentsOfFile:[sheet filename]];

    and adding the representation to an NSImage.

    changing the code to use

          NSImage* newImage = [[NSImage alloc] initWithContentsOfFile:
    [sheet filename]];

    doesn't make any difference, nor does changing which of NSImage's
    drawing methods is used, or drawing it to a Transparency Layer first
    and then drawing the layer.

    Investigating further, I then tried opening or importing the file in
    a bunch of other apps. Some work correctly, some not.

    Works OK:

    Preview, Safari, Pages '08

    Black Rectangle:

    Mail, TextEdit, Pages '06, Stickies

    Similar variable results with various 3rd party apps.

    So clearly there is a bug somewhere and just as clearly someone knows
    how to get around it.

    Any wisdom or help greatly appreciated.

    ...Bob Clair

    ---------------------------------------------------------------------

    I used two cmyk jpegs - one exported from my own app, the other
    created by opening a Photoshop created cmyk tiff in Preview and then
    saving it as a jpeg. The later is at

                  http://www.elroberto.com/cocoa/cmyk_jpeg.jpg
  • On Sep 12, 2007, at 6:24 AM, Robert Clair wrote:

    > Any wisdom or help greatly appreciated.
    >
    > ...Bob Clair

    Have you tried, as an experiment, opening the JPEG using Image I/O?

    Which version of the OS are you working on?

    Scott
  • >
    >> Any wisdom or help greatly appreciated.
    >>
    >> ...Bob Clair
    >
    > Have you tried, as an experiment, opening the JPEG using Image I/O?

    Not yet. I'm vainly hoping for something simpler. :-)
    >
    > Which version of the OS are you working on?

    All the results cited are on 10.4.10.

    ...Bob
  • On Sep 12, 2007, at 9:08 AM, Robert Clair wrote:

    >>
    >>> Any wisdom or help greatly appreciated.
    >>>
    >>> ...Bob Clair
    >>
    >> Have you tried, as an experiment, opening the JPEG using Image I/O?
    >
    > Not yet. I'm vainly hoping for something simpler. :-)

    Simpler?!? :-)  Ok... it is a shame that there's not an (Apple
    provided) NSCGImageImageRep... yet.

    NSURL *urlToFile = [NSURL fileURLWithPath:[sheet filename]];
    CGImageSourceRef mySource = CGImageSourceCreateWithURL((CFURLRef)
    urlToFile)
    CGImageRef myImage = CGImageSourceCreateImageAtIndex(mySource, 0, NULL);
    CFRelease(mySource);

    And then...

    CGContextRef *cgContext = (CGContextRef) [[NSGraphicsContext
    currentContext] currentPort];
    CGContextDrawImage(cgContext, destinationRect, myImage);

    The reason I ask is because my understanding was that in Tiger most
    graphics paths were going through Image I/O.  Certainly I would expect
    newer applications like Page '08 and Safari to do that while older
    applications (and your code) might be going through a Cocoa path that
    has not yet been redirected to go through Image I/O.

    It's a theory any way.

    >> Which version of the OS are you working on?
    >
    > All the results cited are on 10.4.10.

    OK... then there is at least the possibility that my "divergent paths"
    theory above holds water.  If you were on a system that didn't have
    Image I/O as an option then it shouldn't.

    ... Then again, there's a good chance that Image I/O existed on 10.3
    and just didn't have a public API ...

    :-)

    Scott
  • I just downloaded the ImageIO sample app and the image opens correctly.

    Sigh. Yet one more part of the app to re-write.

    ....Bob
  • On Sep 12, 2007, at 9:33 AM, Robert Clair wrote:

    > I just downloaded the ImageIO sample app and the image opens
    > correctly.
    >
    > Sigh. Yet one more part of the app to re-write.
    >
    > ....Bob

    I'm assuming that you're just using a standard NSImage in your
    existing code to load and display these images, but you could just
    create your own NSImageRep subclass that encapsulates ImageIO and
    then there will be no "re-writing" required (since all the places
    that use NSImage will magically get support for using ImageIO via
    your NSImageRep subclass).

    If you search the web, there's a sample that show how to make an
    NSImageRep subclass that supports HTML files (allowing you to open
    web pages as if they were image files) - it's relatively simple to do
    (and sure beats having to rewrite all the places that your app uses
    NSImage to use ImageIO)

    Glenn Andreas                      <gandreas...>
      <http://www.gandreas.com/> wicked fun!
    quadrium2 | build, mutate, evolve, animate  | images, textures,
    fractals, art
  • >
    > Simpler?!? :-)  Ok... it is a shame that there's not an (Apple
    > provided) NSCGImageImageRep... yet.
    >
    > NSURL *urlToFile = [NSURL fileURLWithPath:[sheet filename]];
    > CGImageSourceRef mySource = CGImageSourceCreateWithURL((CFURLRef)
    > urlToFile)
    > CGImageRef myImage = CGImageSourceCreateImageAtIndex(mySource, 0,
    > NULL);
    > CFRelease(mySource);
    >
    > And then...
    >
    > CGContextRef *cgContext = (CGContextRef) [[NSGraphicsContext
    > currentContext] currentPort];
    > CGContextDrawImage(cgContext, destinationRect, myImage);

    Well, I agree it is simple to read in an image and draw it this way.
    What is *not* simple is cramming this into a large app that uses
    NSImage in various places to draw, archive, get at its pixels, etc.
    What I really want is an NSBitmapImageRep.

    The obvious way doesn't work:

    * create a CGImage from a CGImageSource

    * extract the CGColorSpace and other relevant info from the CGImage
    and use it to create a CGBitmapContext

    * draw the CGImage into the CGBitmapContext

    * stuff the pixels from the CGBitmapContext into an NSBitmapImageRep

    * set the profile property for the NSBitmapImageRep... except...
    CURSES ! FOILED AGAIN ! There is apparently no way to get the profile
    out of CGColorSpaceRef.

    Glenn's suggestion of subclassing NSImageRep is interesting and I may
    try it. But I'm also going to see if this hack works:

    * create an image source from the file with CGImageSourceCreateWithURL()

    * use  CGImageDestinationCreateWithData(...) to set up and image
    destination that outputs tiff to CFMutableData

    * use CGImageDestinationAddImageFromSource(...) to put the image into
    the data

    * cast the resulting CFMutableDataRef to NSData*  and use
    [NSBitmapImageRep initWithData:].

    Have to test and make sure nothing gets lost in the journey. Hackery,
    but localized hackery that can be contained in a single routine for
    replacement if something better comes along.

    Bob
  • On Sep 13, 2007, at 8:18 AM, Robert Clair wrote:
    > Well, I agree it is simple to read in an image and draw it this way.
    > What is *not* simple is cramming this into a large app that uses
    > NSImage in various places to draw, archive, get at its pixels, etc.
    > What I really want is an NSBitmapImageRep.

    Right..  Lots of folks have had to create an "CGImageImageRep" which
    is really what you want in this case.  Unfortunately Apple hasn't
    provided one in Tiger, but there is one in Leopard.

    I haven't created one myself, or I would send it to you :-(

    > * set the profile property for the NSBitmapImageRep... except...
    > CURSES ! FOILED AGAIN ! There is apparently no way to get the
    > profile out of CGColorSpaceRef.

    A CGColorSpace may not have a profile to give you.  For example,
    Device color spaces, or those created with
    CGColorSpaceCreateCalibratedGray don't have explicit ICC profiles for
    you to grab.
  • > A CGColorSpace may not have a profile to give you.  For example,
    > Device color spaces, or those created with
    > CGColorSpaceCreateCalibratedGray don't have explicit ICC profiles
    > for you to grab.

    so ???  :-)

    PropertyFooRef  getPropertyFoo()
    {
    if ( propertyFoo relevant to this instance )
      {
      return pointerToPropertyFoo;
      }
    else
      {
      return NULL;
      }
    }

    The fact that some instances may not have what you are after isn't an
    excuse to not let you have that property when it does exist.
    Especially if you *really* want it. Responsible adults should be able
    to check the return value of a function.

    ...Bob
  • The hackery outlined in the earlier post (feed the CGImageSource into
    a CGImageDestination to produce tiff data and then using the tiff
    data to initialize an NSBitmapImage rep) seems to work just fine.

    It also suggests an (ugly but workable) way to extract the profile
    from an CGColorSpaceRef (if it has one): Create a CGBitmapContext
    with the CGColorSpaceRef, draw something, extract a CGImage, feed the
    CGImage to a tiff data producing CGImageDestination, feed the data to
    an NSBitmapImage, and extract the profile via [NSBitmapImageRep
    valueForProperty: NSImageColorSyncProfileData].

    You could also extract the profile from a CGImage the same way.
    Again, ugly, but probably less ugly than launching sips with NSTask,
    which was the only solution mentioned in the archives.

    ...Bob
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today