File indexing completed on 2024-12-15 04:01:21

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 "trace_wrapper.hpp"
0008 #include "utils/quantize.hpp"
0009 #include "model/document.hpp"
0010 #include "model/shapes/stroke.hpp"
0011 #include "model/shapes/fill.hpp"
0012 #include "model/shapes/path.hpp"
0013 #include "model/shapes/rect.hpp"
0014 #include "command/object_list_commands.hpp"
0015 
0016 class glaxnimate::utils::trace::TraceWrapper::Private
0017 {
0018 public:
0019     QImage source_image;
0020     utils::trace::TraceOptions options;
0021     std::vector<QRgb> eem_colors;
0022     model::Image* image = nullptr;
0023     model::Document* document;
0024     model::Composition* comp;
0025     QString name;
0026 
0027     void set_image(const QImage& image)
0028     {
0029         if ( image.format() != QImage::Format_RGBA8888 )
0030             source_image = image.convertToFormat(QImage::Format_RGBA8888);
0031         else
0032             source_image = image;
0033     }
0034 
0035     void result_to_shapes(model::ShapeListProperty& prop, const TraceResult& result, qreal stroke_width)
0036     {
0037         auto fill = std::make_unique<model::Fill>(document);
0038         fill->color.set(result.color);
0039         prop.insert(std::move(fill));
0040 
0041         if ( stroke_width > 0 )
0042         {
0043             auto stroke = std::make_unique<model::Stroke>(document);
0044             stroke->color.set(result.color);
0045             stroke->width.set(stroke_width);
0046             prop.insert(std::move(stroke));
0047         }
0048 
0049         for ( const auto& bez : result.bezier.beziers() )
0050         {
0051             auto path = std::make_unique<model::Path>(document);
0052             path->shape.set(bez);
0053             prop.insert(std::move(path));
0054         }
0055 
0056         for ( const auto& rect : result.rects )
0057         {
0058             auto shape = std::make_unique<model::Rect>(document);
0059             shape->position.set(rect.center());
0060             shape->size.set(rect.size());
0061             prop.insert(std::move(shape));
0062         }
0063     }
0064 
0065 };
0066 
0067 glaxnimate::utils::trace::TraceWrapper::TraceWrapper(model::Image* image)
0068     : TraceWrapper(image->owner_composition(), image->image->pixmap().toImage(), image->object_name())
0069 {
0070     d->image = image;
0071 
0072 }
0073 
0074 glaxnimate::utils::trace::TraceWrapper::TraceWrapper(model::Composition* comp, const QImage& image, const QString& name)
0075     : d(std::make_unique<Private>())
0076 {
0077     d->comp = comp;
0078     d->document = comp->document();
0079     d->name = name;
0080     d->set_image(image);
0081 }
0082 
0083 
0084 glaxnimate::utils::trace::TraceWrapper::~TraceWrapper() = default;
0085 
0086 QSize glaxnimate::utils::trace::TraceWrapper::size() const
0087 {
0088     return d->source_image.size();
0089 }
0090 
0091 glaxnimate::utils::trace::TraceOptions & glaxnimate::utils::trace::TraceWrapper::options()
0092 {
0093     return d->options;
0094 }
0095 
0096 void glaxnimate::utils::trace::TraceWrapper::trace_mono(
0097     const QColor& color, bool inverted, int alpha_threshold, std::vector<TraceResult>& result)
0098 {
0099     result.emplace_back();
0100     Q_EMIT progress_max_changed(100);
0101     result.back().color = color;
0102     utils::trace::Tracer tracer(d->source_image, d->options);
0103     tracer.set_target_alpha(alpha_threshold, inverted);
0104     connect(&tracer, &utils::trace::Tracer::progress, this, [this](qreal value){ Q_EMIT progress_changed(value); });
0105     tracer.set_progress_range(0, 100);
0106     tracer.trace(result.back().bezier);
0107 }
0108 
0109 void glaxnimate::utils::trace::TraceWrapper::trace_exact(
0110     const std::vector<QRgb>& colors, int tolerance, std::vector<TraceResult>& result
0111 )
0112 {
0113     result.reserve(result.size() + colors.size());
0114     Q_EMIT progress_max_changed(100 * colors.size());
0115     int progress_index = 0;
0116     for ( QColor color : colors )
0117     {
0118         result.emplace_back();
0119         result.back().color = color;
0120         utils::trace::Tracer tracer(d->source_image, d->options);
0121         tracer.set_target_color(color, tolerance * tolerance);
0122         tracer.set_progress_range(100 * progress_index, 100 * (progress_index+1));
0123         tracer.trace(result.back().bezier);
0124         ++progress_index;
0125     }
0126 }
0127 
0128 void glaxnimate::utils::trace::TraceWrapper::trace_closest(
0129     const std::vector<QRgb>& colors, std::vector<TraceResult>& result)
0130 {
0131     Q_EMIT progress_max_changed(100 * colors.size());
0132     QImage converted = utils::quantize::quantize(d->source_image, colors);
0133     utils::trace::Tracer tracer(converted, d->options);
0134     result.reserve(result.size() + colors.size());
0135 
0136     for ( int i = 0; i < int(colors.size()); i++ )
0137     {
0138         tracer.set_target_index(i);
0139         tracer.set_progress_range(100 * i, 100 * (i+1));
0140         result.emplace_back();
0141         result.back().color = colors[i];
0142         tracer.trace(result.back().bezier);
0143     }
0144 }
0145 
0146 void glaxnimate::utils::trace::TraceWrapper::trace_pixel(std::vector<TraceResult>& result)
0147 {
0148     auto pixdata = utils::trace::trace_pixels(d->source_image);
0149     result.reserve(pixdata.size());
0150     for ( const auto& p : pixdata )
0151         result.push_back({p.first, {}, p.second});
0152 }
0153 
0154 glaxnimate::model::Group* glaxnimate::utils::trace::TraceWrapper::apply(
0155     std::vector<TraceResult>& trace, qreal stroke_width
0156 )
0157 {
0158     auto layer = std::make_unique<model::Group>(d->document);
0159     auto created = layer.get();
0160     layer->name.set(i18n("Traced %1", d->name));
0161 
0162     if ( trace.size() == 1 )
0163     {
0164         d->result_to_shapes(layer->shapes, trace[0], stroke_width);
0165     }
0166     else
0167     {
0168         for ( const auto& result : trace )
0169         {
0170             auto group = std::make_unique<model::Group>(d->document);
0171             group->name.set(result.color.name());
0172             group->group_color.set(result.color);
0173             d->result_to_shapes(group->shapes, result, stroke_width);
0174             layer->shapes.insert(std::move(group));
0175         }
0176     }
0177 
0178     if ( d->image )
0179     {
0180         layer->transform->copy(d->image->transform.get());
0181         d->document->push_command(new command::AddObject<model::ShapeElement>(
0182             d->image->owner(), std::move(layer), d->image->position()+1
0183         ));
0184     }
0185     else
0186     {
0187         d->document->push_command(new command::AddObject<model::ShapeElement>(
0188             &d->comp->shapes, std::move(layer)
0189         ));
0190     }
0191 
0192 //     created->recursive_rename();
0193     return created;
0194 }
0195 
0196 const QImage & glaxnimate::utils::trace::TraceWrapper::image() const
0197 {
0198     return d->source_image;
0199 }
0200 
0201 const std::vector<QRgb>& glaxnimate::utils::trace::TraceWrapper::eem_colors() const
0202 {
0203     if ( d->eem_colors.empty() )
0204         d->eem_colors = utils::quantize::edge_exclusion_modes(d->source_image, 256);
0205     return d->eem_colors;
0206 }
0207 
0208 glaxnimate::utils::trace::TraceWrapper::Preset
0209     glaxnimate::utils::trace::TraceWrapper::preset_suggestion() const
0210 {
0211     int w = d->source_image.width();
0212     int h = d->source_image.height();
0213     if ( w > 1024 || h > 1024 )
0214         return Preset::ComplexPreset;
0215 
0216     auto color_count = utils::quantize::color_frequencies(d->source_image).size();
0217     if ( w < 128 && h < 128 && color_count < 128 )
0218         return Preset::PixelPreset;
0219 
0220     color_count = eem_colors().size();
0221 
0222     if ( w < 1024 && h < 1024 && color_count < 32 )
0223         return Preset::FlatPreset;
0224     else
0225         return Preset::ComplexPreset;
0226 }
0227 
0228 
0229 
0230 void glaxnimate::utils::trace::TraceWrapper::trace_preset(
0231     Preset preset, int complex_posterization, std::vector<QRgb> &colors, std::vector<TraceResult>& result
0232 )
0233 {
0234     d->options.set_min_area(16);
0235     d->options.set_smoothness(0.75);
0236     switch ( preset )
0237     {
0238         case utils::trace::TraceWrapper::ComplexPreset:
0239             colors = utils::quantize::octree(d->source_image, complex_posterization);
0240             trace_closest(colors, result);
0241             break;
0242         case utils::trace::TraceWrapper::FlatPreset:
0243             colors = eem_colors();
0244             trace_closest(colors, result);
0245             break;
0246         case utils::trace::TraceWrapper::PixelPreset:
0247             trace_pixel(result);
0248             break;
0249     }
0250 }