본문 바로가기

프로그래밍/iOS

[iOS] 24bit BMP파일 만들기

은행권에서 요구가 있어 24bitBMP를 만들 일이 있었다.


원래 우리가 사진을 찍으면 RGBA 각각 8bit씩 32bit의 사진이 나온다.

여기에서 A값만 날리면 24bit RGB데이터가 나오고

여기 위에다가 BMP헤더만 붙이면 되는 아주 간단한(?) 일이 었다.


간단하긴 개뿔 내가 무슨 BMP헤더를 잘 아나 이미지를 많이 다뤄봤나...


그래서 만듬



작업하면서 계속 안돼길래

16진수로 계속 확인했던건 안비밀


typedef struct s_bitmap_header {

    // Bitmap file header

    UInt16 fileType;

    UInt32 fileSize;

    UInt16 reserved1;

    UInt16 reserved2;

    UInt32 bitmapOffset;

    

    // DIB Header

    UInt32 headerSize;

    UInt32 width;

    UInt32 height;

    UInt16 colorPlanes;

    UInt16 bitsPerPixel;

    UInt32 compression;

    UInt32 bitmapSize;

    UInt32 horizontalResolution;

    UInt32 verticalResolution;

    UInt32 colorsUsed;

    UInt32 colorsImportant;

} t_bitmap_header;

// 함수 사용 

+ (NSMutableData *)convertTo24BMPWithImage:(UIImage *)image {

    if (image == nil || image.size.width <= 0 || image.size.height <= 0) {

        return nil;

    }

    

    NSData *pixelData = (__bridge_transfer NSData *)CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));

    

    unsigned char *pixelBytes = (unsigned char *)[pixelData bytes]; //이미지의 byte수

    int width = (int)CGImageGetWidth(image.CGImage);

    int height = (int)CGImageGetHeight(image.CGImage);

    

    // 24 bit RGB변환

    int rgb24DataLen = width * height * 3;

    Byte *rgb24Data = malloc(rgb24DataLen * sizeof(Byte));

    if (rgb24Data == NULL)

        return nil;

    

    

    for (int j = 0; j < height; ++j) {

        unsigned char * src = pixelBytes + j * width * 4;

        unsigned char * dst = rgb24Data + j * width * 3;

        for (int i = 0; i< width; ++i) {

            *(dst) = *(src+2);

            *(dst+1) = *(src+1);

            *(dst+2) = *(src);

            

            dst+=3;

            src+=4;

        }

    }

    

    pixelBytes = nil;

    

    // 이미지 패딩 값 계산

    Byte *dummyBytesPerRow = nil;

    BOOL hasDummy = NO;

    int BYTE_PER_PIXEL = 3;

    int BMP_WIDTH_OF_TIMES = 4;

    int rowWidthInBytes = BYTE_PER_PIXEL * width;

    int byteLength = (BMP_WIDTH_OF_TIMES - (rowWidthInBytes % BMP_WIDTH_OF_TIMES));

    

    if(rowWidthInBytes % BMP_WIDTH_OF_TIMES > 0) {

        hasDummy = YES;

        dummyBytesPerRow = malloc(byteLength * sizeof(Byte));

        

        for(int i=0;i<byteLength; i++) {

            dummyBytesPerRow[i] = (Byte)0xFF;

        }

    }

    

    // BMP 헤더 작성 (해당 내용은 위키의 bmp파일 포멧 참조)

    t_bitmap_header header;

    int imageSize = (rowWidthInBytes+(hasDummy?byteLength:0)) * height;

    int imageDataOffset = 0x36;

    int fileSize = imageSize + imageDataOffset;

    

    header.fileType = 0x4D42;  // BM

    header.fileSize = fileSize;

    header.reserved1 = 0x0000;

    header.reserved2 = 0x0000;

    header.bitmapOffset = 0x00000036;

    header.headerSize = 0x00000028;

    header.width = (hasDummy?(byteLength==3?1:0):0)+width;

    header.height = height;

    header.colorPlanes = 0x0001;

    header.bitsPerPixel = 0x0018;

    header.compression = 0x00000000;

    header.bitmapSize = imageSize;

    header.horizontalResolution = 0x00000000;

    header.verticalResolution = 0x00000000;

    header.colorsUsed = 0x00000000;

    header.colorsImportant = 0x00000000;

    

    // Data에 BMP 헤더 작성하기

    NSMutableData* bmpData = [[NSMutableData alloc] init];

    [bmpData appendBytes:&(header.fileType) length:sizeof(UInt16)];

    [bmpData appendBytes:&(header.fileSize) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.reserved1) length:sizeof(UInt16)];

    [bmpData appendBytes:&(header.reserved2) length:sizeof(UInt16)];

    [bmpData appendBytes:&(header.bitmapOffset) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.headerSize) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.width) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.height) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.colorPlanes) length:sizeof(UInt16)];

    [bmpData appendBytes:&(header.bitsPerPixel) length:sizeof(UInt16)];

    [bmpData appendBytes:&(header.compression) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.bitmapSize) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.horizontalResolution) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.verticalResolution) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.colorsUsed) length:sizeof(UInt32)];

    [bmpData appendBytes:&(header.colorsImportant) length:sizeof(UInt32)];

    

    // 24bit RGB값에 패딩 값 추가하기

    Byte *pData = malloc(fileSize * sizeof(Byte));

    Byte *pRawData = rgb24Data;

    unsigned long dwLine=((((width * 24) + 31) &~ 31) >> 3);

    if (pData) {

        int i=0;

        long lCount=3*width*height;

        for(lCount-=width*3; lCount > 0; lCount-=width*3) {

            memcpy(&pData[i], &pRawData[lCount], width*3);

            if (hasDummy) {

                memcpy(&pData[i], dummyBytesPerRow, byteLength);

            }

            

            i+=(long)dwLine;

        }

    }

    

    // 패딩 값이 추가된 24bit RGB데이터를 NSData로

    NSData *rawData = [[NSData alloc] initWithBytes:pData length:imageSize];

    [bmpData appendData:rawData]; // 헤더 데이터에 24bit RGB 데이터 추가하기

    

    if (hasDummy)

        free(dummyBytesPerRow);

    free(rgb24Data);

    free(pData);

    

    return bmpData;

}