File indexing completed on 2024-05-19 04:56:12

0001 /**
0002  * \file pictureframe.cpp
0003  * Frame containing picture.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 03 Mar 2008
0008  *
0009  * Copyright (C) 2008-2018  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "pictureframe.h"
0028 #include <QFile>
0029 #include <QDataStream>
0030 #include <QCoreApplication>
0031 #include <QMimeDatabase>
0032 #include <QMimeType>
0033 #include <QtEndian>
0034 
0035 namespace {
0036 
0037 /**
0038  * List of picture type strings, NULL terminated.
0039  */
0040 const char* const pictureTypeNames[] = {
0041   QT_TRANSLATE_NOOP("@default", "Other"),
0042   QT_TRANSLATE_NOOP("@default", "32x32 pixels PNG file icon"),
0043   QT_TRANSLATE_NOOP("@default", "Other file icon"),
0044   QT_TRANSLATE_NOOP("@default", "Cover (front)"),
0045   QT_TRANSLATE_NOOP("@default", "Cover (back)"),
0046   QT_TRANSLATE_NOOP("@default", "Leaflet page"),
0047   QT_TRANSLATE_NOOP("@default", "Media"),
0048   QT_TRANSLATE_NOOP("@default", "Lead artist/lead performer/soloist"),
0049   QT_TRANSLATE_NOOP("@default", "Artist/performer"),
0050   QT_TRANSLATE_NOOP("@default", "Conductor"),
0051   QT_TRANSLATE_NOOP("@default", "Band/Orchestra"),
0052   QT_TRANSLATE_NOOP("@default", "Composer"),
0053   QT_TRANSLATE_NOOP("@default", "Lyricist/text writer"),
0054   QT_TRANSLATE_NOOP("@default", "Recording Location"),
0055   QT_TRANSLATE_NOOP("@default", "During recording"),
0056   QT_TRANSLATE_NOOP("@default", "During performance"),
0057   QT_TRANSLATE_NOOP("@default", "Movie/video screen capture"),
0058   QT_TRANSLATE_NOOP("@default", "A bright coloured fish"),
0059   QT_TRANSLATE_NOOP("@default", "Illustration"),
0060   QT_TRANSLATE_NOOP("@default", "Band/artist logotype"),
0061   QT_TRANSLATE_NOOP("@default", "Publisher/Studio logotype"),
0062   nullptr
0063 };
0064 
0065 /**
0066  * List of untranslated picture type strings, NULL terminated.
0067  */
0068 const char* const pictureTypeStrings[] = {
0069   "Other",
0070   "Png Icon",
0071   "Icon",
0072   "Front",
0073   "Back",
0074   "Leaflet",
0075   "Media",
0076   "Lead Artist",
0077   "Artist",
0078   "Conductor",
0079   "Band",
0080   "Composer",
0081   "Lyricist",
0082   "Recording Location",
0083   "During Recording",
0084   "During Performance",
0085   "Video Capture",
0086   "Fish",
0087   "Illustration",
0088   "Band Logotype",
0089   "Publisher Logotype",
0090   nullptr
0091 };
0092 
0093 }
0094 
0095 
0096 /**
0097  * Construct properties from a new image.
0098  * @param data image data
0099  */
0100 PictureFrame::ImageProperties::ImageProperties(const QByteArray& data)
0101 {
0102   if (loadFromData(data)) {
0103     m_imageHash = qHash(data);
0104   } else {
0105     m_width = 0;
0106     m_height = 0;
0107     m_depth = 0;
0108     m_numColors = 0;
0109     m_imageHash = 0;
0110   }
0111 }
0112 
0113 bool PictureFrame::ImageProperties::loadFromData(const QByteArray& data)
0114 {
0115   if (const int len = data.size();
0116       len > 2 && data.at(0) == '\xff' && data.at(1) == '\xd8') {
0117     // JPEG
0118     int i = 2;
0119     while (i + 3 < len) {
0120       if (data.at(i)== '\xff') {
0121         quint8 marker = static_cast<quint8>(data.at(i + 1));
0122         quint16 sectionLen = qFromBigEndian<quint16>(
0123               reinterpret_cast<const uchar*>(data.constData()) + i + 2);
0124         if (marker == 0xda) {
0125           break; // start of scan
0126         }
0127         if ((marker == 0xc0 || marker == 0xc2) && i + 9 < len && sectionLen >= 8) {
0128           quint8 precision = static_cast<quint8>(data.at(i + 4));
0129           quint16 height = qFromBigEndian<quint16>(
0130             reinterpret_cast<const uchar*>(data.constData()) + i + 5);
0131           quint16 width = qFromBigEndian<quint16>(
0132             reinterpret_cast<const uchar*>(data.constData()) + i + 7);
0133           quint8 components = static_cast<quint8>(data.at(i + 9));
0134           m_width = width;
0135           m_height = height;
0136           m_depth = precision * components;
0137           m_numColors = 0;
0138           return true;
0139         }
0140         i += sectionLen + 2;
0141       } else {
0142         break;
0143       }
0144     }
0145   } else if (len > 8 && data.startsWith("\x89PNG\r\n\x1a\n")) {
0146     // PNG
0147     int i = 8;
0148     while (i + 8 < len) {
0149       quint32 chunkLen = qFromBigEndian<quint32>(
0150             reinterpret_cast<const uchar*>(data.constData()) + i);
0151       if (QByteArray chunkName = data.mid(i + 4, 4);
0152           chunkName == "IHDR" && i + 20 < len && chunkLen >= 13) {
0153         quint32 width = qFromBigEndian<quint32>(
0154               reinterpret_cast<const uchar*>(data.constData()) + i + 8);
0155         quint32 height = qFromBigEndian<quint32>(
0156               reinterpret_cast<const uchar*>(data.constData()) + i + 12);
0157         quint8 depth = static_cast<quint8>(data.at(i + 16));
0158         quint8 color = static_cast<quint8>(data.at(i + 17));
0159         // The next three bytes are compression, filter, interlace.
0160         m_width = static_cast<int>(width);
0161         m_height = static_cast<int>(height);
0162         m_numColors = 0;
0163         if (color == 0 || color == 3) {
0164           m_depth = depth;
0165         } else if (color == 2) {
0166           m_depth = depth * 3;
0167         } else if (color == 4 || color == 6) {
0168           m_depth = depth * 4;
0169         }
0170         if (!(color & 1)) {
0171           return true; // not indexed, no need to read the PLTE chunk
0172         }
0173       } else if (chunkName == "PLTE") {
0174         m_numColors = chunkLen / 3;
0175         return true;
0176       }
0177       i += chunkLen + 12;
0178     }
0179   }
0180   return false;
0181 }
0182 
0183 
0184 /**
0185  * Constructor.
0186  *
0187  * @param data        binary picture data
0188  * @param description description
0189  * @param pictureType picture type
0190  * @param mimeType    MIME type
0191  * @param enc         text encoding
0192  * @param imgFormat   image format
0193  */
0194 PictureFrame::PictureFrame(
0195   const QByteArray& data,
0196   const QString& description,
0197   PictureType pictureType,
0198   const QString& mimeType,
0199   TextEncoding enc,
0200   const QString& imgFormat)
0201 {
0202   setType(FT_Picture);
0203   setFields(*this, enc, imgFormat, mimeType, pictureType, description, data);
0204 }
0205 
0206 /**
0207  * Constructor.
0208  *
0209  * @param frame general frame
0210  */
0211 PictureFrame::PictureFrame(const Frame& frame)
0212 {
0213   *static_cast<Frame*>(this) = frame; // clazy:exclude=unneeded-cast
0214   setType(FT_Picture);
0215 
0216   // Make sure all fields are available in the correct order
0217   TextEncoding enc = TE_ISO8859_1;
0218   PictureType pictureType = PT_CoverFront;
0219   QString imgFormat(QLatin1String("JPG")), mimeType(QLatin1String("image/jpeg")), description;
0220   QByteArray data;
0221   getFields(*this, enc, imgFormat, mimeType, pictureType, description, data);
0222   setFields(*this, enc, imgFormat, mimeType, pictureType, description, data);
0223 }
0224 
0225 /**
0226  * Set all properties.
0227  *
0228  * @param frame       frame to set
0229  * @param enc         text encoding
0230  * @param imgFormat   image format
0231  * @param mimeType    MIME type
0232  * @param pictureType picture type
0233  * @param description description
0234  * @param data        binary picture data
0235  * @param imgProps    optional METADATA_BLOCK_PICTURE image properties
0236  */
0237 void PictureFrame::setFields(Frame& frame,
0238                              TextEncoding enc, const QString& imgFormat,
0239                              const QString& mimeType, PictureType pictureType,
0240                              const QString& description, const QByteArray& data,
0241                              const ImageProperties* imgProps)
0242 {
0243   Field field;
0244   FieldList& fields = frame.fieldList();
0245   fields.clear();
0246 
0247   field.m_id = ID_TextEnc;
0248   field.m_value = enc;
0249   fields.push_back(field);
0250 
0251   field.m_id = ID_ImageFormat;
0252   field.m_value = imgFormat;
0253   fields.push_back(field);
0254 
0255   field.m_id = ID_MimeType;
0256   field.m_value = mimeType;
0257   fields.push_back(field);
0258 
0259   field.m_id = ID_PictureType;
0260   field.m_value = pictureType;
0261   fields.push_back(field);
0262 
0263   field.m_id = ID_Description;
0264   field.m_value = description;
0265   fields.push_back(field);
0266 
0267   field.m_id = ID_Data;
0268   field.m_value = data;
0269   fields.push_back(field);
0270 
0271   if (imgProps && !imgProps->isNull()) {
0272     field.m_id = ID_ImageProperties;
0273     field.m_value.setValue(*imgProps);
0274     fields.push_back(field);
0275   }
0276 
0277   frame.setValue(description);
0278 }
0279 
0280 /**
0281  * Set all properties of a GEOB frame.
0282  *
0283  * @param frame       frame to set
0284  * @param enc         text encoding
0285  * @param mimeType    MIME type
0286  * @param fileName    file name
0287  * @param description description
0288  * @param data        binary data
0289  */
0290 void PictureFrame::setGeobFields(
0291     Frame& frame, TextEncoding enc, const QString& mimeType,
0292     const QString& fileName, const QString& description, const QByteArray& data)
0293 {
0294   Field field;
0295   FieldList& fields = frame.fieldList();
0296   fields.clear();
0297 
0298   field.m_id = ID_TextEnc;
0299   field.m_value = enc;
0300   fields.push_back(field);
0301 
0302   field.m_id = ID_MimeType;
0303   field.m_value = mimeType;
0304   fields.push_back(field);
0305 
0306   field.m_id = ID_Filename;
0307   field.m_value = fileName;
0308   fields.push_back(field);
0309 
0310   field.m_id = ID_Description;
0311   field.m_value = description;
0312   fields.push_back(field);
0313 
0314   field.m_id = ID_Data;
0315   field.m_value = data;
0316   fields.push_back(field);
0317 
0318   frame.setValue(description);
0319 }
0320 
0321 /**
0322  * Get all properties.
0323  * Unavailable fields are not set.
0324  *
0325  * @param frame       frame to get
0326  * @param enc         text encoding
0327  * @param imgFormat   image format
0328  * @param mimeType    MIME type
0329  * @param pictureType picture type
0330  * @param description description
0331  * @param data        binary picture data
0332  * @param imgProps    optional METADATA_BLOCK_PICTURE image properties
0333  */
0334 void PictureFrame::getFields(const Frame& frame,
0335                              TextEncoding& enc, QString& imgFormat,
0336                              QString& mimeType, PictureType& pictureType,
0337                              QString& description, QByteArray& data,
0338                              ImageProperties* imgProps)
0339 {
0340   for (auto it = frame.getFieldList().constBegin();
0341        it != frame.getFieldList().constEnd();
0342        ++it) {
0343     switch (it->m_id) {
0344       case ID_TextEnc:
0345         enc = static_cast<TextEncoding>(it->m_value.toInt());
0346         break;
0347       case ID_ImageFormat:
0348         imgFormat = it->m_value.toString();
0349         break;
0350       case ID_MimeType:
0351         mimeType = it->m_value.toString();
0352         break;
0353       case ID_PictureType:
0354         pictureType = static_cast<PictureType>(it->m_value.toInt());
0355         break;
0356       case ID_Description:
0357         description = it->m_value.toString();
0358         break;
0359       case ID_Data:
0360         data = it->m_value.toByteArray();
0361         break;
0362       case ID_ImageProperties:
0363         if (imgProps) {
0364           *imgProps = it->m_value.value<ImageProperties>();
0365         }
0366         break;
0367       default:
0368         qDebug("Unknown picture field ID");
0369     }
0370   }
0371 }
0372 
0373 /**
0374  * Check if all the fields of two picture frames are equal.
0375  * @param f1 first picture frame
0376  * @param f2 second picture frame
0377  * @return true if equal.
0378  */
0379 bool PictureFrame::areFieldsEqual(const Frame& f1, const Frame& f2)
0380 {
0381   TextEncoding enc1, enc2;
0382   QString imgFormat1, imgFormat2;
0383   QString mimeType1, mimeType2;
0384   PictureType pictureType1, pictureType2;
0385   QString description1, description2;
0386   QByteArray data1, data2;
0387   getFields(f1, enc1, imgFormat1, mimeType1, pictureType1, description1, data1);
0388   getFields(f2, enc2, imgFormat2, mimeType2, pictureType2, description2, data2);
0389   return data1 == data2 && description1 == description2 &&
0390          mimeType1 == mimeType2 && pictureType1 == pictureType2 &&
0391          imgFormat1 == imgFormat2 && enc1 == enc2;
0392 }
0393 
0394 /**
0395  * Set text encoding.
0396  *
0397  * @param frame frame to set
0398  * @param enc   text encoding
0399  *
0400  * @return true if field found and set.
0401  */
0402 bool PictureFrame::setTextEncoding(Frame& frame, TextEncoding enc)
0403 {
0404   return setField(frame, ID_TextEnc, enc);
0405 }
0406 
0407 /**
0408  * Get text encoding.
0409  *
0410  * @param frame frame to get
0411  * @param enc   the text encoding is returned here
0412  *
0413  * @return true if field found.
0414  */
0415 bool PictureFrame::getTextEncoding(const Frame& frame, TextEncoding& enc)
0416 {
0417   if (QVariant var(getField(frame, ID_TextEnc)); var.isValid()) {
0418     enc = static_cast<TextEncoding>(var.toInt());
0419     return true;
0420   }
0421   return false;
0422 }
0423 
0424 /**
0425  * Set image format.
0426  *
0427  * @param frame     frame to set
0428  * @param imgFormat image format
0429  *
0430  * @return true if field found and set.
0431  */
0432 bool PictureFrame::setImageFormat(Frame& frame, const QString& imgFormat)
0433 {
0434   return setField(frame, ID_ImageFormat, imgFormat);
0435 }
0436 
0437 /**
0438  * Get image format.
0439  *
0440  * @param frame     frame to get
0441  * @param imgFormat the image format is returned here
0442  *
0443  * @return true if field found.
0444  */
0445 bool PictureFrame::getImageFormat(const Frame& frame, QString& imgFormat)
0446 {
0447   if (QVariant var(getField(frame, ID_ImageFormat)); var.isValid()) {
0448     imgFormat = var.toString();
0449     return true;
0450   }
0451   return false;
0452 }
0453 
0454 /**
0455  * Set MIME type.
0456  *
0457  * @param frame   frame to set
0458  * @param mimeType MIME type
0459  *
0460  * @return true if field found and set.
0461  */
0462 bool PictureFrame::setMimeType(Frame& frame, const QString& mimeType)
0463 {
0464   return setField(frame, ID_MimeType, mimeType);
0465 }
0466 
0467 /**
0468  * Get MIME type.
0469  *
0470  * @param frame    frame to get
0471  * @param mimeType the MIME type is returned here
0472  *
0473  * @return true if field found.
0474  */
0475 bool PictureFrame::getMimeType(const Frame& frame, QString& mimeType)
0476 {
0477   if (QVariant var(getField(frame, ID_MimeType)); var.isValid()) {
0478     mimeType = var.toString();
0479     return true;
0480   }
0481   return false;
0482 }
0483 
0484 /**
0485  * Set picture type.
0486  *
0487  * @param frame       frame to set
0488  * @param pictureType picture type
0489  *
0490  * @return true if field found and set.
0491  */
0492 bool PictureFrame::setPictureType(Frame& frame, PictureType pictureType)
0493 {
0494   return setField(frame, ID_PictureType, pictureType);
0495 }
0496 
0497 /**
0498  * Get picture type.
0499  *
0500  * @param frame       frame to get
0501  * @param pictureType the picture type is returned here
0502  *
0503  * @return true if field found.
0504  */
0505 bool PictureFrame::getPictureType(const Frame& frame, PictureType& pictureType)
0506 {
0507   if (QVariant var(getField(frame, ID_PictureType)); var.isValid()) {
0508     pictureType = static_cast<PictureType>(var.toInt());
0509     return true;
0510   }
0511   return false;
0512 }
0513 
0514 /**
0515  * Set description.
0516  *
0517  * @param frame       frame to set
0518  * @param description description
0519  *
0520  * @return true if field found and set.
0521  */
0522 bool PictureFrame::setDescription(Frame& frame, const QString& description)
0523 {
0524   return setField(frame, ID_Description, description);
0525 }
0526 
0527 /**
0528  * Get description.
0529  *
0530  * @param frame       frame to get
0531  * @param description the description is returned here
0532  *
0533  * @return true if field found.
0534  */
0535 bool PictureFrame::getDescription(const Frame& frame, QString& description)
0536 {
0537   if (QVariant var(getField(frame, ID_Description)); var.isValid()) {
0538     description = var.toString();
0539     return true;
0540   }
0541   return false;
0542 }
0543 
0544 /**
0545  * Set binary data.
0546  *
0547  * @param frame frame to set
0548  * @param data  binary data
0549  *
0550  * @return true if field found and set.
0551  */
0552 bool PictureFrame::setData(Frame& frame, const QByteArray& data)
0553 {
0554   return setField(frame, ID_Data, data);
0555 }
0556 
0557 /**
0558  * Get binary data.
0559  *
0560  * @param frame frame to get
0561  * @param data  the binary data is returned here
0562  *
0563  * @return true if field found.
0564  */
0565 bool PictureFrame::getData(const Frame& frame, QByteArray& data)
0566 {
0567   if (QVariant var(getField(frame, ID_Data)); var.isValid()) {
0568     data = var.toByteArray();
0569     return true;
0570   }
0571   return false;
0572 }
0573 
0574 /**
0575  * Read binary data from file.
0576  *
0577  * @param frame frame to set
0578  * @param fileName name of data file
0579  *
0580  * @return true if file read, field found and set.
0581  */
0582 bool PictureFrame::setDataFromFile(Frame& frame, const QString& fileName)
0583 {
0584   bool result = false;
0585   if (!fileName.isEmpty()) {
0586     QFile file(fileName);
0587     if (file.open(QIODevice::ReadOnly)) {
0588       int size = file.size();
0589       auto data = new char[size];
0590       QDataStream stream(&file);
0591       stream.readRawData(data, size);
0592       auto ba = QByteArray(data, size);
0593       result = setData(frame, ba);
0594       delete [] data;
0595       file.close();
0596     }
0597   }
0598   return result;
0599 }
0600 
0601 /**
0602  * Save binary data to a file.
0603  *
0604  * @param frame    frame
0605  * @param fileName name of data file to save
0606  *
0607  * @return true if field found and saved.
0608  */
0609 bool PictureFrame::writeDataToFile(const Frame& frame, const QString& fileName)
0610 {
0611   QByteArray ba;
0612   if (getData(frame, ba)) {
0613     QFile file(fileName);
0614     if (file.open(QIODevice::WriteOnly)) {
0615       QDataStream stream(&file);
0616       stream.writeRawData(ba.data(), ba.size());
0617       file.close();
0618       return true;
0619     }
0620   }
0621   return false;
0622 }
0623 
0624 /**
0625  * Get the MIME type and image format from a file.
0626  *
0627  * @param fileName name of data file
0628  * @param imgFormat if not null, the ID3v2.2 PIC image format ("JGP" or "PNG")
0629  * is set here
0630  *
0631  * @return mime type of file, null if not recognized.
0632  */
0633 QString PictureFrame::getMimeTypeForFile(const QString& fileName,
0634                                          QString* imgFormat)
0635 {
0636   QMimeDatabase mimeDb;
0637   QString mimeType = mimeDb.mimeTypeForFile(fileName).name();
0638   if (imgFormat) {
0639     if (mimeType == QLatin1String("image/jpeg")) {
0640       *imgFormat = QLatin1String("JPG");
0641     } else if (mimeType == QLatin1String("image/png")) {
0642       *imgFormat = QLatin1String("PNG");
0643     }
0644   }
0645   return mimeType;
0646 }
0647 
0648 /**
0649  * Set the MIME type and image format from a file.
0650  *
0651  * @param frame frame to set
0652  * @param fileName name of data file
0653  *
0654  * @return true if field found and set.
0655  */
0656 bool PictureFrame::setMimeTypeFromFileName(Frame& frame, const QString& fileName)
0657 {
0658   QString imgFormat;
0659   if (QString mimeType = getMimeTypeForFile(fileName, &imgFormat);
0660       !mimeType.isEmpty()) {
0661     return setMimeType(frame, mimeType) && setImageFormat(frame, imgFormat);
0662   }
0663   return false;
0664 }
0665 
0666 namespace {
0667 
0668 /**
0669  * Get a 32-bit number from a byte array stored in big-endian order.
0670  *
0671  * @param data byte array
0672  * @param index index of first byte in data
0673  *
0674  * @return big endian 32-bit value.
0675  */
0676 unsigned long getBigEndianULongFromByteArray(const QByteArray& data, int index)
0677 {
0678   return
0679      (static_cast<unsigned char>(data[index + 3]) & 0xff)        |
0680     ((static_cast<unsigned char>(data[index + 2]) & 0xff) << 8)  |
0681     ((static_cast<unsigned char>(data[index + 1]) & 0xff) << 16) |
0682     ((static_cast<unsigned char>(data[index + 0]) & 0xff) << 24);
0683 }
0684 
0685 /**
0686  * Render a 32-bit number to a byte array in big-endian order.
0687  *
0688  * @param value 32-bit value
0689  * @param data  byte array
0690  * @param index index of first byte in data
0691  */
0692 void renderBigEndianULongToByteArray(unsigned long value,
0693                                      QByteArray& data, int index)
0694 {
0695   data[index + 3] = value & 0xff;
0696   value >>= 8;
0697   data[index + 2] = value & 0xff;
0698   value >>= 8;
0699   data[index + 1] = value & 0xff;
0700   value >>= 8;
0701   data[index + 0] = value & 0xff;
0702 }
0703 
0704 /**
0705  * Copy characters into a byte array.
0706  *
0707  * @param str   source string
0708  * @param data  destination byte array
0709  * @param index index of first byte in data
0710  * @param len   number of bytes to copy
0711  */
0712 void renderCharsToByteArray(const char* str, QByteArray& data,
0713                             int index, int len)
0714 {
0715   for (int i = 0; i < len; ++i) {
0716     data[index++] = *str++;
0717   }
0718 }
0719 
0720 }
0721 
0722 /**
0723  * Set picture from a base64 string.
0724  *
0725  * @param frame       frame to set
0726  * @param base64Value base64 string
0727  */
0728 void PictureFrame::setFieldsFromBase64(Frame& frame, const QString& base64Value)
0729 {
0730   QByteArray ba = QByteArray::fromBase64(base64Value.toLatin1());
0731   PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront;
0732   QString mimeType(QLatin1String("image/jpeg"));
0733   QString description(QLatin1String(""));
0734   ImageProperties imgProps;
0735   if (frame.getInternalName() == QLatin1String("METADATA_BLOCK_PICTURE")) {
0736     auto baSize = static_cast<unsigned long>(ba.size());
0737     if (baSize < 32) return;
0738     int index = 0;
0739     pictureType = static_cast<PictureFrame::PictureType>(
0740       getBigEndianULongFromByteArray(ba, index));
0741     index += 4;
0742     unsigned long mimeLen = getBigEndianULongFromByteArray(ba, index);
0743     index += 4;
0744     if (baSize < index + mimeLen + 24) return;
0745     mimeType = QString::fromLatin1(ba.data() + index, mimeLen);
0746     index += mimeLen;
0747     unsigned long descLen = getBigEndianULongFromByteArray(ba, index);
0748     index += 4;
0749     if (baSize < index + descLen + 20) return;
0750     description = QString::fromUtf8(ba.data() + index, descLen);
0751     index += descLen;
0752     uint width = getBigEndianULongFromByteArray(ba, index);
0753     index += 4;
0754     uint height = getBigEndianULongFromByteArray(ba, index);
0755     index += 4;
0756     uint depth = getBigEndianULongFromByteArray(ba, index);
0757     index += 4;
0758     uint numColors = getBigEndianULongFromByteArray(ba, index);
0759     index += 4;
0760     unsigned long picLen = getBigEndianULongFromByteArray(ba, index);
0761     index += 4;
0762     if (baSize < index + picLen) return;
0763     ba = ba.mid(index);
0764     imgProps = ImageProperties(width, height, depth, numColors, ba);
0765   }
0766   PictureFrame::setFields(
0767     frame, TE_UTF8, QLatin1String(""), mimeType,
0768     pictureType, description, ba, &imgProps);
0769 }
0770 
0771 /**
0772  * Get picture to a base64 string.
0773  *
0774  * @param frame       frame to get
0775  * @param base64Value base64 string to set
0776  */
0777 void PictureFrame::getFieldsToBase64(const Frame& frame, QString& base64Value)
0778 {
0779   TextEncoding enc;
0780   PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront;
0781   QString imgFormat, mimeType, description;
0782   QByteArray pic;
0783   ImageProperties imgProps;
0784   PictureFrame::getFields(frame, enc, imgFormat, mimeType,
0785                           pictureType, description, pic, &imgProps);
0786   if (frame.getInternalName() == QLatin1String("METADATA_BLOCK_PICTURE")) {
0787     QByteArray mimeStr = mimeType.toLatin1();
0788     QByteArray descStr = description.toUtf8();
0789     int mimeLen = mimeStr.length();
0790     int descLen = descStr.length();
0791     int picLen = pic.size();
0792     QByteArray ba(32 + mimeLen + descLen + picLen, '\0');
0793     int index = 0;
0794     renderBigEndianULongToByteArray(pictureType, ba, index);
0795     index += 4;
0796     renderBigEndianULongToByteArray(mimeLen, ba, index);
0797     index += 4;
0798     renderCharsToByteArray(mimeStr, ba, index, mimeLen);
0799     index += mimeLen;
0800     renderBigEndianULongToByteArray(descLen, ba, index);
0801     index += 4;
0802     renderCharsToByteArray(descStr, ba, index, descLen);
0803     index += descLen;
0804 
0805     if (!imgProps.isValidForImage(pic)) {
0806       imgProps = ImageProperties(pic);
0807     }
0808 
0809     renderBigEndianULongToByteArray(imgProps.width(), ba, index);
0810     index += 4;
0811     renderBigEndianULongToByteArray(imgProps.height(), ba, index);
0812     index += 4;
0813     renderBigEndianULongToByteArray(imgProps.depth(), ba, index);
0814     index += 4;
0815     renderBigEndianULongToByteArray(imgProps.numColors(), ba, index);
0816     index += 4;
0817 
0818     renderBigEndianULongToByteArray(picLen, ba, index);
0819     index += 4;
0820     renderCharsToByteArray(pic.data(), ba, index, picLen);
0821     pic = ba;
0822   }
0823   base64Value = QString::fromLatin1(pic.toBase64());
0824 }
0825 
0826 /**
0827  * Get a translated string for a picture type.
0828  *
0829  * @param type picture type
0830  *
0831  * @return picture type, null string if unknown.
0832  */
0833 QString PictureFrame::getPictureTypeName(PictureType type)
0834 {
0835   if (static_cast<int>(type) >= 0 &&
0836       static_cast<int>(type) < static_cast<int>(
0837         std::size(pictureTypeNames) - 1)) {
0838     return QCoreApplication::translate("@default", pictureTypeNames[type]);
0839   }
0840   return QString();
0841 }
0842 
0843 /**
0844  * List of picture type strings, NULL terminated.
0845  */
0846 const char* const* PictureFrame::getPictureTypeNames()
0847 {
0848   return pictureTypeNames;
0849 }
0850 
0851 /**
0852  * Get an untranslated string for a picture type.
0853  *
0854  * @param type picture type
0855  *
0856  * @return picture type, 0 if unknown.
0857  */
0858 const char* PictureFrame::getPictureTypeString(PictureType type)
0859 {
0860   return static_cast<int>(type) >= 0 &&
0861       static_cast<int>(type) < static_cast<int>(
0862         std::size(pictureTypeStrings) - 1)
0863       ? pictureTypeStrings[type] : nullptr;
0864 }
0865 
0866 /**
0867  * List of untranslated picture type strings, NULL terminated.
0868  */
0869 const char* const* PictureFrame::getPictureTypeStrings()
0870 {
0871   return pictureTypeStrings;
0872 }
0873 
0874 /**
0875  * Get picture type from an untranslated string.
0876  *
0877  * @param str untranslated picture type string
0878  *
0879  * @return picture type, PT_Other if unknown.
0880  */
0881 PictureFrame::PictureType PictureFrame::getPictureTypeFromString(const char* str)
0882 {
0883   for (unsigned int i = 0;
0884        i < std::size(pictureTypeStrings) - 1;
0885        ++i) {
0886     if (qstricmp(str, pictureTypeStrings[i]) == 0) {
0887       return static_cast<PictureType>(i);
0888     }
0889   }
0890   return PT_Other;
0891 }