LCOV - code coverage report
Current view: top level - src/engraving/dom - image.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 52.6 % 230 121
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "image.h"
      24              : 
      25              : #include "io/file.h"
      26              : #include "io/fileinfo.h"
      27              : 
      28              : #include "draw/iimageprovider.h" // IWYU pragma: keep
      29              : #include "draw/types/pixmap.h"
      30              : #include "draw/svgrenderer.h"
      31              : 
      32              : #include "../editing/editdata.h"
      33              : #include "../editing/elementeditdata.h"
      34              : 
      35              : #include "imageStore.h"
      36              : #include "masterscore.h"
      37              : #include "mscore.h"
      38              : #include "score.h"
      39              : 
      40              : #include "log.h"
      41              : 
      42              : using namespace mu;
      43              : using namespace muse::io;
      44              : using namespace muse::draw;
      45              : using namespace mu::engraving;
      46              : 
      47              : namespace mu::engraving {
      48              : //---------------------------------------------------------
      49              : //   propertyList
      50              : //---------------------------------------------------------
      51              : 
      52              : static bool defaultAutoScale        = false;
      53              : static bool defaultLockAspectRatio  = true;
      54              : static bool defaultSizeIsSpatium    = true;
      55              : 
      56              : //---------------------------------------------------------
      57              : //   Image
      58              : //---------------------------------------------------------
      59              : 
      60           24 : Image::Image(DummyParentOr<EngravingItem> parent)
      61           24 :     : BSymbol(ElementType::IMAGE, parent, ElementFlag::MOVABLE)
      62              : {
      63           24 :     m_imageType        = ImageType::NONE;
      64           24 :     m_size            = SizeF(0.0, 0.0);
      65           24 :     m_storeItem       = 0;
      66           24 :     m_dirty           = false;
      67           24 :     m_lockAspectRatio = defaultLockAspectRatio;
      68           24 :     m_autoScale       = defaultAutoScale;
      69           24 :     m_sizeIsSpatium   = defaultSizeIsSpatium;
      70           24 : }
      71              : 
      72            6 : Image::Image(const Image& img)
      73            6 :     : BSymbol(img)
      74              : {
      75            6 :     m_imageType        = img.m_imageType;
      76            6 :     m_buffer           = img.m_buffer;
      77            6 :     m_size            = img.m_size;
      78            6 :     m_lockAspectRatio = img.m_lockAspectRatio;
      79            6 :     m_autoScale       = img.m_autoScale;
      80            6 :     m_dirty           = img.m_dirty;
      81            6 :     m_storeItem       = img.m_storeItem;
      82            6 :     m_sizeIsSpatium   = img.m_sizeIsSpatium;
      83            6 :     if (m_storeItem) {
      84            3 :         m_storeItem->reference(this);
      85              :     }
      86            6 :     if (m_imageType == ImageType::RASTER) {
      87            3 :         m_rasterDoc = img.m_rasterDoc ? std::make_shared<Pixmap>(*img.m_rasterDoc) : nullptr;
      88            3 :     } else if (m_imageType == ImageType::SVG) {
      89            0 :         m_svgDoc = img.m_svgDoc ? new SvgRenderer(m_storeItem->buffer()) : 0;
      90              :     }
      91            6 :     setZ(img.z());
      92            6 : }
      93              : 
      94              : //---------------------------------------------------------
      95              : //   Image
      96              : //---------------------------------------------------------
      97              : 
      98           60 : Image::~Image()
      99              : {
     100           30 :     if (m_storeItem) {
     101           24 :         m_storeItem->dereference(this);
     102              :     }
     103           30 :     if (m_imageType == ImageType::SVG) {
     104            0 :         delete m_svgDoc;
     105              :     }
     106           60 : }
     107              : 
     108              : //---------------------------------------------------------
     109              : //   setImageType
     110              : //---------------------------------------------------------
     111              : 
     112           21 : void Image::setImageType(ImageType t)
     113              : {
     114           21 :     m_imageType = t;
     115           21 :     if (m_imageType == ImageType::SVG) {
     116            0 :         m_svgDoc = 0;
     117           21 :     } else if (m_imageType == ImageType::RASTER) {
     118           21 :         m_rasterDoc.reset();
     119              :     } else {
     120            0 :         LOGD("illegal image type");
     121              :     }
     122           21 : }
     123              : 
     124              : //---------------------------------------------------------
     125              : //   imageSize
     126              : //---------------------------------------------------------
     127              : 
     128           34 : muse::SizeF Image::imageSize() const
     129              : {
     130           34 :     if (!isValid()) {
     131            8 :         return muse::SizeF();
     132              :     }
     133              : 
     134           26 :     if (m_imageType == ImageType::RASTER) {
     135           26 :         muse::Size rasterSize = m_rasterDoc->size();
     136           26 :         return muse::SizeF(rasterSize.width(), rasterSize.height());
     137              :     }
     138              : 
     139            0 :     return m_svgDoc->defaultSize();
     140              : }
     141              : 
     142              : //---------------------------------------------------------
     143              : //   isImageFramed
     144              : //---------------------------------------------------------
     145              : 
     146            0 : bool Image::isImageFramed() const
     147              : {
     148            0 :     if (!ownershipParent()) {
     149            0 :         return false;
     150              :     }
     151              : 
     152            0 :     return ownershipParent()->isBox();
     153              : }
     154              : 
     155              : //---------------------------------------------------------
     156              : //   imageAspectRatio
     157              : //---------------------------------------------------------
     158              : 
     159            0 : double Image::imageAspectRatio() const
     160              : {
     161            0 :     return m_size.width() / m_size.height();
     162              : }
     163              : 
     164              : //---------------------------------------------------------
     165              : //   setImageHeight
     166              : //---------------------------------------------------------
     167              : 
     168            0 : void Image::updateImageHeight(const double& height)
     169              : {
     170            0 :     double aspectRatio = imageAspectRatio();
     171              : 
     172            0 :     m_size.setHeight(height);
     173              : 
     174            0 :     if (m_lockAspectRatio) {
     175            0 :         m_size.setWidth(height * aspectRatio);
     176              :     }
     177            0 : }
     178              : 
     179              : //---------------------------------------------------------
     180              : //   setImageWidth
     181              : //---------------------------------------------------------
     182              : 
     183            0 : void Image::updateImageWidth(const double& width)
     184              : {
     185            0 :     double aspectRatio = imageAspectRatio();
     186              : 
     187            0 :     m_size.setWidth(width);
     188              : 
     189            0 :     if (m_lockAspectRatio) {
     190            0 :         m_size.setHeight(width / aspectRatio);
     191              :     }
     192            0 : }
     193              : 
     194              : //---------------------------------------------------------
     195              : //   imageHeight
     196              : //---------------------------------------------------------
     197              : 
     198            0 : double Image::imageHeight() const
     199              : {
     200            0 :     return m_size.height();
     201              : }
     202              : 
     203              : //---------------------------------------------------------
     204              : //   imageWidth
     205              : //---------------------------------------------------------
     206              : 
     207            0 : double Image::imageWidth() const
     208              : {
     209            0 :     return m_size.width();
     210              : }
     211              : 
     212           21 : bool Image::loadFromStore(const std::string& storePath)
     213              : {
     214           21 :     m_storePath = storePath;
     215           21 :     if (storePath.empty()) {
     216            0 :         return false;
     217              :     }
     218              : 
     219           21 :     m_storeItem = imageStore.getImage(storePath);
     220           21 :     if (!m_storeItem) {
     221            2 :         return false;
     222              :     }
     223           19 :     m_storeItem->reference(this);
     224              : 
     225           19 :     std::string suffix = muse::io::suffix(storePath);
     226           19 :     if (suffix == "svg" || suffix == "svgz") {
     227            0 :         setImageType(ImageType::SVG);
     228              :     } else {
     229           19 :         setImageType(ImageType::RASTER);
     230              :     }
     231              : 
     232           19 :     return true;
     233           19 : }
     234              : 
     235              : //---------------------------------------------------------
     236              : //   loadFromFile
     237              : //    load image from file and put into ImageStore
     238              : //    return true on success
     239              : //---------------------------------------------------------
     240              : 
     241            5 : bool Image::loadFromFile(muse::io::path_t path)
     242              : {
     243            5 :     if (path.empty()) {
     244            0 :         return false;
     245              :     }
     246              : 
     247            5 :     if (FileInfo(path).isRelative()) {
     248              :         // if file path is relative, prepend score path
     249            5 :         path = masterScore()->fileInfo()->absoluteDirPath() + '/' + path;
     250              :     }
     251              : 
     252            5 :     File f(path);
     253            5 :     if (!f.open(IODevice::ReadOnly)) {
     254            3 :         LOGD() << "failed load file: " << path;
     255            3 :         return false;
     256              :     }
     257            2 :     muse::ByteArray ba = f.readAll();
     258            2 :     f.close();
     259              : 
     260            2 :     m_storeItem = imageStore.add(path.toStdString(), ba);
     261            2 :     m_storeItem->reference(this);
     262            2 :     if (path.hasSuffix("svg") || path.hasSuffix("svgz")) {
     263            0 :         setImageType(ImageType::SVG);
     264              :     } else {
     265            2 :         setImageType(ImageType::RASTER);
     266              :     }
     267            2 :     return true;
     268            5 : }
     269              : 
     270              : //---------------------------------------------------------
     271              : //   loadFromData
     272              : //    load image from data and put into ImageStore
     273              : //    return true on success
     274              : //---------------------------------------------------------
     275              : 
     276            0 : bool Image::loadFromData(const std::string& name, const muse::ByteArray& ba)
     277              : {
     278            0 :     m_storeItem = imageStore.add(name, ba);
     279            0 :     m_storeItem->reference(this);
     280              : 
     281            0 :     muse::io::path_t ioPath(name);
     282            0 :     if (ioPath.hasSuffix("svg") || ioPath.hasSuffix("svgz")) {
     283            0 :         setImageType(ImageType::SVG);
     284              :     } else {
     285            0 :         setImageType(ImageType::RASTER);
     286              :     }
     287            0 :     return true;
     288            0 : }
     289              : 
     290              : //---------------------------------------------------------
     291              : //   startDrag
     292              : //---------------------------------------------------------
     293              : 
     294            0 : void Image::startDragGrip(EditData& data)
     295              : {
     296            0 :     BSymbol::startDragGrip(data);
     297            0 :     ElementEditDataPtr eed = data.getData(this);
     298              : 
     299            0 :     eed->pushProperty(Pid::SIZE);
     300            0 : }
     301              : 
     302              : //---------------------------------------------------------
     303              : //   dragGrip
     304              : //---------------------------------------------------------
     305              : 
     306            0 : void Image::dragGrip(EditData& ed)
     307              : {
     308            0 :     if (ed.curGrip == Grip::MIDDLE) {
     309            0 :         setOffset(offset() + ed.evtDelta);
     310            0 :         triggerLayout();
     311            0 :         return;
     312              :     }
     313              : 
     314            0 :     double ratio = m_size.width() / m_size.height();
     315            0 :     double dx = ed.delta.x();
     316            0 :     double dy = ed.delta.y();
     317            0 :     if (m_sizeIsSpatium) {
     318            0 :         double _spatium = spatium();
     319            0 :         dx /= _spatium;
     320            0 :         dy /= _spatium;
     321              :     } else {
     322            0 :         dx /= DPMM;
     323            0 :         dy /= DPMM;
     324              :     }
     325            0 :     if (ed.curGrip == Grip::START) {
     326            0 :         m_size.setWidth(m_size.width() + dx);
     327            0 :         if (m_lockAspectRatio) {
     328            0 :             m_size.setHeight(m_size.width() / ratio);
     329              :         }
     330            0 :     } else if (ed.curGrip == Grip::END) {
     331            0 :         m_size.setHeight(m_size.height() + dy);
     332            0 :         if (m_lockAspectRatio) {
     333            0 :             m_size.setWidth(m_size.height() * ratio);
     334              :         }
     335              :     }
     336              : 
     337            0 :     triggerLayout();
     338              : }
     339              : 
     340              : //---------------------------------------------------------
     341              : //   gripsPositions
     342              : //---------------------------------------------------------
     343              : 
     344            0 : std::vector<PointF> Image::gripsPositions(const EditData&) const
     345              : {
     346            0 :     RectF r(pageBoundingRect());
     347              :     return {
     348            0 :         PointF(r.x() + r.width(), r.y() + r.height() * .5),
     349            0 :         PointF(r.x() + r.width() * .5, r.y() + r.height())
     350            0 :     };
     351              : }
     352              : 
     353              : //---------------------------------------------------------
     354              : //   pixel2Size
     355              : //---------------------------------------------------------
     356              : 
     357           34 : SizeF Image::pixel2size(const SizeF& s) const
     358              : {
     359           34 :     return s / (m_sizeIsSpatium ? spatium() : DPMM);
     360              : }
     361              : 
     362              : //---------------------------------------------------------
     363              : //   size2pixel
     364              : //---------------------------------------------------------
     365              : 
     366           60 : SizeF Image::size2pixel(const SizeF& s) const
     367              : {
     368           60 :     return s * (m_sizeIsSpatium ? spatium() : DPMM);
     369              : }
     370              : 
     371           67 : void Image::init()
     372              : {
     373           67 :     if (m_imageType == ImageType::SVG && !m_svgDoc) {
     374            0 :         if (m_storeItem) {
     375            0 :             m_svgDoc = new SvgRenderer(m_storeItem->buffer());
     376              :         }
     377           67 :     } else if (m_imageType == ImageType::RASTER && !m_rasterDoc) {
     378           21 :         if (m_storeItem) {
     379           21 :             m_rasterDoc = imageProvider()->createPixmap(m_storeItem->buffer());
     380           21 :             if (!m_rasterDoc->isNull()) {
     381           21 :                 m_dirty = true;
     382              :             }
     383              :         }
     384              :     }
     385           67 :     if (m_size.isNull()) {
     386           11 :         m_size = pixel2size(imageSize());
     387              :     }
     388           67 : }
     389              : 
     390              : //---------------------------------------------------------
     391              : //   getProperty
     392              : //---------------------------------------------------------
     393              : 
     394          299 : PropertyValue Image::getProperty(Pid propertyId) const
     395              : {
     396          299 :     switch (propertyId) {
     397           23 :     case Pid::AUTOSCALE:
     398           23 :         return autoScale();
     399           23 :     case Pid::SIZE:
     400           23 :         return PropertyValue::fromValue(size());
     401            0 :     case Pid::IMAGE_HEIGHT:
     402            0 :         return imageHeight();
     403            0 :     case Pid::IMAGE_WIDTH:
     404            0 :         return imageWidth();
     405            0 :     case Pid::IMAGE_FRAMED:
     406            0 :         return isImageFramed();
     407           23 :     case Pid::LOCK_ASPECT_RATIO:
     408           23 :         return lockAspectRatio();
     409           23 :     case Pid::SIZE_IS_SPATIUM:
     410           23 :         return sizeIsSpatium();
     411          207 :     default:
     412          207 :         return EngravingItem::getProperty(propertyId);
     413              :     }
     414              : }
     415              : 
     416              : //---------------------------------------------------------
     417              : //   setProperty
     418              : //---------------------------------------------------------
     419              : 
     420           14 : bool Image::setProperty(Pid propertyId, const PropertyValue& v)
     421              : {
     422           14 :     bool rv = true;
     423           14 :     score()->addRefresh(canvasBoundingRect());
     424           14 :     switch (propertyId) {
     425            0 :     case Pid::AUTOSCALE:
     426            0 :         setAutoScale(v.toBool());
     427            0 :         break;
     428           14 :     case Pid::SIZE:
     429           14 :         setSize(v.value<SizeF>());
     430           14 :         break;
     431            0 :     case Pid::IMAGE_HEIGHT:
     432            0 :         updateImageHeight(v.toDouble());
     433            0 :         break;
     434            0 :     case Pid::IMAGE_WIDTH:
     435            0 :         updateImageWidth(v.toDouble());
     436            0 :         break;
     437            0 :     case Pid::IMAGE_FRAMED:
     438            0 :         break;
     439            0 :     case Pid::LOCK_ASPECT_RATIO:
     440            0 :         setLockAspectRatio(v.toBool());
     441            0 :         break;
     442            0 :     case Pid::SIZE_IS_SPATIUM: {
     443            0 :         SizeF s = size2pixel(m_size);
     444            0 :         setSizeIsSpatium(v.toBool());
     445            0 :         m_size = pixel2size(s);
     446              :     }
     447            0 :     break;
     448            0 :     default:
     449            0 :         rv = EngravingItem::setProperty(propertyId, v);
     450            0 :         break;
     451              :     }
     452           14 :     setGenerated(false);
     453           14 :     m_dirty = true;
     454           14 :     triggerLayout();
     455           14 :     return rv;
     456              : }
     457              : 
     458              : //---------------------------------------------------------
     459              : //   propertyDefault
     460              : //---------------------------------------------------------
     461              : 
     462          299 : PropertyValue Image::propertyDefault(Pid id) const
     463              : {
     464          299 :     switch (id) {
     465           23 :     case Pid::AUTOSCALE:
     466           23 :         return defaultAutoScale;
     467           23 :     case Pid::SIZE:
     468           23 :         return PropertyValue::fromValue(pixel2size(imageSize()));
     469            0 :     case Pid::IMAGE_HEIGHT:
     470            0 :         return pixel2size(imageSize()).height();
     471            0 :     case Pid::IMAGE_WIDTH:
     472            0 :         return pixel2size(imageSize()).width();
     473            0 :     case Pid::IMAGE_FRAMED:
     474            0 :         return isImageFramed();
     475           23 :     case Pid::LOCK_ASPECT_RATIO:
     476           23 :         return defaultLockAspectRatio;
     477           23 :     case Pid::SIZE_IS_SPATIUM:
     478           23 :         return defaultSizeIsSpatium;
     479          207 :     default:
     480          207 :         return EngravingItem::propertyDefault(id);
     481              :     }
     482              : }
     483              : }
        

Generated by: LCOV version 2.0-1