创建单色CGImageRef(每像素1位位图)

时间:2012-11-15 04:03:01

标签: objective-c c macos core-graphics core-image

我试图以1位单色显示图像的预览,例如,不是灰度,而是黑色和黑色。它应该是图像传真时图像的显示方式。 Formats as low as 1-bit per pixel aren't available on OS X,只有8位灰度。有没有办法使用Core Graphics或其他框架(理想情况下是抖动)来实现这种效果?

我知道有一个名为CIColorMonochrome的过滤器,但这只会将图像转换为灰度。

1 个答案:

答案 0 :(得分:3)

创建一个1位深的NSImageRep(以及在CG世界中)是不支持AFAIK的,所以我们必须手动完成。将CIImage用于此任务可能很有用。在这里,我走古典(你可以称之为老式)的方式。这是一个代码,显示我们如何做到这一点。首先从NSImageRep创建灰色图像,因此无论源图像将被格式化(也可以是PDF文件),我们都有一个定义良好且格式简单的格式。得到的灰度图像是双色调图像的来源。以下是创建灰色图像的代码:(不考虑源图像的size / resolution,只计算像素数!):

- (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];
}

在下一个方法中,我们从给定的NSImageRep(它的一个子类)创建一个NXBitmapImageRep(每像素位数= 1,每个像素的样本数= 1),并将使用刚刚给出的方法:

- (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];
}

对于误差扩散,我使用以下代码直接改变灰度图像的位图。这是允许的,因为灰色图像本身不再使用。

// 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是在方法

之前定义的宏
#define clamp(z) ( (z>255)?255 : ((z<0)?0:z) ) 

这使得无符号字节字节具有有效值(0 <= z <= 255)