1 
2 
3 // TnzCore includes
4 #include "tfilepath.h"
5 
6 // TnzLib includes
7 #include "toonz/txshsimplelevel.h"
8 #include "toonz/preferences.h"
9 
10 #include "toonz/onionskinmask.h"
11 
12 //*****************************************************************************************
13 //    Macros
14 //*****************************************************************************************
15 
16 #define MINFADE 0.35
17 #define MAXFADE 0.95
18 
19 //*****************************************************************************************
20 //    Local namespace
21 //*****************************************************************************************
22 
23 namespace {
24 
getIncrement(int paperThickness)25 double inline getIncrement(int paperThickness) {
26   struct locals {
27     inline static void fillIncrements(double *values, int a, int b) {
28       double slope = (values[b] - values[a]) / (b - a);
29       for (int i = a + 1; i < b; ++i) values[i] = values[i - 1] + slope;
30     }
31   };  // locals
32 
33   static double Incr[101] = {-1.0};
34 
35   if (Incr[0] == -1.0) {
36     Incr[0]   = 0.0;
37     Incr[10]  = 0.05;
38     Incr[50]  = 0.12;
39     Incr[90]  = 0.3;
40     Incr[100] = MAXFADE - MINFADE;
41 
42     locals::fillIncrements(Incr, 0, 10);
43     locals::fillIncrements(Incr, 10, 50);
44     locals::fillIncrements(Incr, 50, 90);
45     locals::fillIncrements(Incr, 90, 100);
46   }
47 
48   return Incr[paperThickness];
49 }
50 
51 }  // namespace
52 
53 //***************************************************************************
54 //    OnionSkinMask  implementation
55 //***************************************************************************
56 
clear()57 void OnionSkinMask::clear() {
58   m_fos.clear();
59   m_mos.clear();
60 
61   m_shiftTraceStatus = DISABLED;
62 
63   m_ghostAff[0]    = TAffine();
64   m_ghostAff[1]    = TAffine();
65   m_ghostCenter[0] = TPointD();
66   m_ghostCenter[1] = TPointD();
67   m_ghostFrame[0]  = 0;
68   m_ghostFrame[1]  = 0;
69 }
70 
71 //-------------------------------------------------------------------
72 
getAll(int currentRow,std::vector<int> & output) const73 void OnionSkinMask::getAll(int currentRow, std::vector<int> &output) const {
74   output.clear();
75   output.reserve(m_fos.size() + m_mos.size());
76 
77   std::vector<int>::const_iterator fosIt, fosEnd(m_fos.end());
78   std::vector<int>::const_iterator mosIt, mosEnd(m_mos.end());
79 
80   for (fosIt = m_fos.begin(), mosIt = m_mos.begin();
81        fosIt != fosEnd && mosIt != mosEnd;) {
82     int fos = *fosIt, mos = *mosIt + currentRow;
83 
84     if (fos < mos) {
85       if (fos != currentRow) output.push_back(fos);
86 
87       ++fosIt;
88     } else {
89       if (mos != currentRow) output.push_back(mos);
90 
91       ++mosIt;
92     }
93   }
94 
95   for (; fosIt != fosEnd; ++fosIt)
96     if (*fosIt != currentRow) output.push_back(*fosIt);
97 
98   for (; mosIt != mosEnd; ++mosIt) {
99     int mos = *mosIt + currentRow;
100     if (mos != currentRow) output.push_back(mos);
101   }
102 }
103 
104 //-------------------------------------------------------------------
105 
setMos(int drow,bool on)106 void OnionSkinMask::setMos(int drow, bool on) {
107   assert(drow != 0);
108 
109   typedef std::vector<int>::iterator Iter;
110   std::pair<Iter, Iter> r = std::equal_range(m_mos.begin(), m_mos.end(), drow);
111 
112   if (on) {
113     if (r.first == r.second) m_mos.insert(r.first, drow);
114   } else {
115     if (r.first != r.second) m_mos.erase(r.first, r.second);
116   }
117 }
118 
119 //-------------------------------------------------------------------
120 
setFos(int row,bool on)121 void OnionSkinMask::setFos(int row, bool on) {
122   typedef std::vector<int>::iterator Iter;
123   std::pair<Iter, Iter> r = std::equal_range(m_fos.begin(), m_fos.end(), row);
124 
125   if (on) {
126     if (r.first == r.second) m_fos.insert(r.first, row);
127   } else {
128     if (r.first != r.second) m_fos.erase(r.first, r.second);
129   }
130 }
131 
132 //-------------------------------------------------------------------
133 
isFos(int row) const134 bool OnionSkinMask::isFos(int row) const {
135   return std::binary_search(m_fos.begin(), m_fos.end(), row);
136 }
137 
138 //-------------------------------------------------------------------
139 
isMos(int drow) const140 bool OnionSkinMask::isMos(int drow) const {
141   return std::binary_search(m_mos.begin(), m_mos.end(), drow);
142 }
143 
144 //-------------------------------------------------------------------
145 
getMosRange(int & drow0,int & drow1) const146 bool OnionSkinMask::getMosRange(int &drow0, int &drow1) const {
147   if (m_mos.empty()) {
148     drow0 = 0, drow1 = -1;
149     return false;
150   } else {
151     drow0 = m_mos.front(), drow1 = m_mos.back();
152     return true;
153   }
154 }
155 
156 //-------------------------------------------------------------------
157 
getOnionSkinFade(int rowsDistance)158 double OnionSkinMask::getOnionSkinFade(int rowsDistance) {
159   if (rowsDistance == 0) return 0.9;
160 
161   double fade =
162       MINFADE +
163       abs(rowsDistance) *
164           getIncrement(Preferences::instance()->getOnionPaperThickness());
165   return tcrop(fade, MINFADE, MAXFADE);
166 }
167 
168 //-------------------------------------------------------------------
169 
setShiftTraceGhostAff(int index,const TAffine & aff)170 void OnionSkinMask::setShiftTraceGhostAff(int index, const TAffine &aff) {
171   assert(0 <= index && index < 2);
172   m_ghostAff[index] = aff;
173 }
174 
175 //-------------------------------------------------------------------
176 
setShiftTraceGhostCenter(int index,const TPointD & center)177 void OnionSkinMask::setShiftTraceGhostCenter(int index, const TPointD &center) {
178   assert(0 <= index && index < 2);
179   m_ghostCenter[index] = center;
180 }
181 
182 //***************************************************************************
183 //    OnionSkinMaskModifier  implementation
184 //***************************************************************************
185 
OnionSkinMaskModifier(OnionSkinMask mask,int currentRow)186 OnionSkinMaskModifier::OnionSkinMaskModifier(OnionSkinMask mask, int currentRow)
187     : m_oldMask(mask)
188     , m_curMask(mask)
189     , m_firstRow(0)
190     , m_lastRow(0)
191     , m_curRow(currentRow)
192     , m_status(0) {}
193 
194 //-------------------------------------------------------------------
195 
click(int row,bool isFos)196 void OnionSkinMaskModifier::click(int row, bool isFos) {
197   assert(m_status == 0);
198 
199   m_firstRow = m_lastRow = row;
200   if (isFos) {
201     assert(row != m_curRow);
202 
203     if (m_curMask.isEnabled() && m_curMask.isFos(row)) {
204       m_status = 2;  // spegnere fos
205       m_curMask.setFos(row, false);
206     } else {
207       if (!m_curMask.isEnabled()) {
208         m_curMask.clear();
209         m_curMask.enable(true);
210       }
211 
212       m_curMask.setFos(row, true);
213       m_status = 3;  // accendere fos
214     }
215   } else {
216     int drow = row - m_curRow;
217     if (drow != 0 && m_curMask.isEnabled() && m_curMask.isMos(drow)) {
218       m_status = 4;  // spegnere mos
219       m_curMask.setMos(drow, false);
220     } else if (drow == 0) {
221       m_status = 8 + 4 + 1;  // accendere mos; partito da 0
222     } else {
223       if (!m_curMask.isEnabled()) m_curMask.enable(true);
224       m_curMask.setMos(drow, true);
225       m_status = 4 + 1;  // accendere mos;
226     }
227   }
228 }
229 
230 //-------------------------------------------------------------------
231 
drag(int row)232 void OnionSkinMaskModifier::drag(int row) {
233   if (m_status & 128) return;
234 
235   if (row == m_lastRow) return;
236 
237   m_status |= 64;  // moved
238 
239   int n = row - m_lastRow, d = 1;
240   if (n < 0) n = -n, d = -d;
241 
242   int oldr = m_lastRow, r = oldr + d;
243 
244   for (int i = 0; i < n; ++i, r += d) {
245     if (m_status & 4) {
246       if (!m_curMask.isEnabled()) {
247         m_curMask.clear();
248         m_curMask.enable(true);
249       }
250       if (r != m_curRow) m_curMask.setMos(r - m_curRow, (m_status & 1) != 0);
251     } else
252       m_curMask.setFos(r, (m_status & 1) != 0);
253   }
254 
255   m_lastRow = row;
256 }
257 
258 //-------------------------------------------------------------------
259 
release(int row)260 void OnionSkinMaskModifier::release(int row) {
261   if (m_status & 128) return;
262   if ((m_status & 64) == 0    // non si e' mosso
263       && (m_status & 8) == 8  // e' partito da zero
264       && row == m_curRow) {
265     if (!m_curMask.isEmpty() && m_curMask.isEnabled())
266       m_curMask.enable(false);
267     else {
268       m_curMask.enable(true);
269       if (m_curMask.isEmpty()) {
270         m_curMask.setMos(-1, true);
271         m_curMask.setMos(-2, true);
272         m_curMask.setMos(-3, true);
273       }
274     }
275   }
276 }
277