Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move tile caching code inside tilecache.js. #659

Merged
merged 13 commits into from Jun 12, 2015

Conversation

avandecreme
Copy link
Member

I was looking on how to handle modifying the tiles in an asynchronous way in case the operation is lengthy.
That would also allow interaction with http://camanjs.com/.

The idea would be to provide a way to modify a tile before it is stored in cache. Probably some sort of callback or event inside onTileLoad in tiledimage.js

This first commit is just some cleanup. I suspect it might also save a bit of memory because the ImageRecord was keeping a reference to the image even if it has been rendered.
I deleted a method, hopefully this is internal enough for not being a breaking change.

@avandecreme
Copy link
Member Author

Ok this second commit actually add a tile-loaded event.
However, that is not enough for my purpose.
I would need some promise system so that the tile is added to the cache only when all the event handlers are done.

Any idea on that?

Here is how I am using this with Caman (I know I am currently wasting memory :) )

osd.addHandler( "tile-loaded", function ( event ) {
     var tile = event.tile;
     var tiledImage = event.tiledImage;
     var image = event.image;
     var canvas = document.createElement( 'canvas' );
     canvas.width = image.width;
     canvas.height = image.height;
     tile._renderedContext = canvas.getContext( '2d' );
     tile._renderedContext.drawImage( image, 0, 0 );
     Caman( canvas, function () {
         this.threshold( 100 );
         this.render( function () {
             // This function should just resolve the promise
             // Right now, I need to comment out everything in setTileLoaded except the event raising.
             tile.loading = false;
             tile.loaded = true;
             var cutoff = Math.ceil( Math.log(
                 tiledImage.source.getTileSize( tile.level ) ) / Math.log( 2 ) );
             tiledImage._tileCache.cacheTile( {
                 image: image,
                 tile: tile,
                 cutoff: cutoff,
                 tiledImage: tiledImage
             } );
             tiledImage._needsDraw = true;
         } );
     } );
 } );

 osd.addHandler( "tile-drawing", function ( event ) {
     var tile = event.tile;
     var rendered = event.rendered;
     var imgData = tile._renderedContext.getImageData( 0, 0,
         tile._renderedContext.canvas.width,
         tile._renderedContext.canvas.height);
     rendered.putImageData( imgData, 0, 0 );
 } );

@@ -863,12 +861,8 @@ function updateTile( tiledImage, drawLevel, haveDrawn, x, y, level, levelOpacity
var imageRecord = tiledImage._tileCache.getImageRecord(tile.url);
if (imageRecord) {
tile.loaded = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably don't need this line, since it's in setTileLoaded.

@iangilman
Copy link
Member

Great idea!

Regarding the need for something like promises, I'm hesitant to actually include a promise polyfill, so maybe we should come up with something lightweight that serves our needs. Off the top of my head, perhaps the event could include a function you can call to signal you need more time. Something like this:

osd.addHandler( "tile-loaded", function ( event ) {
    var complete = event.getCompletionCallback();
    ...
    Caman( canvas, function () {
        ...
        this.render( function () {
            complete();
        };
    });
});

If during the handler you don't call getCompletionCallback, it's assumed you don't need the extra time. What do you think?

Just another note: before we land this, please make sure these changes don't have an adverse affect on the non-canvas pipeline. They shouldn't, of course, but it's good to be sure!

@avandecreme
Copy link
Member Author

Regarding the need for something like promises, I'm hesitant to actually include a promise polyfill

Yes, I had the same thought.

If during the handler you don't call getCompletionCallback, it's assumed you don't need the extra time. What do you think?

That could work I think. That means that we need to keep track of the number of times the getCompletionCallback method is called so that the actual callback is only called when all handlers are done.
That also means that if an handler has 2 asyncs methods to call, it should do something like this:

osd.addHandler( "tile-loaded", function ( event ) {
    var camanComplete = event.getCompletionCallback();
    Caman( canvas, function () {
        ...
        this.render( function () {
            camanComplete();
        };
    });

    var anotherComplete = event.getCompletionCallback();
    otherAsyncMethod( function successCallback () {
        anotherComplete();
    });
});

Correct?

Just another note: before we land this, please make sure these changes don't have an adverse affect on the non-canvas pipeline. They shouldn't, of course, but it's good to be sure!

When I compared before/after my patch the non-canvas method was not great in both cases. I am getting black rectangles where tiles are not yet loaded. Is that expected?

@iangilman
Copy link
Member

Yes, counting the number of times it's called would be necessary. Being able to call it multiple times from the same handler is a nice bonus!

No, black rectangles aren't expected...sounds like maybe we've had some degradation on the non-canvas path. Could you file an issue for it?

@avandecreme
Copy link
Member Author

I just created the issue #661 regarding useCanvas = false.

Regarding this pull request, I am getting good results with it.
I want to study if I can now modify the effect after tiles have been loaded but if I need any other hook, I guess that can be done in another PR.

@@ -989,6 +975,53 @@ function onTileLoad( tiledImage, tile, time, image ) {
tiledImage._needsDraw = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needsDraw happens too soon now...it should go inside the completionCallback of setTileLoaded, so it happens after the tile is actually truly loaded.

@iangilman
Copy link
Member

Just some little details at this point...

@avandecreme
Copy link
Member Author

I just realized it would be nice to have a tile-unloaded event to allow the user to clean up the memory he allocated in tile-loaded.

If we want to be consistent with the tile-loaded event, it should be triggered on the viewer as well.
However the tile.unload method has no reference to the viewer.
So maybe we can add that code inside tilecache._unloadTile. We will need to add the tiledImage and image as parameter of this method so that we can trigger the event with the same parameters than the tile-loaded event.
I am not sure if the completion call back would make sense though. I can't think of any use case for it with the unload event.

* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
* @property {function} getCompletionCallback - A function giving a callback to call
* when the asynchronous processing of the image is done. The image will be
* marked as .entirely loaded when the callback has been called once for each
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've got a stray period before entirely.

@iangilman
Copy link
Member

Your proposal for tile-unloaded sounds good to me. No worries adding the extra parameters, since it's a private function. I agree it doesn't need any async features.

@@ -1,7 +1,8 @@
# editorconfig.org
root = true

[*]
# We need to specify each folder specifically to avoid including test/lib and test/data
[{Gruntfile.js,src/**,test/*,test/demo/**,test/helpers/**,test/modules/**}]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is totally unrelated to the rest of the PR but the content of test/lib and test/data was keeping getting modified due to editorconfig. So I had to do something about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool; good to fix then!

@iangilman
Copy link
Member

Looks lovely! @avandecreme Are you ready for it to land? Looks ready to me...

@avandecreme
Copy link
Member Author

Yes looks good to me as well!

iangilman added a commit that referenced this pull request Jun 12, 2015
Move tile caching code inside tilecache.js.
@iangilman iangilman merged commit a318318 into openseadragon:master Jun 12, 2015
@iangilman
Copy link
Member

Awesome; merged. I'm looking forward to how people use this new feature!

@avandecreme
Copy link
Member Author

I am hoping to provide a filter plugin using it soon :)

On Fri, Jun 12, 2015 at 7:23 PM, Ian Gilman notifications@github.com
wrote:

Awesome; merged. I'm looking forward to how people use this new feature!


Reply to this email directly or view it on GitHub
#659 (comment)
.

@iangilman
Copy link
Member

Awesome :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants