0

I'm writing an SVG editor. I have placed a kind of 'Magic Eye' on the page where the user can see the entire SVG draw and the zoomed area around the mouse cursor. Of course the problem is Memory usage and fast rendering. For this reason, step of modification or zooming the software create a reduced Image of the svg draw and it will use it for the Magic Eye rendering. The result is very nice but.... I am facing a problem, I discovered that the garbage collector doesn't free the images created and also the blobs. So after a few time I have the memory filled with Images. This is the routine I wrote for this job:

var RenderPosition = function(obj) {
        try{
            var clearCanvas = function(context, canvas) {
                context.clearRect(0, 0, canvas.width, canvas.height);
                var w = canvas.width;
                canvas.width = 1;
                canvas.width = w;
                };  
            var PrepareBlob = function(blob){
                glb._ThumbUrl = glb._DOMURL.createObjectURL(blob);
                glb._MagicImg = new Image();
                glb._MagicImg.src = glb._ThumbUrl; 
                };
            var PosizViewFilling = function(e){
                obj.pDC.drawImage(this,
                    obj.srt.x,
                    obj.srt.y,
                    obj.dms.width,
                    obj.dms.height);
                obj.canvas.toBlob(PrepareBlob);
                this.removeEventListener('load',PosizViewFilling,true);
                this.src='';
                delete this;
                };
            clearCanvas(obj.pDC,obj.canvas);
            if (glb._MagicImg!==null) delete(glb._MagicImg); 
            glb._DOMURL.revokeObjectURL(glb._ThumbUrl);
            var Big_img = new Image();
            Big_img.addEventListener('load',PosizViewFilling, true);
            Big_img.src = 'data:image/svg+xml;base64,'+btoa(obj.dw);  //data from a svg draw
            }
        catch(err){
            console.log(err.message);
            }
        };

As you can see the routine creates in first the Big_image with the SVG draw. After it creates a resized image in memory. I tried a different approach but also the only Big_image and the obj.dw is enough to live memory leaks. What is wrong? It may be I'm not able to see my bug. I hope I can get a suggestion from different perspectives.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
Gildo
  • 43
  • 6
  • @Paul LeBeau: I have tested your solution. It works fine but it is very slow!!! Unfortunately we have to handle big SVG draws and the thumbnail refresh requires the same time required for refresh the total main draw area itself.... for this reason your solution seems good only for very small pictures...... – Gildo Feb 15 '16 at 11:33

3 Answers3

0

You need to have an explicit delete glb._MagicImg; When you no longer require this object.

Also see: Deleting Objects in JavaScript For further info.

Community
  • 1
  • 1
Arif Burhan
  • 507
  • 4
  • 12
0

To get the best out of javascript it is always to good idea to reuse resources if you can.

Your code is exceedingly wasteful.

As I see it you want to create a smaller version of a large (complex?) SVG image. It looks like you dump any previous copies when you create the new one.

A possible Solution for you that will not chew memory. You need two images. One for the SVG and one for the magicEye (thumb). The thumb image can be a canvas, create it once and draw the SVG to it when needed. The other image for the SVG also only needs to be created once, only add the load listener once set its URL = "" when you don't need it any more. Keep it for next time you need it.

The following code will load one of 3 SVG images and convert it to an image (canvas) then do it again in 100ms.

It will not chew more resources than what is required for the two images (ignoring pending GC dumps).

