API  0.9.10
CPImage.j
Go to the documentation of this file.
1 /*
2  * CPImage.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 @protocol CPImageDelegate <CPObject>
27 
28 @optional
29 - (void)imageDidLoad:(CPImage)anImage;
30 - (void)imageDidError:(CPImage)anImage;
31 - (void)imageDidAbort:(CPImage)anImage;
32 
33 @end
34 
38 
46 
47 CPImageDidLoadNotification = @"CPImageDidLoadNotification";
48 
49 // Image Names
50 CPImageNameColorPanel = @"CPImageNameColorPanel";
51 CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted";
52 
53 var imagesForNames = { },
55  ImageDescriptionFormat = "%s {\n filename: \"%s\",\n size: { width:%f, height:%f }\n}";
56 
57 AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0);
58 AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0);
59 
72 function CPImageInBundle()
73 {
74  var filename = arguments[0],
75  size = nil,
76  bundle = nil;
77 
78  if (typeof(arguments[1]) === "number")
79  {
80  if (arguments[1] !== nil && arguments[1] !== undefined)
81  size = CGSizeMake(arguments[1], arguments[2]);
82 
83  bundle = arguments[3];
84  }
85  else if (typeof(arguments[1]) === "object")
86  {
87  size = arguments[1];
88  bundle = arguments[2];
89  }
90 
91  if (!bundle)
92  bundle = [CPBundle mainBundle];
93 
94  if (size)
95  return [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:filename] size:size];
96 
97  return [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:filename]];
98 }
99 
100 function CPAppKitImage(aFilename, aSize)
101 {
102  return CPImageInBundle(aFilename, aSize, [CPBundle bundleForClass:[CPView class]]);
103 }
104 
125 @implementation CPImage : CPObject
126 {
127  CGSize _size;
128  CPString _filename;
129  CPString _name;
130 
131  id <CPImageDelegate> _delegate;
132  unsigned _loadStatus;
133  unsigned _implementedDelegateMethods;
134 
135  Image _image;
136 }
137 
138 - (id)init
139 {
140  return [self initByReferencingFile:@"" size:CGSizeMake(-1, -1)];
141 }
142 
152 - (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
153 {
154  // Quietly return nil like in Cocoa, rather than crashing later.
155  if (aFilename === undefined || aFilename === nil)
156  return nil;
157 
158  self = [super init];
159 
160  if (self)
161  {
162  _size = CGSizeMakeCopy(aSize);
163  _filename = aFilename;
164  _loadStatus = CPImageLoadStatusInitialized;
165  }
166 
167  return self;
168 }
169 
176 - (id)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize
177 {
178  self = [self initByReferencingFile:aFilename size:aSize];
179 
180  if (self)
181  [self load];
182 
183  return self;
184 }
185 
192 - (id)initWithContentsOfFile:(CPString)aFilename
193 {
194  self = [self initByReferencingFile:aFilename size:CGSizeMake(-1, -1)];
195 
196  if (self)
197  [self load];
198 
199  return self;
200 }
201 
207 - (id)initWithData:(CPData)someData
208 {
209  var base64 = [someData base64],
210  type = [base64 hasPrefix:@"/9j/4AAQSkZJRgABAQEASABIAAD/"] ? @"jpg" : @"png",
211  dataURL = "data:image/" + type + ";base64," + base64;
212 
213  return [self initWithContentsOfFile:dataURL];
214 }
215 
220 {
221  return _filename;
222 }
223 
229 {
230 #if PLATFORM(DOM)
231  var dataURL;
232 
233  if ([_filename hasPrefix:@"data:image"])
234  dataURL = _filename;
236  {
237  var canvas = document.createElement("canvas"),
238  ctx = canvas.getContext("2d");
239 
240  canvas.width = _image.width,
241  canvas.height = _image.height;
242 
243  ctx.drawImage(_image, 0, 0);
244 
245  dataURL = canvas.toDataURL("image/png");
246  }
247  else
248  return nil;
249 
250  var base64 = dataURL.replace(/^data:image\/png;base64,/, "");
251  return [CPData dataWithBase64:base64];
252 #endif
253 }
254 
259 - (void)setSize:(CGSize)aSize
260 {
261  _size = CGSizeMakeCopy(aSize);
262 }
263 
267 - (CGSize)size
268 {
269  return CGSizeMakeCopy(_size);
270 }
271 
272 + (id)imageNamed:(CPString)aName
273 {
274  var image = imagesForNames[aName];
275 
276  if (image)
277  return image;
278 
279  var imageOrSize = AppKitImageForNames[aName];
280 
281  if (!imageOrSize)
282  return nil;
283 
284  if (!imageOrSize.isa)
285  {
286  imageOrSize = CPAppKitImage("CPImage/" + aName + ".png", imageOrSize);
287 
288  [imageOrSize setName:aName];
289 
290  AppKitImageForNames[aName] = imageOrSize;
291  }
292 
293  return imageOrSize;
294 }
295 
296 - (BOOL)setName:(CPString)aName
297 {
298  if (_name === aName)
299  return YES;
300 
301  if (imagesForNames[aName])
302  return NO;
303 
304  _name = aName;
305 
306  imagesForNames[aName] = self;
307 
308  return YES;
309 }
310 
312 {
313  return _name;
314 }
315 
319 - (Image)image
320 {
321  return _image;
322 }
323 
328 - (void)setDelegate:(id <CPImageDelegate>)aDelegate
329 {
330  if (_delegate === aDelegate)
331  return;
332 
333  _delegate = aDelegate;
334  _implementedDelegateMethods = 0;
335 
336  if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
337  _implementedDelegateMethods |= CPImageDelegate_imageDidLoad_;
338 
339  if ([_delegate respondsToSelector:@selector(imageDidError:)])
340  _implementedDelegateMethods |= CPImageDelegate_imageDidError_;
341 
342  if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
343  _implementedDelegateMethods |= CPImageDelegate_imageDidAbort_;
344 }
345 
349 - (id)delegate
350 {
351  return _delegate;
352 }
353 
357 - (unsigned)loadStatus
358 {
359  return _loadStatus;
360 }
361 
368 - (void)load
369 {
370  if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted)
371  return;
372 
373  _loadStatus = CPImageLoadStatusLoading;
374 
375 #if PLATFORM(DOM)
376  _image = new Image();
377 
378  var isSynchronous = YES;
379 
380  // FIXME: We need a better/performance way of doing this.
381  _image.onload = function ()
382  {
383  if (isSynchronous)
384  window.setTimeout(function() { [self _imageDidLoad]; }, 0);
385  else
386  {
387  [self _imageDidLoad];
388  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
389  }
390  [self _derefFromImage];
391  };
392 
393  _image.onerror = function ()
394  {
395  if (isSynchronous)
396  window.setTimeout(function() { [self _imageDidError]; }, 0);
397  else
398  {
399  [self _imageDidError];
400  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
401  }
402  [self _derefFromImage];
403  };
404 
405  _image.onabort = function ()
406  {
407  if (isSynchronous)
408  window.setTimeout(function() { [self _imageDidAbort]; }, 0);
409  else
410  {
411  [self _imageDidAbort];
412  [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
413  }
414  [self _derefFromImage];
415  };
416 
417  _image.src = _filename;
418 
419  // onload and friends may fire after this point but BEFORE the end of the run loop,
420  // crazy, I know. So don't set isSynchronous here, rather wait a bit longer.
421  window.setTimeout(function() { isSynchronous = NO; }, 0);
422 #endif
423 }
424 
426 {
427  return YES;
428 }
429 
431 {
432  return NO;
433 }
434 
436 {
437  return NO;
438 }
439 
441 {
442  var filename = [self filename],
443  size = [self size];
444 
445  if (filename.indexOf("data:") === 0)
446  {
447  var index = filename.indexOf(",");
448 
449  if (index > 0)
450  filename = [CPString stringWithFormat:@"%s,%s...%s", filename.substr(0, index), filename.substr(index + 1, 10), filename.substr(filename.length - 10)];
451  else
452  filename = "data:<unknown type>";
453  }
454 
455  return [CPString stringWithFormat:ImageDescriptionFormat, [super description], filename, size.width, size.height];
456 }
457 
458 /* @ignore */
459 - (void)_derefFromImage
460 {
461  _image.onload = null;
462  _image.onerror = null;
463  _image.onabort = null;
464 }
465 
466 /* @ignore */
467 - (void)_imageDidLoad
468 {
469  _loadStatus = CPImageLoadStatusCompleted;
470 
471  // FIXME: IE is wrong on image sizes????
472  if (!_size || (_size.width == -1 && _size.height == -1))
473  _size = CGSizeMake(_image.width, _image.height);
474 
476  postNotificationName:CPImageDidLoadNotification
477  object:self];
478 
479  if (_implementedDelegateMethods & CPImageDelegate_imageDidLoad_)
480  [_delegate imageDidLoad:self];
481 }
482 
483 /* @ignore */
484 - (void)_imageDidError
485 {
486  _loadStatus = CPImageLoadStatusReadError;
487 
488  if (_implementedDelegateMethods & CPImageDelegate_imageDidError_)
489  [_delegate imageDidError:self];
490 }
491 
492 /* @ignore */
493 - (void)_imageDidAbort
494 {
495  _loadStatus = CPImageLoadStatusCancelled;
496 
497  if (_implementedDelegateMethods & CPImageDelegate_imageDidAbort_)
498  [_delegate imageDidAbort:self];
499 }
500 
501 @end
502 
503 @implementation CPImage (CPCoding)
504 
510 - (id)initWithCoder:(CPCoder)aCoder
511 {
512  return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
513 }
514 
519 - (void)encodeWithCoder:(CPCoder)aCoder
520 {
521  [aCoder encodeObject:_filename forKey:@"CPFilename"];
522  [aCoder encodeSize:_size forKey:@"CPSize"];
523 }
524 
525 @end
526 
527 @implementation CPThreePartImage : CPObject
528 {
529  CPArray _imageSlices;
530  BOOL _isVertical;
531 }
532 
533 - (id)initWithImageSlices:(CPArray)imageSlices isVertical:(BOOL)isVertical
534 {
535  self = [super init];
536 
537  if (self)
538  {
539  _imageSlices = imageSlices;
540  _isVertical = isVertical;
541  }
542 
543  return self;
544 }
545 
547 {
548  return @"";
549 }
550 
551 - (CPArray)imageSlices
552 {
553  return _imageSlices;
554 }
555 
556 - (BOOL)isVertical
557 {
558  return _isVertical;
559 }
560 
562 {
563  return NO;
564 }
565 
567 {
568  return YES;
569 }
570 
572 {
573  return NO;
574 }
575 
576 @end
577 
578 var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
579  CPThreePartImageIsVerticalKey = @"CPThreePartImageIsVerticalKey";
580 
582 
583 - (id)initWithCoder:(CPCoder)aCoder
584 {
585  self = [super init];
586 
587  if (self)
588  {
589  _imageSlices = [aCoder decodeObjectForKey:CPThreePartImageImageSlicesKey];
590  _isVertical = [aCoder decodeBoolForKey:CPThreePartImageIsVerticalKey];
591  }
592 
593  return self;
594 }
595 
596 - (void)encodeWithCoder:(CPCoder)aCoder
597 {
598  [aCoder encodeObject:_imageSlices forKey:CPThreePartImageImageSlicesKey];
599  [aCoder encodeBool:_isVertical forKey:CPThreePartImageIsVerticalKey];
600 }
601 
602 @end
603 
604 
605 @implementation CPNinePartImage : CPObject
606 {
607  CPArray _imageSlices;
608 }
609 
610 - (id)initWithImageSlices:(CPArray)imageSlices
611 {
612  self = [super init];
613 
614  if (self)
615  _imageSlices = imageSlices;
616 
617  return self;
618 }
619 
621 {
622  return @"";
623 }
624 
625 - (CPArray)imageSlices
626 {
627  return _imageSlices;
628 }
629 
631 {
632  return NO;
633 }
634 
636 {
637  return NO;
638 }
639 
641 {
642  return YES;
643 }
644 
645 @end
646 
647 var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey";
648 
650 
651 - (id)initWithCoder:(CPCoder)aCoder
652 {
653  self = [super init];
654 
655  if (self)
656  _imageSlices = [aCoder decodeObjectForKey:CPNinePartImageImageSlicesKey];
657 
658  return self;
659 }
660 
661 - (void)encodeWithCoder:(CPCoder)aCoder
662 {
663  [aCoder encodeObject:_imageSlices forKey:CPNinePartImageImageSlicesKey];
664 }
665 
666 @end
CPData data()
Definition: CPImage.j:228
CPImageLoadStatusUnexpectedEOF
Definition: CPImage.j:44
function CPImageInBundle()
Definition: CPImage.j:72
var AppKitImageForNames
Definition: CPImage.j:54
BOOL isSingleImage()
Definition: CPImage.j:425
CPImageLoadStatusLoading
Definition: CPImage.j:40
var CPThreePartImageIsVerticalKey
Definition: CPImage.j:579
CGSize size()
Definition: CPImage.j:267
The main run loop for the application.
Definition: CPRunLoop.h:2
var ImageDescriptionFormat
Definition: CPImage.j:55
CPImageNameColorPanelHighlighted
Definition: CPImage.j:51
CPImageLoadStatusCompleted
Definition: CPImage.j:41
var CPNinePartImageImageSlicesKey
Definition: CPImage.j:647
A Cappuccino wrapper for any data type.
Definition: CPData.h:2
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
var CPImageDelegate_imageDidError_
Definition: CPImage.j:36
CPImageLoadStatusInvalidData
Definition: CPImage.j:43
CPImageNameColorPanel
Definition: CPImage.j:50
CPString description()
Definition: CPObject.j:358
CPNotificationCenter defaultCenter()
id delegate()
Definition: CPImage.j:349
BOOL isNinePartImage()
Definition: CPImage.j:435
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
CPString name()
Definition: CPImage.j:311
An immutable string (collection of characters).
Definition: CPString.h:2
CPBundle mainBundle()
Definition: CPBundle.j:82
var CPThreePartImageImageSlicesKey
Definition: CPImage.j:578
CPData dataWithBase64:(CPString aString)
Definition: CPData.j:73
Definition: CPImage.h:2
function CPFeatureIsCompatible(aFeature)
CPImageDidLoadNotification
Definition: CPImage.j:47
id initWithContentsOfFile:size:(CPString aFilename, [size] CGSize aSize)
Definition: CPImage.j:176
function CPAppKitImage(aFilename, aSize)
Definition: CPImage.j:100
CPImageLoadStatusReadError
Definition: CPImage.j:45
id init()
Definition: CPImage.j:138
id initWithContentsOfFile:(CPString aFilename)
Definition: CPImage.j:192
CPHTMLCanvasFeature
CPDate limitDateForMode:(CPString aMode)
Definition: CPRunLoop.j:342
CPString filename()
Definition: CPImage.j:219
Image image()
Definition: CPImage.j:319
CPImageLoadStatusCancelled
Definition: CPImage.j:42
unsigned loadStatus()
Definition: CPImage.j:357
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
void load()
Definition: CPImage.j:368
id init()
Definition: CPObject.j:145
Sends messages (CPNotification) between objects.
CPString description()
Definition: CPImage.j:440
CPImageLoadStatusInitialized
Definition: CPImage.j:39
var CPImageDelegate_imageDidAbort_
Definition: CPImage.j:37
var imagesForNames
Definition: CPImage.j:53
id initByReferencingFile:size:(CPString aFilename, [size] CGSize aSize)
Definition: CPImage.j:152
CPString base64()
Definition: CPData.j:141
id alloc()
Definition: CPObject.j:130
var CPImageDelegate_imageDidLoad_
Definition: CPImage.j:35
Definition: CPView.j:136
id stringWithFormat:(CPString format, [,] ...)
Definition: CPString.j:166
BOOL isThreePartImage()
Definition: CPImage.j:430