[MapTool] New Export (screenshot) options

Progress reports and musings from the developers on the current gaming tools.

Moderators: dorpond, trevor, Azhrei

Post Reply
MetaGamer
Kobold
Posts: 15
Joined: Sat Jun 26, 2010 3:39 am

[MapTool] New Export (screenshot) options

Post by MetaGamer »

To introduce myself to the MapTools code, I took it upon myself to add a rather simple feature that some others had expressed an interest in, and which I thought was within my domain of experience (graphics).

The full feature list is as follows:
Screenshot
* File->Export->Screenshot As... now has the option of exporting the 'Entire Map'
** For GM's this is the entire extents of all objects (tokens, drawings, Fog-of-War, etc.)
** For players, this is the entire extents of the revealed Fog-of-War (so players can't use the screenshot to see where the map goes)
** The user can turn off certain layers and features to make the screen capture more useful. The Player has fewer options, of course.
** The exported screenshot is meant to be suitable for photoshopping (if Fog, visibility, and tokens are disabled).
** The screenshots are taken at the a magnification of 100%, or in other words, so that the background tiling texture and background map are at 1:1 with their original size.

Edit Map
The really nifty Map->New Map... dialog can now be used to modify existing maps via Map->Edit Map. You can change anything about the map here, including the background image, fog color, tile texture, etc. This is useful for importing a modified screenshot to use as a new background image. It can also be used to salvage a map that you made a minor mistake on, or for making variations of an existing map, or whatever.

Adjust Map Board
This works similar to Map->Adjust Grid... except it allows you to reposition the background image. This can be useful, for example, if you re-import an image but it has changed size slightly.

Developer Notes
* I've tried not to leave any dangling bits: this is meant to be a solid and complete patch. There are certainly more features that could be added (changing magnification comes to mind), but none of the features I've added should be glitchy or tweaky. IF you find anything that is, please let me know so I can fix it.
* You can take some very large screenshots with this. I've taken 7000x7000 pixel images. If the program runs out of memory trying to create the image, it tries to fail gracefully, but I'm not a Java expert so I can't guarantee the memory manager won't throw up later. If anyone knows how to do a test before trying a memory allocation, I'm all ears.
* I did implement client-server methods so changes to the zone are reflected to the clients. Note however, that using Edit Map... will cause the entire zone to be deleted from the client, and then resent to the client. This will necessitate the players (or GM) switching the client to that zone if they were previously looking at it. (I couldn't figure out how to check which zone a client was looking at, so I didn't want to force the client to switch to the updated zone, in the event they were looking at something else.)
* There is a confusing assortment of techniques used to notify/invalidate/flush various objects related to rendering. I'm not experienced at Java, so I'm not in a position to judge whether this is good OO design or the result of evolving code. I had to do a lot of spelunking and I have to admit that when it came to these issues I felt like I was 'settling' on a solution rather than being 100% happy with it. I suspect that my unease is mostly due to the many rendering optimizations that are meant to make it draw faster, and my uncertainty about why the original coders chose e.g. ModelChangeEvents vs. invalidation flags in various circumstances. Nevertheless, I'm pretty sure I didn't change the behavior of the renderer: this should by readily confirmed by inspection. (A good diff program like Beyond Compare makes it easier than the Eclipse compare utility).

Patch Notes
None of these features is essential: they are all utilities for the map-builders who want to tweak their maps for a higher quality appearance, or just to fix things after working on a map for a while. So I would understand a reluctance to include them in a future 1.3 build, as the goal is working toward stability (if I understand correctly). But if anyone wants to test out the patch, I would appreciate it.

The Patch
maptool1.3nb70.export.diff.zip
.diff, .jfrm and .png files for the patch.
(32.56 KiB) Downloaded 172 times
Attached is a patch (diff) from version 1.3b70. If you have the Eclipse environment working, r-click on the maptools projects, select Team->Apply Patch... and choose this diff file. In addition, there are several non-text files (also attached) you need to manually copy into the correct file locations. Two are Abeille forms and the other is an icon for control panel tool.
maptool\src\net\rptools\maptool\client\ui\forms\exportDialog.jfrm
maptool\src\net\rptools\maptool\client\ui\forms\adjustBoardControlPanel.jfrm
maptool\src\net\rptools\maptool\client\image\board.png

MetaGamer
Kobold
Posts: 15
Joined: Sat Jun 26, 2010 3:39 am

Re: [MapTool] New Export (screenshot) options

Post by MetaGamer »

And here is the diff file from version 1.3b71.
maptool1.3b71.export.diff.zip
Just the .diff file. See .zip from 1.3b70 for other files.
(23.74 KiB) Downloaded 166 times

User avatar
plothos
Great Wyrm
Posts: 1890
Joined: Sat Jun 21, 2008 1:07 am

Re: [MapTool] New Export (screenshot) options

Post by plothos »

Sounds very cool. Hopefully they'll be folded into b72. :)
Thanks!!!
Drop-In Utilities:
My Spell Manager for D&D3.5 and PFRPG
My Inventory Manager for D&D and PFRPG, but more generally useable than that.
My Message Manager -- My Top-Down D&D Token Images
and my Custom Initiative & Status/Spell-Effect Tracker (work in progress, but functional).

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: [MapTool] New Export (screenshot) options

Post by Azhrei »

Sounds very cool -- thanks for the work!

You are welcome (read: encouraged!) to submit Abeille forms in XML format instead of jfrm. That will allow SVN to keep changes as diffs instead of storing the entire binary. The files themselves are larger of course, and making this change will require that the source be updated to use the correct filename extension, but we're moving in the direction of XML in the future...

I'm hoping that folks with more experience with the rendering code will take a look at this (jfrazierjf, I'm looking at you! :)). The quirk with EditMap deleting the zone and recreating it seems a bit excessive, but I'll bet there are no Hessian commands for changing the other parameters and hence the need to create a new one. Perhaps jfrazierjf or Rumble can look into that as well if they have time? I know they've both been involved with that because of the typing notification stuff.

