4

我正在尝试以 1 位单色显示图像的预览,例如,不是灰度,而是黑白双色。它应该指示图像在传真后的外观。低至每像素 1 位的格式在 OS X 上不可用,只有 8 位灰度。有没有办法使用 Core Graphics 或其他框架(理想情况下使用抖动)来实现这种效果?

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

4

1 回答 1

3

AFAIK 不支持创建 1 位深度的 NSImageRep(以及在 CG 世界中),因此我们必须手动进行。使用 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) );
    }
}

钳位是在方法之前定义的宏

#define clamp(z) ( (z>255)?255 : ((z<0)?0:z) ) 

这使得 unsigned char 字节具有有效值 (0<=z<=255)

于 2012-11-15T21:27:44.383 回答