File indexing completed on 2025-02-02 04:11:04

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "bitmap.hpp"
0008 #include <QPainter>
0009 #include <QImageWriter>
0010 #include <QImageReader>
0011 #include <QFileInfo>
0012 #include <QBuffer>
0013 #include <QUrl>
0014 
0015 #include "model/document.hpp"
0016 #include "model/assets/assets.hpp"
0017 #include "command/object_list_commands.hpp"
0018 
0019 GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Bitmap)
0020 
0021 void glaxnimate::model::Bitmap::paint(QPainter* painter) const
0022 {
0023     painter->drawPixmap(0, 0, image);
0024 }
0025 
0026 void glaxnimate::model::Bitmap::refresh(bool rebuild_embedded)
0027 {
0028     QImageReader reader;
0029     QImage qimage;
0030 
0031     bool load_data = true;
0032 
0033     if ( rebuild_embedded || data.get().isEmpty() )
0034     {
0035         if ( !filename.get().isEmpty() )
0036         {
0037             QFileInfo finfo = file_info();
0038             if ( !finfo.isFile() )
0039                 return;
0040             reader.setFileName(finfo.absoluteFilePath());
0041             format.set(reader.format());
0042             qimage = reader.read();
0043             if ( rebuild_embedded && embedded() )
0044                 data.set(build_embedded(qimage));
0045             load_data = false;
0046         }
0047         else if ( !url.get().isEmpty() )
0048         {
0049             document()->assets()->network_downloader.get(QUrl(url.get()), [this, rebuild_embedded](QByteArray response){
0050                 QImageReader reader;
0051                 QImage qimage;
0052                 QBuffer buf(&response);
0053                 buf.open(QIODevice::ReadOnly);
0054                 reader.setDevice(&buf);
0055                 format.set(reader.format());
0056                 qimage = reader.read();
0057                 if ( rebuild_embedded && embedded() )
0058                     data.set(build_embedded(qimage));
0059 
0060                 image = QPixmap::fromImage(qimage);
0061                 width.set(image.width());
0062                 height.set(image.height());
0063 
0064                 document()->graphics_invalidated();
0065                 Q_EMIT loaded();
0066             }, this);
0067             return;
0068         }
0069     }
0070 
0071     if ( load_data )
0072     {
0073         QBuffer buf(const_cast<QByteArray*>(&data.get()));
0074         buf.open(QIODevice::ReadOnly);
0075         reader.setDevice(&buf);
0076         format.set(reader.format());
0077         qimage = reader.read();
0078     }
0079 
0080     image = QPixmap::fromImage(qimage);
0081     width.set(image.width());
0082     height.set(image.height());
0083 
0084     Q_EMIT loaded();
0085 }
0086 
0087 QByteArray glaxnimate::model::Bitmap::build_embedded(const QImage& img) const
0088 {
0089     QByteArray new_data;
0090     QBuffer buf(&new_data);
0091     buf.open(QIODevice::WriteOnly);
0092     QImageWriter writer(&buf, format.get().toLatin1());
0093     writer.write(img);
0094     return new_data;
0095 }
0096 
0097 bool glaxnimate::model::Bitmap::embedded() const
0098 {
0099     return !data.get().isEmpty();
0100 }
0101 
0102 void glaxnimate::model::Bitmap::embed(bool embedded)
0103 {
0104     if ( embedded == this->embedded() )
0105         return;
0106 
0107     if ( !embedded )
0108         data.set_undoable({});
0109     else
0110         data.set_undoable(build_embedded(image.toImage()));
0111 }
0112 
0113 void glaxnimate::model::Bitmap::on_refresh()
0114 {
0115     refresh(false);
0116 }
0117 
0118 QIcon glaxnimate::model::Bitmap::instance_icon() const
0119 {
0120     return image;
0121 }
0122 
0123 bool glaxnimate::model::Bitmap::from_url(const QUrl& url)
0124 {
0125     if ( url.scheme().isEmpty() || url.scheme() == "file" )
0126         return from_file(url.path());
0127 
0128     if ( url.scheme() == "data" )
0129         return from_base64(url.path());
0130 
0131     this->url.set(url.toString());
0132 
0133     return true;
0134 }
0135 
0136 bool glaxnimate::model::Bitmap::from_file(const QString& file)
0137 {
0138     filename.set(file);
0139     return !image.isNull();
0140 }
0141 
0142 bool glaxnimate::model::Bitmap::from_base64(const QString& data)
0143 {
0144     auto chunks = data.split(',');
0145     if ( chunks.size() != 2 )
0146         return false;
0147     auto mime_settings = chunks[0].split(';');
0148     if ( mime_settings.size() != 2 || mime_settings[1] != "base64" )
0149         return false;
0150 
0151     auto formats = QImageReader::imageFormatsForMimeType(mime_settings[0].toLatin1());
0152     if ( formats.empty() )
0153         return false;
0154 
0155     auto decoded = QByteArray::fromBase64(chunks[1].toLatin1());
0156     format.set(formats[0]);
0157     this->data.set(decoded);
0158     return !image.isNull();
0159 }
0160 
0161 
0162 bool glaxnimate::model::Bitmap::from_raw_data(const QByteArray& data)
0163 {
0164     QBuffer buf(const_cast<QByteArray*>(&data));
0165     buf.open(QBuffer::ReadOnly);
0166     auto format = QImageReader::imageFormat(&buf);
0167     if ( format.isEmpty() )
0168         return false;
0169 
0170     this->format.set(format);
0171     this->data.set(data);
0172     return !image.isNull();
0173 
0174 }
0175 
0176 QUrl glaxnimate::model::Bitmap::to_url() const
0177 {
0178     if ( !embedded() )
0179     {
0180         return QUrl::fromLocalFile(file_info().absoluteFilePath());
0181     }
0182 
0183     QByteArray fmt = format.get().toLatin1();
0184     QByteArray mime_type;
0185     for ( const auto& mime : QImageWriter::supportedMimeTypes() )
0186         if ( QImageWriter::imageFormatsForMimeType(mime).contains(fmt) )
0187         {
0188             mime_type = mime;
0189             break;
0190         }
0191 
0192     if ( mime_type.isEmpty() )
0193         return {};
0194 
0195     QString data_url = "data:";
0196     data_url += mime_type;
0197     data_url += ";base64,";
0198     data_url += data.get().toBase64();
0199     return QUrl(data_url);
0200 }
0201 
0202 QString glaxnimate::model::Bitmap::object_name() const
0203 {
0204     if ( embedded() )
0205         return i18n("Embedded image");
0206     return QFileInfo(filename.get()).fileName();
0207 }
0208 
0209 
0210 QFileInfo glaxnimate::model::Bitmap::file_info() const
0211 {
0212     return QFileInfo(document()->io_options().path, filename.get());
0213 }
0214 
0215 
0216 bool glaxnimate::model::Bitmap::remove_if_unused(bool)
0217 {
0218     if ( users().empty() )
0219     {
0220         document()->push_command(new command::RemoveObject(
0221             this,
0222             &document()->assets()->images->values
0223         ));
0224         return true;
0225     }
0226     return false;
0227 }
0228 
0229 void glaxnimate::model::Bitmap::set_pixmap(const QImage& pix, const QString& format)
0230 {
0231     this->format.set(format);
0232     data.set(build_embedded(pix));
0233 }
0234 
0235 QByteArray glaxnimate::model::Bitmap::image_data() const
0236 {
0237     if ( !data.get().isEmpty() )
0238         return data.get();
0239 
0240     if ( image.isNull() )
0241         return {};
0242 
0243     return build_embedded(image.toImage());
0244 }
0245 
0246 QSize glaxnimate::model::Bitmap::size() const
0247 {
0248     return {width.get(), height.get()};
0249 }