I look forward to hearing what they think of the patch. Thanks again for the work, MetaGamer!

User avatar
jfrazierjr
Deity
Posts: 5176
Joined: Tue Sep 11, 2007 7:31 pm

Re: [MapTool] New Export (screenshot) options

Post by jfrazierjr »

Azhrei wrote:I'm hoping that folks with more experience with the rendering code will take a look at this (jfrazierjf, I'm looking at you! :)).
ROFL.. you have got to be kidding... what little I know is based on copying Trevor's code somewhere else to make a new feature. I tend to bumble into a few things that work and 10 times that many that I give up on...lol
Azhrei wrote: The quirk with EditMap deleting the zone and recreating it seems a bit excessive, but I'll bet there are no Hessian commands for changing the other parameters and hence the need to create a new one. Perhaps jfrazierjf or Rumble can look into that as well if they have time? I know they've both been involved with that because of the typing notification stuff.
Not really... the server/client command is segregated 100% from the nuts and bolts of how the data is transfered over the network. My first guess based on 30 seconds looking at the code is that a new GUID is generated and the zone object has no setGUID() method of constructor parameter. I guess it's possible to add a new method handler(say, editZone()(or perhaps editZone() to match some of the other methods) with GUID(and perhaps other parameters.. not sure) and have the MapTool.getFrame().getZoneRenderers() replace the existing zone with that GUID with the updated one. Would need quite a few changes I expect...

