Create a monochrome CGImageRef (1 bit per pixel bitmap)

I am trying to show a preview of an image in 1-bit monochrome, as in, not in shades of gray, but in bit black and white. This should be an indicator of how the image will look if it is sent by fax. Formats up to 1 bit per pixel are not available in OS X , only 8-bit shades of gray. Is there a way to achieve this effect using Core Graphics or another structure (ideally with anti-aliasing)?

I know that there is a CIColorMonochrome filter, but this only converts the image to grayscale.

+4
source share
1 answer

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]; // here is the place to use dithering or error diffusion (code below) // iterate over all pixels NSInteger grayBPR = [grayRep bytesPerRow]; NSInteger binBPR = [newRep bytesPerRow]; NSInteger pWide = [newRep pixelsWide]; for( NSInteger row=0; row<numberOfRows; row++ ){ unsigned char *rowDataSource = bitmapDataSource + row*grayBPR; unsigned char *rowDataDest = bitmapDataDest + row*binBPR; NSInteger destCol = 0; unsigned char bw = 0; for( NSInteger col = 0; col<pWide; ){ unsigned char gray = rowDataSource[col]; if( gray>127 ) {bw |= (1<<(7-col%8)); }; col++; if( (col%8 == 0) || (col==pWide) ){ rowDataDest[destCol] = bw; bw = 0; destCol++; } } } // save as PNG for testing and return [[newRep representationUsingType:NSPNGFileType properties:nil] writeToFile:@"/tmp/bin_1.png" atomically:YES]; return [newRep autorelease]; } 

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)

+3
source

Source: https://habr.com/ru/post/1446133/


All Articles