var thumbImage = {
    width:100,
    height:100,

}
var SVG_images = [
    '<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  width="256" height="256" id="testSVG" ><defs></defs><path d="M169.5,9Q184.7,10.5,186,23Q187.9,7.2,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q154.2,20.1,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q183.8,107.2,167.5,110Q147.1,112.3,142,101.5Q137.6,87,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q178.2,73.6,140,48.5Q138.7,29.4,144,20.5Q155.2,7.7,169.5,9Z M37.5,13Q54.2,14.8,75,15.5Q76.1,30.5,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q40.5,60.1,30,33Q6.7,36.9,4,27.5Q.8,8.8,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q138,38.5,104,31L104,49Q126.7,40.1,127,58.5Q97.6,72.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M121.5,130Q141,128.2,157,153.5Q178.3,206,162.5,232Q155.3,234.7,150,227.5Q147.7,219.5,149,192Q124.5,197.1,106.5,191Q102.8,198.8,96,225.5Q86.5,237.5,78,224.5Q90.4,147.8,121.5,130Z M124,149.5Q120.7,154.4,112,172.5Q120,176.9,143,174Q140.8,156.3,124,149.5Z"  fill="#000000" fill-rule="evenodd" ></path></svg>',
    '<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  width="256" height="256" id="testSVG" ><defs></defs><path d="M162.5,9Q171.4,8.5,179.5,12Q186.2,17.8,186,23Q186.8,14,191.5,13Q192,13.6,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q162.8,20.1,159,25.5Q155.2,30.4,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q185.4,104,172.5,109Q151.2,113.2,144,104.5Q135.6,89.6,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q173.8,80.1,169,70.5Q157.6,60.5,146.5,63Q141.6,57.7,140,48.5Q138.7,29.4,144,20.5Q153.2,11.1,162.5,9Z M37.5,13Q54.2,14.8,75,15.5Q74.6,21.7,71.5,25Q58.8,29.6,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q23,97.5,35,60.5Q36.2,59.8,30,33Q6.7,36.9,4,27.5Q2.9,17.9,9.5,15Q37.6,13.9,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q136.6,28.8,129.5,32Q129.5,32.2,104,31L104,49Q113.1,46.7,120.5,47Q127.7,50.6,127,58.5Q125,67.9,103.5,69Q101.4,70.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M78.5,117Q97.7,121.6,159.5,129Q164.2,130.1,174,144.5Q176.2,144.4,176,212.5Q170,236.9,163.5,244Q113.5,247.9,65,241.5Q58.3,227.7,76,214.5Q72.8,199.7,72,144Q57,146.3,59,131.5Q58.8,128.3,78.5,117Z M106,146.5Q95.5,150.6,98,170Q132.5,179.7,150,168.5L149.5,153Q128.8,145.8,106,146.5Z M98,193.5Q102.4,222.6,106.5,225Q122.3,231.2,133.5,233Q148.3,229.2,153,222.5Q157.6,207.3,152.5,196Q130.7,198.5,98,193.5Z"  fill="#000000" fill-rule="evenodd" ></path></svg>',
    '<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  width="256" height="256" id="testSVG" ><defs></defs><path d="M162.5,9Q183.7,7.2,186,23Q187.9,7.2,250,16.5Q252.5,29.3,243.5,36Q238.9,38.5,228,35Q227.1,86.5,231.5,107Q198.9,110.6,198,104.5Q211.3,64.1,209,34.5Q189.3,31.3,187,25Q189.3,39.3,176.5,38Q179.3,24.7,171.5,21Q154.2,20.1,156,39.5Q156.6,50.9,187,63.5Q194,79.1,191,92.5Q185.4,104,172.5,109Q151.2,113.2,144,104.5Q135.6,89.6,147.5,82Q159.4,95.9,160.5,95Q169.1,96,173,86.5Q178.2,73.6,140,48.5Q135.3,15.1,162.5,9Z M37.5,13Q54.2,14.8,75,15.5Q76.1,30.5,49,30Q62.5,97.7,58.5,102Q48.5,102.7,24,98.5Q40.5,60.1,30,33Q6.7,36.9,4,27.5Q.8,8.8,37.5,13Z M91.5,15Q136.5,14.6,136,19.5Q138,38.5,104,31L104,49Q126.7,40.1,127,58.5Q97.6,72.3,101,83Q118.5,80.7,128.5,83Q139.4,95,126.5,104Q81.5,107.5,79,97.5Q84.9,14,91.5,15Z M121.5,129Q151.9,129.3,159.5,135Q167.3,139.1,165,163.5Q164,172.2,143.5,177Q131.8,176.1,134.5,151Q124.6,153.1,107.5,155Q89.6,186.1,98,210.5Q113.7,214.2,136.5,210Q141,195.7,146.5,194Q168.3,192.4,168,210.5Q165.1,227.3,147.5,237Q125.6,242.4,103.5,242Q85.1,237.3,73,212.5Q67,196.5,70,173.5Q75,154.4,88.5,138Q98.1,130.5,121.5,129Z"  fill="#000000" fill-rule="evenodd" ></path></svg>',
]