Azhrei wrote: I look forward to hearing what they think of the patch. Thanks again for the work, MetaGamer!
Will take a look at it when I get some time (in the next 2-3 weeks perhaps.. but perhaps as early as tomorrow night since my wife is supposed to have a girls night out... this assumes I don't play on my XBox instead...lol), but given that I don't really have a personal stake in this feature, I may not really have much to say...(as in, I am not sold of it's need as I doubt I will ever use it)
I save all my Campaign Files to DropBox. Not only can I access a campaign file from pretty much any OS that will run Maptool(Win,OSX, linux), but each file is versioned, so if something goes crazy wild, I can always roll back to a previous version of the same file.

Get your Dropbox 2GB via my referral link, and as a bonus, I get an extra 250 MB of space. Even if you don't don't use my link, I still enthusiastically recommend Dropbox..

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: [MapTool] New Export (screenshot) options

Post by Azhrei »

Sorry, Joe! You said you had written the light/aura stuff, so I thought perhaps you knew more about the rendering code. I'll played around in it a little bit, but certainly can't claim to know how it works.

I just looked at the diff in detail and for the most part I like the style of the patch (coding style, javadoc comments for all funcs (!), and so on).

@MetaGamer: I'd really like to see takeFullMapScreenShot() (or whatever the name was) return an ImageWriter instead of a BufferedImage. If it did that then I'd consider patching this into b72 so that others could play with it.

MetaGamer
Kobold
Posts: 15
Joined: Sat Jun 26, 2010 3:39 am

Re: [MapTool] New Export (screenshot) options

Post by MetaGamer »

Azhrei wrote:Sounds very cool -- thanks for the work!

You are welcome (read: encouraged!) to submit Abeille forms in XML format instead of jfrm.
Aha! I read something about "using XML in the future", but I didn't realize it was just a "Save As..." option in Abeille. I thought someone had gone mad and wanted to code all dialog boxes by hand or something. :p

Seems like it would pretty simple to convert all the existing .jfrms to .xml once and for all... If there's nothing tricky about it that is scaring you off, I'd be happy to do the grunt work.
Azhrei wrote:I'm hoping that folks with more experience with the rendering code will take a look at this (jfrazierjf, I'm looking at you! :)). The quirk with EditMap deleting the zone and recreating it seems a bit excessive, but I'll bet there are no Hessian commands for changing the other parameters and hence the need to create a new one. Perhaps jfrazierjf or Rumble can look into that as well if they have time? I know they've both been involved with that because of the typing notification stuff.
Yeah, I have to admit to being lazy about that. Is there some way to automatically generate that code, or at least a skeleton of it? I did implement a sync for moving the board around just to see how it's done, but it was somewhat tortuous for such a minor optimization that won't be used very often. On the other hand, one could certainly argue that if the feature was 'better' people might find other uses for it (changing fog color on-the-fly to set the atmosphere, for example).
I'd really like to see takeFullMapScreenShot() (or whatever the name was) return an ImageWriter instead of a BufferedImage. If it did that then I'd consider patching this into b72 so that others could play with it.
Ok... I'll figure out what that is and see if I can do it.

User avatar
jfrazierjr
Deity
Posts: 5176
Joined: Tue Sep 11, 2007 7:31 pm

Re: [MapTool] New Export (screenshot) options

Post by jfrazierjr »

Azhrei wrote:Sorry, Joe! You said you had written the light/aura stuff, so I thought perhaps you knew more about the rendering code. I'll played around in it a little bit, but certainly can't claim to know how it works.
No problem... I didn't say I wrote the light/aura stuff(Trevor did all that), just that I have done quite a few hacks in there and the vision code to add some functionality to it... mostly this just deals with how to return a shape object(ie, a light shape or vision shape) that the ZoneRenderer then uses to deal with what token can see what...

What I have done in that area:

* Add GM and Owner flags for Aura type lights basically, there is a call to "get all aura type lights" and my changes were to just filter which lights were returned based on some details such as are you the owner of said token or the GM, etc

* Vision shapes, which really is not in the ZoneRenderer at all... it just asks for a list of shapes (java Area class) and then Trevors code "clips" the visible area based on those shapes.


Now.. with all that said... I am trying my hardest to get my head wrapped around how I might do the super secret project I am working on and if it works, I will be be MUCH more intimate with that code that I probably want to be.. 8)
I save all my Campaign Files to DropBox. Not only can I access a campaign file from pretty much any OS that will run Maptool(Win,OSX, linux), but each file is versioned, so if something goes crazy wild, I can always roll back to a previous version of the same file.

Get your Dropbox 2GB via my referral link, and as a bonus, I get an extra 250 MB of space. Even if you don't don't use my link, I still enthusiastically recommend Dropbox..

