Creating a 1-bit deep NSImageRep (as well as in the CG world) AFAIK is not supported, so we must do this manually. It might be useful to use CIImage for this task. Here I go classic (you can call it old fashioned). Here is the code that shows how we can do this. First, a gray image is created from NSImageRep, so we have a well-defined and simple format, regardless of the original image format (there may also be a PDF file). The resulting gray image is the source of the biton image. Here is the code to create the gray image: (without respecting the size / resolution original image, only the number of pixels!):
- (NSBitmapImageRep *) grayRepresentationOf:(NSImageRep *)aRep { NSBitmapImageRep *newRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:[aRep pixelsWide] pixelsHigh:[aRep pixelsHigh] bitsPerSample:8 samplesPerPixel:1 hasAlpha:NO //must be NO ! isPlanar:NO colorSpaceName:NSCalibratedWhiteColorSpace bytesPerRow:0 bitsPerPixel:0 ]; // this new imagerep has (as default) a resolution of 72 dpi [NSGraphicsContext saveGraphicsState]; NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:newRep]; if( context==nil ){ NSLog( @"*** %s context is nil", __FUNCTION__ ); return nil; } [NSGraphicsContext setCurrentContext:context]; [aRep drawInRect:NSMakeRect( 0, 0, [newRep pixelsWide], [newRep pixelsHigh] )]; [NSGraphicsContext restoreGraphicsState]; return [newRep autorelease]; }
In the following method, we create an NXBitmapImageRep (bit per pixel = 1, samples per pixel = 1) from the given NSImageRep (one of its subclasses) and we will use the method just given:
- (NSBitmapImageRep *) binaryRepresentationOf:(NSImageRep *)aRep { NSBitmapImageRep *grayRep = [aRep grayRepresentation]; if( grayRep==nil ) return nil; NSInteger numberOfRows = [grayRep pixelsHigh]; NSInteger numberOfCols = [grayRep pixelsWide]; NSBitmapImageRep *newRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:numberOfCols pixelsHigh:numberOfRows bitsPerSample:1 samplesPerPixel:1 hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedWhiteColorSpace bitmapFormat:0 bytesPerRow:0 bitsPerPixel:0 ]; unsigned char *bitmapDataSource = [grayRep bitmapData]; unsigned char *bitmapDataDest = [newRep bitmapData];
To propagate the errors, I used the following code, which directly converts a bitmap image of a gray image. This is allowed because the gray image itself is no longer used.
// change bitmapDataSource : use Error-Diffusion for( NSInteger row=0; row<numberOfRows-1; row++ ){ unsigned char *currentRowData = bitmapDataSource + row*grayBPR; unsigned char *nextRowData = currentRowData + grayBPR; for( NSInteger col = 1; col<numberOfCols; col++ ){ NSInteger origValue = currentRowData[col]; NSInteger newValue = (origValue>127) ? 255 : 0; NSInteger error = -(newValue - origValue); currentRowData[col] = newValue; currentRowData[col+1] = clamp(currentRowData[col+1] + (7*error/16)); nextRowData[col-1] = clamp( nextRowData[col-1] + (3*error/16) ); nextRowData[col] = clamp( nextRowData[col] + (5*error/16) ); nextRowData[col+1] = clamp( nextRowData[col+1] + (error/16) ); } }
clamp is a macro defined before a method
#define clamp(z) ( (z>255)?255 : ((z<0)?0:z) )
This means that unsigned char bytes have valid values (0 <= z <= 255)
source share