Page MenuHomePhabricator

Increased memory consumption using .svg files for icon
Closed, ResolvedPublic

Description

.svg image files are used for many icons in MITK plugins. Since these svg files have big icon dimensions and using them causes memory allocation error.

One such plugin using .svg files is PointListwidget. On opening three or more plugins using PointlistWidget, warning messages spam the console (See below for screenshot) .

. Here are the steps to reproduce it:

  1. Open Mitkworkbench
  2. Open IGT Tracking toolbox
  3. Open IGT Navigation Tool Calibration
  4. Open IGT Fiducial registration
  5. Open tracking Lab

Note: All these plugins have PointListWidget internally.

On analyzing the issue, it happens only in 32bit build because in 32bit the memory allocated for each process is only 2GB and it is not sufficient for these PoinlistWidget plugins.

There are two possible solutions suggested to this .

  1. Increasing the amount of memory available for a 32bit application. This can be done by modifying the header of application's binary. The link below explains this.

https://www.coveros.com/increasing-the-amount-of-memory-available-to-a-32-bit-windows-application/

  1. Next suggestion is to edit the properties of svg file icon. Setting the width and height of icon to smaller size helps. The first answer in the link below suggest this.

https://stackoverflow.com/questions/40805509/can-i-stop-stationary-svg-images-from-using-excesssive-cpu-resources-in-qml

Event Timeline

From my discussion with Patmaa earlier, it seems reasonable to decrease the image size as specified in the tags of the svg files (which is often huge e.g. 1792x1792 for arrow-down.svg in QmitkWidgetExt). Upon loading these images as e.g. button icons, Qt allocates memory for the specified size each time a widget containing this button is loaded regardless how they later appear in the GUI. Descreasing to e.g. 200x200 should still be sufficient for button icons even on high resolution displays.

@sridhara found out, that Qt allocates memory for SVGs according to the width and height of these files. So basically it is a memory issue as we have tons of icons with very large dimensions. I'll batch replace these dimensions to 128x128 or something like that.

Wrote a simple application that outputs all SVGs with a set viewBox, width, and height to something larger than 1000x1000. These SVGs can simply be made smaller by changing the width and height to a percentage like "10%". For future reference, here's the code:

#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>

using namespace std;
using namespace std::experimental;

bool equalsCaseInsensitive(const string& left, const string& right)
{
  return equal(left.begin(), left.end(), right.begin(), right.end(), [](char left, char right) {
    return tolower(left) == tolower(right);
  });
}

string readFile(const string path)
{
  ifstream file(path);
  stringstream buffer;
  buffer << file.rdbuf();
  return buffer.str();
}

int main()
{
  const string MITK_PATH = R"(F:\MITK-2018.04\)";
  vector<string> svgPaths;

  for (const auto& dirEntry : filesystem::recursive_directory_iterator(MITK_PATH))
  {
    if (equalsCaseInsensitive(".svg", dirEntry.path().extension().string()))
      svgPaths.push_back(dirEntry.path().string());
  }

  regex svgElemRegEx(R"(<svg[^>]+>)");
  regex viewBoxAttrRegEx(R"(\s(?:viewBox)\s*=\s*"\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*")");
  regex widthAttrRegEx(R"(\swidth\s*=\s*"(\d+(?:\.\d*)?))");
  regex heightAttrRegEx(R"(\height\s*=\s*"(\d+(?:\.\d*)?))");

  for (const auto& svgPath : svgPaths)
  {
    auto svgFile = readFile(svgPath);
    smatch svgElemMatches;

    if (regex_search(svgFile, svgElemMatches, svgElemRegEx))
    {
      auto svgElement = svgElemMatches[0].str();
      smatch viewBoxAttrMatches;

      if (regex_search(svgElement, viewBoxAttrMatches, viewBoxAttrRegEx))
      {
        if (5 == viewBoxAttrMatches.size())
        {
          auto viewBoxWidth = std::stoi(viewBoxAttrMatches[3].str());
          auto viewBoxHeight = std::stoi(viewBoxAttrMatches[4].str());

          if (1000 * 1000 < viewBoxWidth * viewBoxHeight)
          {
            smatch widthAttrMatches;
            smatch heightAttrMatches;

            if (regex_search(svgElement, widthAttrMatches, widthAttrRegEx) &&
                regex_search(svgElement, heightAttrMatches, heightAttrRegEx))
            {
              auto width = std::stoi(widthAttrMatches[1].str());
              auto height = std::stoi(heightAttrMatches[1].str());

              if (1000 * 1000 < width * height)
              {
                cout << svgPath << endl;
              }
            }
          }
        }
      }
    }
  }

  cin.get();
}

Thanks again @sridhara for figuring out the actual cause! 👍