Posts tagged “objective-c

iPhone Development Tutorial

Mac OS X Stack Overflow Status Item!

I’ve become a huge fan of Stack Overflow over the last few weeks. The community there is helpful and fast and there are quite a few questions about Cocoa and Objective-C! It’s gotten to the point where I visit SO whenever my code is compiling – so I thought it was time to take matters into my own hands and make things easier to follow.

I’ve made a Stack Overflow status item for Mac OS X (also a “menu bar item” or a “system icon”) that shows your reputation and lists questions on the front page containing your interesting tags:

SO Status Item in Action

It’s pretty primitive at this point – you can click a question to view it, or click the tiny arrow to go to your user page. It updates every 90 seconds using the RSS and ATOM feeds from the site, so the most active questions are always available at a glance. It’s compatible with Mac OS X 10.5 and 10.6 – so download it and give it a shot! I know there are a lot of things that could be added – so leave a comment and let me know what you think.

Download the Stack Overflow Status Item for Mac OS X (0.5 MB)

PackBits algorithm in Objective-C

The PackBits algorithm is one of the TIFF data compression methods, and it’s also used for pixel data in Photoshop PSD and TGA files. It was originally developed for MacPaint, and although it’s still widely used, there isn’t a whole lot of information online about it. I spent some time this weekend writing a category to extend the NSData class and support the packBits algorithm, and I think I’ve finally got it.

PackBits is really dirt simple. If you have a string of hex values like 00 00 00 FF FF FF FF, it replaces series of  3 or more identical bytes with a count, and then the repeated byte. The input above would be 03 00 04 FF. A prefix is also attached to series of different bytes so the decoder knows how many bytes to pass through before looking for another header byte.

The full documentation of PackBits can be found on Wikipedia. My implementation is a modified version of the one available here: http://michael.dipperstein.com/rle/index.html (source link at very bottom of page). That implementation is a modification of the official PackBits algorithm that allows slightly larger runs of data to be encoded in a single header + byte pair. I was interested in the original implementation only (since data written from any customized encoder could not be opened by a standard decoder) so I changed it back to the standard algorithm.

The primary function provided in the PackBitsAdditions category below is:

[objc] – (NSData*)packedBitsForRange:(NSRange)range skip:(int)skip[/objc]

This function returns the packBits representation of the data in “range”, advancing “skip” bytes with each read. For instance, to read and encode every byte in “range”, you would provide skip = 1. To read and encode only every 4th byte, you would pass skip = 4. This may seem like an odd implementation, but it’s very handy when you need to encode the channels of an RGBA image separately (as in PSD format).

The other function in the category is packedBitsDescription. It describes the packed bits and the process that would be followed to decode them. This function could be easily extended to actually decode the data.

If you find this code useful, please leave a comment! I debugged this for quite a while, and I’d be happy to help if you run into any issues with my implementation!

NSDataPackBitsAdditions.h

[objc]

@interface NSData (PackBitsAdditions)

- (NSString*)packedBitsDescription;
- (NSData*)packedBitsForRange:(NSRange)range skip:(int)skip;

@end
[/objc]

NSDataPackBitsAdditions.m

[objc]
@implementation NSData (PackBitsAdditions)

- (NSString*)packedBitsDescription
{
NSMutableString * description = [NSMutableString string];
char * row = (char*)[self bytes];
int pbOffset = 0;
int pbResultBytes = 0;

while (pbOffset < [self length]){
int headerByte = (int)row[pbOffset];
if (headerByte < 0){
int repeatTimes = 1-headerByte;
UInt8 repeatByte = (UInt8)row[pbOffset+1];
[description appendFormat: @"Printing %u %d times. ", repeatByte, repeatTimes];

pbResultBytes += repeatTimes;
pbOffset += 2;
} else if (headerByte >= 0){
[description appendFormat: @"Printing %d literal bytes. ", headerByte + 1];
pbResultBytes += headerByte + 1;
pbOffset += 2 + headerByte;
}
}

[description appendFormat: @"Total: %d bytes decoded.", pbResultBytes];
return description;
}

