1 
2 
3 #include "toonz/toonzimageutils.h"
4 #include "tstroke.h"
5 #include "tpalette.h"
6 #include "tofflinegl.h"
7 #include "tvectorrenderdata.h"
8 #include "tvectorgl.h"
9 #include "trop.h"
10 #include "toonz/ttileset.h"
11 #include "toonz/tcenterlinevectorizer.h"
12 #include "tregion.h"
13 #include "trasterimage.h"
14 #include "ttoonzimage.h"
15 #include "ttzpimagefx.h"
16 #include "tlevel_io.h"
17 #include "tsystem.h"
18 #include "tstream.h"
19 #include "tsimplecolorstyles.h"
20 //-------------------------------------------------------------------
21 
22 namespace {
23 
24 const int BackgroundStyle = 0;
25 
26 //-------------------------------------------------------------------
27 
rasterizeStroke(TOfflineGL * & gl,const TRect & rasBounds,TStroke * stroke,TPalette * palette,const TRectD & clip,bool doAntialias)28 TRect rasterizeStroke(TOfflineGL *&gl, const TRect &rasBounds, TStroke *stroke,
29                       TPalette *palette, const TRectD &clip, bool doAntialias) {
30   TRectD bbox = clip * stroke->getBBox();
31   TRect rect  = convert(bbox).enlarge(1) * rasBounds;
32 
33   if (rect.getLx() <= 0 || rect.getLy() <= 0) return TRect();
34 
35   gl = new TOfflineGL(rect.getSize());
36   gl->makeCurrent();
37   gl->clear(TPixel32::White);
38 
39   TPaletteP plt      = palette->clone();
40   int styleId        = stroke->getStyle();
41   TColorStyleP style = plt->getStyle(styleId);
42   assert(style);
43   style->setMainColor(TPixel32::Black);
44 
45   TTranslation affine(-convert(rect.getP00()));
46   TVectorRenderData rd(affine, gl->getBounds(), plt.getPointer(), 0, false,
47                        doAntialias);
48   tglDraw(rd, stroke);
49 
50   glFinish();
51 
52   return rect;
53 }
54 
55 //-------------------------------------------------------------------
56 
rasterizeStroke(TOfflineGL * & gl,TRect rasBounds,TStroke * stroke,TRectD clip,bool filled=false)57 TRect rasterizeStroke(TOfflineGL *&gl, TRect rasBounds, TStroke *stroke,
58                       TRectD clip, bool filled = false) {
59   TDimension d = rasBounds.getSize();
60 
61   TPointD offset(d.lx * 0.5, d.ly * 0.5);
62   TTranslation offsetMatrix(offset);
63 
64   TRectD strokeBBox = stroke->getBBox().enlarge(2);
65   if (!clip.isEmpty())
66     strokeBBox = offsetMatrix * (strokeBBox * clip);
67   else
68     strokeBBox = offsetMatrix * strokeBBox;
69 
70   TRect rect = ToonzImageUtils::convertWorldToRaster(strokeBBox, 0) * rasBounds;
71   if (!rect.isEmpty()) {
72     gl = new TOfflineGL(rect.getSize());
73     gl->makeCurrent();
74     glClearColor(1, 1, 1, 1);
75     glClear(GL_COLOR_BUFFER_BIT);
76 
77     TPalette *palette = new TPalette();
78     TTranslation affine(-strokeBBox.getP00() + offset);
79     TVectorRenderData rd(affine, gl->getBounds(), palette, 0, false, true);
80 
81     int oldStyle = stroke->getStyle();
82     stroke->setStyle(1);
83 
84     if (filled) {
85       TVectorImage vi;
86       vi.addStroke(new TStroke(*stroke));
87       vi.findRegions();
88       vi.selectFill(vi.getBBox().enlarge(2), 0, 1, false, true, false);
89       tglDraw(rd, &vi);
90     } else
91       tglDraw(rd, stroke);
92 
93     delete palette;
94     stroke->setStyle(oldStyle);
95     glFinish();
96   }
97   return rect;
98 }
99 
100 //-------------------------------------------------------------------
101 
rasterizeRegion(TOfflineGL * & gl,TRect rasBounds,TRegion * region,TRectD clip)102 TRect rasterizeRegion(TOfflineGL *&gl, TRect rasBounds, TRegion *region,
103                       TRectD clip) {
104   TRectD regionBBox               = region->getBBox();
105   if (!clip.isEmpty()) regionBBox = regionBBox * clip;
106 
107   TRect rect = convert(regionBBox) * rasBounds;
108   if (!rect.isEmpty()) {
109     gl = new TOfflineGL(rect.getSize());
110     gl->makeCurrent();
111     gl->clear(TPixel32::White);
112     glPushAttrib(GL_ALL_ATTRIB_BITS);
113     glEnable(GL_ALPHA_TEST);
114     glAlphaFunc(GL_GREATER, 0);
115 
116     TPalette *palette = new TPalette();
117     TTranslation affine(-convert(rect.getP00()));
118     TVectorRenderData rd(affine, gl->getBounds(), palette, 0, true, true);
119 
120     int oldStyle = region->getStyle();
121     region->setStyle(1);
122     tglDraw(rd, region);
123 
124     glDisable(GL_ALPHA_TEST);
125     glPopAttrib();
126 
127     region->setStyle(oldStyle);
128     glFinish();
129     delete palette;
130   }
131   return rect;
132 }
133 
134 //-------------------------------------------------------------------
135 
fastAddPaintRegion(const TToonzImageP & ti,TRegion * region,int newPaintId,int maxStyleId,TRectD clip=TRectD ())136 void fastAddPaintRegion(const TToonzImageP &ti, TRegion *region, int newPaintId,
137                         int maxStyleId, TRectD clip = TRectD()) {
138   TRasterCM32P ras = ti->getRaster();
139   TOfflineGL *gl;
140   TRect rect = rasterizeRegion(gl, ras->getBounds(), region, clip);
141   if (rect.isEmpty()) return;
142 
143   TRaster32P glRas = gl->getRaster();
144   assert(TPixelCM32::getMaxTone() == 255);
145   ras->lock();
146   glRas->lock();
147 
148   for (int y = rect.y0; y <= rect.y1; y++) {
149     TPixel32 *inPix    = glRas->pixels(y - rect.y0);
150     TPixelCM32 *outPix = ras->pixels(y) + rect.x0;
151     TPixel32 *inEndPix = inPix + rect.x1 - rect.x0 + 1;
152     for (; inPix < inEndPix; ++outPix, ++inPix)
153       if (inPix->r < 128) {
154         outPix->setPaint(newPaintId);
155         outPix->setTone(255);
156       }
157   }
158 
159   ras->unlock();
160   glRas->unlock();
161 
162   delete gl;
163 
164   TRegion *subregion;
165   UINT i = 0;
166   for (; i < region->getSubregionCount(); ++i) {
167     subregion = region->getSubregion(i);
168     fastAddPaintRegion(ti, subregion,
169                        std::min(maxStyleId, subregion->getStyle()), maxStyleId);
170   }
171 }
172 }
173 
174 //-------------------------------------------------------------------
175 
convertWorldToRaster(const TRectD area,const TToonzImageP ti)176 TRect ToonzImageUtils::convertWorldToRaster(const TRectD area,
177                                             const TToonzImageP ti) {
178   if (area.isEmpty()) return TRect();
179   if (!ti || !ti->getRaster())
180     return TRect(tfloor(area.x0), tfloor(area.y0), tfloor(area.x1) - 1,
181                  tfloor(area.y1) - 1);
182   TRasterCM32P ras = ti->getRaster();
183   TRectD rect(area + ras->getCenterD());
184   return TRect(tfloor(rect.x0), tfloor(rect.y0), tceil(rect.x1) - 1,
185                tceil(rect.y1) - 1);
186 }
187 
188 //-------------------------------------------------------------------
189 
convertRasterToWorld(const TRect area,const TToonzImageP ti)190 TRectD ToonzImageUtils::convertRasterToWorld(const TRect area,
191                                              const TToonzImageP ti) {
192   if (area.isEmpty()) return TRectD();
193 
194   TRectD rect(area.x0, area.y0, area.x1 + 1, area.y1 + 1);
195   if (ti && ti->getRaster()) rect = rect - ti->getRaster()->getCenterD();
196   return rect;
197 }
198 
199 //-------------------------------------------------------------------
200 
201 // clip in coordinate world (cioe' della stroke)
202 
203 // overlaying inks, blend inks always "lose" on normal inks
204 
fastAddInkStroke(const TToonzImageP & ti,TStroke * stroke,int inkId,bool selective,bool filled,TRectD clip,bool doAntialiasing=true,const std::set<int> & blendInks=std::set<int> ())205 static TRect fastAddInkStroke(
206     const TToonzImageP &ti, TStroke *stroke, int inkId, bool selective,
207     bool filled, TRectD clip, bool doAntialiasing = true,
208     const std::set<int> &blendInks = std::set<int>()) {
209   TRasterCM32P ras = ti->getRaster();
210   TOfflineGL *gl   = 0;
211 
212   TRect rectRender = rasterizeStroke(gl, ras->getBounds(), stroke,
213                                      ti->getPalette(), clip, doAntialiasing);
214   if (!gl) return TRect();
215 
216   TRaster32P glRas    = gl->getRaster();
217   TRasterCM32P outRas = ras->extract(rectRender);
218   assert(glRas->getSize() == outRas->getSize());
219 
220   assert(TPixelCM32::getMaxTone() == 255);
221   outRas->lock();
222   glRas->lock();
223   bool isBlendInkUp = (blendInks.find(inkId) != blendInks.end());
224   for (int y = 0; y < outRas->getLy(); ++y) {
225     TPixel32 *inPix    = glRas->pixels(y);
226     TPixelCM32 *outPix = outRas->pixels(y);
227     for (int x = 0; x < outRas->getLx(); ++outPix, ++inPix, ++x) {
228       int upTone = inPix->r;
229       int dnTone = outPix->getTone();
230 
231       if (doAntialiasing) {
232         // overlaying inks, blend inks always "lose" on normal inks
233 
234         bool isBlendInkDn =
235             (blendInks.find(outPix->getInk()) != blendInks.end());
236 
237         if (isBlendInkUp && !isBlendInkDn && dnTone < 255) continue;
238 
239         if (isBlendInkDn && !isBlendInkUp && upTone < 255) {
240           *outPix = TPixelCM32(inkId, outPix->getPaint(), upTone);
241           continue;
242         }
243 
244         // The upper pixel replaces the lower one, *if*
245         //  a) It's more opaque
246         //  b) If the ink id id not the same, ink is not completely transparent
247         //  c)  ...
248         if ((upTone <= dnTone) &&
249             ((outPix->getInk() == inkId) || (upTone != 255)) &&
250             (!selective || !outPix->isPureInk()))
251           *outPix = TPixelCM32(inkId, outPix->getPaint(), upTone);
252       } else {
253         if (!selective || !outPix->isPureInk()) {
254           // if(upTone<=192 && upTone<=dnTone)
255           if (upTone == 0) *outPix = TPixelCM32(inkId, outPix->getPaint(), 0);
256         }
257       }
258     }
259   }
260   outRas->unlock();
261   glRas->unlock();
262   delete gl;
263 
264   return rectRender;
265 }
266 
267 // clip in coordinate world (cioe' della stroke)
addInkStroke(const TToonzImageP & ti,TStroke * stroke,int inkId,bool selective,bool filled,TRectD clip,bool doAntialiasing)268 TRect ToonzImageUtils::addInkStroke(const TToonzImageP &ti, TStroke *stroke,
269                                     int inkId, bool selective, bool filled,
270                                     TRectD clip, bool doAntialiasing) {
271   TStroke *s      = new TStroke(*stroke);
272   TPoint tiCenter = ti->getRaster()->getCenter();
273   s->transform(TTranslation(tiCenter.x, tiCenter.y));
274   TRect rect =
275       fastAddInkStroke(ti, s, inkId, selective, filled, clip, doAntialiasing);
276   rect -= tiCenter;
277   return rect;
278 }
279 
280 //-------------------------------------------------------------------
281 
changeColorStroke(const TToonzImageP & ti,const ChangeColorStrokeSettings & settings)282 TRect ToonzImageUtils::changeColorStroke(
283     const TToonzImageP &ti, const ChangeColorStrokeSettings &settings) {
284   if (!settings.changeInk && !settings.changePaint) return TRect();
285 
286   TRasterCM32P ras = ti->getRaster();
287   TOfflineGL *gl;
288   TRect rect =
289       rasterizeStroke(gl, ras->getBounds(), settings.stroke, settings.clip);
290   if (rect.isEmpty()) {
291     return rect;
292   }
293   TRaster32P glRas = gl->getRaster();
294   ras->lock();
295   glRas->lock();
296 
297   for (int y = rect.y0; y <= rect.y1; ++y) {
298     TPixel32 *inPix    = glRas->pixels(y - rect.y0);
299     TPixelCM32 *outPix = ras->pixels(y) + rect.x0;
300     TPixel32 *inEndPix = inPix + rect.x1 - rect.x0 + 1;
301     for (; inPix < inEndPix; ++outPix, ++inPix) {
302       if (inPix->r < 128 && settings.changeInk && !outPix->isPurePaint())
303         outPix->setInk(settings.colorId);
304       if (inPix->r < 128 && settings.changePaint &&
305           (settings.maskPaintId == -1 ||
306            outPix->getPaint() == settings.maskPaintId))
307         outPix->setPaint(settings.colorId);
308     }
309   }
310   delete gl;
311   ras->unlock();
312   glRas->unlock();
313   return rect;
314 }
315 
316 //-------------------------------------------------------------------
317 
eraseRect(const TToonzImageP & ti,const TRectD & area,int maskId,bool onInk,bool onPaint)318 TRect ToonzImageUtils::eraseRect(const TToonzImageP &ti, const TRectD &area,
319                                  int maskId, bool onInk, bool onPaint) {
320   assert(onInk || onPaint);
321   TRasterCM32P ras = ti->getRaster();
322   TRect rect       = convertWorldToRaster(area, ti) * ras->getBounds();
323   if (rect.isEmpty()) return rect;
324   ras->lock();
325 
326   for (int y = rect.y0; y <= rect.y1; y++) {
327     TPixelCM32 *pix    = ras->pixels(y) + rect.x0;
328     TPixelCM32 *endPix = ras->pixels(y) + rect.x1 + 1;
329     for (; pix < endPix; ++pix) {
330       if (onPaint && (maskId == -1 || maskId == pix->getPaint()))
331         pix->setPaint(BackgroundStyle);
332 
333       if (onInk && (maskId == -1 || maskId == pix->getInk()))
334         *pix = TPixelCM32(BackgroundStyle, pix->getPaint(),
335                           TPixelCM32::getMaxTone());
336     }
337   }
338   ras->unlock();
339   return rect;
340 }
341 
342 //-------------------------------------------------------------------
343 
paste(const TToonzImageP & ti,const TTileSetCM32 * tileSet)344 std::vector<TRect> ToonzImageUtils::paste(const TToonzImageP &ti,
345                                           const TTileSetCM32 *tileSet) {
346   std::vector<TRect> rects;
347   TRasterCM32P raster = ti->getRaster();
348   // for(int i=0;i<tileSet->getTileCount();i++)
349   for (int i = tileSet->getTileCount() - 1; i >= 0; i--) {
350     const TTileSetCM32::Tile *tile = tileSet->getTile(i);
351     TRasterCM32P rasCM32;
352     tile->getRaster(rasCM32);
353     assert(!!rasCM32);
354     raster->copy(rasCM32, tile->m_rasterBounds.getP00());
355     rects.push_back(tile->m_rasterBounds);
356   }
357   return rects;
358 }
359 
360 //-------------------------------------------------------------------
361 
362 // DA RIFARE
363 // e' lenta da far schifo
364 
365 //! Converts a TVectorImage into a TToonzImage. The input vector image
366 //! is transformed through the passed affine \b aff, and put into a
367 //! TToonzImage strictly covering the bounding box of the transformed
368 //! vector image. The output image has its lower-left position in the
369 //! world reference specified by the \b pos parameter, which is granted to
370 //! be an integer displacement of the passed value. Additional parameters
371 //! include an integer \b enlarge by which the output image is enlarged with
372 //! respect to the transformed image's bbox, and the bool \b transformThickness
373 //! to specify whether the transformation should involve strokes' thickensses
374 //! or not.
vectorToToonzImage(const TVectorImageP & vimage,const TAffine & aff,TPalette * palette,const TPointD & outputPos,const TDimension & outputSize,const std::vector<TRasterFxRenderDataP> * fxs,bool transformThickness)375 TToonzImageP ToonzImageUtils::vectorToToonzImage(
376     const TVectorImageP &vimage, const TAffine &aff, TPalette *palette,
377     const TPointD &outputPos, const TDimension &outputSize,
378     const std::vector<TRasterFxRenderDataP> *fxs, bool transformThickness) {
379   if (!vimage || !palette) return 0;
380 
381   // Transform the vector image through aff
382   TVectorImageP vi = vimage->clone();
383   vi->transform(aff, transformThickness);
384 
385   // Allocate the output ToonzImage
386   TRasterCM32P raster(outputSize.lx, outputSize.ly);
387   raster->clear();
388   TToonzImageP ti(raster, raster->getBounds());
389   ti->setPalette(palette->clone());
390 
391   // Shift outputPos to the origin
392   vi->transform(TTranslation(-outputPos));
393 
394   int strokeCount = vi->getStrokeCount();
395   std::vector<int> strokeIndex(strokeCount);
396   std::vector<TStroke *> strokes(strokeCount);
397   int maxStyleId = palette->getStyleCount() - 1;
398 
399   int i;
400   for (i = 0; i < strokeCount; ++i) {
401     strokeIndex[i] = i;
402     strokes[i]     = vi->getStroke(i);
403   }
404   vi->notifyChangedStrokes(strokeIndex, strokes);
405   int regionCount = vi->getRegionCount();
406 
407   // In such reference, the clip for rendering strokes is the output size
408   TRectD clip(TDimensionD(outputSize.lx, outputSize.ly));
409 
410   std::set<int> colors;
411   if (fxs) {
412     for (i = 0; i < (int)fxs->size(); i++) {
413       SandorFxRenderData *sandorData =
414           dynamic_cast<SandorFxRenderData *>((*fxs)[i].getPointer());
415       if (sandorData && sandorData->m_type == BlendTz) {
416         std::string indexes =
417             ::to_string(sandorData->m_blendParams.m_colorIndex);
418         std::vector<std::string> items;
419         parseIndexes(indexes, items);
420         PaletteFilterFxRenderData paletteFilterData;
421         insertIndexes(items, &paletteFilterData);
422         colors.insert(paletteFilterData.m_colors.begin(),
423                       paletteFilterData.m_colors.end());
424       }
425     }
426   }
427 
428   int k, l;
429   for (i = 0; i < strokeCount;) {
430     // Draw all regions which have the same group.
431     for (k = 0; k < regionCount; ++k)
432       if (vi->areDifferentGroup(i, false, k, true) == -1) {
433         TRegion *region = vi->getRegion(k);
434         fastAddPaintRegion(ti, region, std::min(maxStyleId, region->getStyle()),
435                            maxStyleId);
436       }
437 
438     // Find the first stroke which does not belong to the group
439     for (k = i;
440          k < strokeCount && vi->areDifferentGroup(i, false, k, false) == -1;
441          ++k)
442       ;
443 
444     // Draw all found strokes
445     for (l = i; l < k; ++l) {
446       TStroke *stroke = vi->getStroke(l);
447 
448       bool visible       = false;
449       int styleId        = stroke->getStyle();
450       TColorStyleP style = palette->getStyle(styleId);
451       assert(style);
452       int colorCount = style->getColorParamCount();
453       if (colorCount == 0)
454         visible = true;
455       else {
456         visible = false;
457         for (int j = 0; j < style->getColorParamCount() && !visible; j++) {
458           TPixel32 color            = style->getColorParamValue(j);
459           if (color.m != 0) visible = true;
460         }
461       }
462       if (visible)
463         fastAddInkStroke(ti, stroke, std::min(maxStyleId, stroke->getStyle()),
464                          false, false, clip, true, colors);
465     }
466     i = k;
467   }
468 
469   return ti;
470 }
471 
472 //-------------------------------------------------------------------
473 
loadTzPalette(const TFilePath & pltFile)474 TPalette *ToonzImageUtils::loadTzPalette(const TFilePath &pltFile) {
475   TImageP pltImg;
476   TImageReader loader(pltFile);
477   pltImg = loader.load();
478 
479   TRasterImageP pltRasImg(pltImg);
480   if (!pltRasImg) return 0;
481 
482   TRaster32P rasPlt = pltRasImg->getRaster();
483   if (!rasPlt) return 0;
484 
485   std::map<int, std::pair<std::string, std::string>> pltColorNames;
486   std::map<int, std::pair<std::string, std::string>>::iterator it;
487   loader.getTzpPaletteColorNames(pltColorNames);
488 
489   TPalette *palette = new TPalette();
490 
491   const int offset = 0;
492 
493   assert(rasPlt->getLy() == 2);
494   rasPlt->lock();
495   TPixel32 *pixelRow = rasPlt->pixels(0);
496   int x              = 0;
497   int count          = palette->getStyleCount();
498 
499   for (; x < rasPlt->getLx(); ++x) {
500     TSolidColorStyle *style = new TSolidColorStyle(pixelRow[x]);
501     if ((it = pltColorNames.find(x)) != pltColorNames.end()) {
502       std::string styleName = it->second.second;
503       style->setName(::to_wstring(styleName));
504     }
505     if (x < count) palette->setStyle(x, style);
506     // palette->setStyle(x, pixelRow[x]);
507     else
508       palette->addStyle(style);
509   }
510 
511   // aggiungo solo i colori usati (salvo il BG)
512 
513   TPalette::Page *page = palette->getPage(0);
514   // nella pagina c'e' gia' lo sfondo e il colore 1:
515   // tolgo quest'ultimo
516   page->removeStyle(1);
517   // aggiungo gli altri
518   std::map<std::wstring, int> pages;
519   std::map<std::wstring, int>::iterator itpage;
520 
521   pixelRow = rasPlt->pixels(1);
522   for (x = 0; x < rasPlt->getLx(); ++x) {
523     if ((it = pltColorNames.find(x)) != pltColorNames.end()) {
524       std::wstring pageName;
525       pageName = ::to_wstring(it->second.first);
526       if (x == 0) {
527         page = palette->getPage(0);
528         page->setName(pageName);
529         pages[pageName] = 0;
530       } else if ((itpage = pages.find(pageName)) != pages.end())
531         page = palette->getPage(itpage->second);
532       else {
533         page            = palette->addPage(pageName);
534         pages[pageName] = page->getIndex();
535       }
536     } else
537       page = palette->getPage(0);
538 
539     if (pixelRow[x].r == 255) page->addStyle(offset + x);
540   }
541   rasPlt->unlock();
542 
543   return palette;
544 }
545 
546 //-------------------------------------------------------------------
547 
getUsedStyles(std::set<int> & styles,const TToonzImageP & ti)548 void ToonzImageUtils::getUsedStyles(std::set<int> &styles,
549                                     const TToonzImageP &ti) {
550   TRasterCM32P ras = ti->getRaster();
551   if (!ras) return;
552   int lx = ras->getLx();
553   int ly = ras->getLy();
554   ras->lock();
555   for (int y = 0; y < ly; y++) {
556     TPixelCM32 *pix    = ras->pixels(y);
557     TPixelCM32 *endPix = pix + lx;
558     while (pix < endPix) {
559       styles.insert(pix->getInk());
560       styles.insert(pix->getPaint());
561       ++pix;
562     }
563   }
564   ras->unlock();
565 }
566 
scrambleStyles(const TToonzImageP & ti,std::map<int,int> styleTable)567 void ToonzImageUtils::scrambleStyles(const TToonzImageP &ti,
568                                      std::map<int, int> styleTable) {
569   TRasterCM32P ras = ti->getRaster();
570   if (!ras) return;
571   if (styleTable.empty()) return;
572   std::map<int, int>::iterator it;
573   std::vector<int> lut(4096, -1);
574   bool isIdentity = true;
575   for (it = styleTable.begin(); it != styleTable.end(); ++it) {
576     int j = it->first, k = it->second;
577     assert(j >= 0);
578     assert(j < 1000000);
579     if (j >= (int)lut.size()) lut.resize(j + 1, -1);
580     lut[j]                 = k;
581     if (j != k) isIdentity = false;
582   }
583   if (isIdentity) return;
584 
585   int m  = lut.size();
586   int lx = ras->getLx();
587   int ly = ras->getLy();
588   ras->lock();
589   for (int y = 0; y < ly; y++) {
590     TPixelCM32 *pix    = ras->pixels(y);
591     TPixelCM32 *endPix = pix + lx;
592     while (pix < endPix) {
593       int ink                                               = pix->getInk();
594       if (0 <= ink && ink < m && lut[ink] >= 0) ink         = lut[ink];
595       int paint                                             = pix->getPaint();
596       if (0 <= paint && paint < m && lut[paint] >= 0) paint = lut[paint];
597       if (ink != pix->getInk() || paint != pix->getPaint()) {
598         *pix = TPixelCM32(ink, paint, pix->getTone());
599       }
600       ++pix;
601     }
602   }
603   ras->unlock();
604 }
605 
606 //----------------------------------------------------------------------------------
607 
608 #ifdef LEVO
609 
convertToTlv(const TFilePath & levelPathIn)610 bool ToonzImageUtils::convertToTlv(const TFilePath &levelPathIn) {
611   try {
612     TFilePath levelPathOut = levelPathIn.getParentDir() +
613                              TFilePath(levelPathIn.getWideName() + L".tlv");
614 
615     TLevelReaderP lr(levelPathIn);
616     TLevelP level = lr->loadInfo();
617 
618     TLevelWriterP lw(levelPathOut, 0);
619 
620     TPalette *plt = new TPalette();
621 
622     TLevel::Iterator it  = level->begin();
623     TLevel::Iterator end = level->end();
624     for (; it != level->end(); ++it) {
625       try {
626         TImageReaderP ir  = lr->getFrameReader(it->first);
627         TRasterImageP img = ir->load();
628         double dpix, dpiy;
629         img->getDpi(dpix, dpiy);
630         TRasterCM32P raster(convert(img->getBBox()).getSize());
631 
632         TRop::convert(raster, img->getRaster());
633 
634         TImageWriterP iw = lw->getFrameWriter(it->first);
635         TToonzImageP outimg(raster, raster->getBounds());
636         outimg->setDpi(dpix, dpiy);
637         outimg->setPalette(plt);
638         iw->save(outimg);
639 
640       } catch (...) {
641         return false;
642         // string msg="Frame "+toString(it->first.getNumber())+": conversion
643         // failed!";
644         // cout << msg << endl;
645       }
646     }
647 
648     TFilePath pltPath = lw->getFilePath().withNoFrame().withType("tpl");
649     if (TSystem::touchParentDir(pltPath)) {
650       if (TSystem::doesExistFileOrLevel(pltPath))
651         TSystem::removeFileOrLevel(pltPath);
652       TOStream os(pltPath);
653       os << plt;
654     }
655 
656     lr = TLevelReaderP();
657     lw = TLevelWriterP();
658     // delete plt;
659 
660     return true;
661   } catch (...) {
662     return false;
663   }
664 }
665 
666 #endif
667 
668 //----------------------------------------------------------------------------------
669 
eraseImage(const TToonzImageP & ti,const TRaster32P & image,const TPoint & pos,bool invert,bool eraseInk,bool erasePaint,bool selective,int styleId)670 void ToonzImageUtils::eraseImage(const TToonzImageP &ti,
671                                  const TRaster32P &image, const TPoint &pos,
672                                  bool invert, bool eraseInk, bool erasePaint,
673                                  bool selective, int styleId) {
674   TRect rasBounds   = ti->getRaster()->getBounds();
675   TRect imageBounds = image->getBounds() + pos;
676 
677   if (invert) {
678     /*----------------------------------------
679     ┌───┬─┐
680     │③  │②│
681     ├─┬─┤ │
682     │④│★│ │
683     │ ├─┴─┤
684     │ │①  │
685     └─┴───┘
686 ★はFreeHandで囲んだ領域のバウンディングボックス
687 外側のワクはラスタ画像のフチ
688 -----------------------------------------*/
689     TRect rect;
690     /*- ①の部分を消す -*/
691     if (rasBounds.y0 != imageBounds.y0) {
692       rect = TRect(imageBounds.x0, rasBounds.y0, rasBounds.x1, imageBounds.y0);
693       ToonzImageUtils::eraseRect(
694           ti, ToonzImageUtils::convertRasterToWorld(rect, ti),
695           selective ? styleId : -1, eraseInk, erasePaint);
696     }
697     /*- ②の部分を消す -*/
698     if (imageBounds.x1 != rasBounds.x1) {
699       rect = TRect(imageBounds.x1, imageBounds.y0, rasBounds.x1, rasBounds.y1);
700       ToonzImageUtils::eraseRect(
701           ti, ToonzImageUtils::convertRasterToWorld(rect, ti),
702           selective ? styleId : -1, eraseInk, erasePaint);
703     }
704     /*- ③の部分を消す -*/
705     if (imageBounds.y1 != rasBounds.y1) {
706       rect = TRect(rasBounds.x0, imageBounds.y1, imageBounds.x1, rasBounds.y1);
707       ToonzImageUtils::eraseRect(
708           ti, ToonzImageUtils::convertRasterToWorld(rect, ti),
709           selective ? styleId : -1, eraseInk, erasePaint);
710     }
711     /*- ④の部分を消す -*/
712     if (rasBounds.x0 != imageBounds.x0) {
713       rect = TRect(rasBounds.x0, rasBounds.y0, imageBounds.x0, imageBounds.y1);
714       ToonzImageUtils::eraseRect(
715           ti, ToonzImageUtils::convertRasterToWorld(rect, ti),
716           selective ? styleId : -1, eraseInk, erasePaint);
717     }
718   }
719 
720   TRasterCM32P workRas = ti->getRaster()->extract(imageBounds);
721 
722   int y;
723   for (y = 0; y < workRas->getLy(); y++) {
724     TPixelCM32 *outPix    = workRas->pixels(y);
725     TPixelCM32 *outEndPix = outPix + workRas->getLx();
726     TPixel32 *inPix       = image->pixels(y);
727     for (; outPix != outEndPix; outPix++, inPix++) {
728       bool canEraseInk =
729           !selective || (selective && styleId == outPix->getInk());
730       bool canErasePaint =
731           !selective || (selective && styleId == outPix->getPaint());
732 
733       int paint, tone;
734       if (!invert) {
735         paint = inPix->m > 0 && erasePaint && canErasePaint
736                     ? 0
737                     : outPix->getPaint();
738         tone = inPix->m > 0 && eraseInk && canEraseInk
739                    ? std::max(outPix->getTone(), (int)inPix->m)
740                    : outPix->getTone();
741       } else {
742         paint = inPix->m < 255 && erasePaint && canErasePaint
743                     ? 0
744                     : outPix->getPaint();
745         tone = inPix->m < 255 && eraseInk && canEraseInk
746                    ? std::max(outPix->getTone(), 255 - (int)inPix->m)
747                    : outPix->getTone();
748       }
749       *outPix = TPixelCM32(outPix->getInk(), paint, tone);
750     }
751   }
752 }
753 
754 //-----------------------------------------------------------------------
755 
premultiply(const TFilePath & levelPath)756 std::string ToonzImageUtils::premultiply(const TFilePath &levelPath) {
757   assert(0);
758   /*
759 if (levelPath==TFilePath())
760 return "";
761 
762 if(!TSystem::doesExistFileOrLevel(levelPath))
763 return string("Can't find level")+toString(levelPath.getWideString());
764 
765 TFileType::Type type = TFileType::getInfo(levelPath);
766 if(type == TFileType::CMAPPED_LEVEL)
767 return "Cannot premultiply the selected file.";
768 
769 if (type == TFileType::VECTOR_LEVEL || type == TFileType::VECTOR_IMAGE)
770 return "Cannot premultiply a vector-based level.";
771 
772 if (type != TFileType::RASTER_LEVEL && type != TFileType::RASTER_IMAGE)
773 return "Cannot premultiply the selected file.";
774 
775 try
776 {
777 TLevelReaderP lr = TLevelReaderP(levelPath);
778 if (!lr) return "";
779 TLevelP level =  lr->loadInfo();
780 if(!level || level->getFrameCount()==0) return "";
781 string format = levelPath.getType();
782 
783 
784 TPropertyGroup* prop =
785 TApplication::instance()
786 ->getCurrentScene()
787 ->getProperties()
788 ->getOutputProperties()
789 ->getFileFormatProperties(format)
790 ->clone();
791 assert(prop);
792 
793 TEnumProperty *p = (TEnumProperty*)prop->getProperty("Bits Per Pixel");
794 int bpp = p?atoi((toString(p->getValue()).c_str())):32;
795 if (bpp!=32 && bpp!=64) //non ha senso premoltiplicare senza il canale alpha...
796 {
797 if (bpp<32)
798 p->setValue(L"32(RGBM)");
799 else
800 p->setValue(L"64(RGBM)");
801 }
802 
803 bool isMovie = (format=="mov" || format=="avi" || format=="3gp");
804 
805 
806 TLevelWriterP lw;
807 if (!isMovie)
808 {
809 lw = TLevelWriterP(levelPath, prop);
810 if (!lw) return "";
811 }
812 
813 int count = 0;
814 TLevel::Iterator it = level->begin();
815 
816 for (;it!=level->end(); ++it)
817 {
818 TImageReaderP ir = lr->getFrameReader(it->first);
819 
820 TRasterImageP rimg = (TRasterImageP)ir->load();
821 if (!rimg)
822 continue;
823 
824 TRop::premultiply(rimg->getRaster());
825 ir = 0;
826 
827 if (isMovie)
828 level->setFrame(it->first, rimg);
829 else
830 {
831 TImageWriterP iw = lw->getFrameWriter(it->first);
832 iw->save(rimg);
833 iw = 0;
834 }
835 }
836 
837 lr = TLevelReaderP();
838 
839 if (isMovie)
840 {
841 TSystem::deleteFile(levelPath);
842 lw = TLevelWriterP(levelPath, prop);
843 if (!lw) return "";
844 lw->save(level);
845 }
846 if (prop)
847 delete prop;
848 }catch(...)
849 {
850   return "Cannot premultiply the selected file.";
851 }
852 */
853 
854   return "";
855 }
856