Back to index |
What is Dynamic MNG? |
Dynamic MNG is a small extension to the MNG specification, that will allow simple rollover images to be created within a single MNG file. It is intended to make it easier to create rollover buttons on webpages, without the use of complicated javascripts and multiple files. Sure enough you can create the same effect with flash(tm), but if you need a small simple solution, possibly with transparency in the mix (* only with Browsers that have native MNG support!), this is the perfect alternative. (see the rationale to see why I developed this) |
How does it work? |
Dynamic MNG requires specially designed MNG files and proper coordination between a MNG decoder and the user-interface. This results in two changes on my part of the job: 1) an extension to the MNG specification to indicate a MNG as being dynamic and to control its flow based on event-desciptors. 2) modification of libmng to allow an application to relay trapped events to the library, which in turn will alter its processing of the MNG according to the type of event. These things are explaned in much more detail in the next two topics. |
Description of the "evNT\" chunk |
(separate text version available here) This is proposed as a top-level MNG chunk that affects the overall handling of the MNG stream with respect to flow-control of segments based on user-interaction. It is proposed to add an "evNT\" chunk, to propogate flow-control over the MNG based on external events. The chunk can appear at the MNG top level, but must not appear after a SAVE chunk. The appearance of 1 or more evNT chunks marks the MNG stream as a so-called 'dynamic' MNG. The evNT chunk contains 1 or more event-descriptors. Event-descriptors are matched by a decoder against events generated by the user-interface. At the moment only general mouse-pointer events are defined. These events should be accompanied by x/y-coordinates that indicate the position of the pointer relative to the top-left of the image canvas. So an x/y of 0/0 means the left-most,top-most pixel on the canvas; 25/10 means the pixel on the 11th row of the canvas 26 pixels from the left. The global evNT chunk contain 1 or more event-descriptors. An event-descriptor contains the following data: Event_type: 1 byte (unsigned integer) 0 = unknown event 1 = mouse enter 2 = mouse move 3 = mouse leave 4 = mouse key down 5 = mouse key up Mask_type: 1 byte (unsigned integer) 0 = no mask 1 = mask by frame 2 = mask by object 3 = mask by object using pixel-value 4 = mask by frame and object 5 = mask by frame and object using pixel-value Left: 4 bytes (signed integer) Must be omitted if the mask type is equal to 0, 2 or 3 Right: 4 bytes (signed integer) Must be omitted if the mask type is equal to 0, 2 or 3 Top: 4 bytes (signed integer) Must be omitted if the mask type is equal to 0, 2 or 3 Bottom: 4 bytes (signed integer) Must be omitted if the mask type is equal to 0, 2 or 3 Object_id: 2 bytes (unsigned integer) Must be omitted if the mask type is equal to 0 or 1 Index: 1 byte (unsigned integer) Must be omitted if the mask type is equal to 0, 1, 2 or 4 Segment_name: 1..79 bytes (Latin-1 string) If more than 1 event-descriptor is present in the chunk, they will be separated by a single null byte. There is no null byte separator after the last event-descriptor. If more than 1 evNT chunk is present the event-descriptors of subsequent chunks are added to the list of previously stored event-descriptors. The event type indicates the type of event this descriptor will be associated with. Multiple descriptors with the same event type may appear. A decoder must iterate through the descriptors in the order in which they appear within the MNG stream and within evNT chunks, until a match with a given event is found based on event type and a possible mask. It is not an error for descriptors to contain the same event type and overlapping masks. However the decoder must only honor the first matching descriptor. The mask type indicates if and what type of mask should be used to match a given event with this event-descriptor, but only if the event type has already matched: For mask type = 0 the event-descriptor matches. For mask type = 1 the event-descriptor matches if the x/y coordinates associated with the event lie within the rectangle given by the Left, Right, Top & Bottom parameters (Left and Top are inclusive, Right and Bottom are exclusive). For mask type = 2 the event-descriptor matches if the pixel of the stored object identified by the object id and indicated by the x/y coordinates associated with the event, contains a non-zero value. For mask type = 3 the event-descriptor matches if the pixel of the stored object identified by the object id and indicated by the x/y coordinates associated with the event, contains a value equal to the Index parameter. For mask type = 4 the event-descriptor matches if the x/y coordinates associated with the event lie within the rectangle given by the Left, Right, Top & Bottom parameters (Left and Top are inclusive, Right and Bottom are exclusive), and, if the pixel of the stored object identified by the object id and indicated by the x/y coordinates associated with the event minus the Left/Top parameters, contains a non-zero value. For mask type = 5 the event-descriptor matches if the x/y coordinates associated with the event lie within the rectangle given by the Left, Right, Top & Bottom parameters (Left and Top are inclusive, Right and Bottom are exclusive), and, if the pixel of the stored object identified by the object id and indicated by the x/y coordinates associated with the event minus the Left/Top parameters, contains a value equal to the Index parameter. The object id should match the object id of a stored object with a color type equal to 0 (grayscale) or 3 (indexed), and a bit depth of 8 or less. It is not an error if the object does not exist or doesn't have the proper color type or bit depth. However in this case the event-descriptor will never match. The stored object(s) must appear in the prologue segment prior to the SAVE chunk. The segment name indicates the SEEK chunk the decoder should locate and resume its flow when the event-descriptor matches a given event. It must follow the format of a tEXt keyword: It must consist only of printable Latin-1 characters and must not have leading or trailing blanks, but can have single embedded blanks. There must be at least one and no more than 79 characters in the keyword. There is no null byte terminator within the segment name. There is a single null byte terminator after the segment name if the event-descriptor is followed by another event-descriptor. Segment names are case-sensitive. Use caution when printing or displaying keywords (Refer to Security considerations in the MNG specification, Chapter 17). It is an error if no SEEK chunk with the given segment name can be found. Decoders capable of handling the evNT chunk and events generated by the user-interface should treat a 'dynamic' MNG stream slightly different from a 'regular' stream. If 1 or more evNT chunks are encountered, the event-descriptors should be stored for later matching with user-interface generated events, and it must subsequently ignore segments other than the prologue segment and the first segment in the stream. When the user-interface triggers an event, the decoder must locate the first event-descriptor that matches the given event type and x/y coordinates. If found, the decoder should locate the SEEK chunk with a segment name equal to that in the event-descriptor and process the chunk and all subsequent chunks upto but not including the next SEEK chunk. For simplicity and to prevent machine lockups, a decoder may decide to ignore events when it is still processing a previous event or when it is still processing the prologue segment and the first segment. Decoders may ignore the TERM chunk when encountered inside a 'dynamic' MNG. Encoders should construct a 'dynamic' MNG stream that will appear normal when viewed by decoders that do not recognize the evNT chunk or do not handle events generated by the user-interface. The last segment in the stream should contain the 'regular' animation sequence or image. |
Support in libmng |
libmng was extended with two HLAPI functions: - mng_bool mng_status_dynamic (mng_handle hHandle); This function allows the app to check whether or not the MNG actually contains dynamic content (eg. 1 or more evNT chunks are present). This should be checked before any trapped event is sent to libmng. - mng_retcode mng_trapevent (mng_handle hHandle; mng_uint8 iEventtype; mng_int32 iX, iY); This is the function an app should call for each trapped event. iEventtype must contain the type of event as described above for the evNT chunk. iX and iY must contain the coordinates of the pointer relative to the top-left corner of the image's canvas. The function will try to locate a matching event-descriptor for the given event. If found, it will call the settimer() callback to signal the application it is ready again to do some display-processing. This setup will ensure that handling of the event will take as little time as possible, and also that it can be called from a different thread as the one displaying the MNG and controlling the output-canvas. The application should set a timer (or take some similar action) in its settimer() callback, and call mng_displayresume() when the timer triggers. The function will return an error if the MNG being processed is not dynamic. Note!!! All of the 'dynamic' MNG code inside libmng is included only when the MNG_SUPPORT_DYNAMICMNG conditional is defined! (However, this is automatically defined when a DLL or SO is created.) |
What software is out there that supports it? |
Not a lot yet, but the list is growing... - libmng 1.0.5 contains the necessary code to support dynamic MNG images. - I've rigged MNGeye to allow generation of dynamic MNGs with evNT chunks. There's a download available here [1.0MB]. - The Delphi mngview example in the libmng distro was updated to allow testing. It'll be part of the upcoming 1.0.5 release. A temporary binary can be downloaded here (contains preliminary libmng.dll; do not distribute this!!!!!) - Jason Summers MNG4IE ActiveX control for MSIE and MNGPLG, the Netscape-style Windows plugin, now support dynamic MNG. Links can be found here. - Christian Biesinger is putting a big effort into adding support to Mozilla, so let's hope the nightly builds will sooner or later have it included. |
Sample MNG files |
If you are viewing these with MS Internet Explorer you need Jason Summer's MNG4IE plugin version 1.0.1 or later! The same goes for Jason's Netscape plugin (v1.0.1 or later)! Mozilla users will have to wait a few more days... (ps. a download can be found below the samples!) Sample buttons: Sample image map: Note that the circular buttons and the map of Australia are partially transparent. However they are not embedded as inline image (<IMG> tag), so the 'background' that shines through might be black or something! I've done this to make sure browsers with a MNG plugin or ActiveX control and Mozilla/Netscape6+ will (most likely) all work this way. As it turns out Mozilla and Jason's ActiveX (MNG4IE) actually work just beautifully! Download these samples in zip-format. |
Rationale |
Why? Why another format with dynamic content? I agree that it may be unnecessary, given the fact that there is already flash(tm) and the same can be achieved with javascript. However neither seem to fulfill entirely what I would like. Javascript is cumbersome and doesn't always work between different versions of browsers, adds a ton of sourcecode that needs to be downloaded by surfers and generally makes a website a fair bit slower. Flash(tm) does a great job, but as a plugin can't give partially transparent content. Not to mention that both may not be available on a users platform or simply turned off. Yes, I know, MNG isn't supported everywhere either, but the start is there and I hope it to make it into the major browsers within the next couple of years. To make sure the future will give us dynamic MNG enabled browsers, I needed to act now, so I did. Why not use existing MNG constructs? That would have been another possibility. MNG contains some provisions for dynamics through the synchronization id's in a FRAM chunk and the URI specializers as defined in chapter 13.4 of the MNG specification. However, the URI specializers would again require javascript to generate dynamic behavior, and the FRAM sync-id's can only define dynamic behavior in a sequential manner and without specific x/y coordinates. So again they don't do exactly what I had in mind. Hence my effort for dynamics through the evNT chunk and some modification in libmng. The whole operation only took about a day and a half. Now it's simply up to the implementors to add support in their software. |
Back to index |