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 }