1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2008-2009 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
15 //- only invalidate regions
16 //- only render changed regions
21 #include <sys/types.h>
28 #include "multiscaleimage.h"
29 #include "tilesource.h"
30 #include "deepzoomimagetilesource.h"
31 #include "file-downloader.h"
32 #include "multiscalesubimage.h"
33 #include "bitmapimage.h"
38 #define MSI_STARTTIMER(id) if (G_UNLIKELY (debug_flags & RUNTIME_DEBUG_MSI)) TimeSpan id##_t_start = get_now()
39 #define MSI_ENDTIMER(id,str) if (G_UNLIKELY (debug_flags & RUNTIME_DEBUG_MSI)) TimeSpan id##_t_end = get_now(); printf ("timing of '%s' ended took (%f ms)\n", str, id##_t_end, (double)(id##_t_end - id##_t_start) / 10000)
42 #define ENDTIMER(id,str)
46 guint64
pow2(int pow
) {
47 return ((guint64
) 1 << pow
);
67 return g_new0 (QTree
, 1);
71 qtree_insert (QTree
* root
, int level
, guint64 x
, guint64 y
)
73 if (x
>= (pow2 (level
)) || y
>= (pow2 (level
))) {
77 g_warning ("QuadTree index out of range.");
82 g_warning ("passing a NULL QTree to qtree_insert");
88 if (y
< (pow2 (level
))) {
89 if (x
< (pow2 (level
))) {
91 node
->l0
= g_new0 (QTree
, 1);
92 node
->l0
->parent
= node
;
97 node
->l1
= g_new0 (QTree
, 1);
98 node
->l1
->parent
= node
;
104 if (x
< (pow2 (level
))) {
106 node
->l2
= g_new0 (QTree
, 1);
107 node
->l2
->parent
= node
;
113 node
->l3
= g_new0 (QTree
, 1);
114 node
->l3
->parent
= node
;
126 qtree_set_value (QTree
* node
, void *data
)
128 //FIXME: the destroy method should be a ctor argument
129 if (node
->has_value
&& node
->data
)
130 cairo_surface_destroy ((cairo_surface_t
*)node
->data
);
132 node
->has_value
= true;
137 qtree_lookup (QTree
* root
, int level
, guint64 x
, guint64 y
)
139 if (x
>= (pow2 (level
)) || y
>= (pow2 (level
))) {
141 // we seem to run into an infinite loop sporadically here for drt #2014 completely spamming the test output.
142 // abort to get a stack trace.
145 g_warning ("QuadTree index out of range.");
149 while (level
-- > 0) {
153 if (y
< (pow2 (level
))) {
154 if (x
< (pow2 (level
))) {
161 if (x
< (pow2 (level
))) {
175 qtree_lookup_data (QTree
* root
, int level
, guint64 x
, guint64 y
)
177 QTree
*node
= qtree_lookup (root
, level
, x
, y
);
178 if (node
&& node
->has_value
)
183 //FIXME: merge qtree_next_sibling and _qtree_next_sibling in a single
184 //function, with an elegant loop to avoid recursion.
186 _qtree_next_sibling (QTree
*node
, guint64
*i
, guint64
*j
, int l
)
192 g_warning ("Empty node");
196 if (!node
->parent
) //no parent, we're probably at the root
199 if (node
== node
->parent
->l0
) {
201 return node
->parent
->l1
;
203 if (node
== node
->parent
->l1
) {
206 return node
->parent
->l2
;
208 if (node
== node
->parent
->l2
) {
210 return node
->parent
->l3
;
212 if (node
== node
->parent
->l3
) {
215 QTree
*next_parent
= _qtree_next_sibling (node
->parent
, i
, j
, l
+ 1);
218 return next_parent
->l0
;
223 g_warning ("Broken parent link, this is bad");
228 qtree_remove (QTree
* node
, int depth
)
230 if (node
&& node
->has_value
) {
231 node
->has_value
= false;
233 cairo_surface_destroy ((cairo_surface_t
*)node
->data
);
241 qtree_remove (node
->l0
, depth
- 1);
242 qtree_remove (node
->l1
, depth
- 1);
243 qtree_remove (node
->l2
, depth
- 1);
244 qtree_remove (node
->l3
, depth
- 1);
249 qtree_remove_at (QTree
* root
, int level
, guint64 x
, guint64 y
, int depth
)
251 QTree
* node
= qtree_lookup (root
, level
, x
, y
);
252 qtree_remove (node
, depth
);
256 qtree_has_value (QTree
* node
)
258 return node
->has_value
;
262 qtree_destroy (QTree
*root
)
267 //FIXME: the destroy func should be a qtree ctor option
269 cairo_surface_destroy ((cairo_surface_t
*)(root
->data
));
273 qtree_destroy (root
->l0
);
274 qtree_destroy (root
->l1
);
275 qtree_destroy (root
->l2
);
276 qtree_destroy (root
->l3
);
284 enum BitmapImageStatus
{
290 struct BitmapImageContext
292 BitmapImageStatus state
;
293 BitmapImage
*bitmapimage
;
304 morton (int n
, int *x
, int *y
) {
305 n
= (n
& 0x99999999) + ((n
& 0x22222222) << 1) + ((n
& 0x44444444) >> 1);
306 n
= (n
& 0xc3c3c3c3) + ((n
& 0x0c0c0c0c) << 2) + ((n
& 0x30303030) >> 2);
307 n
= (n
& 0xf00ff00f) + ((n
& 0x00f000f0) << 4) + ((n
& 0x0f000f00) >> 4);
308 n
= (n
& 0xff0000ff) + ((n
& 0x0000ff00) << 8) + ((n
& 0x00ff0000) >> 8);
317 n
= (n
& 0x11111111) + ((n
& 0x44444444) >> 1);
318 n
= (n
& 0x03030303) + ((n
& 0x30303030) >> 2);
319 n
= (n
& 0x000f000f) + ((n
& 0x0f000f00) >> 4);
320 return (n
& 0x000000ff) + ((n
& 0x00ff0000) >> 8);
326 n
= (n
& 0x88888888) + ((n
& 0x22222222) << 1);
327 n
= (n
& 0xc0c0c0c0) + ((n
& 0x0c0c0c0c) << 2);
328 n
= (n
& 0xf000f000) + ((n
& 0x00f000f0) << 4);
329 n
= (n
& 0xff000000) + ((n
& 0x0000ff00) << 8);
339 MultiScaleImage::MultiScaleImage () :
340 subimages_sorted(false),
341 pending_motion_completed(false),
347 // static bool init = true;
350 // MultiScaleImage::SubImagesProperty->SetValueValidator (MultiScaleSubImageCollectionValidator);
352 providers
[PropertyPrecedence_DynamicValue
] = new MultiScaleImagePropertyValueProvider (this, PropertyPrecedence_DynamicValue
);
354 SetObjectType (Type::MULTISCALEIMAGE
);
355 cache
= g_hash_table_new_full (g_int_hash
, g_int_equal
, g_free
, (GDestroyNotify
)qtree_destroy
);
358 MultiScaleImage::~MultiScaleImage ()
362 g_hash_table_destroy (cache
);
367 MultiScaleImage::ZoomAboutLogicalPoint (double zoomIncrementFactor
, double zoomCenterLogicalX
, double zoomCenterLogicalY
)
369 LOG_MSI ("\nzoomabout logical %f (%f, %f)\n", zoomIncrementFactor
, zoomCenterLogicalX
, zoomCenterLogicalY
);
372 zoom_sb
->PauseWithError (NULL
);
374 pan_sb
->PauseWithError (NULL
);
376 double viewport_width
;
377 Point viewport_origin
;
379 if (GetUseSprings () && zoom_sb
&& pan_sb
) {
380 viewport_width
= zoom_target
;
381 viewport_origin
= pan_target
;
383 viewport_width
= GetViewportWidth ();
384 viewport_origin
= *GetViewportOrigin ();
387 double width
= viewport_width
/ zoomIncrementFactor
;
388 SetViewportWidth (width
);
389 if (!isnan(zoomCenterLogicalX
) && !isnan(zoomCenterLogicalY
)) {
390 SetViewportOrigin (new Point (zoomCenterLogicalX
- (zoomCenterLogicalX
- viewport_origin
.x
) / zoomIncrementFactor
,
391 zoomCenterLogicalY
- (zoomCenterLogicalY
- viewport_origin
.y
) / zoomIncrementFactor
));
396 MultiScaleImage::ElementToLogicalPoint (Point elementPoint
)
398 Point
*vp_origin
= GetViewportOrigin ();
399 double vp_width
= GetViewportWidth ();
400 double actual_width
= GetActualWidth ();
401 return Point (vp_origin
->x
+ (double)elementPoint
.x
* vp_width
/ actual_width
,
402 vp_origin
->y
+ (double)elementPoint
.y
* vp_width
/ actual_width
);
406 MultiScaleImage::LogicalToElementPoint (Point logicalPoint
)
408 Point
*vp_origin
= GetViewportOrigin ();
409 double vp_width
= GetViewportWidth ();
410 double actual_width
= GetActualWidth ();
411 return Point ((logicalPoint
.x
- vp_origin
->x
) * actual_width
/ vp_width
,
412 (logicalPoint
.y
- vp_origin
->y
) * actual_width
/ vp_width
);
416 MultiScaleImage::DownloadTile (BitmapImageContext
*bictx
, Uri
*tile
, void *user_data
)
419 BitmapImageContext
*ctx
;
420 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
) {
421 if (ctx
->state
!= BitmapImageFree
&& ctx
->bitmapimage
->GetUriSource()->operator==(*tile
)) {
422 //LOG_MSI ("Tile %s is already being downloaded\n", tile->ToString ());
427 //LOG_MSI ("downloading tile %s\n", tile->ToString ());
429 bictx
->state
= BitmapImageBusy
;
430 bictx
->node
= (QTree
*)user_data
;
432 SetIsDownloading (true);
433 bictx
->bitmapimage
->SetDownloadPolicy (MsiPolicy
);
434 bictx
->bitmapimage
->SetUriSource (tile
);
437 //Only used for DeepZoom sources
439 MultiScaleImage::HandleDzParsed ()
441 //if the source is a collection, fill the subimages list
442 MultiScaleTileSource
*source
= GetSource ();
443 MultiScaleSubImageCollection
*subs
= GetSubImages ();
445 if (source
->GetImageWidth () >= 0 && source
->GetImageHeight () >= 0)
446 SetValue (MultiScaleImage::AspectRatioProperty
, Value ((double)source
->GetImageWidth () / (double)source
->GetImageHeight ()));
448 DeepZoomImageTileSource
*dsource
;
450 if (source
->Is (Type::DEEPZOOMIMAGETILESOURCE
) &&
451 (dsource
= (DeepZoomImageTileSource
*)source
)) {
453 MultiScaleSubImage
*si
;
454 for (i
= 0; (si
= (MultiScaleSubImage
*)g_list_nth_data (dsource
->subimages
, i
)); i
++) {
456 SetValue (MultiScaleImage::SubImagesProperty
, new MultiScaleSubImageCollection ());
463 //Get the first tiles
464 BitmapImageContext
*bitmapimagectx
;
466 int shared_index
= -1;
468 QTree
*shared_cache
= (QTree
*)g_hash_table_lookup (cache
, &shared_index
);
470 g_hash_table_insert (cache
, new int(shared_index
), (shared_cache
= qtree_new ()));
473 while ((bitmapimagectx
= GetFreeBitmapImageContext ())) {
474 Uri
*tile
= new Uri ();
475 if (source
->get_tile_func (layer
, 0, 0, tile
, source
)) {
476 QTree
*node
= qtree_insert (shared_cache
, layer
, 0, 0);
477 DownloadTile (bitmapimagectx
, tile
, node
);
483 EmitImageOpenSucceeded ();
487 fade_finished (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
489 MultiScaleImage
*msi
= (MultiScaleImage
*) closure
;
490 msi
->FadeFinished ();
494 MultiScaleImage::FadeFinished ()
497 if (!is_fading
&& !is_zooming
&& !is_panning
)
498 EmitMotionFinished ();
502 zoom_finished (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
504 MultiScaleImage
*msi
= (MultiScaleImage
*) closure
;
505 msi
->ZoomFinished ();
509 MultiScaleImage::ZoomFinished ()
512 if (!is_fading
&& !is_zooming
&& !is_panning
)
513 EmitMotionFinished ();
517 pan_finished (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
519 MultiScaleImage
*msi
= (MultiScaleImage
*) closure
;
524 MultiScaleImage::PanFinished ()
527 if (!is_fading
&& !is_zooming
&& !is_panning
)
528 EmitMotionFinished ();
532 tile_available (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
534 // LOG_MSI ("Tile downloaded %s\n", ((BitmapImage *)sender)->GetUriSource ()->ToString ());
535 ((MultiScaleImage
*)closure
)->TileOpened ((BitmapImage
*)sender
);
539 MultiScaleImage::TileOpened (BitmapImage
*bitmapimage
)
541 BitmapImageContext
*ctx
= GetBitmapImageContext (bitmapimage
);
542 ctx
->state
= BitmapImageDone
;
544 bool is_downloading
= false;
545 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
546 is_downloading
|= (ctx
->state
== BitmapImageBusy
);
547 SetIsDownloading (is_downloading
);
552 tile_failed (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
554 // LOG_MSI ("Failed to download tile %s\n", ((BitmapImage *)sender)->GetUriSource ()->ToString ());
555 ((MultiScaleImage
*)closure
)->TileFailed ((BitmapImage
*)sender
);
559 MultiScaleImage::TileFailed (BitmapImage
*bitmapimage
)
561 BitmapImageContext
*ctx
= GetBitmapImageContext (bitmapimage
);
562 if (ctx
->retry
< 5) {
563 bitmapimage
->SetUriSource (bitmapimage
->GetUriSource ());
564 ctx
->retry
= ctx
->retry
+ 1;
566 ctx
->state
= BitmapImageFree
;
568 LOG_MSI ("caching a NULL for %s\n", ctx
->bitmapimage
->GetUriSource()->ToString ());
569 qtree_set_value (ctx
->node
, NULL
);
572 bool is_downloading
= false;
573 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
574 is_downloading
|= (ctx
->state
== BitmapImageBusy
);
575 SetIsDownloading (is_downloading
);
582 MultiScaleImage::GetBitmapImageContext (BitmapImage
*bitmapimage
)
584 BitmapImageContext
*ctx
;
586 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
587 if (ctx
->bitmapimage
== bitmapimage
)
593 MultiScaleImage::StopDownloading ()
595 BitmapImageContext
*ctx
;
597 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
) {
598 ctx
->bitmapimage
->Abort ();
599 ctx
->bitmapimage
->Dispose ();
600 ctx
->bitmapimage
->unref ();
601 ctx
->state
= BitmapImageFree
;
606 g_list_free (bitmapimages
);
611 MultiScaleImage::GetFreeBitmapImageContext ()
614 BitmapImageContext
*ctx
;
616 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
617 if (ctx
->state
== BitmapImageFree
)
620 if (g_list_length (bitmapimages
) < num_dl
) {
621 ctx
= new BitmapImageContext ();
622 ctx
->state
= BitmapImageFree
;
623 ctx
->bitmapimage
= new BitmapImage ();
624 ctx
->bitmapimage
->AddHandler (ctx
->bitmapimage
->ImageOpenedEvent
, tile_available
, this);
625 ctx
->bitmapimage
->AddHandler (ctx
->bitmapimage
->ImageFailedEvent
, tile_failed
, this);
626 bitmapimages
= g_list_append (bitmapimages
, ctx
);
634 MultiScaleImage::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
636 LOG_MSI ("MSI::Render\n");
638 MultiScaleTileSource
*source
= GetSource ();
640 LOG_MSI ("no sources set, nothing to render\n");
644 //Process the downloaded tile
646 BitmapImageContext
*ctx
;
647 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
) {
648 cairo_surface_t
*surface
;
650 if (ctx
->state
!= BitmapImageDone
|| !(surface
= cairo_surface_reference (ctx
->bitmapimage
->GetSurface (cr
))))
653 // Uri *tile = ctx->bitmapimage->GetUriSource ();
654 cairo_surface_set_user_data (surface
, &width_key
, new int (ctx
->bitmapimage
->GetPixelWidth ()), g_free
);
655 cairo_surface_set_user_data (surface
, &height_key
, new int (ctx
->bitmapimage
->GetPixelHeight ()), g_free
);
658 fadein_sb
= new Storyboard ();
659 fadein_sb
->SetManualTarget (this);
660 fadein_sb
->SetTargetProperty (fadein_sb
, new PropertyPath ("(MultiScaleImage.TileFade)"));
661 fadein_animation
= new DoubleAnimation ();
662 fadein_animation
->SetDuration (Duration (source
->GetTileBlendTime ()));
663 TimelineCollection
*tlc
= new TimelineCollection ();
664 tlc
->Add (static_cast<DoubleAnimation
*> (fadein_animation
));
665 fadein_sb
->SetChildren(tlc
);
666 fadein_sb
->AddHandler (Storyboard::CompletedEvent
, fade_finished
, this);
668 fadein_sb
->SetName ("Multiscale Fade-In");
671 fadein_sb
->PauseWithError (NULL
);
674 //LOG_MSI ("animating Fade from %f to %f\n\n", GetValue(MultiScaleImage::TileFadeProperty)->AsDouble(), GetValue(MultiScaleImage::TileFadeProperty)->AsDouble() + 0.9);
675 double *to
= new double (GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble() + 0.9);
676 fadein_animation
->SetFrom (GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble());
677 fadein_animation
->SetTo (*to
);
681 fadein_sb
->BeginWithError(NULL
);
683 cairo_surface_set_user_data (surface
, &full_opacity_at_key
, to
, g_free
);
684 LOG_MSI ("caching %s\n", ctx
->bitmapimage
->GetUriSource()->ToString ());
685 qtree_set_value (ctx
->node
, surface
);
687 ctx
->bitmapimage
->SetUriSource (NULL
);
688 ctx
->state
= BitmapImageFree
;
691 bool is_collection
= source
&&
692 source
->Is (Type::DEEPZOOMIMAGETILESOURCE
) &&
693 ((DeepZoomImageTileSource
*)source
)->IsCollection () &&
696 if (source
->GetImageWidth () < 0 && !is_collection
) {
697 LOG_MSI ("nothing to render so far...\n");
698 if (source
->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
699 ((DeepZoomImageTileSource
*)source
)->set_callbacks (multi_scale_image_handle_dz_parsed
, multi_scale_image_emit_image_open_failed
, multi_scale_image_on_source_property_changed
, this);
700 ((DeepZoomImageTileSource
*)source
)->Download ();
706 if (!source
->get_tile_func
) {
707 g_warning ("no get_tile_func set\n");
713 RenderCollection (cr
, region
);
715 RenderSingle (cr
, region
);
719 MultiScaleImage::RenderCollection (cairo_t
*cr
, Region
*region
)
721 LOG_MSI ("\nMSI::RenderCollection\n");
723 double msi_w
= GetActualWidth ();
724 double msi_h
= GetActualHeight ();
725 double msi_ar
= GetAspectRatio();
726 double msivp_ox
= GetViewportOrigin()->x
;
727 double msivp_oy
= GetViewportOrigin()->y
;
728 double msivp_w
= GetViewportWidth();
730 if (!GetSource ()->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
731 g_warning ("RenderCollection called for a non deepzoom tile source. this should not happen");
734 DeepZoomImageTileSource
*dzits
= (DeepZoomImageTileSource
*)GetSource ();
736 Rect viewport
= Rect (msivp_ox
, msivp_oy
, msivp_w
, msivp_w
/msi_ar
);
738 MultiScaleSubImageCollection
*subs
= GetSubImages ();
739 if (!subimages_sorted
) {
740 subs
->ResortByZIndex ();
741 subimages_sorted
= true;
744 //using the "-1" index for the shared cache
745 int shared_index
= -1;
746 QTree
*shared_cache
= (QTree
*)g_hash_table_lookup (cache
, &shared_index
);
748 g_hash_table_insert (cache
, new int(shared_index
), (shared_cache
= qtree_new ()));
751 for (i
= 0; i
< subs
->GetCount (); i
++) {
752 MultiScaleSubImage
*sub_image
= (MultiScaleSubImage
*)g_ptr_array_index (subs
->z_sorted
, i
);
754 int index
= sub_image
->GetId();
755 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &index
);
757 g_hash_table_insert (cache
, new int(index
), (subimage_cache
= qtree_new ()));
759 double subvp_ox
= sub_image
->GetViewportOrigin()->x
;
760 double subvp_oy
= sub_image
->GetViewportOrigin()->y
;
761 double subvp_w
= sub_image
->GetViewportWidth();
762 double sub_w
= sub_image
->source
->GetImageWidth ();
763 double sub_h
= sub_image
->source
->GetImageHeight ();
764 double sub_ar
= sub_image
->GetAspectRatio();
767 //expressing the subimage viewport in main viewport coordinates.
768 Rect sub_vp
= Rect (-subvp_ox
/ subvp_w
, -subvp_oy
/ subvp_w
, 1.0/subvp_w
, 1.0/(sub_ar
* subvp_w
));
770 //render only if the subimage viewport intersects with this viewport
771 if (!sub_vp
.IntersectsWith (viewport
))
773 LOG_MSI ("Intersects with main viewport...rendering\n");
776 if (frexp (MAX (sub_w
, sub_h
), &layers
) == 0.5)
780 frexp (msi_w
/ (subvp_w
* msivp_w
* MIN (1.0, sub_ar
)), &optimal_layer
);
781 optimal_layer
= MIN (optimal_layer
, layers
);
782 LOG_MSI ("number of layers: %d\toptimal layer for this: %d\n", layers
, optimal_layer
);
785 int from_layer
= optimal_layer
;
786 while (from_layer
>= 0) {
789 bool blending
= FALSE
; //means at least a tile is not yet fully blended
791 int tile_width
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileWidth () : dzits
->GetTileWidth ();
792 int tile_height
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileHeight (): dzits
->GetTileHeight ();
794 //in msi relative coord
795 double v_tile_w
= tile_width
* (double) (pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
796 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
797 //LOG_MSI ("virtual tile size at layer %d; %fx%f\n", from_layer, v_tile_w, v_tile_h);
800 for (i
= (int)((MAX(msivp_ox
, sub_vp
.x
) - sub_vp
.x
)/v_tile_w
); i
* v_tile_w
< MIN(msivp_ox
+ msivp_w
, sub_vp
.x
+ sub_vp
.width
) - sub_vp
.x
;i
++) {
801 for (j
= (int)((MAX(msivp_oy
, sub_vp
.y
) - sub_vp
.y
)/v_tile_h
); j
* v_tile_h
< MIN(msivp_oy
+ msivp_w
/msi_ar
, sub_vp
.y
+ sub_vp
.width
/sub_ar
) - sub_vp
.y
;j
++) {
803 cairo_surface_t
* image
= NULL
;
806 if (from_layer
> dzits
->GetMaxLevel ()) {
807 if ((image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, from_layer
, i
, j
)))
809 } else if ((image
= (cairo_surface_t
*)qtree_lookup_data (shared_cache
, from_layer
,
810 morton_x (sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
811 morton_y (sub_image
->n
) * (pow2 (from_layer
)) / tile_height
)))
814 if (image
&& *(double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
)) > GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble ())
818 if (found
> 0 && to_layer
< from_layer
)
819 to_layer
= from_layer
;
820 if (found
== count
&& (!blending
|| from_layer
== 0))
827 LOG_MSI ("rendering layers from %d to %d\n", from_layer
, to_layer
);
828 double fade
= GetValue (MultiScaleImage::TileFadeProperty
)->AsDouble();
829 if (from_layer
>= 0) {
832 cairo_rectangle (cr
, 0, 0, msi_w
, msi_h
);
834 cairo_scale (cr
, msi_w
/ msivp_w
, msi_w
/ msivp_w
); //scale to widget
837 -msivp_ox
+ sub_vp
.x
,
838 -msivp_oy
+ sub_vp
.y
);
844 cairo_rectangle (cr
, 0, 0, sub_w
, sub_h
);
847 if (IS_TRANSLUCENT (sub_image
->GetOpacity ()))
848 cairo_push_group (cr
);
850 int layer_to_render
= from_layer
;
851 while (layer_to_render
<= to_layer
) {
852 int tile_width
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ?sub_image
->source
->GetTileWidth () : dzits
->GetTileWidth ();
853 int tile_height
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileHeight () : dzits
->GetTileHeight ();
855 double v_tile_w
= tile_width
* (double)(pow2 (layers
- layer_to_render
)) * sub_vp
.width
/ sub_w
;
856 double v_tile_h
= tile_height
* (double)(pow2 (layers
- layer_to_render
)) * sub_vp
.width
/ sub_w
;
859 for (i
= (int)((MAX(msivp_ox
, sub_vp
.x
) - sub_vp
.x
)/v_tile_w
); i
* v_tile_w
< MIN(msivp_ox
+ msivp_w
, sub_vp
.x
+ sub_vp
.width
) - sub_vp
.x
;i
++) {
860 for (j
= (int)((MAX(msivp_oy
, sub_vp
.y
) - sub_vp
.y
)/v_tile_h
); j
* v_tile_h
< MIN(msivp_oy
+ msivp_w
/msi_ar
, sub_vp
.y
+ sub_vp
.width
/sub_ar
) - sub_vp
.y
;j
++) {
861 cairo_surface_t
*image
= NULL
;
862 bool shared_tile
= false;
863 if (layer_to_render
> dzits
->GetMaxLevel())
864 image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, layer_to_render
, i
, j
);
866 //Check in the shared levels
869 image
= (cairo_surface_t
*)qtree_lookup_data (shared_cache
, layer_to_render
,
870 morton_x(sub_image
->n
) * pow2 (layer_to_render
) / tile_width
,
871 morton_y(sub_image
->n
) * pow2 (layer_to_render
) / tile_height
);
877 LOG_MSI ("rendering subimage %d %d %d %d\n", sub_image
->id
, layer_to_render
, i
, j
);
881 pow2 (layers
- layer_to_render
),
882 pow2 (layers
- layer_to_render
));
890 (int)(-morton_x(sub_image
->n
) * (pow2 (layer_to_render
))) % tile_width
,
891 (int)(-morton_y(sub_image
->n
) * (pow2 (layer_to_render
))) % tile_height
);
895 cairo_set_source_surface (cr
, image
, 0, 0);
897 double *opacity
= (double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
));
898 double combined
= 1.0;
900 if (opacity
&& *opacity
> fade
)
901 combined
= MIN(1.0 - *opacity
+ fade
, 1.0);
903 if (IS_TRANSLUCENT (combined
))
904 cairo_paint_with_alpha (cr
, combined
);
914 if (IS_TRANSLUCENT (sub_image
->GetOpacity ())) {
915 cairo_pattern_t
*data
= cairo_pop_group (cr
);
916 if (cairo_pattern_status (data
) == CAIRO_STATUS_SUCCESS
) {
917 cairo_set_source (cr
, data
);
918 cairo_paint_with_alpha (cr
, sub_image
->GetOpacity ());
920 cairo_pattern_destroy (data
);
926 if (!GetAllowDownloading ())
929 BitmapImageContext
*bitmapimagectx
;
930 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
933 //Get the next tiles..
934 while (from_layer
< optimal_layer
) {
937 //if the subimage is unparsed, trigger the download
938 if (from_layer
> dzits
->GetMaxLevel () && !((DeepZoomImageTileSource
*)sub_image
->source
)->IsDownloaded () ) {
939 ((DeepZoomImageTileSource
*)sub_image
->source
)->set_callbacks ((void(*)(MultiScaleImage
*))uielement_invalidate
, multi_scale_image_emit_image_failed
, NULL
, this);
940 ((DeepZoomImageTileSource
*)sub_image
->source
)->Download ();
944 int tile_width
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ?sub_image
->source
->GetTileWidth () : dzits
->GetTileWidth ();
945 int tile_height
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileHeight (): dzits
->GetTileHeight ();
947 double v_tile_w
= tile_width
* (double)(pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
948 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
951 for (i
= (int)((MAX(msivp_ox
, sub_vp
.x
) - sub_vp
.x
)/v_tile_w
); i
* v_tile_w
< MIN(msivp_ox
+ msivp_w
, sub_vp
.x
+ sub_vp
.width
) - sub_vp
.x
;i
++) {
952 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
954 for (j
= (int)((MAX(msivp_oy
, sub_vp
.y
) - sub_vp
.y
)/v_tile_h
); j
* v_tile_h
< MIN(msivp_oy
+ msivp_w
/msi_ar
, sub_vp
.y
+ sub_vp
.width
/sub_ar
) - sub_vp
.y
;j
++) {
955 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
957 Uri
*tile
= new Uri ();
958 if (from_layer
<= dzits
->GetMaxLevel ()) {
959 QTree
*node
= qtree_insert (shared_cache
,
961 morton_x(sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
962 morton_y(sub_image
->n
) * (pow2 (from_layer
)) / tile_height
);
963 if (!qtree_has_value (node
)
964 && dzits
->get_tile_func (from_layer
,
965 morton_x(sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
966 morton_y(sub_image
->n
) * (pow2 (from_layer
)) / tile_height
,
968 DownloadTile (bitmapimagectx
, tile
, node
);
970 QTree
*node
= qtree_insert (subimage_cache
, from_layer
, i
, j
);
971 if (!qtree_has_value (node
)
972 && dzits
->get_tile_func (from_layer
, i
, j
, tile
, sub_image
->source
))
973 DownloadTile (bitmapimagectx
, tile
, node
);
983 MultiScaleImage::RenderSingle (cairo_t
*cr
, Region
*region
)
985 MultiScaleTileSource
*source
= GetSource ();
986 double msi_w
= GetActualWidth ();
987 double msi_h
= GetActualHeight ();
988 double msi_ar
= GetAspectRatio ();
989 double im_w
= source
->GetImageWidth ();
990 double im_h
= source
->GetImageHeight ();
991 int tile_width
= source
->GetTileWidth ();
992 int tile_height
= source
->GetTileHeight ();
993 double vp_ox
= GetViewportOrigin()->x
;
994 double vp_oy
= GetViewportOrigin()->y
;
995 double vp_w
= GetViewportWidth ();
997 if (msi_w
<= 0.0 || msi_h
<= 0.0)
998 return; //invisible widget, nothing to render
1000 //Number of layers in the MSI, aka the lowest powerof2 that's bigger than width and height
1002 if (frexp (MAX (im_w
, im_h
), &layers
) == 0.5)
1005 //optimal layer for this... aka "best viewed at"
1007 if (frexp (msi_w
/ (vp_w
* MIN (1.0, msi_ar
)) , &optimal_layer
) == 0.5)
1009 optimal_layer
= MIN (optimal_layer
, layers
);
1010 LOG_MSI ("number of layers: %d\toptimal layer for this: %d\n", layers
, optimal_layer
);
1012 //We have to figure all the layers that we'll have to render:
1013 //- from_layer is the highest COMPLETE layer that we can display (all tiles are there and blended (except for level 0, where it might not be blended yet))
1014 //- to_layer is the highest PARTIAL layer that we can display (contains at least 1 tiles partially blended)
1017 int from_layer
= optimal_layer
;
1019 //using the "-1" index for the single image case
1021 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &index
);
1022 if (!subimage_cache
)
1023 g_hash_table_insert (cache
, new int(index
), (subimage_cache
= qtree_new ()));
1025 while (from_layer
>= 0) {
1028 bool blending
= FALSE
; //means at least a tile is not yet fully blended
1030 //v_tile_X is the virtual tile size at this layer in relative coordinates
1031 double v_tile_w
= tile_width
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1032 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1034 //This double loop iterate over the displayed part of the image and find all (i,j) being top-left corners of tiles
1035 for (i
= MAX(0, (int)(vp_ox
/ v_tile_w
)); i
* v_tile_w
< MIN(vp_ox
+ vp_w
, 1.0); i
++) {
1036 for (j
= MAX(0, (int)(vp_oy
/ v_tile_h
)); j
* v_tile_h
< MIN(vp_oy
+ vp_w
/ msi_w
* msi_h
, 1.0 / msi_ar
); j
++) {
1038 cairo_surface_t
*image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, from_layer
, i
, j
);
1042 if (image
&& *(double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
)) > GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble ())
1047 if (found
> 0 && to_layer
< from_layer
)
1048 to_layer
= from_layer
;
1049 if (found
== count
&& (!blending
|| from_layer
== 0))
1055 //cairo_push_group (cr);
1059 cairo_matrix_t render_xform
;
1060 cairo_matrix_init_identity (&render_xform
);
1061 cairo_matrix_scale (&render_xform
, msi_w
/ vp_w
, msi_w
/ vp_w
);
1062 cairo_matrix_translate (&render_xform
, -vp_ox
, -vp_oy
);
1063 cairo_matrix_scale (&render_xform
, 1.0 / im_w
, 1.0 / im_w
);
1066 * here we want to clip to the bounds of the image to ensure we don't
1067 * have scaling artifacts on the edges but the image potentially has bounds
1068 * larger that cairo can handle right now so we transform the image
1069 * bounds to the viewport coordinate space and do intersection and clipping
1070 * there to work around the cairo coordinate limitations.
1072 Rect
vp_bounds (0, 0, msi_w
, msi_h
);
1073 Rect
im_bounds (0, 0, im_w
, im_h
);
1074 im_bounds
= im_bounds
.Transform (&render_xform
);
1075 Rect render_region
= vp_bounds
.Intersection (im_bounds
);
1076 render_region
.Draw (cr
);
1079 cairo_transform (cr
, &render_xform
);
1081 LOG_MSI ("rendering layers from %d to %d\n", from_layer
, to_layer
);
1083 double fade
= GetValue (MultiScaleImage::TileFadeProperty
)->AsDouble();
1084 int layer_to_render
= MAX (0, from_layer
);
1085 while (layer_to_render
<= to_layer
) {
1087 double v_tile_w
= tile_width
* (double)(pow2 (layers
- layer_to_render
)) / im_w
;
1088 double v_tile_h
= tile_height
* (double)(pow2 (layers
- layer_to_render
)) / im_w
;
1089 for (i
= MAX(0, (int)(vp_ox
/ v_tile_w
)); i
* v_tile_w
< MIN(vp_ox
+ vp_w
, 1.0); i
++) {
1090 for (j
= MAX(0, (int)(vp_oy
/ v_tile_h
)); j
* v_tile_h
< MIN(vp_oy
+ vp_w
/ msi_w
* msi_h
, 1.0 / msi_ar
); j
++) {
1091 cairo_surface_t
*image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, layer_to_render
, i
, j
);
1095 LOG_MSI ("rendering %d %d %d\n", layer_to_render
, i
, j
);
1098 cairo_scale (cr
, (pow2 (layers
- layer_to_render
)), (pow2 (layers
- layer_to_render
))); //scale to image size
1100 cairo_translate (cr
, i
* tile_width
, j
* tile_height
);
1102 cairo_set_source_surface (cr
, image
, 0, 0);
1104 double *opacity
= (double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
));
1105 double combined
= 1.0;
1107 if (opacity
&& *opacity
> fade
)
1108 combined
= MIN(1.0 - *opacity
+ fade
, 1.0);
1110 if (IS_TRANSLUCENT (combined
))
1111 cairo_paint_with_alpha (cr
, combined
);
1121 // cairo_pop_group_to_source (cr);
1123 if (!GetAllowDownloading ())
1126 BitmapImageContext
*bitmapimagectx
;
1127 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
1130 //Get the next tile(s)...
1131 while (from_layer
< optimal_layer
) {
1134 double v_tile_w
= tile_width
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1135 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1138 for (i
= MAX(0, (int)(vp_ox
/ v_tile_w
)); i
* v_tile_w
< MIN(vp_ox
+ vp_w
, 1.0); i
++) {
1139 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
1141 for (j
= MAX(0, (int)(vp_oy
/ v_tile_h
)); j
* v_tile_h
< MIN(vp_oy
+ vp_w
/ msi_w
* msi_h
, 1.0 / msi_ar
); j
++) {
1142 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
1144 Uri
*tile
= new Uri ();
1145 QTree
*node
= qtree_insert (subimage_cache
, from_layer
, i
, j
);
1146 if (!qtree_has_value (node
)) {
1147 if (source
->get_tile_func (from_layer
, i
, j
, tile
, source
))
1148 DownloadTile (bitmapimagectx
, tile
, node
);
1150 qtree_set_value (node
, NULL
);
1159 MultiScaleImage::OnSourcePropertyChanged ()
1161 //abort all downloaders
1164 DeepZoomImageTileSource
*newsource
;
1166 if (GetSource ()->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
1167 if ((newsource
= GetValue (MultiScaleImage::SourceProperty
)->AsDeepZoomImageTileSource ())) {
1168 newsource
->set_callbacks (multi_scale_image_handle_dz_parsed
, multi_scale_image_emit_image_open_failed
, multi_scale_image_on_source_property_changed
, this);
1169 newsource
->Download ();
1172 EmitImageOpenSucceeded ();
1176 //Reset the viewport
1177 ClearValue (MultiScaleImage::InternalViewportWidthProperty
, true);
1178 ClearValue (MultiScaleImage::InternalViewportOriginProperty
, true);
1179 //SetValue (MultiScaleImage::ViewportOriginProperty, Deployment::GetCurrent ()->GetTypes ()->GetProperty (MultiScaleImage::ViewportOriginProperty)->GetDefaultValue());
1180 //SetValue (MultiScaleImage::ViewportWidthProperty, Deployment::GetCurrent ()->GetTypes ()->GetProperty (MultiScaleImage::ViewportWidthProperty)->GetDefaultValue());
1182 //Invalidate the whole cache
1184 g_hash_table_destroy (cache
);
1185 cache
= g_hash_table_new_full (g_int_hash
, g_int_equal
, g_free
, (GDestroyNotify
)qtree_destroy
);
1188 //Reset the subimages
1189 GetSubImages()->Clear ();
1191 //register the callback for InvalidateTileLayers
1193 GetSource ()->set_invalidate_tile_layer_func (multi_scale_image_invalidate_tile_layer
, this);
1199 MultiScaleImage::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
1201 if (args
->GetId () == MultiScaleImage::AllowDownloadingProperty
) {
1202 if (args
->GetNewValue()->AsBool ())
1208 if (args
->GetId () == MultiScaleImage::InternalViewportOriginProperty
) {
1209 Emit (MultiScaleImage::ViewportChangedEvent
);
1213 if (args
->GetId () == MultiScaleImage::InternalViewportWidthProperty
) {
1214 Emit (MultiScaleImage::ViewportChangedEvent
);
1218 if (args
->GetId () == MultiScaleImage::ViewportOriginProperty
) {
1219 pan_target
= Point (args
->GetNewValue ()->AsPoint ()->x
, args
->GetNewValue ()->AsPoint ()->y
);
1220 SetInternalViewportOrigin (args
->GetNewValue ()->AsPoint ());
1221 ClearValue (MultiScaleImage::ViewportOriginProperty
, false);
1224 if (args
->GetId () == MultiScaleImage::ViewportWidthProperty
) {
1225 zoom_target
= args
->GetNewValue ()->AsDouble ();
1226 SetInternalViewportWidth (args
->GetNewValue ()->AsDouble ());
1227 ClearValue (MultiScaleImage::ViewportWidthProperty
, false);
1230 if (args
->GetId () == MultiScaleImage::TileFadeProperty
) {
1231 //There's 2 options here,
1232 // - loop all the tiles, update their opacity, and only invalidate a subregion
1233 // - Invalidate all, and compute the new opacity on the tiles that needs to be rendered.
1234 //Both options are unfortunately quite expensive :(
1235 //LOG_MSI ("TileFade changed to %f\n", args->GetNewValue()->AsDouble ());
1239 if (args
->GetId () == MultiScaleImage::SourceProperty
) {
1240 OnSourcePropertyChanged ();
1243 if (args
->GetId () == MultiScaleImage::UseSpringsProperty
) {
1244 if (!args
->GetNewValue()->AsBool ()) {
1246 double *endpoint
= GetZoomAnimationEndPoint ();
1247 zoom_sb
->StopWithError (NULL
);
1248 SetViewportWidth (*endpoint
);
1251 Point
*endpoint
= GetPanAnimationEndPoint ();
1252 pan_sb
->StopWithError (NULL
);
1253 SetViewportOrigin (endpoint
);
1258 if (args
->GetProperty ()->GetOwnerType () != Type::MULTISCALEIMAGE
) {
1259 MediaBase::OnPropertyChanged (args
, error
);
1263 NotifyListenersOfPropertyChange (args
, error
);
1267 MultiScaleImage::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
1269 subimages_sorted
= false;
1274 MultiScaleImage::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
1276 if (args
->GetId () == MultiScaleSubImage::ViewportWidthProperty
)
1278 if (args
->GetId () == MultiScaleSubImage::ViewportOriginProperty
)
1280 if (args
->GetId () == MultiScaleSubImage::ZIndexProperty
) {
1281 subimages_sorted
= false;
1287 MultiScaleImage::EmitImageFailed ()
1289 LOG_MSI ("MSI::Emitting image failed\n");
1290 Emit (MultiScaleImage::ImageFailedEvent
);
1294 MultiScaleImage::EmitImageOpenFailed ()
1296 LOG_MSI ("MSI::Emitting image open failed\n");
1297 MoonError moon_error
;
1298 MoonError::FillIn (&moon_error
, MoonError::EXCEPTION
, -2147467259, "");
1299 Emit (MultiScaleImage::ImageOpenFailedEvent
, new ErrorEventArgs (UnknownError
, moon_error
));
1303 MultiScaleImage::EmitImageOpenSucceeded ()
1305 LOG_MSI ("\nMSI::Emitting open suceeded\n");
1306 Emit (MultiScaleImage::ImageOpenSucceededEvent
);
1307 // This is a hack that removes at least one timeout (#291),
1308 // possibly because an invalidation gets lost somehow.
1309 // Since we only start downloading when we try to
1310 // render the msi, the test effectively hangs.
1311 FullInvalidate (true);
1315 MultiScaleImage::EmitMotionFinished ()
1317 LOG_MSI ("Emitting MotionFinished\n");
1318 pending_motion_completed
= false;
1319 Emit (MultiScaleImage::MotionFinishedEvent
);
1323 MultiScaleImage::GetPanAnimationEndPoint ()
1325 return pan_animation
->GetKeyFrames ()->GetValueAt (0)->AsSplinePointKeyFrame ()->GetValue ();
1329 MultiScaleImage::SetPanAnimationEndPoint (Point value
)
1331 pan_animation
->GetKeyFrames ()->GetValueAt (0)->AsSplinePointKeyFrame ()->SetValue (value
);
1335 MultiScaleImage::GetZoomAnimationEndPoint ()
1337 return zoom_animation
->GetKeyFrames ()->GetValueAt (0)->AsSplineDoubleKeyFrame ()->GetValue ();
1341 MultiScaleImage::SetZoomAnimationEndPoint (double value
)
1343 zoom_animation
->GetKeyFrames ()->GetValueAt (0)->AsSplineDoubleKeyFrame ()->SetValue (value
);
1347 MultiScaleImage::SetInternalViewportWidth (double value
)
1349 if (!GetUseSprings ()) {
1350 if (!pending_motion_completed
) {
1351 AddTickCall ((TickCallHandler
)multi_scale_image_emit_motion_finished
);
1352 pending_motion_completed
= true;
1354 SetValue (MultiScaleImage::InternalViewportWidthProperty
, Value (value
));
1359 zoom_sb
= new Storyboard ();
1360 zoom_sb
->SetManualTarget (this);
1361 zoom_sb
->SetTargetProperty (zoom_sb
, new PropertyPath ("(MultiScaleImage.InternalViewportWidth)"));
1362 zoom_sb
->AddHandler (Storyboard::CompletedEvent
, zoom_finished
, this);
1363 zoom_animation
= new DoubleAnimationUsingKeyFrames ();
1364 zoom_animation
->SetDuration (Duration::FromSeconds (4));
1365 zoom_animation
->SetKeyFrames (DOPtr
<DoubleKeyFrameCollection
> (new DoubleKeyFrameCollection ()));
1366 DOPtr
<SplineDoubleKeyFrame
> keyframe (new SplineDoubleKeyFrame ());
1367 keyframe
->SetKeySpline (DOPtr
<KeySpline
> (new KeySpline (.05, .5, 0, 1.0)));
1368 keyframe
->SetKeyTime (KeyTime::FromPercent (1.0));
1369 zoom_animation
->GetKeyFrames ()->Add (static_cast<SplineDoubleKeyFrame
*>(keyframe
));
1371 DOPtr
<TimelineCollection
> tlc (new TimelineCollection ());
1372 tlc
->Add (static_cast<DoubleAnimationUsingKeyFrames
*>(zoom_animation
));
1373 zoom_sb
->SetChildren(tlc
);
1375 zoom_sb
->SetName ("Multiscale Zoom");
1378 zoom_sb
->PauseWithError (NULL
);
1381 LOG_MSI ("animating zoom from %f to %f\n\n", GetInternalViewportWidth(), value
)
1385 SetZoomAnimationEndPoint (value
);
1386 zoom_sb
->BeginWithError (NULL
);
1390 MultiScaleImage::SetInternalViewportOrigin (Point
* value
)
1392 if (!GetUseSprings ()) {
1393 if (!pending_motion_completed
) {
1394 AddTickCall ((TickCallHandler
)multi_scale_image_emit_motion_finished
);
1395 pending_motion_completed
= true;
1397 SetValue (MultiScaleImage::InternalViewportOriginProperty
, Value (*value
));
1402 pan_sb
= new Storyboard ();
1403 pan_sb
->SetManualTarget (this);
1404 pan_sb
->SetTargetProperty (pan_sb
, new PropertyPath ("(MultiScaleImage.InternalViewportOrigin)"));
1405 pan_sb
->AddHandler (Storyboard::CompletedEvent
, pan_finished
, this);
1406 pan_animation
= new PointAnimationUsingKeyFrames ();
1407 pan_animation
->SetDuration (Duration::FromSeconds (4));
1408 pan_animation
->SetKeyFrames (DOPtr
<PointKeyFrameCollection
> (new PointKeyFrameCollection ()));
1409 SplinePointKeyFrame
*keyframe
= new SplinePointKeyFrame ();
1410 keyframe
->SetKeySpline (DOPtr
<KeySpline
> (new KeySpline (.05, .5, 0, 1.0)));
1411 keyframe
->SetKeyTime (KeyTime::FromPercent (1.0));
1412 pan_animation
->GetKeyFrames ()->Add (keyframe
);
1414 TimelineCollection
*tlc
= new TimelineCollection ();
1415 tlc
->Add (static_cast<PointAnimationUsingKeyFrames
*> (pan_animation
));
1416 pan_sb
->SetChildren(tlc
);
1418 pan_sb
->SetName ("Multiscale Pan");
1421 pan_sb
->PauseWithError (NULL
);
1424 SetPanAnimationEndPoint (*value
);
1425 pan_sb
->BeginWithError (NULL
);
1429 MultiScaleImage::SetIsIdle (bool value
)
1431 SetValue (MultiScaleImage::IsIdleProperty
, Value (value
));
1435 MultiScaleImage::SetIsDownloading (bool value
)
1437 SetValue (MultiScaleImage::IsDownloadingProperty
, Value (value
));
1441 MultiScaleImage::LogicalToElementX (int x
, int y
)
1443 return LogicalToElementPoint (Point (x
, y
)).x
;
1447 MultiScaleImage::LogicalToElementY (int x
, int y
)
1449 return LogicalToElementPoint (Point (x
, y
)).y
;
1452 MultiScaleSubImage
*
1453 MultiScaleImage::GetIthSubImage (int index
)
1455 MultiScaleSubImageCollection
*sub_images
= GetSubImages ();
1458 if (sub_images
== NULL
)
1461 value
= sub_images
->GetValueAt (index
);
1466 return value
->AsMultiScaleSubImage ();
1470 MultiScaleImage::GetSubImageCount ()
1472 MultiScaleSubImageCollection
*sub_images
= GetSubImages ();
1474 if (sub_images
== NULL
)
1476 return sub_images
->GetCount ();
1480 MultiScaleImage::InvalidateTileLayer (int level
, int tilePositionX
, int tilePositionY
, int tileLayer
)
1482 if (GetSource ()->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
1483 g_warning ("calling InvalidateTileLayer on DeepZoom Images makes no sense\n");
1490 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &index
);
1492 qtree_remove_at (subimage_cache
, level
, tilePositionX
, tilePositionY
, 0);
1498 * MultiScaleImagePropertyValueProvider
1501 MultiScaleImagePropertyValueProvider::MultiScaleImagePropertyValueProvider (MultiScaleImage
*msi
, PropertyPrecedence precedence
)
1502 : FrameworkElementProvider (msi
, precedence
)
1504 viewport_origin
= NULL
;
1505 viewport_width
= NULL
;
1508 MultiScaleImagePropertyValueProvider::~MultiScaleImagePropertyValueProvider ()
1510 delete viewport_origin
;
1511 delete viewport_width
;
1515 MultiScaleImagePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
1517 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1520 if (property
->GetId () == MultiScaleImage::ViewportOriginProperty
)
1521 return GetViewportOrigin ();
1522 if (property
->GetId () == MultiScaleImage::ViewportWidthProperty
)
1523 return GetViewportWidth ();
1524 return FrameworkElementProvider::GetPropertyValue (property
);
1528 MultiScaleImagePropertyValueProvider::GetViewportOrigin ()
1530 MultiScaleImage
*msi
= (MultiScaleImage
*) obj
;
1532 delete viewport_origin
;
1533 viewport_origin
= new Value (*(msi
->GetInternalViewportOrigin ()));
1534 return viewport_origin
;
1538 MultiScaleImagePropertyValueProvider::GetViewportWidth ()
1540 MultiScaleImage
*msi
= (MultiScaleImage
*) obj
;
1542 delete viewport_width
;
1543 viewport_width
= new Value (msi
->GetInternalViewportWidth ());
1544 return viewport_width
;