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 "pngwriter.h"
24 :
25 : #include <cmath>
26 : #include <QImage>
27 : #include <QBuffer>
28 :
29 : #include "engraving/dom/mscore.h"
30 :
31 : #include "notation/inotation.h"
32 : #include "notation/inotationpainting.h"
33 :
34 : #include "log.h"
35 :
36 : using namespace mu::iex::imagesexport;
37 : using namespace mu::project;
38 : using namespace mu::notation;
39 : using namespace muse;
40 : using namespace muse::io;
41 :
42 0 : std::vector<INotationWriter::UnitType> PngWriter::supportedUnitTypes() const
43 : {
44 0 : return { UnitType::PER_PAGE };
45 : }
46 :
47 0 : Ret PngWriter::write(INotationPtr notation, io::IODevice& destinationDevice, const Options& options)
48 : {
49 0 : IF_ASSERT_FAILED(notation) {
50 0 : return make_ret(Ret::Code::UnknownError);
51 : }
52 :
53 0 : const float CANVAS_DPI = configuration()->exportPngDpiResolution();
54 :
55 0 : INotationPainting::Options opt;
56 0 : opt.fromPage = muse::value(options, OptionKey::PAGE_NUMBER, Val(0)).toInt();
57 0 : opt.toPage = opt.fromPage;
58 0 : opt.trimMarginPixelSize = configuration()->trimMarginPixelSize();
59 0 : opt.deviceDpi = CANVAS_DPI;
60 0 : opt.printPageBackground = false; // Printed by us using image.fill
61 :
62 0 : const SizeF pageSizeInch = notation->painting()->pageSizeInch(opt);
63 :
64 0 : int width = std::lrint(pageSizeInch.width() * CANVAS_DPI);
65 0 : int height = std::lrint(pageSizeInch.height() * CANVAS_DPI);
66 :
67 0 : QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
68 0 : image.setDotsPerMeterX(std::lrint((CANVAS_DPI * 1000) / mu::engraving::INCH));
69 0 : image.setDotsPerMeterY(std::lrint((CANVAS_DPI * 1000) / mu::engraving::INCH));
70 :
71 0 : const bool TRANSPARENT_BACKGROUND = muse::value(options, OptionKey::TRANSPARENT_BACKGROUND,
72 0 : Val(configuration()->exportPngWithTransparentBackground())).toBool();
73 0 : image.fill(TRANSPARENT_BACKGROUND ? Qt::transparent : Qt::white);
74 :
75 0 : muse::draw::Painter painter(&image, "pngwriter");
76 :
77 0 : notation->painting()->paintPng(&painter, opt);
78 :
79 0 : if (configuration()->exportPngWithGrayscale()) {
80 0 : convertImageToGrayscale(image);
81 : }
82 :
83 0 : QByteArray qdata;
84 0 : QBuffer buf(&qdata);
85 0 : buf.open(QIODevice::WriteOnly);
86 :
87 0 : image.save(&buf, "png");
88 :
89 0 : ByteArray data = ByteArray::fromQByteArrayNoCopy(qdata);
90 0 : destinationDevice.write(data);
91 :
92 0 : return true;
93 0 : }
94 :
95 0 : void PngWriter::convertImageToGrayscale(QImage& image)
96 : {
97 : // We convert every pixel to gray, preserving alpha channel (necessary for transparent background)
98 0 : for (int y = 0; y < image.height(); ++y) {
99 0 : QRgb* scanLine = reinterpret_cast<QRgb*>(image.scanLine(y));
100 0 : for (int x = 0; x < image.width(); ++x) {
101 0 : QRgb& pixel = scanLine[x];
102 0 : uint ci = uint(qGray(pixel));
103 0 : pixel = qRgba(ci, ci, ci, qAlpha(pixel));
104 : }
105 : }
106 0 : }
|