How to create 8-, 4-, and 1-bit representations of NSImage
Asked Answered
C

3

10

I had created 32 bit NSImage with following code.

 NSBitmapImageRep *sourceRep = [[NSBitmapImageRep alloc] initWithData: imageData];

        // create a new bitmap representation scaled down

            NSBitmapImageRep *newRep = 
                [[NSBitmapImageRep alloc] 
                    initWithBitmapDataPlanes: NULL
                    pixelsWide: imageSize
                    pixelsHigh: imageSize
                    bitsPerSample: 8
                    samplesPerPixel: 4
                    hasAlpha: YES
                    isPlanar: NO
                    colorSpaceName: NSCalibratedRGBColorSpace
                    bytesPerRow: 0
                    bitsPerPixel: 0];

            // save the graphics context, create a bitmap context and set it as current
            [NSGraphicsContext saveGraphicsState] ;
            NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep: newRep];
            [NSGraphicsContext setCurrentContext: context] ;

            // draw the bitmap image representation in it and restore the context
            [sourceRep drawInRect: NSMakeRect(0.0f, 0.0f, imageSize, imageSize)] ;
            [NSGraphicsContext restoreGraphicsState] ;

            // set the size of the new bitmap representation
            [newRep setSize: NSMakeSize(imageSize,imageSize)] ;

            NSDictionary *imageProps2 = [NSDictionary dictionaryWithObjectsAndKeys:
                                         [NSNumber numberWithFloat:1.0], kCGImageDestinationLossyCompressionQuality,
                                         nil];
            imageData = [newRep representationUsingType: NSPNGFileType properties: imageProps2];
  NSImage *bitImage  = [[NSImage alloc]initWithData:imageData];

Now I need to create 8 bit(256 Colors),4 bit(16 Colors),1 bit(Black & White) NSBitmapImageRep representation. what I want to do now?

Commentary answered 2/8, 2013 at 11:32 Comment(7)
Have you considered Core Image filters?Overnice
@peterhosey,any restrictions on license or runtime considerations? like must be available as built in lib in 10.6 etc?Sleepless
@GradyPlayer: I don't have any such restrictions, no. (I'm just bountying questions for the holidays; I don't need this for work or something.)Loesceke
@PeterHosey wow that is pretty darn nice of you, happy holidays.Sleepless
@GradyPlayer: Thank you, and happy holidays to you, too.Loesceke
Peter is definitely a nice guy, who bountied once on my question before. Happy holidays, Peter.Lacteous
@Unheilig: Happy holidays to you, too.Loesceke
S
4

Unfortunately it seems that Cocoa doesn't support operating on paletted images.

I've been trying that before and my conclusion is that it's not possible for PNG. NSGIFFileType is a hardcoded exception, and Graphics Contexts are even more limited than bitmap representations (e.g. RGBA is supported only with premultiplied alpha).

To work around it I convert NSBitmapImageRep to raw RGBA bitmap, use libimagequant to remap it to a palette and then libpng or lodepng to write the PNG file.

Stairwell answered 16/12, 2013 at 2:40 Comment(0)
C
2

Sadly, I believe you can't using core graphics. Graphics contexts don't support anything with that few bits.

The documentation has a table of supported pixel formats.

Apparently Carbon had (has?) support for it, as seen referenced here where they also lament Cocoa's lack of support for it:

Turns out that basically Cocoa/Quartz does not support downsampling images to 8-bit colour. It supports drawing them, and it supports upsampling, but not going the other way. I guess this is a deliberate design on Apple's part to move away from indexed images as a standard graphics data type - after all, 32-bit colour is much simpler, right? Well, it is, but there are still useful uses for 8-bit. So..... what to do? One possibility is using Carbon, since General/QuickDraw's General/GWorld does support downsampling, etc.

From this thread

Consalve answered 10/12, 2013 at 7:47 Comment(0)
S
1

well This is probably going to be too long for a comment...

It sure seems like this just isn't possible... all of cocoa's drawing parts really seem to want to use 24-bit color colorspaces... I was able to make an 8bit NSBitmapImageRep but it was grayscale.

So I guess we have to figure out the why here. If you want to be able to use NSImages that are backed by certain types of representations, I don't think that is possible.

if you want to naively down sample (change to the closest 24/32-bit value to any pixel), that is very possible; this would be to give the appearance of 8-bit images.

If you want to be able to write these files out with good dithering / index colors then I think the best option would be to write to an image format that supports what you want (like writing to a 256 color GIF).

If you wanted to do this downsampling yourself for some reason, there are 2 issues at hand:

  1. Pallet or CLUT selection.
  2. Dithering.

If you didn't want to use indexed colors and just wanted to break the 8-bits into 3-3-2 RGB that is a little bit easier, but the result is much worse than indexed color.

The 4 bit is a bit tricker, because I don't really even know of a good historical use of 4-bit color.

I used indexed color to display escape times from a mandelbrot set in a little project I did once...

I just verified that it doesn't work anymore (was old fixed render pipeline OpenGL).

but basically for the view you would use glPixelMapuiv to map the index colors to a byte value, then display the byte buffer with glDrawPixels;

So... I guess if you comment and say why you are trying to do what you are doing we may be able to help.

Sleepless answered 15/12, 2013 at 0:58 Comment(1)
Wild guess: Generating old icon representations, such as 'icl8', 'icl4', and 'ICN#', from a 32-bit icon.Loesceke

© 2022 - 2024 — McMap. All rights reserved.