{"id":15,"date":"2009-04-20T12:50:31","date_gmt":"2009-04-20T17:50:31","guid":{"rendered":"http:\/\/developer.casgrain.com\/?p=15"},"modified":"2009-05-18T11:51:38","modified_gmt":"2009-05-18T16:51:38","slug":"day-two-ichibi-and-imagesoup","status":"publish","type":"post","link":"https:\/\/developer.casgrain.com\/?p=15","title":{"rendered":"Day Two: iChibi and ImageSoup"},"content":{"rendered":"<h2>Six Days of Cocoa: Day Two<\/h2>\n<h3>Day Two: <i>code-name iChibi<\/i><\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/developer.casgrain.com\/images\/Flele_playing_4-5_img1.png\" width=\"260\" height=\"420\" alt=\"\" align=\"right\" hspace=\"12\"\/><\/p>\n<p>My whole family is very much into all things japanese (including, of course, <em>manga<\/em> and <em>anime<\/em>). The other day, they found a little &#8220;sound-playing ghost&#8221; called <a href=\"http:\/\/navy.nm.land.to\/ukgk\/flele\/\">Flele<\/a> (I have no idea what the name means).<\/p>\n<p>\nAs far as I can tell, Flele is a small application that you drop one or more MP3s on, and it starts to &#8220;sing&#8221; when you click on the character&#8217;s hair. I have no idea how it actually works, since it requires Windows and the Japanese language pack. But my daughter and I quickly hashed out that what we could do would be a globally-floating iTunes controller, who would respond to click and animate when music was playing (we&#8217;ll figure out the &#8220;singing&#8221; part later).<\/p>\n<h4>iChibi<\/h4>\n<p>My <a href=\"http:\/\/cookieschao.deviantart.com\/\">daughter<\/a> drew lots of PNGs this weekend (using <a href=\"http:\/\/www.corel.com\/painter\/\">Painter<\/a>, of course), and we assembled them in sequences to make animations. The iChibi can blink, and lights turn on\/off on its headphones whenever music is playing.<\/p>\n<p>\niChibi can be dragged anywhere on screen, and will float on top of all windows.<\/p>\n<p>\nClicking on its left ear starts playing in iTunes, on the right ear stops playing. The left headphone goes to the previous track, the right headphone to the next track.<br \/>\n<br \/>\nClicking on the zipper gives a Settings panel, where you can control the its size, opacity and which playlist the songs are coming from.<\/p>\n<p>\nThe higher the song rating, the happier it will look.<\/p>\n<p>\niTunes support is done through the excellent <a href=\"http:\/\/code.google.com\/p\/eyetunes\/\">Eyetunes Framework<\/a>.<\/p>\n<p>\niChibi works on MacOSX 10.5 and later.<\/p>\n<p>\nIf you are interested in following the development of iChibi, follow <code><a href=\"http:\/\/twitter.com\/ichibiapp\">@ichibiapp<\/a><\/code> on Twitter.<\/p>\n<p><br clear=\"all\"\/><\/p>\n<h4>ImageSoup<\/h4>\n<p>Since iChibi uses lots of images for animations, and I didn&#8217;t want to keep multiple copies of the same image in memory (for example, if an image is re-used in multiple animation loops), I figured I should make a small class to hold all the images, loading them as necessary. I named this class <code>ImageSoup<\/code>, referring to the Newton&#8217;s filesystem of course&#8230;<\/p>\n<p>\n<code>ImageSoup<\/code> is very simple, and does not need to be its own class (after all, it&#8217;s a dictionary). But it is nice to be able to abstract that implementation detail out of your code, as well as the image-loading code. What if your source image was an <a href=\"http:\/\/flyingmeat.com\/acorn\/\">Acorn<\/a> image or a Painter RIFF? You could hide the image-loading code in ImageSoup, and your calling code need not be aware of this.<\/p>\n<p>\nTo use <code>ImageSoup<\/code>, just create an <code>ImageSoup*<\/code> instance and always ask it to load your images (using the full path to the image):<\/p>\n<pre>\n#import \"ImageSoup.h\"\n\n[...]\n\nImageSoup* allImages = [[ImageSoup alloc] init];\n\n[...]\n\nNSImage* myImage = [allImages getImageAtPath: @\"Full\/path\/to\/image\"];\n\n<\/pre>\n<p>If the image exists and has been loaded previously, <code>ImageSoup<\/code> will return it right away. If the image does not exist, <code>ImageSoup<\/code> will load it and store it for future reference.<br \/>\n<br \/>\nWhen you release your <code>ImageSoup*<\/code>, all its images will be released.<\/p>\n<p>\n<strong>ImageSoup.h<\/strong><\/p>\n<pre>\n\/\/\n\/\/  ImageSoup.h\n\/\/  iChibi\n\/\/\n\/\/  Created by Philippe on 09-04-19.\n\/\/  Copyright 2009 Philippe Casgrain. All rights reserved.\n\/\/\n\n#import &lt;Cocoa\/Cocoa.h&gt;\n\n@interface ImageSoup : NSObject\n{\n  NSMutableDictionary* _images;\n}\n\n- (NSImage*) getImageAtPath: (NSString*) path;\n\n@end\n<\/pre>\n<p>\n<strong>ImageSoup.m<\/strong><\/p>\n<pre>\n\/\/\n\/\/  ImageSoup.m\n\/\/  iChibi\n\/\/\n\/\/  Created by Philippe on 09-04-19.\n\/\/  Copyright 2009 Philippe Casgrain. All rights reserved.\n\/\/\n\n#import \"ImageSoup.h\"\n\n@implementation ImageSoup\n\n- (id) init\n{\n  self = [super init];\n  if (self != nil) \n  {\n    _images = [[NSMutableDictionary dictionaryWithCapacity: 0] retain];\n  }\n  return self;\n}\n\n- (void) dealloc\n{\n  [_images removeAllObjects];\n  [_images release];\n  [super dealloc];\n}\n\n\n- (NSImage*) getImageAtPath: (NSString*) path\n{\n  NSImage* retrievedImage = [_images valueForKey: path];\n  if (retrievedImage == nil)\n  {\n    NSImage* image = [[NSImage alloc] initWithContentsOfFile: path];\n    if (image)\n      [_images setObject: image forKey: path];\n    [image release];\n    retrievedImage = image;\n  }\n  return retrievedImage;\n}\n\n@end\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Six Days of Cocoa: Day Two Day Two: code-name iChibi My whole family is very much into all things japanese (including, of course, manga and anime). The other day, they found a little &#8220;sound-playing ghost&#8221; called Flele (I have no idea what the name means). As far as I can tell, Flele is a small [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,6,8],"tags":[],"class_list":["post-15","post","type-post","status-publish","format-standard","hentry","category-graphics","category-leopard","category-sixdays"],"_links":{"self":[{"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts\/15","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=15"}],"version-history":[{"count":2,"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions"}],"predecessor-version":[{"id":22,"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions\/22"}],"wp:attachment":[{"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}