var createThumb = function(svgData) {

            var loadImg = function(){ 
                if(thumbImage.image === undefined){ // check if the image exists?                    
                    thumbImage.image = document.createElement("canvas"); // create it if not
                }
                // resize it. Could test if this is neede but keeping it simple.
                thumbImage.width = thumbImage.width;
                thumbImage.height = thumbImage.height;
                // is there a 2d context 
                if(thumbImage.image.ctx === undefined){
                    // no context so create it.
                    thumbImage.image.ctx = thumbImage.image.getContext("2d");
                }
                // the resize may be the same and thus no free clear so clear 
                thumbImage.image.ctx.clearRect(0,0,thumbImage.width,thumbImage.height);
                // draw the SVG image onto the magicImg.
                thumbImage.image.ctx.drawImage(this, 0,0,thumbImage.width,thumbImage.height);
                this.src = "";

            };
            // does the thumb image exist.
            if( thumbImage.tempImage === undefined){
                thumbImage.tempImage = new Image(); // create it 
                // add listener that can be reused for all other loads.
                thumbImage.tempImage.addEventListener('load',loadImg);
            }
            thumbImage.tempImage.src = 'data:image/svg+xml;base64,'+btoa(svgData);  

    };

var currentSvg = 0;        
function justDoIt(){
    createThumb(SVG_images[currentSvg % SVG_images.length]);
    currentSvg += 1;
    setTimeout(justDoIt,100);
}
justDoIt();

It only creates the canvas and image once when first needed, then reuses them while they exist. When the first original SVG is rasterized to the canvas there is no extra memory needed as the already allocated canvas is has its memory (Unless the thumb size changes).

I have run it for an hour now (10 images a second), and gave it the full suit of Dev tool checks. Everything it allocates end up back in the GC so no memory leaks.

You should be able to adapt it to your needs. Remember reuse rather than delete and reasign. Also the Canvas is an image so there is no need to convert anything to a dataURL unless you need to transport it outside the Javascript immediate context.

Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • Nice code, Thanks, But I have to draw on the Thumbnail, for example, the zoomed area, and when you will move it, refresh the Thumbnail and redraw the box. In c++ it is easy with the XOR op but on the canvas doesn't works for this reason I have to rebuild the image every time.... – Gildo Feb 14 '16 at 15:14
  • @Gildo Just keep the original SVG image and draw what you need to the other canvas when the zoomed area is moved. Hard to know what you need but the 2D API is well thought out and will cover almost all your needs. – Blindman67 Feb 14 '16 at 15:39
  • @Blindman67 I'm sorry, my poor english do not help me explain in detail what I need, In any case, I have understood your suggestion. But unfortunately (may be I'm in wrong) that case doesn't cover my needs. Please look the screenshots. – Gildo Feb 14 '16 at 15:50
0

You may also want to consider letting the SVG scale itself.

#main {
    width: 400px;
    height: 400px;
}

svg {
    width: 100%;
    height: 100%;
}


#thumb, #zoom {
    width: 40px;
    height: 40px;
    border: solid 1px black;
    overflow: hidden;
}

#zoom svg {
    width: 400px;
    height: 400px;
    position: relative;
    top: -140px;
    left: -210px
}
<div id="main">
    <svg id="mainsvg" viewBox="0 0 1000 1000">
        <rect x="100" y="100" width="500" height="500" fill="green"
              transform="rotate(10,350,350)"/>
        <rect x="400" y="400" width="500" height="500" fill="orange"
              transform="rotate(-10,650,650)"/>
    </svg>
</div>
        
        
<div id="thumb">
    <svg xmlns:xlink="http://www.w3.org/1999/xlink">
        <use xlink:href="#mainsvg" />
    </svg>
</div>

<div id="zoom">
    <svg xmlns:xlink="http://www.w3.org/1999/xlink">
        <use xlink:href="#mainsvg" />
    </svg>
</div>
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • That's very nice.... I will try it. the only dubt is about three thinks: 1) the memory occupation 2) How I can visualize on the Thumbnail the viewbox (see the screenshot 1) 3) The speed of this solution... I will try it!!!! – Gildo Feb 14 '16 at 18:16