1// Aseprite
2// Copyright (C) 2019-2020 Igara Studio S.A.
3// Copyright (C) 2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/doc_diff.h"
13
14#include "app/doc.h"
15#include "doc/cel.h"
16#include "doc/image.h"
17#include "doc/layer.h"
18#include "doc/layer_tilemap.h"
19#include "doc/palette.h"
20#include "doc/primitives.h"
21#include "doc/sprite.h"
22#include "doc/tag.h"
23#include "doc/tileset.h"
24#include "doc/tilesets.h"
25
26namespace app {
27
28DocDiff compare_docs(const Doc* a,
29 const Doc* b)
30{
31 DocDiff diff;
32
33 // Don't compare filenames
34 //if (a->filename() != b->filename())...
35
36 // Compare sprite specs
37 if (a->sprite()->width() != b->sprite()->width() ||
38 a->sprite()->height() != b->sprite()->height() ||
39 a->sprite()->pixelFormat() != b->sprite()->pixelFormat()) {
40 diff.anything = diff.canvas = true;
41 }
42
43 // Frames layers
44 if (a->sprite()->totalFrames() != b->sprite()->totalFrames()) {
45 diff.anything = diff.totalFrames = true;
46 }
47 else {
48 for (frame_t f=0; f<a->sprite()->totalFrames(); ++f) {
49 if (a->sprite()->frameDuration(f) != b->sprite()->frameDuration(f)) {
50 diff.anything = diff.frameDuration = true;
51 break;
52 }
53 }
54 }
55
56 // Tags
57 if (a->sprite()->tags().size() != b->sprite()->tags().size()) {
58 diff.anything = diff.tags = true;
59 }
60 else {
61 auto aIt = a->sprite()->tags().begin(), aEnd = a->sprite()->tags().end();
62 auto bIt = b->sprite()->tags().begin(), bEnd = b->sprite()->tags().end();
63 for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) {
64 const Tag* aTag = *aIt;
65 const Tag* bTag = *bIt;
66 if (aTag->fromFrame() != bTag->fromFrame() ||
67 aTag->toFrame() != bTag->toFrame() ||
68 aTag->name() != bTag->name() ||
69 aTag->color() != bTag->color() ||
70 aTag->aniDir() != bTag->aniDir()) {
71 diff.anything = diff.tags = true;
72 }
73 }
74 }
75
76 // Palettes
77 if (a->sprite()->getPalettes().size() != b->sprite()->getPalettes().size()) {
78 const PalettesList& aPals = a->sprite()->getPalettes();
79 const PalettesList& bPals = b->sprite()->getPalettes();
80 auto aIt = aPals.begin(), aEnd = aPals.end();
81 auto bIt = bPals.begin(), bEnd = bPals.end();
82
83 for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) {
84 const Palette* aPal = *aIt;
85 const Palette* bPal = *bIt;
86
87 if (aPal->countDiff(bPal, nullptr, nullptr)) {
88 diff.anything = diff.palettes = true;
89 break;
90 }
91 }
92 }
93
94 // Compare tilesets
95 const tile_index aTilesetSize = (a->sprite()->hasTilesets() ? a->sprite()->tilesets()->size(): 0);
96 const tile_index bTilesetSize = (b->sprite()->hasTilesets() ? b->sprite()->tilesets()->size(): 0);
97 if (aTilesetSize != bTilesetSize) {
98 diff.anything = diff.tilesets = true;
99 }
100 else {
101 for (int i=0; i<aTilesetSize; ++i) {
102 Tileset* aTileset = a->sprite()->tilesets()->get(i);
103 Tileset* bTileset = b->sprite()->tilesets()->get(i);
104
105 if (aTileset->grid().tileSize() != bTileset->grid().tileSize() ||
106 aTileset->size() != bTileset->size()) {
107 diff.anything = diff.tilesets = true;
108 break;
109 }
110 else {
111 for (tile_index ti=0; ti<aTileset->size(); ++ti) {
112 if (!is_same_image(aTileset->get(ti).get(),
113 bTileset->get(ti).get())) {
114 diff.anything = diff.tilesets = true;
115 goto done;
116 }
117 }
118 }
119 }
120 done:;
121 }
122
123 // Compare layers
124 if (a->sprite()->allLayersCount() != b->sprite()->allLayersCount()) {
125 diff.anything = diff.layers = true;
126 }
127 else {
128 LayerList aLayers = a->sprite()->allLayers();
129 LayerList bLayers = b->sprite()->allLayers();
130 auto aIt = aLayers.begin(), aEnd = aLayers.end();
131 auto bIt = bLayers.begin(), bEnd = bLayers.end();
132
133 for (; aIt != aEnd && bIt != bEnd; ++aIt, ++bIt) {
134 const Layer* aLay = *aIt;
135 const Layer* bLay = *bIt;
136
137 if (aLay->type() != bLay->type() ||
138 aLay->name() != bLay->name() ||
139 ((int(aLay->flags()) & int(LayerFlags::PersistentFlagsMask)) !=
140 (int(bLay->flags()) & int(LayerFlags::PersistentFlagsMask))) ||
141 (aLay->isImage() && bLay->isImage() &&
142 (((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity())) ||
143 (aLay->isTilemap() && bLay->isTilemap() &&
144 (((const LayerTilemap*)aLay)->tilesetIndex() != ((const LayerTilemap*)bLay)->tilesetIndex()))) {
145 diff.anything = diff.layers = true;
146 break;
147 }
148
149 if (diff.totalFrames) {
150 for (frame_t f=0; f<a->sprite()->totalFrames(); ++f) {
151 const Cel* aCel = aLay->cel(f);
152 const Cel* bCel = bLay->cel(f);
153
154 if ((!aCel && bCel) ||
155 (aCel && !bCel)) {
156 diff.anything = diff.cels = true;
157 }
158 else if (aCel && bCel) {
159 if (aCel->frame() == bCel->frame() ||
160 aCel->bounds() == bCel->bounds() ||
161 aCel->opacity() == bCel->opacity()) {
162 diff.anything = diff.cels = true;
163 }
164 if (aCel->image() && bCel->image()) {
165 if (aCel->image()->bounds() != bCel->image()->bounds() ||
166 !is_same_image(aCel->image(), bCel->image()))
167 diff.anything = diff.images = true;
168 }
169 else if (aCel->image() != bCel->image())
170 diff.anything = diff.images = true;
171 }
172 }
173 }
174 }
175 }
176
177 // Compare color spaces
178 if (!a->sprite()->colorSpace()->nearlyEqual(*b->sprite()->colorSpace())) {
179 diff.anything = diff.colorProfiles = true;
180 }
181
182 // Compare grid bounds
183 if (a->sprite()->gridBounds() != b->sprite()->gridBounds()) {
184 diff.anything = diff.gridBounds = true;
185 }
186
187 return diff;
188}
189
190} // namespace app
191