MetaGamer
Kobold
Posts: 15
Joined: Sat Jun 26, 2010 3:39 am

Re: [MapTool] New Export (screenshot) options

Post by MetaGamer »

MetaGamer wrote:
Azhrei wrote: I'd really like to see takeFullMapScreenShot() (or whatever the name was) return an ImageWriter instead of a BufferedImage. If it did that then I'd consider patching this into b72 so that others could play with it.
Ok... I'll figure out what that is and see if I can do it.
Hmm... I poked around a bit and I'm not sure what you'd like accomplished. The ExportDialog itself doesn't return anything- it just saves (or FTPs) the file and closes. So you're talking about an optimization? Some particular functionality?

The exporting code does create several copies of the data in memory before it finally hits the disk. So given that we're making (potentially) large images, I could see being concerned about memory usage. It seems that we've got the BufferedImage and the ByteArrayOutputStream (2 copies) at the same time. Then the BufferedImage is freed (back to 1 copy). Then another copy is created to convert the (PNG formatted) ByteArrayOutputStream into a ByteArrayInputStream (i/o is weird). So we have 2 copies again. There are a couple of small buffers (e.g. 5-8kB) created since every function seems to create its own buffer, but then it finally gets sent and it's all freed. The only way to make this more memory efficient would be to re-write the PNG ImageWriter... and I'm pretty sure by this point I've totally missed the point you were trying to make.

I don't understand, sorry.

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: [MapTool] New Export (screenshot) options

Post by Azhrei »

jfrazierjr wrote:Now.. with all that said... I am trying my hardest to get my head wrapped around how I might do the super secret project I am working on and if it works, I will be be MUCH more intimate with that code that I probably want to be.. 8)
Cool. I look forward to hearing more about it. ;) But don't forget to at least pass the idea along -- I'd hate for you to get it all coded up and then have it not jive with the general flow of the code for some reason. (Not that I see that happening with your patches; that comment is more for other code contributors than yourself. Some people here are "known quantities". :))

