534 lines
16 KiB
C++
534 lines
16 KiB
C++
//=============================================================================
|
|
// MuseScore
|
|
// Music Composition & Notation
|
|
//
|
|
// Copyright (C) 2020 MuseScore BVBA and others
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 2.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//=============================================================================
|
|
|
|
#include "avsomrreader.h"
|
|
|
|
#include <QFile>
|
|
#include <QBuffer>
|
|
#include <QXmlStreamReader>
|
|
#include <QPainter>
|
|
|
|
#include "thirdparty/qzip/qzipreader_p.h"
|
|
#include "mscore/preferences.h"
|
|
|
|
#include "avslog.h"
|
|
|
|
using namespace Ms::Avs;
|
|
|
|
AvsOmrReader::AvsOmrReader()
|
|
{
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// glyphColor
|
|
//---------------------------------------------------------
|
|
|
|
QColor AvsOmrReader::glyphColor(AvsOmr::GlyphUsed used) const
|
|
{
|
|
if (_glyphColors.isEmpty()) {
|
|
_glyphColors[AvsOmr::GlyphUsed::Used] = preferences.getColor(PREF_UI_AVSOMR_RECOGNITION_COLOR);
|
|
_glyphColors[AvsOmr::GlyphUsed::Free] = preferences.getColor(PREF_UI_AVSOMR_NOT_RECOGNITION_COLOR);
|
|
_glyphColors[AvsOmr::GlyphUsed::Free_Covered] = preferences.getColor(PREF_UI_AVSOMR_NOT_RECOGNITION_COLOR);
|
|
}
|
|
|
|
return _glyphColors.value(used);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// read
|
|
//---------------------------------------------------------
|
|
|
|
std::shared_ptr<AvsOmr> AvsOmrReader::read(const QString& ormFilePath) const
|
|
{
|
|
QFile zipFile(ormFilePath);
|
|
if (!zipFile.exists()) {
|
|
LOGE() << "not exists orm file: " << zipFile.fileName();
|
|
return nullptr;
|
|
}
|
|
|
|
if (!zipFile.open(QIODevice::ReadOnly)) {
|
|
LOGE() << "failed open orm file: " << zipFile.fileName();
|
|
return nullptr;
|
|
}
|
|
|
|
return read(&zipFile);
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// read
|
|
//---------------------------------------------------------
|
|
|
|
std::shared_ptr<AvsOmr> AvsOmrReader::read(QIODevice* ormFile) const
|
|
{
|
|
MQZipReader zip(ormFile);
|
|
|
|
QByteArray book = zip.fileData("book.xml");
|
|
if (book.isEmpty()) {
|
|
LOGE() << "not exists book.xml";
|
|
return nullptr;
|
|
}
|
|
|
|
QBuffer bookBuf(&book);
|
|
bookBuf.open(QIODevice::ReadOnly);
|
|
|
|
std::shared_ptr<AvsOmr> omr = std::make_shared<AvsOmr>();
|
|
|
|
bool ok = readBook(omr->_book, &bookBuf);
|
|
if (!ok) {
|
|
LOGE() << "failed read book.xml";
|
|
return nullptr;
|
|
}
|
|
|
|
for (uint16_t n = 1; n <= omr->_book.sheets; ++n) {
|
|
QString path = makeSheetPath(n);
|
|
QByteArray sheet = zip.fileData(path);
|
|
if (sheet.isEmpty()) {
|
|
LOGE() << "not exists sheet: " << path;
|
|
continue;
|
|
}
|
|
QBuffer sheetBuf(&sheet);
|
|
sheetBuf.open(QIODevice::ReadOnly);
|
|
|
|
AvsOmr::Sheet* sh = new AvsOmr::Sheet();
|
|
ok = readSheet(*sh, &sheetBuf);
|
|
if (!ok) {
|
|
LOGE() << "failed read sheet: " << path;
|
|
continue;
|
|
}
|
|
|
|
omr->_sheets.insert(sh->num, sh);
|
|
}
|
|
|
|
omr->resolve();
|
|
|
|
AvsOmr::Info& info = omr->_info;
|
|
info.usedColor = glyphColor(AvsOmr::GlyphUsed::Used);
|
|
info.freeColor = glyphColor(AvsOmr::GlyphUsed::Free);
|
|
|
|
for (const AvsOmr::Sheet* sh : omr->_sheets) {
|
|
for (AvsOmr::Glyph* g : sh->glyphs) {
|
|
if (AvsOmr::GlyphUsed::Used == g->used) {
|
|
++info.usedCount;
|
|
} else if (AvsOmr::GlyphUsed::Free == g->used) {
|
|
++info.freeCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return omr;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// makeSheetPath
|
|
//---------------------------------------------------------
|
|
|
|
QString AvsOmrReader::makeSheetPath(uint16_t n) const
|
|
{
|
|
QString name = "sheet#" + QString::number(n);
|
|
return name + "/" + name + ".xml";
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readBook
|
|
//---------------------------------------------------------
|
|
|
|
bool AvsOmrReader::readBook(AvsOmr::Book& b, QIODevice* xmlData) const
|
|
{
|
|
QXmlStreamReader xml(xmlData);
|
|
if (xml.readNextStartElement()) {
|
|
if (!(xml.name() == "book")) {
|
|
return false;
|
|
}
|
|
|
|
while (xml.readNextStartElement()) {
|
|
QStringRef tag = xml.name();
|
|
if ("sheet" == tag) {
|
|
++b.sheets;
|
|
xml.skipCurrentElement();
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ok = !xml.hasError();
|
|
if (!ok) {
|
|
LOGE() << "failed read xml, err: " << xml.errorString();
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readSheet
|
|
//---------------------------------------------------------
|
|
|
|
bool AvsOmrReader::readSheet(AvsOmr::Sheet& sh, QIODevice* xmlData) const
|
|
{
|
|
QXmlStreamReader xml(xmlData);
|
|
if (xml.readNextStartElement()) {
|
|
if (xml.name() == "sheet") {
|
|
sh.num = xml.attributes().value("number").toInt();
|
|
|
|
while (xml.readNextStartElement()) {
|
|
QStringRef tag = xml.name();
|
|
if (tag == "page") {
|
|
readPage(sh.page, xml);
|
|
} else if (tag == "glyph-index") {
|
|
readGlyphs(sh, xml);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ok = !xml.hasError();
|
|
if (!ok) {
|
|
LOGE() << "failed read xml, err: " << xml.errorString();
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readPage
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readPage(AvsOmr::Page& p, QXmlStreamReader& xml) const
|
|
{
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "system") {
|
|
AvsOmr::System sys;
|
|
readSystem(sys, xml);
|
|
p.systems.append(sys);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readSystem
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readSystem(AvsOmr::System& sys, QXmlStreamReader& xml) const
|
|
{
|
|
while (xml.readNextStartElement()) {
|
|
auto tag = xml.name();
|
|
if (tag == "stack") {
|
|
AvsOmr::MStack st;
|
|
readStack(st, xml);
|
|
sys.mstacks.append(st);
|
|
} else if (tag == "part") {
|
|
readPart(sys.part, xml);
|
|
} else if (tag == "free-glyphs") {
|
|
QString str = xml.readElementText();
|
|
sys.freeglyphs = toIDSet(str);
|
|
} else if (tag == "sig") {
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "inters") {
|
|
readInters(sys.inters, xml);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readStack
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readStack(AvsOmr::MStack& st, QXmlStreamReader& xml) const
|
|
{
|
|
auto attrs = xml.attributes();
|
|
st.id = attrs.value("id").toInt();
|
|
st.left = attrs.value("left").toInt();
|
|
st.right = attrs.value("right").toInt();
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readPart
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readPart(AvsOmr::Part& pt, QXmlStreamReader& xml) const
|
|
{
|
|
while (xml.readNextStartElement()) {
|
|
auto tag = xml.name();
|
|
if (tag == "staff") {
|
|
AvsOmr::Staff sf;
|
|
readStaff(sf, xml);
|
|
pt.staffs.append(sf);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readStaff
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readStaff(AvsOmr::Staff& sf, QXmlStreamReader& xml) const
|
|
{
|
|
auto attrs = xml.attributes();
|
|
sf.id = attrs.value("id").toInt();
|
|
|
|
while (xml.readNextStartElement()) {
|
|
auto tag = xml.name();
|
|
if (tag == "header") {
|
|
sf.header.start = xml.attributes().value("start").toInt();
|
|
sf.header.stop = xml.attributes().value("stop").toInt();
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "clef") {
|
|
sf.header.clefID = xml.readElementText().toInt();
|
|
} else if (xml.name() == "key") {
|
|
sf.header.keyID = xml.readElementText().toInt();
|
|
} else if (xml.name() == "time") {
|
|
sf.header.timeID = xml.readElementText().toInt();
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
} else if (tag == "barlines") {
|
|
QString str = xml.readElementText();
|
|
sf.barlines = toIDList(str);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readInters
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readInters(AvsOmr::Inters& its, QXmlStreamReader& xml) const
|
|
{
|
|
while (xml.readNextStartElement()) {
|
|
auto tag = xml.name();
|
|
if ("barline" == tag) {
|
|
AvsOmr::Barline bl;
|
|
readBarline(bl, xml);
|
|
its.barlines.insert(bl.id, bl);
|
|
} else if ("brace" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("clef" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("time-whole" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("beam" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("ledger" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("head" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("stem" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("dynamics" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else if ("alter" == tag) {
|
|
readInterGlyphID(its.usedglyphs, xml);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readBarline
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readBarline(AvsOmr::Barline& bl, QXmlStreamReader& xml) const
|
|
{
|
|
auto attrs = xml.attributes();
|
|
bl.id = attrs.value("id").toInt();
|
|
bl.glyphID = attrs.value("glyph").toInt();
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readInterGlyphID
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readInterGlyphID(QSet<AvsOmr::ID>& gls, QXmlStreamReader& xml) const
|
|
{
|
|
auto attrs = xml.attributes();
|
|
AvsOmr::ID glyphID = attrs.value("glyph").toInt();
|
|
gls.insert(glyphID);
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// toIDList
|
|
//---------------------------------------------------------
|
|
|
|
QList<AvsOmr::ID> AvsOmrReader::toIDList(const QString& str) const
|
|
{
|
|
if (str.isEmpty()) {
|
|
return QList<AvsOmr::ID>();
|
|
}
|
|
|
|
QList<AvsOmr::ID> ids;
|
|
QStringList strList = str.split(' ');
|
|
for (const QString& s : strList) {
|
|
ids.append(s.toInt());
|
|
}
|
|
|
|
return ids;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// toIDSet
|
|
//---------------------------------------------------------
|
|
|
|
QSet<AvsOmr::ID> AvsOmrReader::toIDSet(const QString& str) const
|
|
{
|
|
if (str.isEmpty()) {
|
|
return QSet<AvsOmr::ID>();
|
|
}
|
|
|
|
QSet<AvsOmr::ID> ids;
|
|
QStringList strList = str.split(' ');
|
|
for (const QString& s : strList) {
|
|
ids.insert(s.toInt());
|
|
}
|
|
|
|
return ids;
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readGlyphs
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readGlyphs(AvsOmr::Sheet& sh, QXmlStreamReader& xml) const
|
|
{
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "glyph") {
|
|
auto attrs = xml.attributes();
|
|
AvsOmr::Glyph* g = new AvsOmr::Glyph();
|
|
g->bbox.setLeft(attrs.value("left").toInt());
|
|
g->bbox.setTop(attrs.value("top").toInt());
|
|
g->id = attrs.value("id").toInt();
|
|
|
|
if (sh.isGlyphUsed(g->id)) {
|
|
g->used = AvsOmr::GlyphUsed::Used;
|
|
} else if (sh.isGlyphFree(g->id)) {
|
|
g->used = AvsOmr::GlyphUsed::Free;
|
|
}
|
|
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "run-table") {
|
|
readImage(g->img, xml, glyphColor(g->used));
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
|
|
g->bbox.setRight(g->bbox.left() + g->img.width());
|
|
g->bbox.setBottom(g->bbox.top() + g->img.height());
|
|
|
|
sh.glyphs.insert(g->id, g);
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readImage
|
|
//---------------------------------------------------------
|
|
|
|
void AvsOmrReader::readImage(QImage& img, QXmlStreamReader& xml, const QColor& color) const
|
|
{
|
|
IF_ASSERT(xml.isStartElement() && xml.name() == "run-table") {
|
|
return;
|
|
}
|
|
|
|
auto attrs = xml.attributes();
|
|
int width = attrs.value("width").toInt();
|
|
int height = attrs.value("height").toInt();
|
|
bool isVertical = attrs.value("orientation") == "VERTICAL";
|
|
|
|
img = QImage(width, height, QImage::Format_ARGB32);
|
|
img.fill(Qt::transparent);
|
|
|
|
QPainter p(&img);
|
|
p.setPen(color);
|
|
|
|
auto drawLine = [&p, isVertical](int i, const QString& line) {
|
|
if (line.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QStringList segs = line.split(' ');
|
|
bool isFill{ true };
|
|
int j{ 0 };
|
|
for (const QString& seg : segs) {
|
|
int length = seg.toInt();
|
|
if (length > 0 && isFill) {
|
|
if (isVertical) {
|
|
p.drawLine(i, j, i, j + length);
|
|
} else {
|
|
p.drawLine(j, i, j + length, i);
|
|
}
|
|
}
|
|
j += length;
|
|
isFill = !isFill;
|
|
}
|
|
};
|
|
|
|
int i = 0;
|
|
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "runs") {
|
|
QString line = xml.readElementText();
|
|
drawLine(i, line);
|
|
++i;
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------
|
|
// readPicture
|
|
//---------------------------------------------------------
|
|
|
|
bool AvsOmrReader::readPicture(QImage& img, QIODevice* xmlData) const
|
|
{
|
|
QXmlStreamReader xml(xmlData);
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "run-table") {
|
|
readImage(img, xml, QColor(Qt::darkBlue));
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|