/// <summary>
        /// Strips a set of Visualizations out of animations in the set.  The set is not
        /// modified; rather, a new set is generated with updated entries.
        /// </summary>
        /// <param name="visSet">Input set.</param>
        /// <param name="removedSerials">Serial numbers of removed items.</param>
        /// <param name="newSet">Updated set.</param>
        /// <returns>True if entries were removed.</returns>
        public static bool StripEntriesFromAnimations(VisualizationSet visSet,
                                                      List <int> removedSerials, out VisualizationSet newSet)
        {
            bool somethingRemoved = false;

            newSet = new VisualizationSet(visSet.Count);
            foreach (Visualization vis in visSet)
            {
                if (!(vis is VisualizationAnimation))
                {
                    newSet.Add(vis);
                    continue;
                }

                if (VisualizationAnimation.StripEntries((VisualizationAnimation)vis,
                                                        removedSerials, out VisualizationAnimation newAnim))
                {
                    somethingRemoved = true;
                    if (newAnim.Count != 0)
                    {
                        newSet.Add(newAnim);
                    }
                    else
                    {
                        Debug.WriteLine("Deleting empty animation " + vis.Tag);
                    }
                }
            }
            return(somethingRemoved);
        }
        public override bool Equals(object obj)
        {
            if (!(obj is VisualizationAnimation))
            {
                return(false);
            }
            // Do base-class equality comparison and the ReferenceEquals check.
            if (!base.Equals(obj))
            {
                return(false);
            }
            VisualizationAnimation other = (VisualizationAnimation)obj;

            if (other.mSerialNumbers.Count != mSerialNumbers.Count)
            {
                return(false);
            }
            for (int i = 0; i < mSerialNumbers.Count; i++)
            {
                if (other.mSerialNumbers[i] != mSerialNumbers[i])
                {
                    return(false);
                }
            }
            return(true);
        }
        /// <summary>
        /// Strips serial numbers out of the list.
        /// </summary>
        /// <param name="visAnim">Object to strip serial numbers from.</param>
        /// <param name="removedSerials">List of serial numbers to remove.</param>
        /// <param name="newAnim">Object with changes, or null if nothing changed.</param>
        /// <returns>True if something was actually removed.</returns>
        public static bool StripEntries(VisualizationAnimation visAnim, List <int> removedSerials,
                                        out VisualizationAnimation newAnim)
        {
            bool somethingRemoved = false;

            // Both sets should be small, so not worried about O(m*n).
            List <int> newSerials = new List <int>(visAnim.mSerialNumbers.Count);

            foreach (int serial in visAnim.mSerialNumbers)
            {
                if (removedSerials.Contains(serial))
                {
                    Debug.WriteLine("Removing serial #" + serial + " from " + visAnim.Tag);
                    somethingRemoved = true;
                    continue;
                }
                newSerials.Add(serial);
            }

            if (somethingRemoved)
            {
                newAnim = new VisualizationAnimation(visAnim.Tag, visAnim.VisGenIdent,
                                                     visAnim.VisGenParams, newSerials, visAnim);
            }
            else
            {
                newAnim = null;
            }
            return(somethingRemoved);
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="tag">Unique identifier.</param>
        /// <param name="visGenIdent">Visualization generator identifier.</param>
        /// <param name="visGenParams">Parameters for visualization generator.</param>
        /// <param name="visSerialNumbers">Serial numbers of referenced Visualizations.</param>
        public VisualizationAnimation(string tag, string visGenIdent,
                                      ReadOnlyDictionary <string, object> visGenParams, List <int> visSerialNumbers,
                                      VisualizationAnimation oldObj)
            : base(tag, visGenIdent, visGenParams, oldObj)
        {
            Debug.Assert(visSerialNumbers != null);

            // Make a copy of the list.
            mSerialNumbers = new List <int>(visSerialNumbers.Count);
            foreach (int serial in visSerialNumbers)
            {
                mSerialNumbers.Add(serial);
            }

            CachedImage  = BLANK_IMAGE;
            OverlayImage = ANIM_OVERLAY_IMAGE;
        }
        /// <summary>
        /// Attempts to refresh broken thumbnails across all visualization sets in the project.
        /// </summary>
        /// <param name="project">Project reference.</param>
        public static void RefreshAllThumbnails(DisasmProject project)
        {
            ScriptSupport iapp = null;
            Dictionary <string, IPlugin> plugins = null;

            SortedList <int, VisualizationSet> visSets = project.VisualizationSets;

            foreach (KeyValuePair <int, VisualizationSet> kvp in visSets)
            {
                VisualizationSet visSet = kvp.Value;
                foreach (Visualization vis in visSet)
                {
                    if (vis.HasImage)
                    {
                        continue;
                    }
                    //Debug.WriteLine("Vis needs refresh: " + vis.Tag);

                    if (vis is VisualizationAnimation)
                    {
                        continue;
                    }

                    if (iapp == null)
                    {
                        // Prep the plugins on first need.
                        iapp = new ScriptSupport();
                        project.PrepareScripts(iapp);
                    }

                    if (plugins == null)
                    {
                        plugins = project.GetActivePlugins();
                    }
                    IPlugin_Visualizer vplug = FindPluginByVisGenIdent(plugins,
                                                                       vis.VisGenIdent, out VisDescr visDescr);
                    if (vplug == null)
                    {
                        Debug.WriteLine("Unable to refresh " + vis.Tag + ": plugin not found");
                        continue;
                    }

                    IVisualization2d vis2d;
                    try {
                        vis2d = vplug.Generate2d(visDescr,
                                                 new ReadOnlyDictionary <string, object>(vis.VisGenParams));
                        if (vis2d == null)
                        {
                            Debug.WriteLine("Vis generator returned null");
                        }
                    } catch (Exception ex) {
                        Debug.WriteLine("Vis generation failed: " + ex);
                        vis2d = null;
                    }
                    if (vis2d != null)
                    {
                        //Debug.WriteLine(" Rendered thumbnail: " + vis.Tag);
                        vis.SetThumbnail(vis2d);
                    }
                }
            }

            if (iapp != null)
            {
                project.UnprepareScripts();
            }

            // Now that we've generated images for the Visualizations, update any
            // VisualizationAnimation thumbnails that may have been affected.
            foreach (KeyValuePair <int, VisualizationSet> kvp in visSets)
            {
                VisualizationSet visSet = kvp.Value;
                foreach (Visualization vis in visSet)
                {
                    if (!(vis is VisualizationAnimation))
                    {
                        continue;
                    }
                    VisualizationAnimation visAnim = (VisualizationAnimation)vis;
                    visAnim.GenerateImage(visSets);
                }
            }
        }
示例#6
0
        /// <summary>
        /// Generate one or more GIF image files, and output references to them.
        /// </summary>
        /// <param name="offset">Visualization set file offset.</param>
        /// <param name="sb">String builder for the HTML output.</param>
        private void OutputVisualizationSet(int offset, StringBuilder sb)
        {
            const int IMAGE_SIZE         = 64;
            const int MAX_WIDTH_PER_LINE = 768;

            if (!mProject.VisualizationSets.TryGetValue(offset,
                                                        out VisualizationSet visSet))
            {
                sb.Append("Internal error - visualization set missing");
                Debug.Assert(false);
                return;
            }
            if (visSet.Count == 0)
            {
                sb.Append("Internal error - empty visualization set");
                Debug.Assert(false);
                return;
            }

            string imageDirFileName = Path.GetFileName(mImageDirPath);
            int    outputWidth      = 0;

            for (int index = 0; index < visSet.Count; index++)
            {
                string fileName = "vis" + offset.ToString("x6") + "_" + index.ToString("d2");

                int dispWidth, dispHeight;

                Visualization vis = visSet[index];
                if (vis is VisualizationAnimation)
                {
                    // Animated visualization.
                    VisualizationAnimation visAnim = (VisualizationAnimation)vis;
                    int frameDelay = PluginCommon.Util.GetFromObjDict(visAnim.VisGenParams,
                                                                      VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, 330);
                    AnimatedGifEncoder encoder = new AnimatedGifEncoder();

                    // Gather list of frames.
                    for (int i = 0; i < visAnim.Count; i++)
                    {
                        Visualization avis = VisualizationSet.FindVisualizationBySerial(
                            mProject.VisualizationSets, visAnim[i]);
                        if (avis != null)
                        {
                            encoder.AddFrame(BitmapFrame.Create(avis.CachedImage), frameDelay);
                        }
                        else
                        {
                            Debug.Assert(false);        // not expected
                        }
                    }

#if false
                    // try feeding the animated GIF into our GIF unpacker
                    using (MemoryStream ms = new MemoryStream()) {
                        encoder.Save(ms);
                        Debug.WriteLine("TESTING");
                        UnpackedGif anim = UnpackedGif.Create(ms.GetBuffer());
                        anim.DebugDump();
                    }
#endif

                    // Create new or replace existing image file.
                    fileName += "_ani.gif";
                    string pathName = Path.Combine(mImageDirPath, fileName);
                    try {
                        using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
                            encoder.Save(stream, out dispWidth, out dispHeight);
                        }
                    } catch (Exception ex) {
                        // TODO: add an error report
                        Debug.WriteLine("Error creating animated GIF file '" + pathName + "': " +
                                        ex.Message);
                        dispWidth = dispHeight = 1;
                    }
                }
                else
                {
                    // Bitmap visualization.
                    //
                    // Encode a GIF the same size as the original bitmap.
                    GifBitmapEncoder encoder = new GifBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(vis.CachedImage));

                    // Create new or replace existing image file.
                    fileName += ".gif";
                    string pathName = Path.Combine(mImageDirPath, fileName);
                    try {
                        using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
                            encoder.Save(stream);
                        }
                    } catch (Exception ex) {
                        // Something went wrong with file creation.  We don't have an error
                        // reporting mechanism, so this will just appear as a broken or stale
                        // image reference.
                        // TODO: add an error report
                        Debug.WriteLine("Error creating GIF file '" + pathName + "': " +
                                        ex.Message);
                    }

                    dispWidth  = (int)vis.CachedImage.Width;
                    dispHeight = (int)vis.CachedImage.Height;
                }

                // Output thumbnail-size IMG element, preserving proportions.  I'm assuming
                // images will be small enough that generating a separate thumbnail would be
                // counter-productive.  This seems to look best if the height is consistent
                // across all visualization lines, but that can create some monsters (e.g.
                // a bitmap that's 1 pixel high and 40 wide), so we cap the width.
                int    dimMult = IMAGE_SIZE;
                double maxDim  = dispHeight;
                if (dispWidth > dispHeight * 2)
                {
                    // Too proportionally wide, so use the width as the limit.  Allow it to
                    // up to 2x the max width (which can't cause the thumb height to exceed
                    // the height limit).
                    maxDim   = dispWidth;
                    dimMult *= 2;
                }
                int thumbWidth  = (int)Math.Round(dimMult * (dispWidth / maxDim));
                int thumbHeight = (int)Math.Round(dimMult * (dispHeight / maxDim));
                //Debug.WriteLine(dispWidth + "x" + dispHeight + " --> " +
                //    thumbWidth + "x" + thumbHeight + " (" + maxDim + ")");

                if (outputWidth > MAX_WIDTH_PER_LINE)
                {
                    // Add a line break.  In "pre" mode the bitmaps just run off the right
                    // edge of the screen.  The way we're doing it is imprecise and doesn't
                    // flow with changes to the browser width, but it'll do for now.
                    sb.AppendLine("<br/>");
                    for (int i = 0; i < mColStart[(int)Col.Label]; i++)
                    {
                        sb.Append(' ');
                    }
                    outputWidth = 0;
                }
                else if (index != 0)
                {
                    sb.Append("&nbsp;");
                }
                outputWidth += thumbWidth;

                sb.Append("<img class=\"vis\" alt=\"vis\" src=\"");
                sb.Append(imageDirFileName);
                sb.Append('/');
                sb.Append(fileName);
                sb.Append("\" width=\"" + thumbWidth + "\" height=\"" + thumbHeight + "\"/>");
            }
        }