@MetaGamer: Sorry, I guess I should've gone into a bit more detail. It doesn't help that I said "ImageWriter" and I should have said "ImageReader". :( How's this:

Find the method that returns the BufferedImage. That image is making a copy of the zone and storing it in video memory (hence the "buffered image"). Now imagine how the PNG image writer works: you pass it a BufferedImage and it reads data from that image, compresses it, then writes it to an output file. So instead of a static BufferedImage, suppose there was a class with the same API (ScreenCaptureImage) but which calculated the pixel data dynamically (perhaps by querying the zone?). When the image writer went to read the data from the BufferedImage it would actually be reading from the ScreenCaptureImage instead. This would require little or no memory inside the ScreenCaptureImage although it would consume more cpu (I'm assuming that copying the zone to a BufferedImage is less cpu-intensive than recomputing image data occassionally).

To do this, inherit ScreenCaptureImage from BufferedImage. Add stubs for the various status/config/read methods that call the superclass's methods but add debugging code so that you can see which functions are called and what parameters are passed. You'll now know the minimum API that you need to implement. (Other methods should probably throw exceptions so that you can determine whether they're being called when testing other images.)

You might be able to glean the above information from reading the javadoc, but with Eclipse it's pretty easy to add the overridden methods with the stubs so I would likely consider it faster. YMMV.

Once you know how the image writer wants data returned to it (as it's calling your read() methods) you'll be able to determine how to invoke the zone methods and what kind of caching to implement in your class so that it uses the minimum amount of memory but still performs fairly well.

Post back if you ave more questions. I tried to make that more detailed this time. :roll: 8)

MetaGamer
Kobold
Posts: 15
Joined: Sat Jun 26, 2010 3:39 am

Re: [MapTool] New Export (screenshot) options

Post by MetaGamer »

@MetaGamer: Sorry, I guess I should've gone into a bit more detail. It doesn't help that I said "ImageWriter" and I should have said "ImageReader". :(
I thought about that, but then it seemed like a lot of work. :) But, okay, I'll bite.

BTW: Figuring someone else has probably done this, I just found that the JAI package handles arbitrary sized images for rendering. But it's a pretty intimidating-looking API, not to mention it seems to be disk-backed tile-renderer, which is nice but overkill.

I wrote the following outline as I poked around trying to see what might work. I figure I'll post it just in case you see something wrong before I get too far into it.

Code: Select all

class SlowReadOnlyCachedImage extends IIOImage {
  // settable vars
  Component veryLargeComponent;            // something with a huge height and width, presumably
  long      maxMemory    = 1024*1024*4;    // 4MB is about the size of a 'whole screen'
  long      tileSizeHint = maxMemory / 8;  // the tiles should be big, but not too big (ahh cache design)

  // internal vars
  Raster    rasterTile[];                  // to be allocated on-the-fly
  Dimension tileSize;                      // will be set to actual size of tiles
  Rect      originalComponentBounds;

  // the only method this class really supports, everything else throws exceptions
  Raster getData(Rectangle rect); 
}
The first call to getData(rect) will cause it to guess that all requests will be of the same size. It will set each dimension of tileSize to be at least as big as the rect requested. It will then expand the tileSize until the tile reaches the size in tileSizeHint (stopping when it reaches the size of the Component). In practice, the PNG writer grabs one scanline at a time. The JPEG writer probably grabs 8 scanlines at a time, etc. So the tiles will end up being the entire width of the component, and as tall as they can be before hitting the tileSizeHint.

Then the bounds of the Component will be used as 'window' into the Component. The Component will be resized to a tile that contains the requested area. Then it will be rendered and that will be cached in rasterTile[].

Future calls to getData() will either hit the rasterTile cache or cause a new tile to be rendered.

I'm pretty sure this approach will work for PNG files, as the PNGImageWriter code was accessible. It may or may not work for JPEG: that writer uses an external library for the dirty work, so I can't tell.

Feels icky.

But I can code it up pretty quickly, now that I've done the initial research into how the i/o works.

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: [MapTool] New Export (screenshot) options

Post by Azhrei »

MetaGamer wrote:[...] So the tiles will end up being the entire width of the component, and as tall as they can be before hitting the tileSizeHint.
Yep, exactly. :)

As I mentioned, I would simply start with a proxy version of the class and watch the method calls, but since you've looked at the PNGImageWriter class perhaps you've already done that? I'm more of an "empirical data" kind of guy, even though I may start by reading the docs. I got that way trying to figure out how some of the Swing LayoutManager classes worked! (Differences between {Min|Max|Pref} sizes for some of the layout managers drives me nuts sometimes! :))

MetaGamer
Kobold
Posts: 15
Joined: Sat Jun 26, 2010 3:39 am

Re: [MapTool] New Export (screenshot) options

Post by MetaGamer »

Ok, it's done. The only bit I couldn't get was rendering in a background thread. So if you export a large map, it can take a few seconds and the UI doesn't update.

I've tested this up to about 1.6 billion pixel images (
Spoiler
Proof of a 40,000 x 40,000 pixel screen-shot.
Proof of a 40,000 x 40,000 pixel screen-shot.
ExtraLargeScreenshot.jpg (142.66 KiB) Viewed 5183 times
)... that should be sufficient for most people's needs.

I think I made the diff such that it is a patch from 71, but I'm not 100% on that. I had difficultly switching out the 'base' svn revision from 70 without checking in my files first.

Also, I switched the dialog to .xml, so there is no .jfrm file this time.

known bugs: The default is still to use a single BufferedImage instead of the ImageWriter technique, because I think there is a bug in the ZoneRenderer which doesn't properly cache the bounds of large (e.g. Gargantuan) tokens. The effect is that the token is rendered in some parts of the image and not others.
Attachments
exportDialog_patch_b71.zip
diff from 1.3b71
(47.49 KiB) Downloaded 160 times

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: [MapTool] New Export (screenshot) options

Post by Azhrei »

Excellent!! I'm really stoked to try this out!

I'm still trying to get the localization done to produce b72 but maybe tonight...? Then I'll apply your patch and work with it a bit and do a b73 within a few days.

Thanks for all of this effort!

Post Reply

Return to “Developer Notes”