Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_spriter_export.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QApplication>
10#include <QCheckBox>
11#include <QDomDocument>
12#include <QFileInfo>
13#include <QSlider>
14#include <QDir>
15
16#include <kpluginfactory.h>
17
20
23
24#include <KisDocument.h>
25#include <kis_group_layer.h>
26#include <kis_image.h>
27#include <kis_layer.h>
28#include <kis_node.h>
29#include <kis_painter.h>
30#include <kis_paint_layer.h>
31#include <kis_shape_layer.h>
32#include <kis_file_layer.h>
33#include <kis_clone_layer.h>
34#include <kis_generator_layer.h>
36#include <KisPart.h>
37#include <kis_types.h>
38#include <kis_png_converter.h>
39#include <kis_global.h> // for KisDegreesToRadians
40#include <kis_fast_math.h>
41#include <math.h>
42#include <kis_dom_utils.h>
43#include <kis_layer_utils.h>
44
45K_PLUGIN_FACTORY_WITH_JSON(KisSpriterExportFactory, "krita_spriter_export.json", registerPlugin<KisSpriterExport>();)
46
47KisSpriterExport::KisSpriterExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
48{
49}
50
54
56{
57 QFileInfo fi(fileName);
58
59 QDir d = fi.absoluteDir();
60 d.mkpath(d.path());
61 QRect rc = m_image->bounds().intersected(dev->exactBounds());
62
64 dev = new KisPaintDevice(*dev.data());
66 }
67
68 KisPNGOptions options;
69 options.forceSRGB = true;
70
73
74 KisPNGConverter converter(0);
75 KisImportExportErrorCode res = converter.buildFile(fileName, rc, m_image->xRes(), m_image->yRes(), dev, beginIt, endIt, options, 0);
76
77 return res;
78}
79
80KisImportExportErrorCode KisSpriterExport::parseFolder(KisGroupLayerSP parentGroup, const QString &folderName, const QString &basePath, int *folderId)
81{
82// qDebug() << "parseFolder: parent" << parentGroup->name()
83// << "folderName" << folderName
84// << "basepath" << basePath;
85
86 int currentFolder=0;
87 if(folderId == 0)
88 {
89 folderId = &currentFolder;
90 }
91 QString pathName;
92 if (!folderName.isEmpty()) {
93 pathName = folderName + "/";
94 }
95
96
97 KisNodeSP child = parentGroup->lastChild();
98 while (child) {
99 if (child->visible() && child->inherits("KisGroupLayer")) {
100 KisImportExportErrorCode res = parseFolder(qobject_cast<KisGroupLayer*>(child.data()), child->name().split(" ").first(), basePath + "/" + pathName, folderId);
101 if (!res.isOk()) {
102 return res;
103 }
104 }
105 child = child->prevSibling();
106 }
107
108 Folder folder;
109 folder.id = *folderId;
110 folder.name = folderName;
111 folder.groupName = parentGroup->name();
112
113 int fileId = 0;
114 child = parentGroup->lastChild();
115
116 while (child) {
117 if (child->visible() && !child->inherits("KisGroupLayer") && !child->inherits("KisMask")) {
118 QRectF rc = m_image->bounds().intersected(child->exactBounds());
119 QString layerBaseName = child->name().split(" ").first();
120 SpriterFile file;
121 file.id = fileId++;
122 file.pathName = pathName;
123 file.baseName = layerBaseName;
124 file.layerName = child->name();
125 file.name = folderName + "/" + layerBaseName + ".png";
126
127 qreal xmin = rc.left();
128 qreal ymin = rc.top();
129 qreal xmax = rc.right();
130 qreal ymax = rc.bottom();
131
132 file.width = xmax - xmin;
133 file.height = ymax - ymin;
134 file.x = xmin;
135 file.y = ymin;
136 //qDebug() << "Created file" << file.id << file.name << file.pathName << file.baseName << file.width << file.height << file.layerName;
137 KisImportExportErrorCode result = savePaintDevice(child->projection(), basePath + file.name);
138 if (result.isOk()) {
139 folder.files.append(file);
140 } else {
141 return result;
142 }
143 }
144
145 child = child->prevSibling();
146 }
147
148 if (folder.files.size() > 0) {
149 //qDebug() << "Adding folder" << folder.id << folder.name << folder.groupName << folder.files.length();
150 m_folders.append(folder);
151 (*folderId)++;
152 }
153
155}
156
158{
159 static int boneId = 0;
160 QString groupBaseName = groupLayer->name().split(" ").first();
161 Bone *bone = new Bone;
162 bone->id = boneId++;
163 bone->parentBone = parent;
164 bone->name = groupBaseName;
165
166 if (m_boneLayer) {
167 QRectF rc = m_image->bounds().intersected(m_boneLayer->exactBounds());
168
169 qreal xmin = rc.left();
170 qreal ymin = rc.top();
171 qreal xmax = rc.right();
172 qreal ymax = rc.bottom();
173
174 bone->x = (xmin + xmax) / 2;
175 bone->y = -(ymin + ymax) / 2;
176 bone->width = xmax - xmin;
177 bone->height = ymax - ymin;
178 }
179 else {
180 bone->x = 0.0;
181 bone->y = 0.0;
182 bone->width = 0.0;
183 bone->height = 0.0;
184 }
185
186 if (parent) {
187 bone->localX = bone->x - parent->x;
188 bone->localY = bone->y - parent->y;
189 }
190 else {
191 bone->localX = bone->x;
192 bone->localY = bone->y;
193 }
194
195 bone->localAngle = 0.0;
196 bone->localScaleX = 1.0;
197 bone->localScaleY = 1.0;
198
199 KisNodeSP child = groupLayer->lastChild();
200 while (child) {
201 if (child->visible() && child->inherits("KisGroupLayer")) {
202 bone->bones.append(parseBone(bone, qobject_cast<KisGroupLayer*>(child.data())));
203 }
204 child = child->prevSibling();
205 }
206
207 //qDebug() << "Created bone" << bone->id << "with" << bone->bones.size() << "bones";
208 return bone;
209}
210
211void copyBone(Bone *startBone)
212{
213 startBone->fixLocalX = startBone->localX;
214 startBone->fixLocalY = startBone->localY;
215 startBone->fixLocalAngle = startBone->localAngle;
216 startBone->fixLocalScaleX= startBone->localScaleX;
217 startBone->fixLocalScaleY= startBone->localScaleY;
218
219 Q_FOREACH(Bone *child, startBone->bones) {
220 copyBone(child);
221 }
222}
223
225{
226 qreal boneLocalAngle = 0;
227 qreal boneLocalScaleX = 1;
228
229 if (bone->bones.length() >= 1) {
230 // if a bone has one or more children, point at first child
231 Bone *childBone = bone->bones[0];
232 qreal dx = childBone->x - bone->x;
233 qreal dy = childBone->y - bone->y;
234 if (qAbs(dx) > 0 || qAbs(dy) > 0) {
235 boneLocalAngle = KisFastMath::atan2(dy, dx);
236 boneLocalScaleX = sqrt(dx * dx + dy * dy) / 200;
237 }
238 }
239 else if (bone->parentBone) {
240 // else, if bone has parent, point away from parent
241 qreal dx = bone->x - bone->parentBone->x;
242 qreal dy = bone->y - bone->parentBone->y;
243 if (qAbs(dx) > 0 || qAbs(dy) > 0) {
244 boneLocalAngle = KisFastMath::atan2(dy, dx);
245 boneLocalScaleX = sqrt(dx * dx + dy * dy) / 200;
246 }
247 }
248 // adjust bone angle
249 bone->fixLocalAngle += boneLocalAngle;
250 bone->fixLocalScaleX *= boneLocalScaleX;
251
252 // rotate all the child bones back to world position
253 for (int i = 0; i < bone->bones.length(); ++i) {
254 Bone *childBone = bone->bones[i];
255
256 qreal tx = childBone->fixLocalX;
257 qreal ty = childBone->fixLocalY;
258
259 childBone->fixLocalX = tx * cos(-boneLocalAngle) - ty * sin(-boneLocalAngle);
260 childBone->fixLocalY = tx * sin(-boneLocalAngle) + ty * cos(-boneLocalAngle);
261
262 childBone->fixLocalX /= boneLocalScaleX;
263 childBone->fixLocalAngle -= boneLocalAngle;
264 childBone->fixLocalScaleX /= boneLocalScaleX;
265 }
266
267 // rotate all the child objects back to world position
268 for (int i = 0; i < m_objects.length(); ++i) {
269 if (m_objects[i].bone == bone) {
270 m_objects[i].fixLocalAngle -= boneLocalAngle;
271 m_objects[i].fixLocalScaleX /= boneLocalScaleX;
272 }
273 }
274
275 // process all child bones
276 for (int i = 0; i < bone->bones.length(); ++i) {
277 fixBone(bone->bones[i]);
278 }
279}
280
281void KisSpriterExport::writeBoneRef(const Bone *bone, QDomElement &key, QDomDocument &scml)
282{
283 if (!bone) return;
284 QDomElement boneRef = scml.createElement("bone_ref");
285 key.appendChild(boneRef);
286 boneRef.setAttribute("id", bone->id);
287 if (bone->parentBone) {
288 boneRef.setAttribute("parent", bone->parentBone->id);
289 }
290 boneRef.setAttribute("timeline", m_timelineid++);
291 boneRef.setAttribute("key", "0");
292 Q_FOREACH(const Bone *childBone, bone->bones) {
293 writeBoneRef(childBone, key, scml);
294 }
295}
296
297void KisSpriterExport::writeBone(const Bone *bone, QDomElement &animation, QDomDocument &scml)
298{
299 if (!bone) return;
300 QDomElement timeline = scml.createElement("timeline");
301 animation.appendChild(timeline);
302 timeline.setAttribute("id", m_timelineid);
303 timeline.setAttribute("name", bone->name);
304 timeline.setAttribute("object_type", "bone");
305
306 QDomElement key = scml.createElement("key");
307 timeline.appendChild(key);
308 key.setAttribute("id", "0");
309 key.setAttribute("spin", 0);
310
311 QDomElement boneEl = scml.createElement("bone");
312 key.appendChild(boneEl);
313 boneEl.setAttribute("x", QString::number(bone->fixLocalX, 'f', 2));
314 boneEl.setAttribute("y", QString::number(bone->fixLocalY, 'f', 2));
315 boneEl.setAttribute("angle", QString::number(bone->fixLocalAngle, 'f', 2));
316 boneEl.setAttribute("scale_x", QString::number(bone->fixLocalScaleX, 'f', 2));
317 boneEl.setAttribute("scale_y", QString::number(bone->fixLocalScaleY, 'f', 2));
318
319 m_timelineid++;
320
321 Q_FOREACH(const Bone *childBone, bone->bones) {
322 writeBone(childBone, animation, scml);
323 }
324}
325
326void KisSpriterExport::fillScml(QDomDocument &scml, const QString &entityName)
327{
328 //qDebug() << "Creating scml" << entityName;
329
330 QDomElement root = scml.createElement("spriter_data");
331 scml.appendChild(root);
332 root.setAttribute("scml_version", 1);
333 root.setAttribute("generator", "krita");
334 root.setAttribute("generator_version", qApp->applicationVersion());
335
336 Q_FOREACH(const Folder &folder, m_folders) {
337 QDomElement fe = scml.createElement("folder");
338 root.appendChild(fe);
339 fe.setAttribute("id", folder.id);
340 fe.setAttribute("name", folder.name);
341 Q_FOREACH(const SpriterFile &file, folder.files) {
342 QDomElement fileElement = scml.createElement("file");
343 fe.appendChild(fileElement);
344 fileElement.setAttribute("id", file.id);
345 fileElement.setAttribute("name", file.name);
346 fileElement.setAttribute("width", QString::number(file.width, 'f', 2));
347 fileElement.setAttribute("height", QString::number(file.height, 'f', 2));
348 // qreal pivotX=0;
349 // qreal pivotY=1;
350 // Q_FOREACH(const SpriterObject &object, m_objects) {
351 // if(file.id == object.fileId)
352 // {
353 // pivotX = (0.0 -(object.fixLocalX / file.width));
354 // pivotY = (1.0 -(object.fixLocalY / file.height));
355 // break;
356 // }
357 // }
358 // fileElement.setAttribute("pivot_x", QString::number(pivotX, 'f', 2));
359 // fileElement.setAttribute("pivot_y", QString::number(pivotY, 'f', 2));
360 }
361 }
362
363 // entity
364 QDomElement entity = scml.createElement("entity");
365 root.appendChild(entity);
366 entity.setAttribute("id", "0");
367 entity.setAttribute("name", entityName);
368
369 // entity/animation
370 QDomElement animation = scml.createElement("animation");
371 entity.appendChild(animation);
372 animation.setAttribute("id", "0");
373 animation.setAttribute("name", "default");
374 animation.setAttribute("length", "1000");
375 animation.setAttribute("looping", "false");
376
377 // entity/animation/mainline
378 QDomElement mainline = scml.createElement("mainline");
379 animation.appendChild(mainline);
380
381 QDomElement key = scml.createElement("key");
382 mainline.appendChild(key);
383 key.setAttribute("id", "0");
384
385 m_timelineid = 0;
386 writeBoneRef(m_rootBone, key, scml);
387
388 Q_FOREACH(const SpriterObject &object, m_objects) {
389 QDomElement oe = scml.createElement("object_ref");
390 key.appendChild(oe);
391 oe.setAttribute("id", object.id);
392 if (object.bone) {
393 oe.setAttribute("parent", object.bone->id);
394 }
395 oe.setAttribute("timeline", m_timelineid++);
396 oe.setAttribute("key", "0");
397 oe.setAttribute("z_index", object.id);
398 }
399
400 // entity/animation/timeline
401 m_timelineid = 0;
402 if (m_rootBone) {
403 writeBone(m_rootBone, animation, scml);
404 }
405
406 Q_FOREACH(const SpriterObject &object, m_objects) {
407 Folder folder;
408 Q_FOREACH(const Folder & f, m_folders) {
409 if (f.id == object.folderId) {
410 folder = f;
411 break;
412 }
413 }
414 SpriterFile file;
415 file.id = -1;
416 Q_FOREACH(const SpriterFile &f, folder.files) {
417 if (f.id == object.fileId) {
418 file = f;
419 break;
420 }
421 }
422 Q_ASSERT(file.id >= 0);
423
424 QString objectName = "object-" + file.baseName;
425
426 QDomElement timeline = scml.createElement("timeline");
427 animation.appendChild(timeline);
428 timeline.setAttribute("id", m_timelineid++);
429 timeline.setAttribute("name", objectName);
430
431 QDomElement key = scml.createElement("key");
432 timeline.appendChild(key);
433 key.setAttribute("id", "0");
434 key.setAttribute("spin", "0");
435
436 QDomElement objectEl = scml.createElement("object");
437 key.appendChild(objectEl);
438 objectEl.setAttribute("folder", object.folderId);
439 objectEl.setAttribute("file", object.fileId);
440 objectEl.setAttribute("x", object.fixLocalX);
441 objectEl.setAttribute("y", object.fixLocalY);
442 objectEl.setAttribute("angle", QString::number(kisRadiansToDegrees(object.fixLocalAngle), 'f', 2));
443 objectEl.setAttribute("scale_x", QString::number(object.fixLocalScaleX, 'f', 2));
444 objectEl.setAttribute("scale_y", QString::number(object.fixLocalScaleY, 'f', 2));
445 }
446}
447
448Bone *findBoneByName(Bone *startBone, const QString &name)
449{
450 if (!startBone) return 0;
451 //qDebug() << "findBoneByName" << name << "starting with" << startBone->name;
452
453 if (startBone->name == name) {
454 return startBone;
455 }
456 Q_FOREACH(Bone *child, startBone->bones) {
457 //qDebug() << "looking for" << name << "found" << child->name;
458 if (child->name == name) {
459 return child;
460 }
461 Bone *grandChild = findBoneByName(child, name);
462 if (grandChild){
463 return grandChild;
464 }
465 }
466 return 0;
467}
468
470{
471 QFileInfo fi(filename());
472
473 m_image = document->savingImage();
474
475 if (m_image->rootLayer()->childCount() == 0) {
477 }
478
480
481 m_boneLayer = qobject_cast<KisLayer*>(KisLayerUtils::findNodeByName(root,"bone").data());
482 //qDebug() << "Found boneLayer" << m_boneLayer;
483
484 m_rootLayer= qobject_cast<KisGroupLayer*>(KisLayerUtils::findNodeByName(root,"root").data());
485 //qDebug() << "Fond rootLayer" << m_rootLayer;
486
487 KisImportExportErrorCode result = parseFolder(m_image->rootLayer(), "", fi.absolutePath());
488 if (!result.isOk()) {
489 dbgFile << "There were errors encountered while using the spriter exporter.";
490 return result;
491 }
492
493 m_rootBone = 0;
494
495 if (m_rootLayer) {
497 }
498 // Generate objects
499 int objectId = 0;
500 for (int folderIndex = 0, folderCount = m_folders.size(); folderIndex < folderCount; ++folderIndex) {
501 Folder folder = m_folders[folderCount - 1 - folderIndex];
502 for (int fileIndex = 0, fileCount = folder.files.size(); fileIndex < fileCount; ++ fileIndex) {
503 SpriterFile file = folder.files[fileCount - 1 - fileIndex];
504 SpriterObject spriterObject;
505 spriterObject.id = objectId++;
506 spriterObject.folderId = folder.id;
507 spriterObject.fileId = file.id;
508 spriterObject.x = file.x;
509 spriterObject.y = -file.y;
510 Bone *bone = 0;
511
512 //qDebug() << "file layername" << file.layerName;
513 // layer.name format: "base_name bone(bone_name) slot(slot_name)"
514 if (file.layerName.contains("bone(")) {
515 int start = file.layerName.indexOf("bone(") + 5;
516 int end = file.layerName.indexOf(')', start);
517 QString boneName = file.layerName.mid(start, end - start);
518 bone = findBoneByName(m_rootBone, boneName);
519 }
520
521
522 // layer.name format: "base_name"
523 if (!bone && m_rootBone) {
524 bone = findBoneByName(m_rootBone, file.layerName);
525 }
526 // group.name format: "base_name bone(bone_name)"
527 if (!bone && m_rootBone) {
528 if (folder.groupName.contains("bone(")) {
529 int start = folder.groupName.indexOf("bone(") + 5;
530 int end = folder.groupName.indexOf(')', start);
531 QString boneName = folder.groupName.mid(start, end - start);
532 bone = findBoneByName(m_rootBone, boneName);
533 }
534
535 // group.name format: "base_name"
536 if (!bone) {
537 bone = findBoneByName(m_rootBone, folder.groupName);
538 }
539 }
540
541 if (!bone) {
542 bone = m_rootBone;
543 }
544
545 if (bone) {
546 spriterObject.bone = bone;
547 spriterObject.localX = spriterObject.x - bone->x;
548 spriterObject.localY = spriterObject.y - bone->y;
549 }
550 else {
551 spriterObject.bone = 0;
552 spriterObject.localX = spriterObject.x;
553 spriterObject.localY = spriterObject.y;
554 }
555
556 spriterObject.localAngle = 0;
557 spriterObject.localScaleX = 1.0;
558 spriterObject.localScaleY = 1.0;
559
560 SpriterSlot *slot = 0;
561
562 // layer.name format: "base_name bone(bone_name) slot(slot_name)"
563 if (file.layerName.contains("slot(")) {
564 int start = file.layerName.indexOf("slot(") + 5;
565 int end = file.layerName.indexOf(')', start);
566 slot = new SpriterSlot();
567 slot->name = file.layerName.mid(start, end - start);
568 slot->defaultAttachmentFlag = file.layerName.contains("*");
569 }
570
571 spriterObject.slot = slot;
572
573// qDebug() << "Created object" << spriterObject.id << spriterObject.folderId
574// << spriterObject.fileId << spriterObject.x << spriterObject.y
575// << spriterObject.localX << spriterObject.localY;
576
577 m_objects.append(spriterObject);
578 }
579 }
580
581 // Copy object transforms
582 for (int i = 0; i < m_objects.size(); ++i) {
583 m_objects[i].fixLocalX = m_objects[i].localX;
584 m_objects[i].fixLocalY = m_objects[i].localY;
585 m_objects[i].fixLocalAngle = m_objects[i].localAngle;
586 m_objects[i].fixLocalScaleX = m_objects[i].localScaleX;
587 m_objects[i].fixLocalScaleY = m_objects[i].localScaleY;
588 }
589
590 // Calculate bone angles
591 if (m_rootBone) {
594 }
595
596 // Generate scml
597 QDomDocument scml;
598 fillScml(scml, fi.completeBaseName());
599
600 bool openedHere = false;
601 if (!io->isOpen()) {
602 openedHere = io->open(QIODevice::WriteOnly);
603 if (!openedHere) {
604 // unsuccessful open
606 }
607 }
608
609 QString towrite = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
610 if (io->write(towrite.toUtf8()) != towrite.length()) {
612 }
613 towrite = scml.toString(4).toUtf8();
614 if (io->write(towrite.toUtf8()) != towrite.length()) {
616 }
617
618 delete m_rootBone;
619
620 if (openedHere) {
621 // FIXME: causes crash...
622 //io->close();
623 }
624
626}
627
629{
632 QList<QPair<KoID, KoID> > supportedColorModels;
633 supportedColorModels << QPair<KoID, KoID>()
634 << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
635 addSupportedColorModels(supportedColorModels, "Spriter");
636}
637
638
639
640#include "kis_spriter_export.moc"
VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v)
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
static KisExportCheckRegistry * instance()
vKisAnnotationSP_it endAnnotations()
KisGroupLayerSP rootLayer() const
double xRes() const
double yRes() const
QRect bounds() const override
vKisAnnotationSP_it beginAnnotations()
The base class for import and export filters.
void addSupportedColorModels(QList< QPair< KoID, KoID > > supportedColorModels, const QString &name, KisExportCheckBase::Level level=KisExportCheckBase::PARTIALLY)
void addCapability(KisExportCheckBase *capability)
static bool isColorSpaceSupported(const KoColorSpace *cs)
KisImportExportErrorCode buildFile(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP device, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisPNGOptions options, KisMetaData::Store *metaData)
QRect exactBounds() const
const KoColorSpace * colorSpace() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
KisImportExportErrorCode parseFolder(KisGroupLayerSP parentGroup, const QString &folderName, const QString &basePath, int *folderId=0)
Bone * parseBone(const Bone *parent, KisGroupLayerSP groupLayer)
void fixBone(Bone *bone)
KisGroupLayerSP m_rootLayer
KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration=0) override
QList< SpriterObject > m_objects
KisSpriterExport(QObject *parent, const QVariantList &)
void fillScml(QDomDocument &scml, const QString &entityName)
KisImportExportErrorCode savePaintDevice(KisPaintDeviceSP dev, const QString &fileName)
void writeBone(const Bone *bone, QDomElement &timeline, QDomDocument &scml)
void initializeCapabilities() override
void writeBoneRef(const Bone *bone, QDomElement &mainline, QDomDocument &scml)
QList< Folder > m_folders
K_PLUGIN_FACTORY_WITH_JSON(KritaASCCDLFactory, "kritaasccdl.json", registerPlugin< KritaASCCDL >();) KritaASCCDL
#define dbgFile
Definition kis_debug.h:53
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181
void copyBone(Bone *startBone)
Bone * findBoneByName(Bone *startBone, const QString &name)
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
KRITAIMAGE_EXPORT qreal atan2(qreal y, qreal x)
atan2 replacement
KisNodeSP findNodeByName(KisNodeSP root, const QString &name)
QString name
qreal localScaleY
const Bone * parentBone
qreal fixLocalScaleX
qreal fixLocalAngle
qreal localAngle
QList< Bone * > bones
qreal fixLocalY
qreal fixLocalX
qreal localScaleX
qreal fixLocalScaleY
QString groupName
QList< SpriterFile > files
virtual KisPaintDeviceSP projection() const =0
virtual QRect exactBounds() const
QString name() const
virtual bool visible(bool recursive=false) const
QRect exactBounds() const override
KisNodeSP prevSibling() const
Definition kis_node.cpp:402
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeSP lastChild() const
Definition kis_node.cpp:367
static KoColorSpaceRegistry * instance()
SpriterSlot * slot