2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / deepzoomimagetilesource.cpp
blob71124f07c4ba0fb868d0d5198acef1940f5e23da
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * deepzoomimagetilesource.cpp
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007,2009 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <stdio.h>
17 #include <math.h>
19 #include "debug.h"
20 #include "runtime.h"
22 #include "deepzoomimagetilesource.h"
23 #include "multiscalesubimage.h"
24 #include "uri.h"
25 #include "file-downloader.h"
27 class DisplayRect
29 public:
30 long min_level;
31 long max_level;
32 Rect rect;
34 DisplayRect (long minLevel, long maxLevel)
36 min_level = minLevel;
37 max_level = maxLevel;
41 class SubImage
43 public:
44 int id;
45 int n;
46 Uri *source;
47 long width;
48 long height;
49 double vp_x;
50 double vp_y;
51 double vp_w;
53 bool has_viewport;
54 bool has_size;
56 SubImage ()
58 source = NULL;
59 has_viewport = false;
60 has_size = false;
64 class DZParserinfo
66 public:
67 int depth;
68 int skip;
69 bool error;
70 DeepZoomImageTileSource *source;
72 bool isCollection;
74 //Image attributes
75 int overlap;
76 long image_width, image_height;
77 DisplayRect *current_rect;
78 GList *display_rects;
80 //Collection attributes
81 int max_level;
82 SubImage *current_subimage;
83 GList *sub_images;
85 //Common attributes
86 char *format;
87 int tile_size;
90 DZParserinfo ()
92 depth = 0;
93 skip = -1;
94 error = false;
95 format = NULL;
96 image_width = image_height = tile_size = overlap = 0;
97 current_rect = NULL;
98 display_rects = NULL;
99 sub_images = NULL;
100 current_subimage = NULL;
101 source = NULL;
105 void start_element (void *data, const char *el, const char **attr);
106 void end_element (void *data, const char *el);
108 bool
109 get_tile_layer (int level, int x, int y, Uri *uri, void *userdata)
111 return ((DeepZoomImageTileSource *)userdata)->GetTileLayer (level, x, y, uri);
114 void
115 DeepZoomImageTileSource::Init ()
117 SetObjectType (Type::DEEPZOOMIMAGETILESOURCE);
119 downloaded = false;
120 parsed = false;
121 format = NULL;
122 get_tile_func = get_tile_layer;
123 display_rects = NULL;
124 parsed_callback = NULL;
125 failed_callback = NULL;
126 sourcechanged_callback = NULL;
127 isCollection = false;
128 subimages = NULL;
129 nested = false;
130 get_resource_aborter = NULL;
131 parser = NULL;
134 DeepZoomImageTileSource::DeepZoomImageTileSource ()
136 Init ();
139 DeepZoomImageTileSource::DeepZoomImageTileSource (Uri *uri, bool nested)
141 Init ();
142 this->nested = nested;
143 strip_and_set_uri (uri);
146 //this is only meant to be used at construct time, if the uri is set from xaml, or using new DeepZoomImageTileSource (Uri)
147 // drt511 shows the new DeepZoomImageTileSource (Uri) behavior
148 // drt509 shows that sources set by setting the DP shouldn't be stripped
149 // http://www.silverlightshow.net/showcase/deepzoom/TestPage.html creates from xaml, and need ot be stripped
151 void
152 DeepZoomImageTileSource::strip_and_set_uri (Uri *uri)
154 if (!uri)
155 return;
156 char *s = uri->ToString ();
157 Uri stripped = Uri ();
158 if (g_str_has_prefix (s, "/"))
159 stripped.Parse (s + 1);
160 else
161 stripped.Parse (s);
162 SetValue (DeepZoomImageTileSource::UriSourceProperty, new Value (stripped));
165 DeepZoomImageTileSource::~DeepZoomImageTileSource ()
167 Abort ();
168 g_free (format);
169 delete get_resource_aborter;
172 void
173 DeepZoomImageTileSource::Abort ()
175 if (get_resource_aborter)
176 get_resource_aborter->Cancel ();
177 if (parser)
178 XML_ParserFree (parser);
179 parser = NULL;
182 bool
183 DeepZoomImageTileSource::GetTileLayer (int level, int x, int y, Uri *uri)
185 //check if there tile is listed in DisplayRects
186 if (display_rects) {
187 DisplayRect *cur;
188 int i =0;
189 bool found = false;
190 int layers;
192 frexp ((double) MAX (GetImageWidth (), GetImageHeight ()), &layers);
194 while ((cur = (DisplayRect*)g_list_nth_data (display_rects, i))) {
195 i++;
197 if (!(cur->min_level <= level && level <= cur->max_level))
198 continue;
200 int vtilesize = GetTileWidth () * (layers + 1 - level);
201 Rect virtualtile = Rect (x * vtilesize, y * vtilesize, vtilesize, vtilesize);
202 if (cur->rect.IntersectsWith (virtualtile)) {
203 found = true;
204 break;
208 if (!found)
209 return false;
212 const Uri *baseUri = GetValue (DeepZoomImageTileSource::UriSourceProperty)->AsUri ();
213 const char *filename, *ext;
214 char *image;
216 if (!baseUri)
217 return false;
219 if ((filename = strrchr (baseUri->path, '/')))
220 filename ++;
221 else
222 filename = baseUri->path;
224 if (!(ext = strrchr (filename, '.')))
225 return false;
227 image = g_strdup_printf ("%.*s_files/%d/%d_%d.%s", ext - filename, filename, level, x, y, format);
229 Uri::Copy (baseUri, uri);
230 uri->Combine (image);
231 g_free (image);
233 return true;
236 static void
237 resource_notify (NotifyType type, gint64 args, gpointer user_data)
239 DeepZoomImageTileSource *dzits = (DeepZoomImageTileSource *)user_data;
240 if (type == NotifyFailed)
241 dzits->DownloaderFailed ();
242 else if (type == NotifyCompleted)
243 dzits->DownloaderComplete ();
246 static void
247 dz_write (void *buffer, gint32 offset, gint32 n, gpointer data)
249 DeepZoomImageTileSource *dzits = (DeepZoomImageTileSource *) data;
250 dzits->XmlWrite ((char *) buffer, offset, n);
253 void
254 DeepZoomImageTileSource::XmlWrite (char* buffer, gint32 offset, gint32 n)
256 if (offset == 0) {
257 //Init xml parser
258 LOG_MSI ("Start parsing DeepZoom\n");
259 parser = XML_ParserCreate (NULL);
260 XML_SetElementHandler (parser, start_element, end_element);
261 DZParserinfo *info = new DZParserinfo ();
262 info->source = this;
263 XML_SetUserData (parser, info);
266 if (!XML_Parse (parser, buffer, n, 0)) {
267 printf ("Parser error at line %d:\n%s\n", (int)XML_GetCurrentLineNumber (parser), XML_ErrorString(XML_GetErrorCode(parser)));
268 Abort ();
269 DownloaderFailed ();
273 void
274 DeepZoomImageTileSource::Download ()
276 LOG_MSI ("DZITS::Download ()\n");
277 if (downloaded)
278 return;
280 Application *current = Application::GetCurrent ();
281 Uri *uri = GetUriSource ();
283 if (current && uri) {
284 downloaded = true;
285 if (get_resource_aborter)
286 delete get_resource_aborter;
287 get_resource_aborter = new Cancellable ();
288 current->GetResource (GetResourceBase(), uri, resource_notify, dz_write, MediaPolicy, get_resource_aborter, this);
292 void
293 DeepZoomImageTileSource::DownloaderComplete ()
295 //set isFinal for the parser to complete
296 if (!XML_Parse (parser, NULL, 0, 1)) {
297 printf ("Parser error at line %d:\n%s\n", (int)XML_GetCurrentLineNumber (parser), XML_ErrorString(XML_GetErrorCode(parser)));
298 Abort ();
299 DownloaderFailed ();
300 return;
303 DZParserinfo *info = (DZParserinfo *)XML_GetUserData (parser);
305 if (!info->isCollection) {
306 SetImageWidth (info->image_width);
307 SetImageHeight (info->image_height);
308 SetTileOverlap (info->overlap);
309 display_rects = info->display_rects;
310 } else {
311 subimages = info->sub_images;
312 isCollection = info->isCollection;
313 maxLevel = info->max_level;
316 SetTileWidth (info->tile_size);
317 SetTileHeight (info->tile_size);
318 format = g_strdup (info->format);
320 parsed = true;
322 LOG_MSI ("Done parsing...\n");
324 XML_ParserFree (parser);
325 parser = NULL;
327 if (parsed_callback)
328 parsed_callback (cb_userdata);
331 void
332 DeepZoomImageTileSource::DownloaderFailed ()
334 LOG_MSI ("DZITS::dl failed\n");
335 if (failed_callback)
336 failed_callback (cb_userdata);
339 void
340 DeepZoomImageTileSource::UriSourceChanged ()
342 parsed = false;
343 downloaded = false;
344 if (!nested) {
345 Download ();
348 if (sourcechanged_callback)
349 sourcechanged_callback (cb_userdata);
352 void
353 DeepZoomImageTileSource::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
355 if (args->GetId () == DeepZoomImageTileSource::UriSourceProperty) {
356 Abort ();
357 UriSourceChanged ();
360 if (args->GetProperty ()->GetOwnerType () != Type::DEEPZOOMIMAGETILESOURCE) {
361 DependencyObject::OnPropertyChanged (args, error);
362 return;
365 NotifyListenersOfPropertyChange (args, error);
368 //DeepZoomParsing
369 void
370 start_element (void *data, const char *el, const char **attr)
372 DZParserinfo *info = (DZParserinfo*)data;
374 if (info->skip >= 0) {
375 (info->depth)++;
376 return;
378 switch (info->depth) {
379 case 0:
380 //Image or Collection
381 if (!g_ascii_strcasecmp ("Image", el)) {
382 info->isCollection = false;
383 int i;
384 for (i = 0; attr[i]; i+=2)
385 if (!g_ascii_strcasecmp ("Format", attr[i]))
386 info->format = g_strdup (attr[i+1]);
387 else if (!g_ascii_strcasecmp ("TileSize", attr[i]))
388 info->tile_size = atoi (attr[i+1]);
389 else if (!g_ascii_strcasecmp ("Overlap", attr[i]))
390 info->overlap = atoi (attr[i+1]);
391 else
392 LOG_MSI ("\tunparsed attr %s: %s\n", attr[i], attr[i+1]);
393 } else if (!g_ascii_strcasecmp ("Collection", el)) {
394 info->isCollection = true;
395 int i;
396 for (i = 0; attr[i]; i+=2)
397 if (!g_ascii_strcasecmp ("Format", attr[i]))
398 info->format = g_strdup (attr[i+1]);
399 else if (!g_ascii_strcasecmp ("TileSize", attr[i]))
400 info->tile_size = atoi (attr[i+1]);
401 else if (!g_ascii_strcasecmp ("MaxLevel", attr[i]))
402 info->max_level = atoi (attr[i+1]);
403 else
404 LOG_MSI ("\tunparsed attr %s: %s\n", attr[i], attr[i+1]);
405 } else {
406 printf ("Unexpected element %s\n", el);
407 info->error = true;
409 break;
410 case 1:
411 if (!info->isCollection) {
412 //Size or DisplayRects
413 if (!g_ascii_strcasecmp ("Size", el)) {
414 int i;
415 for (i = 0; attr[i]; i+=2)
416 if (!g_ascii_strcasecmp ("Width", attr[i]))
417 info->image_width = atol (attr[i+1]);
418 else if (!g_ascii_strcasecmp ("Height", attr[i]))
419 info->image_height = atol (attr[i+1]);
420 else
421 LOG_MSI ("\tunparsed attr %s: %s\n", attr[i], attr[i+1]);
422 } else if (!g_ascii_strcasecmp ("DisplayRects", el)) {
423 //no attributes, only contains DisplayRect element
424 } else {
425 printf ("Unexpected element %s\n", el);
426 info->error = true;
428 } else {
429 if (!g_ascii_strcasecmp ("Items", el)) {
430 //no attributes, only contains <I> elements
431 } else {
432 printf ("Unexpected element %d %s\n", info->depth, el);
433 info->error = true;
436 break;
437 case 2:
438 if (!info->isCollection) {
439 //DisplayRect elts
440 if (!g_ascii_strcasecmp ("DisplayRect", el)) {
441 long min_level, max_level;
442 int i;
443 for (i = 0; attr[i]; i+=2)
444 if (!g_ascii_strcasecmp ("MinLevel", attr[i]))
445 min_level = atol (attr[i+1]);
446 else if (!g_ascii_strcasecmp ("MaxLevel", attr[i]))
447 max_level = atol (attr[i+1]);
448 else
449 LOG_MSI ("\tunparsed arg %s: %s\n", attr[i], attr[i+1]);
450 info->current_rect = new DisplayRect (min_level, max_level);
451 } else {
452 printf ("Unexpected element %s\n", el);
453 info->error = true;
455 } else {
456 if (!g_ascii_strcasecmp ("I", el)) {
457 info->current_subimage = new SubImage ();
458 int i;
459 for (i = 0; attr[i]; i+=2)
460 if (!g_ascii_strcasecmp ("N", attr[i]))
461 info->current_subimage->n = atoi (attr[i+1]);
462 else if (!g_ascii_strcasecmp ("Id", attr[i]))
463 info->current_subimage->id = atoi (attr[i+1]);
464 else if (!g_ascii_strcasecmp ("Source", attr[i])) {
465 info->current_subimage->source = new Uri ();
466 info->current_subimage->source->Parse (attr[i+1]);
467 } else
468 LOG_MSI ("\tunparsed arg %s: %s\n", attr[i], attr[i+1]);
470 } else {
471 printf ("Unexpected element %d %s\n", info->depth, el);
472 info->error = true;
475 break;
476 case 3:
477 if (!info->isCollection) {
478 //Rect elt
479 if (!g_ascii_strcasecmp ("Rect", el)) {
480 if (!info->current_rect) {
481 info->error = true;
482 break;
484 int i;
485 for (i = 0; attr[i]; i+=2)
486 if (!g_ascii_strcasecmp ("X", attr[i]))
487 info->current_rect->rect.x = (double)atol (attr[i+1]);
488 else if (!g_ascii_strcasecmp ("Y", attr[i]))
489 info->current_rect->rect.y = (double)atol (attr[i+1]);
490 else if (!g_ascii_strcasecmp ("Width", attr[i]))
491 info->current_rect->rect.width = (double)atol (attr[i+1]);
492 else if (!g_ascii_strcasecmp ("Height", attr[i]))
493 info->current_rect->rect.height = (double)atol (attr[i+1]);
494 else
495 LOG_MSI ("\tunparsed attr %s: %s\n", attr[i], attr[i+1]);
496 info->display_rects = g_list_append (info->display_rects, info->current_rect);
497 info->current_rect = NULL;
498 } else {
499 printf ("Unexpected element %s\n", el);
500 info->error = true;
502 } else {
503 if (!g_ascii_strcasecmp ("Size", el)) {
504 if (!info->current_subimage) {
505 info->error = true;
506 break;
509 info->current_subimage->has_size = true;
511 int i;
512 for (i = 0; attr [i]; i+=2)
513 if (!g_ascii_strcasecmp ("Width", attr[i]))
514 info->current_subimage->width = atol (attr[i+1]);
515 else if (!g_ascii_strcasecmp ("Height", attr[i]))
516 info->current_subimage->height = atol (attr[i+1]);
517 else
518 LOG_MSI ("\tunparsed attr %s.%s: %s\n", el, attr[i], attr[i+1]);
519 } else if (!g_ascii_strcasecmp ("Viewport", el)) {
520 if (!info->current_subimage) {
521 info->error = true;
522 break;
525 info->current_subimage->has_viewport = true;
527 int i;
528 for (i = 0; attr [i]; i+=2)
529 if (!g_ascii_strcasecmp ("X", attr[i]))
530 info->current_subimage->vp_x = g_ascii_strtod (attr[i+1], NULL);
531 else if (!g_ascii_strcasecmp ("Y", attr[i]))
532 info->current_subimage->vp_y = g_ascii_strtod (attr[i+1], NULL);
533 else if (!g_ascii_strcasecmp ("Width", attr[i]))
534 info->current_subimage->vp_w = g_ascii_strtod (attr[i+1], NULL);
535 else
536 LOG_MSI ("\tunparsed attr %s: %s\n", attr[i], attr[i+1]);
537 } else {
538 printf ("Unexpected element %s\n", el);
539 info->error = true;
542 break;
546 (info->depth)++;
549 void
550 DeepZoomImageTileSource::EndElement (void *data, const char *el)
552 DZParserinfo *info = (DZParserinfo*)data;
553 (info->depth)--;
555 if (info->skip < 0) {
556 switch (info->depth) {
557 case 2:
558 if (info->isCollection)
559 if (!g_ascii_strcasecmp ("I", el)) {
560 DeepZoomImageTileSource *subsource = new DeepZoomImageTileSource (info->current_subimage->source, TRUE);
561 MultiScaleSubImage *subi = new MultiScaleSubImage (info->source->GetUriSource (),
562 subsource,
563 info->current_subimage->id,
564 info->current_subimage->n);
565 subsource->SetImageWidth (info->current_subimage->width);
566 subsource->SetImageHeight (info->current_subimage->height);
567 subsource->format = info->format;
568 if (info->current_subimage->has_viewport) {
569 subi->SetViewportOrigin (new Point (info->current_subimage->vp_x, info->current_subimage->vp_y));
570 subi->SetViewportWidth (info->current_subimage->vp_w);
573 if (info->current_subimage->has_size) {
574 subi->SetValue (MultiScaleSubImage::AspectRatioProperty, Value ((double)info->current_subimage->width/(double)info->current_subimage->height));
576 info->sub_images = g_list_append (info->sub_images, subi);
577 info->current_subimage = NULL;
579 break;
583 if (info->skip == info->depth)
584 info->skip = -1;
587 void
588 end_element (void *data, const char *el)
590 DZParserinfo *info = (DZParserinfo*)data;
591 DeepZoomImageTileSource *dzits = (DeepZoomImageTileSource*)info->source;
592 if (!dzits)
593 g_assert ("That's wrong...\n");
594 dzits->EndElement (info, el);