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 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
);
64 typedef QTree QTreeNode
;
69 return g_new0 (QTree
, 1);
73 qtree_insert (QTree
* root
, int level
, guint64 x
, guint64 y
)
75 if (x
>= (pow2 (level
)) || y
>= (pow2 (level
))) {
76 g_warning ("QuadTree index out of range.");
81 g_warning ("passing a NULL QTree to qtree_insert");
85 QTreeNode
*node
= root
;
87 if (y
< (pow2 (level
))) {
88 if (x
< (pow2 (level
))) {
90 node
->l0
= g_new0 (QTreeNode
, 1);
91 node
->l0
->parent
= node
;
96 node
->l1
= g_new0 (QTreeNode
, 1);
97 node
->l1
->parent
= node
;
103 if (x
< (pow2 (level
))) {
105 node
->l2
= g_new0 (QTreeNode
, 1);
106 node
->l2
->parent
= node
;
112 node
->l3
= g_new0 (QTreeNode
, 1);
113 node
->l3
->parent
= node
;
125 qtree_insert_with_value (QTree
* root
, void *data
, int level
, guint64 x
, guint64 y
)
127 QTreeNode
*node
= qtree_insert (root
, level
, x
, y
);
128 node
->has_value
= true;
134 qtree_lookup (QTree
* root
, int level
, guint64 x
, guint64 y
)
136 if (x
>= (pow2 (level
)) || y
>= (pow2 (level
))) {
138 // we seem to run into an infinite loop sporadically here for drt #2014 completely spamming the test output.
139 // abort to get a stack trace.
142 g_warning ("QuadTree index out of range.");
146 while (level
-- > 0) {
150 if (y
< (pow2 (level
))) {
151 if (x
< (pow2 (level
))) {
158 if (x
< (pow2 (level
))) {
172 qtree_lookup_data (QTree
* root
, int level
, guint64 x
, guint64 y
)
174 QTree
*node
= qtree_lookup (root
, level
, x
, y
);
175 if (node
&& node
->has_value
)
181 qtree_remove (QTree
* node
, int depth
)
183 if (node
&& node
->has_value
) {
184 node
->has_value
= false;
185 cairo_surface_destroy ((cairo_surface_t
*)node
->data
);
191 qtree_remove (node
->l0
, depth
- 1);
192 qtree_remove (node
->l1
, depth
- 1);
193 qtree_remove (node
->l2
, depth
- 1);
194 qtree_remove (node
->l3
, depth
- 1);
199 qtree_remove_at (QTree
* root
, int level
, int x
, int y
, int depth
)
201 QTree
* node
= qtree_lookup (root
, level
, x
, y
);
202 qtree_remove (node
, depth
);
206 qtree_has_value_at (QTree
* root
, int level
, guint64 x
, guint64 y
)
208 QTree
*node
= qtree_lookup (root
, level
, x
, y
);
210 return node
->has_value
;
215 qtree_destroy (QTree
*root
)
220 //FIXME: the destroy func should be a qtree ctor option
222 cairo_surface_destroy ((cairo_surface_t
*)(root
->data
));
224 qtree_destroy (root
->l0
);
225 qtree_destroy (root
->l1
);
226 qtree_destroy (root
->l2
);
227 qtree_destroy (root
->l3
);
235 enum BitmapImageStatus
{
241 struct BitmapImageContext
243 BitmapImageStatus state
;
244 BitmapImage
*bitmapimage
;
258 morton (int n
, int *x
, int *y
) {
259 n
= (n
& 0x99999999) + ((n
& 0x22222222) << 1) + ((n
& 0x44444444) >> 1);
260 n
= (n
& 0xc3c3c3c3) + ((n
& 0x0c0c0c0c) << 2) + ((n
& 0x30303030) >> 2);
261 n
= (n
& 0xf00ff00f) + ((n
& 0x00f000f0) << 4) + ((n
& 0x0f000f00) >> 4);
262 n
= (n
& 0xff0000ff) + ((n
& 0x0000ff00) << 8) + ((n
& 0x00ff0000) >> 8);
271 n
= (n
& 0x11111111) + ((n
& 0x44444444) >> 1);
272 n
= (n
& 0x03030303) + ((n
& 0x30303030) >> 2);
273 n
= (n
& 0x000f000f) + ((n
& 0x0f000f00) >> 4);
274 return (n
& 0x000000ff) + ((n
& 0x00ff0000) >> 8);
280 n
= (n
& 0x88888888) + ((n
& 0x22222222) << 1);
281 n
= (n
& 0xc0c0c0c0) + ((n
& 0x0c0c0c0c) << 2);
282 n
= (n
& 0xf000f000) + ((n
& 0x00f000f0) << 4);
283 n
= (n
& 0xff000000) + ((n
& 0x0000ff00) << 8);
293 MultiScaleImage::MultiScaleImage () :
294 subimages_sorted(false),
295 pending_motion_completed(false),
301 // static bool init = true;
304 // MultiScaleImage::SubImagesProperty->SetValueValidator (MultiScaleSubImageCollectionValidator);
306 providers
[PropertyPrecedence_DynamicValue
] = new MultiScaleImagePropertyValueProvider (this, PropertyPrecedence_DynamicValue
);
308 SetObjectType (Type::MULTISCALEIMAGE
);
309 cache
= g_hash_table_new_full (g_int_hash
, g_int_equal
, g_free
, (GDestroyNotify
)qtree_destroy
);
312 MultiScaleImage::~MultiScaleImage ()
315 g_hash_table_destroy (cache
);
318 g_list_free (bitmapimages
);
323 MultiScaleImage::ZoomAboutLogicalPoint (double zoomIncrementFactor
, double zoomCenterLogicalX
, double zoomCenterLogicalY
)
325 LOG_MSI ("\nzoomabout logical %f (%f, %f)\n", zoomIncrementFactor
, zoomCenterLogicalX
, zoomCenterLogicalY
);
328 zoom_sb
->PauseWithError (NULL
);
330 pan_sb
->PauseWithError (NULL
);
332 double viewport_width
;
333 Point viewport_origin
;
335 if (GetUseSprings () && zoom_sb
&& pan_sb
) {
336 viewport_width
= zoom_target
;
337 viewport_origin
= pan_target
;
339 viewport_width
= GetViewportWidth ();
340 viewport_origin
= *GetViewportOrigin ();
343 double width
= viewport_width
/ zoomIncrementFactor
;
344 SetViewportWidth (width
);
345 if (!isnan(zoomCenterLogicalX
) && !isnan(zoomCenterLogicalY
)) {
346 SetViewportOrigin (new Point (zoomCenterLogicalX
- (zoomCenterLogicalX
- viewport_origin
.x
) / zoomIncrementFactor
,
347 zoomCenterLogicalY
- (zoomCenterLogicalY
- viewport_origin
.y
) / zoomIncrementFactor
));
352 MultiScaleImage::ElementToLogicalPoint (Point elementPoint
)
354 Point
*vp_origin
= GetViewportOrigin ();
355 double vp_width
= GetViewportWidth ();
356 double actual_width
= GetActualWidth ();
357 return Point (vp_origin
->x
+ (double)elementPoint
.x
* vp_width
/ actual_width
,
358 vp_origin
->y
+ (double)elementPoint
.y
* vp_width
/ actual_width
);
362 MultiScaleImage::LogicalToElementPoint (Point logicalPoint
)
364 Point
*vp_origin
= GetViewportOrigin ();
365 double vp_width
= GetViewportWidth ();
366 double actual_width
= GetActualWidth ();
367 return Point ((logicalPoint
.x
- vp_origin
->x
) * actual_width
/ vp_width
,
368 (logicalPoint
.y
- vp_origin
->y
) * actual_width
/ vp_width
);
372 MultiScaleImage::DownloadTile (BitmapImageContext
*bictx
, Uri
*tile
, int subimage
, int level
, int x
, int y
)
375 BitmapImageContext
*ctx
;
376 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
) {
377 if (ctx
->state
!= BitmapImageFree
&& ctx
->bitmapimage
->GetUriSource()->operator==(*tile
)) {
378 //LOG_MSI ("Tile %s is already being downloaded\n", tile->ToString ());
383 //LOG_MSI ("downloading tile %s\n", tile->ToString ());
385 bictx
->state
= BitmapImageBusy
;
386 bictx
->subimage
= subimage
;
387 bictx
->level
= level
;
391 SetIsDownloading (true);
392 bictx
->bitmapimage
->SetDownloadPolicy (MsiPolicy
);
393 bictx
->bitmapimage
->SetUriSource (tile
);
396 //multi_scale_image_handle_dz_parsed is only used for DeepZoom sources
398 multi_scale_image_handle_dz_parsed (void *userdata
)
400 MultiScaleImage
*msi
= (MultiScaleImage
*)userdata
;
401 //if the source is a collection, fill the subimages list
402 MultiScaleTileSource
*source
= msi
->GetSource ();
403 MultiScaleSubImageCollection
*subs
= msi
->GetSubImages ();
405 if (source
->GetImageWidth () >= 0 && source
->GetImageHeight () >= 0)
406 msi
->SetValue (MultiScaleImage::AspectRatioProperty
, Value ((double)source
->GetImageWidth () / (double)source
->GetImageHeight ()));
408 DeepZoomImageTileSource
*dsource
;
410 if (source
->Is (Type::DEEPZOOMIMAGETILESOURCE
) &&
411 (dsource
= (DeepZoomImageTileSource
*)source
)) {
413 MultiScaleSubImage
*si
;
414 for (i
= 0; (si
= (MultiScaleSubImage
*)g_list_nth_data (dsource
->subimages
, i
)); i
++) {
416 msi
->SetValue (MultiScaleImage::SubImagesProperty
, new MultiScaleSubImageCollection ());
423 //try to get the first tiles
424 BitmapImageContext
*bitmapimagectx
;
426 //Get the first tiles
427 while ((bitmapimagectx
= msi
->GetFreeBitmapImageContext ())) {
428 Uri
*tile
= new Uri ();
429 if (source
->get_tile_func (layer
, 0, 0, tile
, source
))
430 msi
->DownloadTile (bitmapimagectx
, tile
, -1, layer
, 0, 0);
435 msi
->EmitImageOpenSucceeded ();
438 //multi_scale_image_handle_dz_failes is only used for DeepZoom sources
440 multi_scale_image_handle_dz_failed (void *userdata
)
442 MultiScaleImage
*msi
= (MultiScaleImage
*)userdata
;
443 msi
->EmitImageOpenFailed ();
447 multi_scale_image_handle_dz_urisource_changed (void *userdata
)
449 MultiScaleImage
*msi
= (MultiScaleImage
*)userdata
;
450 msi
->OnSourcePropertyChanged ();
454 multi_scale_subimage_handle_failed (void *userdata
)
456 MultiScaleImage
*msi
= (MultiScaleImage
*)userdata
;
457 msi
->EmitImageFailed ();
461 multi_scale_subimage_handle_parsed (void *userdata
)
463 MultiScaleImage
*msi
= (MultiScaleImage
*)userdata
;
468 fade_finished (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
470 MultiScaleImage
*msi
= (MultiScaleImage
*) closure
;
471 msi
->FadeFinished ();
475 MultiScaleImage::FadeFinished ()
478 if (!is_fading
&& !is_zooming
&& !is_panning
)
479 EmitMotionFinished ();
483 zoom_finished (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
485 MultiScaleImage
*msi
= (MultiScaleImage
*) closure
;
486 msi
->ZoomFinished ();
490 MultiScaleImage::ZoomFinished ()
493 if (!is_fading
&& !is_zooming
&& !is_panning
)
494 EmitMotionFinished ();
498 pan_finished (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
500 MultiScaleImage
*msi
= (MultiScaleImage
*) closure
;
505 MultiScaleImage::PanFinished ()
508 if (!is_fading
&& !is_zooming
&& !is_panning
)
509 EmitMotionFinished ();
513 motion_finished_delayed (EventObject
*sender
)
515 LOG_MSI ("MSI::motion_finished_delayed ()\n");
516 ((MultiScaleImage
*) sender
)->EmitMotionFinished ();
520 multi_scale_image_invalidate_tile_layer (int level
, int tilePositionX
, int tilePositionY
, int tileLayer
, void *userdata
)
522 MultiScaleImage
*msi
= (MultiScaleImage
*)userdata
;
523 msi
->InvalidateTileLayer (level
, tilePositionX
, tilePositionY
, tileLayer
);
527 tile_available (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
529 // LOG_MSI ("Tile downloaded %s\n", ((BitmapImage *)sender)->GetUriSource ()->ToString ());
530 ((MultiScaleImage
*)closure
)->TileOpened ((BitmapImage
*)sender
);
534 MultiScaleImage::TileOpened (BitmapImage
*bitmapimage
)
536 BitmapImageContext
*ctx
= GetBitmapImageContext (bitmapimage
);
537 ctx
->state
= BitmapImageDone
;
539 bool is_downloading
= false;
540 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
541 is_downloading
|= (ctx
->state
== BitmapImageBusy
);
542 SetIsDownloading (is_downloading
);
547 tile_failed (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
549 // LOG_MSI ("Failed to download tile %s\n", ((BitmapImage *)sender)->GetUriSource ()->ToString ());
550 ((MultiScaleImage
*)closure
)->TileFailed ((BitmapImage
*)sender
);
554 MultiScaleImage::TileFailed (BitmapImage
*bitmapimage
)
556 BitmapImageContext
*ctx
= GetBitmapImageContext (bitmapimage
);
557 if (ctx
->retry
< 5) {
558 bitmapimage
->SetUriSource (bitmapimage
->GetUriSource ());
559 ctx
->retry
= ctx
->retry
+ 1;
561 ctx
->state
= BitmapImageFree
;
563 LOG_MSI ("caching a NULL for %s\n", ctx
->bitmapimage
->GetUriSource()->ToString ());
564 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &(ctx
->subimage
));
566 g_hash_table_insert (cache
, new int(ctx
->subimage
), (subimage_cache
= qtree_new ()));
567 qtree_insert_with_value (subimage_cache
, NULL
, ctx
->level
, ctx
->x
, ctx
->y
);
570 bool is_downloading
= false;
571 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
572 is_downloading
|= (ctx
->state
== BitmapImageBusy
);
573 SetIsDownloading (is_downloading
);
580 MultiScaleImage::GetBitmapImageContext (BitmapImage
*bitmapimage
)
582 BitmapImageContext
*ctx
;
584 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
585 if (ctx
->bitmapimage
== bitmapimage
)
591 MultiScaleImage::GetFreeBitmapImageContext ()
594 BitmapImageContext
*ctx
;
596 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
)
597 if (ctx
->state
== BitmapImageFree
)
600 if (g_list_length (bitmapimages
) < num_dl
) {
601 ctx
= new BitmapImageContext ();
602 ctx
->state
= BitmapImageFree
;
603 ctx
->bitmapimage
= new BitmapImage ();
604 ctx
->bitmapimage
->AddHandler (ctx
->bitmapimage
->ImageOpenedEvent
, tile_available
, this);
605 ctx
->bitmapimage
->AddHandler (ctx
->bitmapimage
->ImageFailedEvent
, tile_failed
, this);
606 bitmapimages
= g_list_append (bitmapimages
, ctx
);
614 MultiScaleImage::Render (cairo_t
*cr
, Region
*region
, bool path_only
)
616 LOG_MSI ("MSI::Render\n");
618 //Process the downloaded tile
620 BitmapImageContext
*ctx
;
621 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
) {
622 cairo_surface_t
*surface
;
624 if (ctx
->state
!= BitmapImageDone
|| !(surface
= cairo_surface_reference (ctx
->bitmapimage
->GetSurface (cr
))))
627 // Uri *tile = ctx->bitmapimage->GetUriSource ();
628 cairo_surface_set_user_data (surface
, &width_key
, new int (ctx
->bitmapimage
->GetPixelWidth ()), g_free
);
629 cairo_surface_set_user_data (surface
, &height_key
, new int (ctx
->bitmapimage
->GetPixelHeight ()), g_free
);
632 fadein_sb
= new Storyboard ();
633 fadein_sb
->SetManualTarget (this);
634 fadein_sb
->SetTargetProperty (fadein_sb
, new PropertyPath ("(MultiScaleImage.TileFade)"));
635 fadein_animation
= new DoubleAnimation ();
636 fadein_animation
->SetDuration (Duration::FromSecondsFloat (0.5));
637 TimelineCollection
*tlc
= new TimelineCollection ();
638 tlc
->Add (static_cast<DoubleAnimation
*> (fadein_animation
));
639 fadein_sb
->SetChildren(tlc
);
640 fadein_sb
->AddHandler (Storyboard::CompletedEvent
, fade_finished
, this);
642 fadein_sb
->SetName ("Multiscale Fade-In");
645 fadein_sb
->PauseWithError (NULL
);
648 //LOG_MSI ("animating Fade from %f to %f\n\n", GetValue(MultiScaleImage::TileFadeProperty)->AsDouble(), GetValue(MultiScaleImage::TileFadeProperty)->AsDouble() + 0.9);
649 double *to
= new double (GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble() + 0.9);
650 fadein_animation
->SetFrom (GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble());
651 fadein_animation
->SetTo (*to
);
655 fadein_sb
->BeginWithError(NULL
);
657 cairo_surface_set_user_data (surface
, &full_opacity_at_key
, to
, g_free
);
658 LOG_MSI ("caching %s\n", ctx
->bitmapimage
->GetUriSource()->ToString ());
659 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &(ctx
->subimage
));
661 g_hash_table_insert (cache
, new int(ctx
->subimage
), (subimage_cache
= qtree_new ()));
662 qtree_insert_with_value (subimage_cache
, surface
, ctx
->level
, ctx
->x
, ctx
->y
);
664 ctx
->bitmapimage
->SetUriSource (NULL
);
665 ctx
->state
= BitmapImageFree
;
668 MultiScaleTileSource
*source
= GetSource ();
670 LOG_MSI ("no sources set, nothing to render\n");
674 bool is_collection
= source
&&
675 source
->Is (Type::DEEPZOOMIMAGETILESOURCE
) &&
676 ((DeepZoomImageTileSource
*)source
)->IsCollection () &&
679 if (source
->GetImageWidth () < 0 && !is_collection
) {
680 LOG_MSI ("nothing to render so far...\n");
681 if (source
->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
682 ((DeepZoomImageTileSource
*)source
)->set_callbacks (multi_scale_image_handle_dz_parsed
, multi_scale_image_handle_dz_failed
, multi_scale_image_handle_dz_urisource_changed
, this);
683 ((DeepZoomImageTileSource
*)source
)->Download ();
689 if (!source
->get_tile_func
) {
690 g_warning ("no get_tile_func set\n");
696 RenderCollection (cr
, region
);
698 RenderSingle (cr
, region
);
702 MultiScaleImage::RenderCollection (cairo_t
*cr
, Region
*region
)
704 LOG_MSI ("\nMSI::RenderCollection\n");
706 double msi_w
= GetActualWidth ();
707 double msi_h
= GetActualHeight ();
708 double msi_ar
= GetAspectRatio();
709 double msivp_ox
= GetViewportOrigin()->x
;
710 double msivp_oy
= GetViewportOrigin()->y
;
711 double msivp_w
= GetViewportWidth();
713 if (!GetSource ()->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
714 g_warning ("RenderCollection called for a non deepzoom tile source. this should not happen");
717 DeepZoomImageTileSource
*dzits
= (DeepZoomImageTileSource
*)GetSource ();
719 Rect viewport
= Rect (msivp_ox
, msivp_oy
, msivp_w
, msivp_w
/msi_ar
);
721 MultiScaleSubImageCollection
*subs
= GetSubImages ();
722 if (!subimages_sorted
) {
723 subs
->ResortByZIndex ();
724 subimages_sorted
= true;
727 //using the "-1" index for the shared cache
728 int shared_index
= -1;
729 QTree
*shared_cache
= (QTree
*)g_hash_table_lookup (cache
, &shared_index
);
731 g_hash_table_insert (cache
, new int(shared_index
), (shared_cache
= qtree_new ()));
734 for (i
= 0; i
< subs
->GetCount (); i
++) {
735 MultiScaleSubImage
*sub_image
= (MultiScaleSubImage
*)g_ptr_array_index (subs
->z_sorted
, i
);
737 int index
= sub_image
->GetId();
738 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &index
);
740 g_hash_table_insert (cache
, new int(index
), (subimage_cache
= qtree_new ()));
742 double subvp_ox
= sub_image
->GetViewportOrigin()->x
;
743 double subvp_oy
= sub_image
->GetViewportOrigin()->y
;
744 double subvp_w
= sub_image
->GetViewportWidth();
745 double sub_w
= sub_image
->source
->GetImageWidth ();
746 double sub_h
= sub_image
->source
->GetImageHeight ();
747 double sub_ar
= sub_image
->GetAspectRatio();
750 //expressing the subimage viewport in main viewport coordinates.
751 Rect sub_vp
= Rect (-subvp_ox
/ subvp_w
, -subvp_oy
/ subvp_w
, 1.0/subvp_w
, 1.0/(sub_ar
* subvp_w
));
753 //render only if the subimage viewport intersects with this viewport
754 if (!sub_vp
.IntersectsWith (viewport
))
756 LOG_MSI ("Intersects with main viewport...rendering\n");
759 if (frexp (MAX (sub_w
, sub_h
), &layers
) == 0.5)
763 frexp (msi_w
/ (subvp_w
* msivp_w
* MIN (1.0, sub_ar
)), &optimal_layer
);
764 optimal_layer
= MIN (optimal_layer
, layers
);
765 LOG_MSI ("number of layers: %d\toptimal layer for this: %d\n", layers
, optimal_layer
);
768 int from_layer
= optimal_layer
;
769 while (from_layer
>= 0) {
772 bool blending
= FALSE
; //means at least a tile is not yet fully blended
774 int tile_width
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileWidth () : dzits
->GetTileWidth ();
775 int tile_height
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileHeight (): dzits
->GetTileHeight ();
777 //in msi relative coord
778 double v_tile_w
= tile_width
* (double) (pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
779 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
780 //LOG_MSI ("virtual tile size at layer %d; %fx%f\n", from_layer, v_tile_w, v_tile_h);
783 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
++) {
784 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
++) {
786 cairo_surface_t
* image
= NULL
;
789 if (from_layer
> dzits
->GetMaxLevel ()) {
790 if ((image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, from_layer
, i
, j
)))
792 } else if ((image
= (cairo_surface_t
*)qtree_lookup_data (shared_cache
, from_layer
,
793 morton_x (sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
794 morton_y (sub_image
->n
) * (pow2 (from_layer
)) / tile_height
)))
797 if (image
&& *(double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
)) > GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble ())
801 if (found
> 0 && to_layer
< from_layer
)
802 to_layer
= from_layer
;
803 if (found
== count
&& (!blending
|| from_layer
== 0))
810 LOG_MSI ("rendering layers from %d to %d\n", from_layer
, to_layer
);
811 double fade
= GetValue (MultiScaleImage::TileFadeProperty
)->AsDouble();
812 if (from_layer
>= 0) {
815 cairo_rectangle (cr
, 0, 0, msi_w
, msi_h
);
817 cairo_scale (cr
, msi_w
/ msivp_w
, msi_w
/ msivp_w
); //scale to widget
820 -msivp_ox
+ sub_vp
.x
,
821 -msivp_oy
+ sub_vp
.y
);
827 cairo_rectangle (cr
, 0, 0, sub_w
, sub_h
);
830 if (IS_TRANSLUCENT (sub_image
->GetOpacity ()))
831 cairo_push_group (cr
);
833 int layer_to_render
= from_layer
;
834 while (layer_to_render
<= to_layer
) {
835 int tile_width
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ?sub_image
->source
->GetTileWidth () : dzits
->GetTileWidth ();
836 int tile_height
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileHeight () : dzits
->GetTileHeight ();
838 double v_tile_w
= tile_width
* (double)(pow2 (layers
- layer_to_render
)) * sub_vp
.width
/ sub_w
;
839 double v_tile_h
= tile_height
* (double)(pow2 (layers
- layer_to_render
)) * sub_vp
.width
/ sub_w
;
842 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
++) {
843 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
++) {
844 cairo_surface_t
*image
= NULL
;
845 bool shared_tile
= false;
846 if (layer_to_render
> dzits
->GetMaxLevel())
847 image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, layer_to_render
, i
, j
);
849 //Check in the shared levels
852 image
= (cairo_surface_t
*)qtree_lookup_data (shared_cache
, layer_to_render
,
853 morton_x(sub_image
->n
) * pow2 (layer_to_render
) / tile_width
,
854 morton_y(sub_image
->n
) * pow2 (layer_to_render
) / tile_height
);
860 LOG_MSI ("rendering subimage %d %d %d %d\n", sub_image
->id
, layer_to_render
, i
, j
);
864 pow2 (layers
- layer_to_render
),
865 pow2 (layers
- layer_to_render
));
873 (int)(-morton_x(sub_image
->n
) * (pow2 (layer_to_render
))) % tile_width
,
874 (int)(-morton_y(sub_image
->n
) * (pow2 (layer_to_render
))) % tile_height
);
878 cairo_set_source_surface (cr
, image
, 0, 0);
880 double *opacity
= (double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
));
881 double combined
= 1.0;
883 if (opacity
&& *opacity
> fade
)
884 combined
= MIN(1.0 - *opacity
+ fade
, 1.0);
886 if (IS_TRANSLUCENT (combined
))
887 cairo_paint_with_alpha (cr
, combined
);
897 if (IS_TRANSLUCENT (sub_image
->GetOpacity ())) {
898 cairo_pop_group_to_source (cr
);
899 cairo_paint_with_alpha (cr
, sub_image
->GetOpacity ());
905 if (!GetAllowDownloading ())
908 BitmapImageContext
*bitmapimagectx
;
909 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
912 //Get the next tiles..
913 while (from_layer
< optimal_layer
) {
916 //if the subimage is unparsed, trigger the download
917 if (from_layer
> dzits
->GetMaxLevel () && !((DeepZoomImageTileSource
*)sub_image
->source
)->IsDownloaded () ) {
918 ((DeepZoomImageTileSource
*)sub_image
->source
)->set_callbacks (multi_scale_subimage_handle_parsed
, multi_scale_subimage_handle_failed
, NULL
, this);
919 ((DeepZoomImageTileSource
*)sub_image
->source
)->Download ();
923 int tile_width
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ?sub_image
->source
->GetTileWidth () : dzits
->GetTileWidth ();
924 int tile_height
= (from_layer
> dzits
->GetMaxLevel () && ((DeepZoomImageTileSource
*)sub_image
->source
)->IsParsed ()) ? sub_image
->source
->GetTileHeight (): dzits
->GetTileHeight ();
926 double v_tile_w
= tile_width
* (double)(pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
927 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) * sub_vp
.width
/ sub_w
;
930 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
++) {
931 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
933 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
++) {
934 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
936 Uri
*tile
= new Uri ();
937 if (from_layer
<= dzits
->GetMaxLevel ()) {
938 if (!qtree_has_value_at (shared_cache
, from_layer
,
939 morton_x(sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
940 morton_y(sub_image
->n
) * (pow2 (from_layer
)) / tile_height
)
941 && dzits
->get_tile_func (from_layer
,
942 morton_x(sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
943 morton_y(sub_image
->n
) * (pow2 (from_layer
)) / tile_height
,
945 DownloadTile (bitmapimagectx
, tile
, shared_index
, from_layer
,
946 morton_x(sub_image
->n
) * (pow2 (from_layer
)) / tile_width
,
947 morton_y(sub_image
->n
) * (pow2 (from_layer
)) / tile_height
);
949 if (!qtree_has_value_at (subimage_cache
, from_layer
, i
, j
)
950 && dzits
->get_tile_func (from_layer
, i
, j
, tile
, sub_image
->source
))
951 DownloadTile (bitmapimagectx
, tile
, index
, from_layer
, i
, j
);
961 MultiScaleImage::RenderSingle (cairo_t
*cr
, Region
*region
)
963 MultiScaleTileSource
*source
= GetSource ();
964 double msi_w
= GetActualWidth ();
965 double msi_h
= GetActualHeight ();
966 double msi_ar
= GetAspectRatio ();
967 double im_w
= source
->GetImageWidth ();
968 double im_h
= source
->GetImageHeight ();
969 int tile_width
= source
->GetTileWidth ();
970 int tile_height
= source
->GetTileHeight ();
971 double vp_ox
= GetViewportOrigin()->x
;
972 double vp_oy
= GetViewportOrigin()->y
;
973 double vp_w
= GetViewportWidth ();
975 if (msi_w
<= 0.0 || msi_h
<= 0.0)
976 return; //invisible widget, nothing to render
978 //Number of layers in the MSI, aka the lowest powerof2 that's bigger than width and height
980 if (frexp (MAX (im_w
, im_h
), &layers
) == 0.5)
983 //optimal layer for this... aka "best viewed at"
985 if (frexp (msi_w
/ (vp_w
* MIN (1.0, msi_ar
)) , &optimal_layer
) == 0.5)
987 optimal_layer
= MIN (optimal_layer
, layers
);
988 LOG_MSI ("number of layers: %d\toptimal layer for this: %d\n", layers
, optimal_layer
);
990 //We have to figure all the layers that we'll have to render:
991 //- 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))
992 //- to_layer is the highest PARTIAL layer that we can display (contains at least 1 tiles partially blended)
995 int from_layer
= optimal_layer
;
997 //using the "-1" index for the single image case
999 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &index
);
1000 if (!subimage_cache
)
1001 g_hash_table_insert (cache
, new int(index
), (subimage_cache
= qtree_new ()));
1003 while (from_layer
>= 0) {
1006 bool blending
= FALSE
; //means at least a tile is not yet fully blended
1008 //v_tile_X is the virtual tile size at this layer in relative coordinates
1009 double v_tile_w
= tile_width
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1010 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1013 //This double loop iterate over the displayed part of the image and find all (i,j) being top-left corners of tiles
1014 for (i
= MAX(0, (int)(vp_ox
/ v_tile_w
)); i
* v_tile_w
< MIN(vp_ox
+ vp_w
, 1.0); i
++) {
1015 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
++) {
1017 cairo_surface_t
*image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, from_layer
, i
, j
);
1021 if (image
&& *(double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
)) > GetValue(MultiScaleImage::TileFadeProperty
)->AsDouble ())
1026 if (found
> 0 && to_layer
< from_layer
)
1027 to_layer
= from_layer
;
1028 if (found
== count
&& (!blending
|| from_layer
== 0))
1034 //cairo_push_group (cr);
1039 cairo_rectangle (cr
, 0, 0, msi_w
, msi_h
);
1043 cairo_scale (cr
, msi_w
/ vp_w
, msi_w
/ vp_w
); //scale to viewport
1044 cairo_translate (cr
, -vp_ox
, -vp_oy
);
1045 cairo_scale (cr
, 1.0 / im_w
, 1.0 / im_w
);
1047 cairo_rectangle (cr
, 0, 0, im_w
, im_h
); // clip to image bounds
1050 LOG_MSI ("rendering layers from %d to %d\n", from_layer
, to_layer
);
1052 double fade
= GetValue (MultiScaleImage::TileFadeProperty
)->AsDouble();
1053 int layer_to_render
= MAX (0, from_layer
);
1054 while (layer_to_render
<= to_layer
) {
1056 double v_tile_w
= tile_width
* (double)(pow2 (layers
- layer_to_render
)) / im_w
;
1057 double v_tile_h
= tile_height
* (double)(pow2 (layers
- layer_to_render
)) / im_w
;
1058 for (i
= MAX(0, (int)(vp_ox
/ v_tile_w
)); i
* v_tile_w
< MIN(vp_ox
+ vp_w
, 1.0); i
++) {
1059 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
++) {
1060 cairo_surface_t
*image
= (cairo_surface_t
*)qtree_lookup_data (subimage_cache
, layer_to_render
, i
, j
);
1064 LOG_MSI ("rendering %d %d %d\n", layer_to_render
, i
, j
);
1067 cairo_scale (cr
, (pow2 (layers
- layer_to_render
)), (pow2 (layers
- layer_to_render
))); //scale to image size
1069 cairo_translate (cr
, i
* tile_width
, j
* tile_height
);
1071 cairo_set_source_surface (cr
, image
, 0, 0);
1073 double *opacity
= (double*)(cairo_surface_get_user_data (image
, &full_opacity_at_key
));
1074 double combined
= 1.0;
1076 if (opacity
&& *opacity
> fade
)
1077 combined
= MIN(1.0 - *opacity
+ fade
, 1.0);
1079 if (IS_TRANSLUCENT (combined
))
1080 cairo_paint_with_alpha (cr
, combined
);
1090 // cairo_pop_group_to_source (cr);
1092 if (!GetAllowDownloading ())
1095 BitmapImageContext
*bitmapimagectx
;
1096 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
1099 //Get the next tile(s)...
1100 while (from_layer
< optimal_layer
) {
1103 double v_tile_w
= tile_width
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1104 double v_tile_h
= tile_height
* (double)(pow2 (layers
- from_layer
)) / im_w
;
1107 for (i
= MAX(0, (int)(vp_ox
/ v_tile_w
)); i
* v_tile_w
< MIN(vp_ox
+ vp_w
, 1.0); i
++) {
1108 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
1110 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
++) {
1111 if (!(bitmapimagectx
= GetFreeBitmapImageContext ()))
1113 Uri
*tile
= new Uri ();
1114 if (!qtree_has_value_at (subimage_cache
, from_layer
, i
, j
)) {
1115 if (source
->get_tile_func (from_layer
, i
, j
, tile
, source
))
1116 DownloadTile (bitmapimagectx
, tile
, index
, from_layer
, i
, j
);
1118 qtree_insert_with_value (subimage_cache
, NULL
, from_layer
, i
, j
);
1127 MultiScaleImage::OnSourcePropertyChanged ()
1129 //abort all downloaders
1130 BitmapImageContext
*ctx
;
1132 for (list
= g_list_first (bitmapimages
); list
&& (ctx
= (BitmapImageContext
*)list
->data
); list
= list
->next
) {
1133 ctx
->bitmapimage
->Abort ();
1134 ctx
->state
= BitmapImageFree
;
1137 DeepZoomImageTileSource
*newsource
;
1139 if (GetSource ()->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
1140 if ((newsource
= GetValue (MultiScaleImage::SourceProperty
)->AsDeepZoomImageTileSource ())) {
1141 newsource
->set_callbacks (multi_scale_image_handle_dz_parsed
, multi_scale_image_handle_dz_failed
, multi_scale_image_handle_dz_urisource_changed
, this);
1142 newsource
->Download ();
1145 EmitImageOpenSucceeded ();
1149 //Reset the viewport
1150 ClearValue (MultiScaleImage::InternalViewportWidthProperty
, true);
1151 ClearValue (MultiScaleImage::InternalViewportOriginProperty
, true);
1152 //SetValue (MultiScaleImage::ViewportOriginProperty, Deployment::GetCurrent ()->GetTypes ()->GetProperty (MultiScaleImage::ViewportOriginProperty)->GetDefaultValue());
1153 //SetValue (MultiScaleImage::ViewportWidthProperty, Deployment::GetCurrent ()->GetTypes ()->GetProperty (MultiScaleImage::ViewportWidthProperty)->GetDefaultValue());
1155 //Invalidate the whole cache
1157 g_hash_table_destroy (cache
);
1158 cache
= g_hash_table_new_full (g_int_hash
, g_int_equal
, g_free
, (GDestroyNotify
)qtree_destroy
);
1161 //register the callback for InvalidateTileLayers
1163 GetSource ()->set_invalidate_tile_layer_func (multi_scale_image_invalidate_tile_layer
, this);
1169 MultiScaleImage::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
1171 if (args
->GetId () == MultiScaleImage::AllowDownloadingProperty
&& args
->GetNewValue () && args
->GetNewValue()->AsBool ())
1174 if (args
->GetId () == MultiScaleImage::InternalViewportOriginProperty
) {
1175 Emit (MultiScaleImage::ViewportChangedEvent
);
1179 if (args
->GetId () == MultiScaleImage::InternalViewportWidthProperty
) {
1180 Emit (MultiScaleImage::ViewportChangedEvent
);
1184 if (args
->GetId () == MultiScaleImage::ViewportOriginProperty
) {
1185 pan_target
= Point (args
->GetNewValue ()->AsPoint ()->x
, args
->GetNewValue ()->AsPoint ()->y
);
1186 SetInternalViewportOrigin (args
->GetNewValue ()->AsPoint ());
1187 ClearValue (MultiScaleImage::ViewportOriginProperty
, false);
1190 if (args
->GetId () == MultiScaleImage::ViewportWidthProperty
) {
1191 zoom_target
= args
->GetNewValue ()->AsDouble ();
1192 SetInternalViewportWidth (args
->GetNewValue ()->AsDouble ());
1193 ClearValue (MultiScaleImage::ViewportWidthProperty
, false);
1196 if (args
->GetId () == MultiScaleImage::TileFadeProperty
) {
1197 //There's 2 options here,
1198 // - loop all the tiles, update their opacity, and only invalidate a subregion
1199 // - Invalidate all, and compute the new opacity on the tiles that needs to be rendered.
1200 //Both options are unfortunately quite expensive :(
1201 //LOG_MSI ("TileFade changed to %f\n", args->GetNewValue()->AsDouble ());
1205 if (args
->GetId () == MultiScaleImage::SourceProperty
) {
1206 OnSourcePropertyChanged ();
1209 if (args
->GetProperty ()->GetOwnerType () != Type::MULTISCALEIMAGE
) {
1210 DependencyObject::OnPropertyChanged (args
, error
);
1214 NotifyListenersOfPropertyChange (args
, error
);
1218 MultiScaleImage::OnCollectionChanged (Collection
*col
, CollectionChangedEventArgs
*args
)
1220 subimages_sorted
= false;
1225 MultiScaleImage::OnCollectionItemChanged (Collection
*col
, DependencyObject
*obj
, PropertyChangedEventArgs
*args
)
1227 if (args
->GetId () == MultiScaleSubImage::ViewportWidthProperty
)
1229 if (args
->GetId () == MultiScaleSubImage::ViewportOriginProperty
)
1231 if (args
->GetId () == MultiScaleSubImage::ZIndexProperty
) {
1232 subimages_sorted
= false;
1238 MultiScaleImage::EmitImageFailed ()
1240 LOG_MSI ("MSI::Emitting image failed\n");
1241 Emit (MultiScaleImage::ImageFailedEvent
);
1245 MultiScaleImage::EmitImageOpenFailed ()
1247 LOG_MSI ("MSI::Emitting image open failed\n");
1248 Emit (MultiScaleImage::ImageOpenFailedEvent
);
1252 MultiScaleImage::EmitImageOpenSucceeded ()
1254 LOG_MSI ("\nMSI::Emitting open suceeded\n");
1255 Emit (MultiScaleImage::ImageOpenSucceededEvent
);
1256 // This is a hack that removes at least one timeout (#291),
1257 // possibly because an invalidation gets lost somehow.
1258 // Since we only start downloading when we try to
1259 // render the msi, the test effectively hangs.
1260 FullInvalidate (true);
1264 MultiScaleImage::EmitMotionFinished ()
1266 LOG_MSI ("Emitting MotionFinished\n");
1267 pending_motion_completed
= false;
1268 Emit (MultiScaleImage::MotionFinishedEvent
);
1272 MultiScaleImage::SetInternalViewportWidth (double value
)
1274 if (!GetUseSprings ()) {
1275 if (!pending_motion_completed
) {
1276 AddTickCall (motion_finished_delayed
);
1277 pending_motion_completed
= true;
1279 SetValue (MultiScaleImage::InternalViewportWidthProperty
, Value (value
));
1284 zoom_sb
= new Storyboard ();
1285 zoom_sb
->SetManualTarget (this);
1286 zoom_sb
->SetTargetProperty (zoom_sb
, new PropertyPath ("(MultiScaleImage.InternalViewportWidth)"));
1287 zoom_sb
->AddHandler (Storyboard::CompletedEvent
, zoom_finished
, this);
1288 zoom_animation
= new DoubleAnimationUsingKeyFrames ();
1289 zoom_animation
->SetDuration (Duration::FromSeconds (4));
1290 zoom_animation
->SetKeyFrames (DOPtr
<DoubleKeyFrameCollection
> (new DoubleKeyFrameCollection ()));
1291 DOPtr
<SplineDoubleKeyFrame
> keyframe (new SplineDoubleKeyFrame ());
1292 keyframe
->SetKeySpline (DOPtr
<KeySpline
> (new KeySpline (.05, .5, 0, 1.0)));
1293 keyframe
->SetKeyTime (KeyTime::FromPercent (1.0));
1294 zoom_animation
->GetKeyFrames ()->Add (static_cast<SplineDoubleKeyFrame
*>(keyframe
));
1296 DOPtr
<TimelineCollection
> tlc (new TimelineCollection ());
1297 tlc
->Add (static_cast<DoubleAnimationUsingKeyFrames
*>(zoom_animation
));
1298 zoom_sb
->SetChildren(tlc
);
1300 zoom_sb
->SetName ("Multiscale Zoom");
1303 zoom_sb
->PauseWithError (NULL
);
1306 LOG_MSI ("animating zoom from %f to %f\n\n", GetViewportWidth(), value
)
1310 zoom_animation
->GetKeyFrames ()->GetValueAt (0)->AsSplineDoubleKeyFrame ()->SetValue (value
);
1311 zoom_sb
->BeginWithError (NULL
);
1315 MultiScaleImage::SetInternalViewportOrigin (Point
* value
)
1317 if (!GetUseSprings ()) {
1318 if (!pending_motion_completed
) {
1319 AddTickCall (motion_finished_delayed
);
1320 pending_motion_completed
= true;
1322 SetValue (MultiScaleImage::InternalViewportOriginProperty
, Value (*value
));
1327 pan_sb
= new Storyboard ();
1328 pan_sb
->SetManualTarget (this);
1329 pan_sb
->SetTargetProperty (pan_sb
, new PropertyPath ("(MultiScaleImage.InternalViewportOrigin)"));
1330 pan_sb
->AddHandler (Storyboard::CompletedEvent
, pan_finished
, this);
1331 pan_animation
= new PointAnimationUsingKeyFrames ();
1332 pan_animation
->SetDuration (Duration::FromSeconds (4));
1333 pan_animation
->SetKeyFrames (DOPtr
<PointKeyFrameCollection
> (new PointKeyFrameCollection ()));
1334 SplinePointKeyFrame
*keyframe
= new SplinePointKeyFrame ();
1335 keyframe
->SetKeySpline (DOPtr
<KeySpline
> (new KeySpline (.05, .5, 0, 1.0)));
1336 keyframe
->SetKeyTime (KeyTime::FromPercent (1.0));
1337 pan_animation
->GetKeyFrames ()->Add (keyframe
);
1339 TimelineCollection
*tlc
= new TimelineCollection ();
1340 tlc
->Add (static_cast<PointAnimationUsingKeyFrames
*> (pan_animation
));
1341 pan_sb
->SetChildren(tlc
);
1343 pan_sb
->SetName ("Multiscale Pan");
1346 pan_sb
->PauseWithError (NULL
);
1349 pan_animation
->GetKeyFrames ()->GetValueAt (0)->AsSplinePointKeyFrame ()->SetValue (*value
);
1350 pan_sb
->BeginWithError (NULL
);
1354 MultiScaleImage::SetIsIdle (bool value
)
1356 SetValue (MultiScaleImage::IsIdleProperty
, Value (value
));
1360 MultiScaleImage::SetIsDownloading (bool value
)
1362 SetValue (MultiScaleImage::IsDownloadingProperty
, Value (value
));
1366 MultiScaleImage::LogicalToElementX (int x
, int y
)
1368 return LogicalToElementPoint (Point (x
, y
)).x
;
1372 MultiScaleImage::LogicalToElementY (int x
, int y
)
1374 return LogicalToElementPoint (Point (x
, y
)).y
;
1377 MultiScaleSubImage
*
1378 MultiScaleImage::GetIthSubImage (int index
)
1380 MultiScaleSubImageCollection
*sub_images
= GetSubImages ();
1383 if (sub_images
== NULL
)
1386 value
= sub_images
->GetValueAt (index
);
1391 return value
->AsMultiScaleSubImage ();
1395 MultiScaleImage::GetSubImageCount ()
1397 MultiScaleSubImageCollection
*sub_images
= GetSubImages ();
1399 if (sub_images
== NULL
)
1401 return sub_images
->GetCount ();
1405 MultiScaleImage::InvalidateTileLayer (int level
, int tilePositionX
, int tilePositionY
, int tileLayer
)
1407 if (GetSource ()->Is (Type::DEEPZOOMIMAGETILESOURCE
)) {
1408 g_warning ("calling InvalidateTileLayer on DeepZoom Images makes no sense\n");
1413 QTree
*subimage_cache
= (QTree
*)g_hash_table_lookup (cache
, &index
);
1415 qtree_remove_at (subimage_cache
, level
, tilePositionX
, tilePositionY
, 0);
1421 * MultiScaleImagePropertyValueProvider
1424 MultiScaleImagePropertyValueProvider::MultiScaleImagePropertyValueProvider (MultiScaleImage
*msi
, PropertyPrecedence precedence
)
1425 : FrameworkElementProvider (msi
, precedence
)
1427 viewport_origin
= NULL
;
1428 viewport_width
= NULL
;
1431 MultiScaleImagePropertyValueProvider::~MultiScaleImagePropertyValueProvider ()
1433 delete viewport_origin
;
1434 delete viewport_width
;
1438 MultiScaleImagePropertyValueProvider::GetPropertyValue (DependencyProperty
*property
)
1440 // We verify main thread here too in case some object in the pipeline happens to want a property on the media element
1443 if (property
->GetId () == MultiScaleImage::ViewportOriginProperty
)
1444 return GetViewportOrigin ();
1445 if (property
->GetId () == MultiScaleImage::ViewportWidthProperty
)
1446 return GetViewportWidth ();
1447 return FrameworkElementProvider::GetPropertyValue (property
);
1451 MultiScaleImagePropertyValueProvider::GetViewportOrigin ()
1453 MultiScaleImage
*msi
= (MultiScaleImage
*) obj
;
1455 delete viewport_origin
;
1456 viewport_origin
= new Value (*(msi
->GetInternalViewportOrigin ()));
1457 return viewport_origin
;
1461 MultiScaleImagePropertyValueProvider::GetViewportWidth ()
1463 MultiScaleImage
*msi
= (MultiScaleImage
*) obj
;
1465 delete viewport_width
;
1466 viewport_width
= new Value (msi
->GetInternalViewportWidth ());
1467 return viewport_width
;