- (NSData*)packedBitsForRange:(NSRange)range skip:(int)skip
{
char * bytesIn = [self bytes];
int bytesLength = range.location + range.length;
int bytesOffset = range.location;
NSMutableData * dataOut = [NSMutableData data];

BOOL currIsEOF = NO;
unsigned char currChar; /* current character */
unsigned char charBuf[MAX_READ]; /* buffer of already read characters */
int count; /* number of characters in a run */

/* prime the read loop */
currChar = bytesIn[bytesOffset];
bytesOffset = bytesOffset + skip;
count = 0;

/* read input until there’s nothing left */
while (!currIsEOF)
{
charBuf[count] = (unsigned char)currChar;
count++;

if (count >= MIN_RUN){
int i;

/* check for run charBuf[count - 1] .. charBuf[count - MIN_RUN]*/
for (i = 2; i <= MIN_RUN; i++){
if (currChar != charBuf[count - i]){
/* no run */
i = 0;
break;
}
}

if (i != 0){
/* we have a run write out buffer before run*/
int nextChar;

if (count > MIN_RUN){
/* block size – 1 followed by contents */
UInt8 a = count – MIN_RUN – 1;
[dataOut appendBytes:&a length:sizeof(UInt8)];
[dataOut appendBytes:&charBuf length:sizeof(unsigned char) * (count - MIN_RUN)];
}

/* determine run length (MIN_RUN so far) */
count = MIN_RUN;
while (true){
if (bytesOffset < bytesLength){
nextChar = bytesIn[bytesOffset];
bytesOffset += skip;
} else {
currIsEOF = YES;
nextChar = EOF;
}
if (nextChar != currChar) break;

count++;
if (count == MAX_RUN){
/* run is at max length */
break;
}
}

/* write out encoded run length and run symbol */
UInt8 a = ((int)(1 – (int)(count)));
[dataOut appendBytes:&a length:sizeof(UInt8)];
[dataOut appendBytes:&currChar length:sizeof(UInt8)];

if ((!currIsEOF) && (count != MAX_RUN)){
/* make run breaker start of next buffer */
charBuf[0] = nextChar;
count = 1;
} else {
/* file or max run ends in a run */
count = 0;
}
}
}

if (count == MAX_READ){
int i;

/* write out buffer */
UInt8 a = MAX_COPY – 1;
[dataOut appendBytes:&a length:sizeof(UInt8)];
[dataOut appendBytes:&charBuf[0] length:sizeof(unsigned char) * MAX_COPY];

/* start a new buffer */
count = MAX_READ – MAX_COPY;

/* copy excess to front of buffer */
for (i = 0; i < count; i++)
charBuf[i] = charBuf[MAX_COPY + i];
}

if (bytesOffset < bytesLength)
currChar = bytesIn[bytesOffset];
else
currIsEOF = YES;
bytesOffset += skip;
}

/* write out last buffer */
if (0 != count){
if (count <= MAX_COPY){
/* write out entire copy buffer */
UInt8 a = count – 1;
[dataOut appendBytes:&a length:sizeof(UInt8)];
[dataOut appendBytes:&charBuf length:sizeof(unsigned char) * count];
}
else
{
/* we read more than the maximum for a single copy buffer */
UInt8 a = MAX_COPY – 1;
[dataOut appendBytes:&a length:sizeof(UInt8)];
[dataOut appendBytes:&charBuf length:sizeof(unsigned char) * MAX_COPY];

/* write out remainder */
count -= MAX_COPY;
a = count – 1;
[dataOut appendBytes:&a length:sizeof(UInt8)];
[dataOut appendBytes:&charBuf[MAX_COPY] length:sizeof(unsigned char) * count];
}
}

return dataOut;
}

@end
[/objc]

Creating a UIImage from a CGLayer

CGLayers are great for drawing – especially when things need to be drawn over and over again. Converting a CGLayer to a UIImage is another story, though. NetSketch uses CGLayers for the drawing canvas, but converts them to UIImages when you go to upload your drawing or email it to a friend. The code below shows how it’s done. The CGLayer is drawn into a bitmap CGContext of the same size, and then a CGImage is created around the CGContext. The CGImage can be turned into a UIImage, and you’re done!

Be sure to leave a comment if you find this function useful!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
UIImage* UIImageFromLayer(CGLayerRef layer)
{
    // Create the bitmap context
    CGContextRef    bitmapContext = NULL;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;
    CGSize          size = CGLayerGetSize(layer);
 
 
    // Declare the number of bytes per row. Each pixel in the bitmap in this
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and
    // alpha.
    bitmapBytesPerRow   = (size.width * 4);
    bitmapByteCount     = (bitmapBytesPerRow * size.height);
 
 
    // Allocate memory for image data. This is the destination in memory
    // where any drawing to the bitmap context will be rendered.
    bitmapData = malloc( bitmapByteCount );
 
    if (bitmapData == NULL)
    {
        return nil;
    }
 
    // Create the bitmap context. We want pre-multiplied ARGB, 8-bits
    // per component. Regardless of what the source image format is
    // (CMYK, Grayscale, and so on) it will be converted over to the format
    // specified here by CGBitmapContextCreate.
    bitmapContext = CGBitmapContextCreate (bitmapData, size.width, size.height,8,bitmapBytesPerRow,
                                        CGColorSpaceCreateDeviceRGB(),kCGImageAlphaNoneSkipFirst);
 
 
    if (bitmapContext == NULL)
        // error creating context
        return nil;
 
    CGContextScaleCTM(bitmapContext, 1, -1);
    CGContextTranslateCTM(bitmapContext, 0, -size.height);
 
    // Draw the image to the bitmap context. Once we draw, the memory
    // allocated for the context for rendering will then contain the
    // raw image data in the specified color space.
    CGContextDrawLayerAtPoint(bitmapContext, CGPointZero, layer);
    CGImageRef   img = CGBitmapContextCreateImage(bitmapContext);
    UIImage*     ui_img = [UIImage imageWithCGImage: img];
 
    CGImageRelease(img);
    CGContextRelease(bitmapContext);
    free(bitmapData);
 
    return ui_img;
}