1// PXImage.m 2// Pixen 3// 4// Created by Joe Osborn on Thu Sep 11 2003. 5// Copyright (c) 2003 Open Sword Group. All rights reserved. 6// 7 8#import "PXImage.h" 9#import "PXPixel.h" 10#import "KTMutableMatrix.h" 11 12extern BOOL isTiling; 13 14@implementation PXImage 15 16- initWithSize:(NSSize)aSize 17{ 18 [super init]; 19 width = aSize.width; 20 height = aSize.height; 21 pixelsByColor = [[NSMutableDictionary alloc] initWithCapacity:128]; 22 pixelsByPosition = [[KTMutableMatrix matrixWithCapacity:512*512 cuboidBounds:(unsigned)(width), (unsigned)(height), 0, 0] retain]; 23 rects = malloc(sizeof(NSRect) * (height + 2)); 24 colors = malloc(sizeof(NSColor *) * (height + 2)); 25 return self; 26} 27 28- (NSSize)size 29{ 30 return NSMakeSize(width, height); 31} 32 33- (void)setSize:(NSSize)newSize withOrigin:(NSPoint)origin backgroundColor:(NSColor *)color 34{ 35 id newPixelsByPosition = [[KTMutableMatrix matrixWithCapacity:512*512 cuboidBounds:(unsigned)(newSize.width), (unsigned)(newSize.height), 0, 0] retain]; 36 unsigned int i, j; 37 unsigned int newWidth = newSize.width, newHeight = newSize.height; 38 unsigned int originX = origin.x, originY = origin.y; 39 for(i = 0; i < newWidth; i++) 40 { 41 for(j = 0; j < newHeight; j++) 42 { 43 if (i < originX || j < originY || i >= width + originX || j >= height + originY) { 44 [newPixelsByPosition setObject:[PXPixel withColor:color] atCoordinates:i, j]; 45 } else { 46 [newPixelsByPosition setObject:[self pixelAtPoint:NSMakePoint(i - originX, j - originY)] atCoordinates:i, j]; 47 } 48 } 49 } 50 width = newWidth; 51 height = newHeight; 52 [pixelsByPosition release]; 53 pixelsByPosition = newPixelsByPosition; 54 free(rects); 55 free(colors); 56 rects = malloc(sizeof(NSRect) * (height + 2)); 57 colors = malloc(sizeof(NSColor *) * (height + 2)); 58} 59 60- (void)setSize:(NSSize)newSize 61{ 62 [self setSize:newSize withOrigin:NSMakePoint(0,0) backgroundColor:[NSColor clearColor]]; 63} 64 65- (void)dealloc 66{ 67 [pixelsByColor release]; 68 [pixelsByPosition release]; 69 free(rects); 70 free(colors); 71 [super dealloc]; 72} 73 74- (BOOL)containsPoint:(NSPoint)point 75{ 76 return (isTiling ? YES : NSPointInRect(point, NSMakeRect(0, 0, width, height))); 77} 78 79- (NSColor *)colorAtX:(unsigned int)x y:(unsigned int)y 80{ 81 return [[self pixelAtX:x y:y] color]; 82} 83 84- (NSColor *)colorAtPoint:(NSPoint)aPoint 85{ 86 //if(![self containsPoint:aPoint]) { return nil; } 87 return [[self pixelAtPoint:aPoint] color]; 88} 89 90- pixelOfColor:aColor 91{ 92 if(aColor == nil) { return nil; } 93 id pixel = [pixelsByColor objectForKey:aColor]; 94 if(pixel == nil) 95 { 96 [pixelsByColor setObject:[PXPixel withColor:aColor] forKey:aColor]; 97 pixel = [pixelsByColor objectForKey:aColor]; 98 [[NSNotificationCenter defaultCenter] postNotificationName:@"PXImageColorAddedNotification" object:self userInfo:[NSDictionary dictionaryWithObject:aColor forKey:@"color"]]; 99 } 100 return pixel; 101} 102 103- (NSPoint)correct:(NSPoint)aPoint 104{ 105 NSPoint corrected = aPoint; 106 while(corrected.x < 0) 107 { 108 corrected.x += width; 109 } 110 while(corrected.x >= width) 111 { 112 corrected.x -= width; 113 } 114 while(corrected.y < 0) 115 { 116 corrected.y += height; 117 } 118 while(corrected.y >= height) 119 { 120 corrected.y -= height; 121 } 122 return corrected; 123} 124 125- (unsigned int)correctX:(unsigned int)x 126{ 127 while(x < 0) 128 { 129 x += width; 130 } 131 while(x >= width) 132 { 133 x -= width; 134 } 135 return x; 136} 137 138- (unsigned int)correctY:(unsigned int)y 139{ 140 while(y < 0) 141 { 142 y += height; 143 } 144 while(y >= height) 145 { 146 y -= height; 147 } 148 return y; 149} 150 151- _pixelAtX:(unsigned int)x y:(unsigned int)y 152{ 153 return [pixelsByPosition objectAtCoordinates:x, y]; 154} 155 156- _pixelAtPoint:(NSPoint)aPoint 157{ 158// id pixel = [pixelsByPosition objectAtCoordinates:(unsigned)(aPoint.x), (unsigned)(aPoint.y)]; 159// if ([[pixel color] alphaComponent] == 0) { 160// return nil; 161// } 162 return [self _pixelAtX:aPoint.x y:aPoint.y]; 163} 164 165- pixelAtX:(unsigned int)x y:(unsigned int)y 166{ 167 return [self _pixelAtX:[self correctX:x] y:[self correctY:y]]; 168} 169 170- pixelAtPoint:(NSPoint)aPoint 171{ 172 NSPoint corrected = [self correct:aPoint]; 173 return [self _pixelAtPoint:corrected]; 174} 175 176- (void)setPixel:aPixel atPoint:(NSPoint)aPoint 177{ 178 NSPoint corrected = [self correct:aPoint]; 179 [pixelsByPosition setObject:aPixel atCoordinates:(unsigned)(corrected.x), (unsigned)(corrected.y)]; 180} 181 182- (void)setPixel:aPixel atPoints:(NSArray *)points 183{ 184 id enumerator = [points objectEnumerator]; 185 id current; 186 while ( ( current = [enumerator nextObject] ) ) 187 { 188 [self setPixel:aPixel atPoint:[current pointValue]]; 189 } 190} 191 192- (void)setColor:aColor atPoint:(NSPoint)aPoint 193{ 194 [self setPixel:[self pixelOfColor:aColor] atPoint:aPoint]; 195} 196 197- (void)setColor:(NSColor *)aColor atPoints:(NSArray *)points 198{ 199 [self setPixel:[self pixelOfColor:aColor] atPoints:points]; 200} 201 202- (void)replacePixelsOfColor:oldColor withColor:newColor 203{ 204 id pixel = [self pixelOfColor:oldColor]; 205 if((pixel == nil) || ([oldColor isEqual:newColor])) { return; } 206 [pixel setColor:newColor]; 207 [pixelsByColor setObject:pixel forKey:newColor]; 208 [pixelsByColor removeObjectForKey:oldColor]; 209} 210 211- (void)translateXBy:(float)amountX yBy:(float)amountY 212{ 213 id newMatrix = [KTMutableMatrix matrixWithCapacity:512*512 cuboidBounds:(unsigned)(width), (unsigned)(height), 0, 0]; 214 int i, j; 215 for(i = 0; i < width; i++) 216 { 217 for(j = 0; j < height; j++) 218 { 219 float newX = i + amountX, newY = j + amountY; 220 if(!(newX < 0 || newX >= width || newY < 0 || newY >= height)) 221 { 222 [newMatrix setObject:[self pixelAtPoint:NSMakePoint(i, j)] atCoordinates:(unsigned)newX, (unsigned)newY]; 223 } 224 } 225 } 226 [pixelsByPosition release]; 227 pixelsByPosition = [newMatrix retain]; 228} 229 230 231- (void)flipHorizontally 232{ 233 id newMatrix = [KTMutableMatrix matrixWithCapacity:512*512 cuboidBounds:(unsigned)(width), (unsigned)(height), 0, 0]; 234 int i, j; 235 for(i = 0; i < width; i++) 236 { 237 for(j = 0; j < height; j++) 238 { 239 [newMatrix setObject:[self pixelAtPoint:NSMakePoint(width - i - 1, j)] atCoordinates:(unsigned)i, (unsigned)j]; 240 } 241 } 242 [pixelsByPosition release]; 243 pixelsByPosition = [newMatrix retain]; 244} 245 246- (void)flipVertically 247{ 248 id newMatrix = [KTMutableMatrix matrixWithCapacity:512*512 cuboidBounds:(unsigned)(width), (unsigned)(height), 0, 0]; 249 int i, j; 250 for(i = 0; i < width; i++) 251 { 252 for(j = 0; j < height; j++) 253 { 254 [newMatrix setObject:[self pixelAtPoint:NSMakePoint(i, height - j - 1)] atCoordinates:(unsigned)i, (unsigned)j]; 255 } 256 } 257 [pixelsByPosition release]; 258 pixelsByPosition = [newMatrix retain]; 259} 260 261- (void)drawRect:(NSRect)rect withOpacity:(double)anOpacity fixBug:(BOOL)fixBug 262{ 263 unsigned int startX = MAX(rect.origin.x-1, 0), startY = MAX(rect.origin.y-1, 0); 264 unsigned int offsetX = startX+rect.size.width+1, offsetY = startY+rect.size.height+1; 265 unsigned int i, j; 266 unsigned int count = 0; 267 SEL colorAtXYselector = @selector(colorAtX:y:); 268 IMP colorAtXY = [self methodForSelector:colorAtXYselector]; 269 270 for(i = startX; (i < offsetX) && (i < width); i++) 271 { 272 for(j = startY; (j < offsetY) && (j < height); j++) 273 { 274 rects[count] = NSMakeRect(i,j,1,1); 275 colors[count] = colorAtXY(self, colorAtXYselector, i, j); 276 if (colors[count] == nil) 277 { 278 colors[count] = [NSColor clearColor]; 279 } 280 if (anOpacity != 1) 281 { 282 colors[count] = [colors[count] colorWithAlphaComponent:[colors[count] alphaComponent] * anOpacity]; 283 } 284 count++; 285 } 286 NSRectFillListWithColorsUsingOperation(rects, colors, count, fixBug ? NSCompositeDestinationOver : NSCompositeSourceAtop); 287 count = 0; 288 } 289} 290 291- description 292{ 293 int i, j; 294 NSString * result = @""; 295 for (i = 0; i < width; i++) 296 { 297 for (j = 0; j < height; j++) 298 { 299 result = [result stringByAppendingFormat:@"(%d,%d): %@, ", i, j, [self colorAtPoint:NSMakePoint(i,j)]]; 300 } 301 } 302 return result; 303} 304 305- (void)compositeUnder:anImage 306{ 307 NSImage * compositedImage = [[[NSImage alloc] initWithSize:[self size]] autorelease]; 308 NSRect fullRect = NSMakeRect(0,0,width,height); 309 NSPoint point; 310 int i, j; 311 id pool; 312 313 [compositedImage lockFocus]; 314 [anImage drawRect:fullRect withOpacity:1 fixBug:YES]; 315 [self drawRect:fullRect withOpacity:1 fixBug:YES]; 316 for(i = 0; i < width; i++) 317 { 318 pool = [[NSAutoreleasePool alloc] init]; 319 for(j = 0; j < height; j++) 320 { 321 point = NSMakePoint(i, j); 322 [self setColor:NSReadPixel(point) atPoint:point]; 323 } 324 [pool release]; 325 } 326 [compositedImage unlockFocus]; 327} 328 329- (void)setPixelsByColor:newPixelsByColor 330{ 331 [newPixelsByColor retain]; 332 [pixelsByColor release]; 333 pixelsByColor = newPixelsByColor; 334} 335 336- (void)setPixelsByPosition:newPixelsByPosition 337{ 338 [newPixelsByPosition retain]; 339 [pixelsByPosition release]; 340 pixelsByPosition = newPixelsByPosition; 341} 342 343- copyWithZone:(NSZone *)zone 344{ 345 id new = [[[self class] allocWithZone:zone] initWithSize:[self size]]; 346 /*we're not using this approach because there seems to be a weirdness in -[NSMutableDictionary mutableCopy] that makes the mutably copied dictionary worthless-- it doesn't retain its objects! At least, there were crashes when we used that method that didn't show up when we did -(void)setColor:atPoint: for each pixel. 347 [new setPixelsByColor:[[pixelsByColor mutableCopyWithZone:zone] autorelease]]; 348 [new setPixelsByPosition:[[pixelsByPosition mutableCopyWithZone:zone] autorelease]]; 349*/ 350 unsigned int i, j; 351 for(i = 0; i < width; i++) 352 { 353 for(j = 0; j < height; j++) 354 { 355 [new setColor:[self colorAtPoint:NSMakePoint(i, j)] atPoint:NSMakePoint(i, j)]; 356 } 357 } 358 return new; 359} 360 361@end 362 363 364@implementation PXImage(Archiving) 365 366NSString * PXCurrentVersion = @"r2v4"; 367 368- legacyDiscoverPixelsByPositionFromPositionsByPixel:positionsByPixel 369{ 370 id newPixelsByPosition = [NSMutableDictionary dictionaryWithCapacity:10000]; 371 id pixelEnumerator = [positionsByPixel keyEnumerator]; 372 id currentPixel; 373 while ( ( currentPixel = [pixelEnumerator nextObject] ) ) 374 { 375 id positionEnumerator = [[positionsByPixel objectForKey:currentPixel] objectEnumerator]; 376 id currentPosition; 377 while ( ( currentPosition = [positionEnumerator nextObject] ) ) 378 { 379 [newPixelsByPosition setObject:currentPixel forKey:currentPosition]; 380 } 381 } 382 return newPixelsByPosition; 383} 384 385- (NSSize)legacyDiscoverSizeFromPixelsByPosition:pixels 386{ 387 float w = 0, h = 0; 388 id enumerator = [pixels keyEnumerator]; 389 id current; 390 while ( ( current = [enumerator nextObject] ) ) 391 { 392 NSPoint point = NSPointFromString(current); 393 if(point.x > w) { w = point.x; } 394 if(point.y > h) { h = point.y; } 395 } 396 return NSMakeSize(w+1, h+1); 397} 398 399- legacyDiscoverPixelsByPositionMatrixFromPixelsByPositionDictionary:pixels 400{ 401 id realPixelsByPosition = [KTMutableMatrix matrixWithCapacity:512*512 cuboidBounds:(unsigned)(width), (unsigned)(height), 0, 0]; 402 int i, j; 403 for(i = 0; i < width; i++) 404 { 405 for(j = 0; j < height; j++) 406 { 407 NSPoint point = NSMakePoint(i, j); 408 id pixel = [pixels objectForKey:NSStringFromPoint(point)]; 409 //if(pixel == nil) { pixel = [self pixelOfColor:[NSColor clearColor]]; } 410 [realPixelsByPosition setObject:[pixel retain] atCoordinates:(unsigned)(point.x), (unsigned)(point.y)]; 411 } 412 } 413 return realPixelsByPosition; 414} 415 416- legacyInitWithCoder:coder 417{ 418 id positionsByPixel = [coder decodeObjectForKey:@"positionsByPixel"]; 419 if(positionsByPixel != nil) { pixelsByPosition = [self legacyDiscoverPixelsByPositionFromPositionsByPixel:positionsByPixel]; } 420 if(NSEqualSizes([self size], NSZeroSize)) { 421 NSSize imageSize = [self legacyDiscoverSizeFromPixelsByPosition:pixelsByPosition]; 422 width = imageSize.width; 423 height = imageSize.height; 424 } 425 pixelsByPosition = [[self legacyDiscoverPixelsByPositionMatrixFromPixelsByPositionDictionary:pixelsByPosition] retain]; 426 return self; 427} 428 429- initWithCoder:coder 430{ 431 [super init]; 432 pixelsByColor = [[coder decodeObjectForKey:@"pixelsByColor"] mutableCopy]; 433 pixelsByPosition = [[coder decodeObjectForKey:@"pixelsByPosition"] mutableCopy]; 434 NSSize imageSize = [coder decodeSizeForKey:@"size"]; 435 width = imageSize.width; 436 height = imageSize.height; 437 //if(![PXCurrentVersion isEqual:[coder decodeObjectForKey:@"version"]]) 438 { 439 [self legacyInitWithCoder:coder]; 440 } 441 rects = malloc(sizeof(NSRect) * (height + 2)); 442 colors = malloc(sizeof(NSColor *) * (height + 2)); 443 return self; 444} 445 446- (void)encodeWithCoder:coder 447{ 448 //can't encode version until KTMatrix works properly. we're still faking encoding in the old format. sigh. 449 [coder encodeObject:PXCurrentVersion forKey:@"version"]; 450 [coder encodeSize:[self size] forKey:@"size"]; 451 [coder encodeObject:pixelsByColor forKey:@"pixelsByColor"]; 452 //[coder encodeObject:pixelsByPosition forKey:@"pixelsByPosition"]; 453 id fakePixelsByPosition = [NSMutableDictionary dictionaryWithCapacity:50000]; 454 unsigned i, j; 455 for(i = 0; i < width; i++) 456 { 457 for(j = 0; j < height; j++) 458 { 459 id pixel = [pixelsByPosition objectAtCoordinates:i, j]; 460 if(pixel != nil) { [fakePixelsByPosition setObject:pixel forKey:NSStringFromPoint(NSMakePoint(i, j))]; } 461 } 462 } 463 [coder encodeObject:fakePixelsByPosition forKey:@"pixelsByPosition"]; 464} 465 466@end 467