57a21dcce149090eb26600b51ff0c515182e1b73
[platform/core/graphics/tizenvg.git] / src / savers / tvg / tvgTvgSaver.cpp
1 /*
2  * Copyright (c) 2021 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
3
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 #include "tvgMath.h"
23 #include "tvgSaveModule.h"
24 #include "tvgTvgSaver.h"
25 #include "tvgLzw.h"
26
27 #include <cstring>
28
29 #ifdef _WIN32
30     #include <malloc.h>
31 #elif defined(__linux__)
32     #include <alloca.h>
33 #else
34     #include <stdlib.h>
35 #endif
36
37 static FILE* _fopen(const char* filename, const char* mode)
38 {
39 #if defined(_MSC_VER) && defined(__clang__)
40     FILE *fp;
41     auto err = fopen_s(&fp, filename, mode);
42     if (err != 0) return nullptr;
43     return fp;
44 #else
45     auto fp = fopen(filename, mode);
46     if (!fp) return nullptr;
47     return fp;
48 #endif
49 }
50
51 #define SIZE(A) sizeof(A)
52
53 /************************************************************************/
54 /* Internal Class Implementation                                        */
55 /************************************************************************/
56
57 static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt)
58 {
59     return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt;
60 }
61
62
63 /* if the properties are identical, we can merge the shapes. */
64 static bool _merge(Shape* from, Shape* to)
65 {
66     uint8_t r, g, b, a;
67     uint8_t r2, g2, b2, a2;
68
69     //fill
70     if (from->fill() || to->fill()) return false;
71
72     r = g = b = a = r2 = g2 = b2 = a2 = 0;
73
74     from->fillColor(&r, &g, &b, &a);
75     to->fillColor(&r2, &g2, &b2, &a2);
76
77     if (r != r2 || g != g2 || b != b2 || a != a2) return false;
78
79     //composition
80     if (from->composite(nullptr) != CompositeMethod::None) return false;
81     if (to->composite(nullptr) != CompositeMethod::None) return false;
82
83     //opacity
84     if (from->opacity() != to->opacity()) return false;
85
86     //transform
87     auto t1 = from->transform();
88     auto t2 = to->transform();
89
90     if (!mathEqual(t1.e11, t2.e11) || !mathEqual(t1.e12, t2.e12) || !mathEqual(t1.e13, t2.e13) ||
91         !mathEqual(t1.e21, t2.e21) || !mathEqual(t1.e22, t2.e22) || !mathEqual(t1.e23, t2.e23) ||
92         !mathEqual(t1.e31, t2.e31) || !mathEqual(t1.e32, t2.e32) || !mathEqual(t1.e33, t2.e33)) {
93        return false;
94     }
95
96     //stroke
97     r = g = b = a = r2 = g2 = b2 = a2 = 0;
98
99     from->strokeColor(&r, &g, &b, &a);
100     to->strokeColor(&r2, &g2, &b2, &a2);
101
102     if (r != r2 || g != g2 || b != b2 || a != a2) return false;
103
104     if (fabs(from->strokeWidth() - to->strokeWidth()) > FLT_EPSILON) return false;
105
106     //OPTIMIZE: Yet we can't merge outlining shapes unless we can support merging shapes feature.
107     if (from->strokeWidth() > 0 || to->strokeWidth() > 0) return false;
108
109     if (from->strokeCap() != to->strokeCap()) return false;
110     if (from->strokeJoin() != to->strokeJoin()) return false;
111     if (from->strokeDash(nullptr) > 0 || to->strokeDash(nullptr) > 0) return false;
112     if (from->strokeFill() || to->strokeFill()) return false;
113
114     //fill rule
115     if (from->fillRule() != to->fillRule()) return false;
116
117     //Good, identical shapes, we can merge them.
118     const PathCommand* cmds = nullptr;
119     auto cmdCnt = from->pathCommands(&cmds);
120
121     const Point* pts = nullptr;
122     auto ptsCnt = from->pathCoords(&pts);
123
124     to->appendPath(cmds, cmdCnt, pts, ptsCnt);
125
126     return true;
127 }
128
129
130 bool TvgSaver::saveEncoding(const std::string& path)
131 {
132     if (!compress) return flushTo(path);
133
134     //Try encoding
135     auto uncompressed = buffer.data + headerSize;
136     auto uncompressedSize = buffer.count - headerSize;
137
138     uint32_t compressedSize, compressedSizeBits;
139
140     auto compressed = lzwEncode(uncompressed, uncompressedSize, &compressedSize, &compressedSizeBits);
141
142     //Failed compression.
143     if (!compressed) return flushTo(path);
144
145     //Optimization is ineffective.
146     if (compressedSize >= uncompressedSize) {
147         free(compressed);
148         return flushTo(path);
149     }
150
151     TVGLOG("TVG_SAVER", "%s, compressed: %d -> %d, saved rate: %3.2f%%", path.c_str(), uncompressedSize, compressedSize, (1 - ((float) compressedSize / (float) uncompressedSize)) * 100);
152
153     //Update compress size in the header.
154     uncompressed -= (TVG_HEADER_COMPRESS_SIZE + TVG_HEADER_RESERVED_LENGTH);
155
156     //Compression Flag
157     *uncompressed |= TVG_HEAD_FLAG_COMPRESSED;
158     uncompressed += TVG_HEADER_RESERVED_LENGTH;
159
160     //Uncompressed Size
161     memcpy(uncompressed, &uncompressedSize, TVG_HEADER_UNCOMPRESSED_SIZE);
162     uncompressed += TVG_HEADER_UNCOMPRESSED_SIZE;
163
164     //Comprssed Size
165     memcpy(uncompressed, &compressedSize, TVG_HEADER_COMPRESSED_SIZE);
166     uncompressed += TVG_HEADER_COMPRESSED_SIZE;
167
168     //Compressed Size Bits
169     memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS);
170
171     //Good optimization, flush to file.
172     auto fp = _fopen(path.c_str(), "w+");
173     if (!fp) goto fail;
174
175     //write header
176     if (fwrite(buffer.data, SIZE(uint8_t), headerSize, fp) == 0) goto fail;
177
178     //write compressed data
179     if (fwrite(compressed, SIZE(uint8_t), compressedSize, fp) == 0) goto fail;
180
181     fclose(fp);
182     free(compressed);
183
184     return true;
185
186 fail:
187     if (fp) fclose(fp);
188     if (compressed) free(compressed);
189     return false;
190 }
191
192
193 bool TvgSaver::flushTo(const std::string& path)
194 {
195     auto fp = _fopen(path.c_str(), "w+");
196     if (!fp) return false;
197
198     if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) {
199         fclose(fp);
200         return false;
201     }
202     fclose(fp);
203
204     return true;
205 }
206
207
208 /* WARNING: Header format shall not changed! */
209 bool TvgSaver::writeHeader()
210 {
211     headerSize = TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + SIZE(vsize) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE;
212
213     buffer.grow(headerSize);
214
215     //1. Signature
216     auto ptr = buffer.ptr();
217     memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH);
218     ptr += TVG_HEADER_SIGNATURE_LENGTH;
219
220     //2. Version
221     memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH);
222     ptr += TVG_HEADER_VERSION_LENGTH;
223
224     buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH);
225
226     //3. View Size
227     writeData(vsize, SIZE(vsize));
228     ptr += SIZE(vsize);
229
230     //4. Reserved data + Compress size
231     memset(ptr, 0x00, TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
232     buffer.count += (TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE);
233
234     return true;
235 }
236
237
238 void TvgSaver::writeTag(TvgBinTag tag)
239 {
240     buffer.grow(SIZE(TvgBinTag));
241     memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag));
242     buffer.count += SIZE(TvgBinTag);
243 }
244
245
246 void TvgSaver::writeCount(TvgBinCounter cnt)
247 {
248     buffer.grow(SIZE(TvgBinCounter));
249     memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter));
250     buffer.count += SIZE(TvgBinCounter);
251 }
252
253
254 void TvgSaver::writeReservedCount(TvgBinCounter cnt)
255 {
256     memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter));
257 }
258
259
260 void TvgSaver::reserveCount()
261 {
262     buffer.grow(SIZE(TvgBinCounter));
263     buffer.count += SIZE(TvgBinCounter);
264 }
265
266
267 TvgBinCounter TvgSaver::writeData(const void* data, TvgBinCounter cnt)
268 {
269     buffer.grow(cnt);
270     memcpy(buffer.ptr(), data, cnt);
271     buffer.count += cnt;
272
273     return cnt;
274 }
275
276
277 TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data)
278 {
279     auto growCnt = SERIAL_DONE(cnt);
280
281     buffer.grow(growCnt);
282
283     auto ptr = buffer.ptr();
284
285     *ptr = tag;
286     ++ptr;
287
288     memcpy(ptr, &cnt, SIZE(TvgBinCounter));
289     ptr += SIZE(TvgBinCounter);
290
291     memcpy(ptr, data, cnt);
292     ptr += cnt;
293
294     buffer.count += growCnt;
295
296     return growCnt;
297 }
298
299
300 TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag)
301 {
302     if (!mathIdentity(transform)) return writeTagProperty(tag, SIZE(Matrix), transform);
303     return 0;
304 }
305
306
307 TvgBinCounter TvgSaver::serializePaint(const Paint* paint, const Matrix* pTransform)
308 {
309     TvgBinCounter cnt = 0;
310
311     //opacity
312     auto opacity = paint->opacity();
313     if (opacity < 255) {
314         cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, SIZE(opacity), &opacity);
315     }
316
317     //composite
318     const Paint* cmpTarget = nullptr;
319     auto cmpMethod = paint->composite(&cmpTarget);
320     if (cmpMethod != CompositeMethod::None && cmpTarget) {
321         cnt += serializeComposite(cmpTarget, cmpMethod, pTransform);
322     }
323
324     return cnt;
325 }
326
327
328 /* Propagate parents properties to the child so that we can skip saving the parent. */
329 TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, const Matrix* transform)
330 {
331     const Paint* compTarget = nullptr;
332     auto compMethod = parent->composite(&compTarget);
333
334     /* If the parent & the only child have composition, we can't skip the parent...
335        Or if the parent has the transform and composition, we can't skip the parent... */
336     if (compMethod != CompositeMethod::None) {
337         if (transform || child->composite(nullptr) != CompositeMethod::None) return 0;
338     }
339
340     //propagate opacity
341     uint32_t opacity = parent->opacity();
342
343     if (opacity < 255) {
344         uint32_t tmp = (child->opacity() * opacity);
345         if (tmp > 0) tmp /= 255;
346         const_cast<Paint*>(child)->opacity(tmp);
347     }
348
349     //propagate composition
350     if (compTarget) const_cast<Paint*>(child)->composite(unique_ptr<Paint>(compTarget->duplicate()), compMethod);
351
352     return serialize(child, transform);
353 }
354
355
356 TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform)
357 {
358     auto it = this->iterator(scene);
359     if (it->count() == 0) {
360         delete(it);
361         return 0;
362     }
363
364     //Case - Only Child: Skip saving this scene.
365     if (it->count() == 1) {
366         auto cnt = serializeChild(scene, it->next(), cTransform);
367         if (cnt > 0) {
368             delete(it);
369             return cnt;
370         }
371     }
372
373     it->begin();
374
375     //Case - Delegator Scene: This scene is just a delegator, we can skip this:
376     if (scene->composite(nullptr) == CompositeMethod::None && scene->opacity() == 255) {
377         auto ret = serializeChildren(it, cTransform, false);
378         delete(it);
379         return ret;
380     }
381
382     //Case - Serialize Scene & its children
383     writeTag(TVG_TAG_CLASS_SCENE);
384     reserveCount();
385
386     auto cnt = serializeChildren(it, cTransform, true) + serializePaint(scene, pTransform);
387
388     delete(it);
389
390     writeReservedCount(cnt);
391
392     return SERIAL_DONE(cnt);
393 }
394
395
396 TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform)
397 {
398     const Fill::ColorStop* stops = nullptr;
399     auto stopsCnt = fill->colorStops(&stops);
400     if (!stops || stopsCnt == 0) return 0;
401
402     writeTag(tag);
403     reserveCount();
404
405     TvgBinCounter cnt = 0;
406
407     //radial fill
408     if (fill->identifier() == TVG_CLASS_ID_RADIAL) {
409         float args[3];
410         static_cast<const RadialGradient*>(fill)->radial(args, args + 1, args + 2);
411         cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, SIZE(args), args);
412     //linear fill
413     } else {
414         float args[4];
415         static_cast<const LinearGradient*>(fill)->linear(args, args + 1, args + 2, args + 3);
416         cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, SIZE(args), args);
417     }
418
419     if (auto flag = static_cast<TvgBinFlag>(fill->spread()))
420         cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag);
421     cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops);
422
423     auto gTransform = fill->transform();
424     if (pTransform) gTransform = mathMultiply(pTransform, &gTransform);
425
426     cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM);
427
428     writeReservedCount(cnt);
429
430     return SERIAL_DONE(cnt);
431 }
432
433
434 TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform)
435 {
436     writeTag(TVG_TAG_SHAPE_STROKE);
437     reserveCount();
438
439     //width
440     auto width = shape->strokeWidth();
441     if (preTransform) width *= sqrtf(powf(pTransform->e11, 2.0f) + powf(pTransform->e21, 2.0f));  //we know x/y scaling factors are same.
442     auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, SIZE(width), &width);
443
444     //cap
445     if (auto flag = static_cast<TvgBinFlag>(shape->strokeCap()))
446         cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag);
447
448     //join
449     if (auto flag = static_cast<TvgBinFlag>(shape->strokeJoin()))
450         cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag);
451
452     //fill
453     if (auto fill = shape->strokeFill()) {
454         cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr));
455     } else {
456         uint8_t color[4] = {0, 0, 0, 0};
457         shape->strokeColor(color, color + 1, color + 2, color + 3);
458         cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, SIZE(color), &color);
459     }
460
461     //dash
462     const float* dashPattern = nullptr;
463     auto dashCnt = shape->strokeDash(&dashPattern);
464     if (dashPattern && dashCnt > 0) {
465         TvgBinCounter dashCntSize = SIZE(dashCnt);
466         TvgBinCounter dashPtrnSize = dashCnt * SIZE(dashPattern[0]);
467
468         writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN);
469         writeCount(dashCntSize + dashPtrnSize);
470         cnt += writeData(&dashCnt, dashCntSize);
471         cnt += writeData(dashPattern, dashPtrnSize);
472         cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
473     }
474
475     writeReservedCount(cnt);
476
477     return SERIAL_DONE(cnt);
478 }
479
480
481 TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transform, bool preTransform)
482 {
483     const PathCommand* cmds = nullptr;
484     auto cmdCnt = shape->pathCommands(&cmds);
485     const Point* pts = nullptr;
486     auto ptsCnt = shape->pathCoords(&pts);
487
488     if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0;
489
490     writeTag(TVG_TAG_SHAPE_PATH);
491     reserveCount();
492
493     /* Reduce the binary size.
494        Convert PathCommand(4 bytes) to TvgBinFlag(1 byte) */
495     TvgBinFlag* outCmds = (TvgBinFlag*)alloca(SIZE(TvgBinFlag) * cmdCnt);
496     for (uint32_t i = 0; i < cmdCnt; ++i) {
497         outCmds[i] = static_cast<TvgBinFlag>(cmds[i]);
498     }
499
500     auto cnt = writeData(&cmdCnt, SIZE(cmdCnt));
501     cnt += writeData(&ptsCnt, SIZE(ptsCnt));
502     cnt += writeData(outCmds, SIZE(TvgBinFlag) * cmdCnt);
503
504     //transform?
505     if (preTransform) {
506         if (!mathEqual(transform->e11, 1.0f) || !mathZero(transform->e12) || !mathZero(transform->e13) ||
507             !mathZero(transform->e21) || !mathEqual(transform->e22, 1.0f) || !mathZero(transform->e23) ||
508             !mathZero(transform->e31) || !mathZero(transform->e32) || !mathEqual(transform->e33, 1.0f)) {
509             auto p = const_cast<Point*>(pts);
510             for (uint32_t i = 0; i < ptsCnt; ++i) mathMultiply(p++, transform);
511         }
512     }
513
514     cnt += writeData(pts, ptsCnt * SIZE(pts[0]));
515
516     writeReservedCount(cnt);
517
518     return SERIAL_DONE(cnt);
519 }
520
521
522 TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform)
523 {
524     writeTag(TVG_TAG_CLASS_SHAPE);
525     reserveCount();
526     TvgBinCounter cnt = 0;
527
528     //fill rule
529     if (auto flag = static_cast<TvgBinFlag>(shape->fillRule())) {
530         cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag);
531     }
532
533     //the pre-transformation can't be applied in the case when the stroke is dashed or irregulary scaled
534     bool preTransform = true;
535
536     //stroke
537     if (shape->strokeWidth() > 0) {
538         uint8_t color[4] = {0, 0, 0, 0};
539         shape->strokeColor(color, color + 1, color + 2, color + 3);
540         auto fill = shape->strokeFill();
541         if (fill || color[3] > 0) {
542             if (!mathEqual(cTransform->e11, cTransform->e22) || (mathZero(cTransform->e11) && !mathEqual(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false;
543             cnt += serializeStroke(shape, cTransform, preTransform);
544         }
545     }
546
547     //fill
548     if (auto fill = shape->fill()) {
549         cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr));
550     } else {
551         uint8_t color[4] = {0, 0, 0, 0};
552         shape->fillColor(color, color + 1, color + 2, color + 3);
553         if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
554     }
555
556     cnt += serializePath(shape, cTransform, preTransform);
557
558     if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
559     cnt += serializePaint(shape, pTransform);
560
561     writeReservedCount(cnt);
562
563     return SERIAL_DONE(cnt);
564 }
565
566
567 /* Picture has either a vector scene or a bitmap. */
568 TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform)
569 {
570     auto it = this->iterator(picture);
571
572     //Case - Vector Scene:
573     if (it->count() == 1) {
574         auto cnt = serializeChild(picture, it->next(), cTransform);
575         //Only child, Skip to save Picture...
576         if (cnt > 0) {
577             delete(it);
578             return cnt;
579         /* Unfortunately, we can't skip the Picture because it might have a compositor,
580            Serialize Scene(instead of the Picture) & its scene. */
581         } else {
582             writeTag(TVG_TAG_CLASS_SCENE);
583             reserveCount();
584             auto cnt = serializeChildren(it, cTransform, true) + serializePaint(picture, pTransform);
585             writeReservedCount(cnt);
586             delete(it);
587             return SERIAL_DONE(cnt);
588         }
589     }
590     delete(it);
591
592     //Case - Bitmap Image:
593     uint32_t w, h;
594     auto pixels = picture->data(&w, &h);
595     if (!pixels) return 0;
596
597     writeTag(TVG_TAG_CLASS_PICTURE);
598     reserveCount();
599
600     TvgBinCounter cnt = 0;
601     TvgBinCounter sizeCnt = SIZE(w);
602     TvgBinCounter imgSize = w * h * SIZE(pixels[0]);
603
604     writeTag(TVG_TAG_PICTURE_RAW_IMAGE);
605     writeCount(2 * sizeCnt + imgSize);
606
607     cnt += writeData(&w, sizeCnt);
608     cnt += writeData(&h, sizeCnt);
609     cnt += writeData(pixels, imgSize);
610     cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
611
612     //Bitmap picture needs the transform info.
613     cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
614
615     cnt += serializePaint(picture, pTransform);
616
617     writeReservedCount(cnt);
618
619     return SERIAL_DONE(cnt);
620 }
621
622
623 TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform)
624 {
625     writeTag(TVG_TAG_PAINT_CMP_TARGET);
626     reserveCount();
627
628     auto flag = static_cast<TvgBinFlag>(cmpMethod);
629     auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag);
630
631     cnt += serialize(cmpTarget, pTransform, true);
632
633     writeReservedCount(cnt);
634
635     return SERIAL_DONE(cnt);
636 }
637
638
639 TvgBinCounter TvgSaver::serializeChildren(Iterator* it, const Matrix* pTransform, bool reserved)
640 {
641     TvgBinCounter cnt = 0;
642
643     //Merging shapes. the result is written in the children.
644     Array<const Paint*> children;
645     children.reserve(it->count());
646     children.push(it->next());
647
648     while (auto child = it->next()) {
649         if (child->identifier() == TVG_CLASS_ID_SHAPE) {
650             //only dosable if the previous child is a shape.
651             auto target = children.ptr() - 1;
652             if ((*target)->identifier() == TVG_CLASS_ID_SHAPE) {
653                 if (_merge((Shape*)child, (Shape*)*target)) {
654                     continue;
655                 }
656             }
657         }
658         children.push(child);
659     }
660
661     //The children of a reserved scene
662     if (reserved && children.count > 1) {
663         cnt += writeTagProperty(TVG_TAG_SCENE_RESERVEDCNT, SIZE(children.count), &children.count);
664     }
665
666     //Serialize merged children.
667     auto child = children.data;
668     for (uint32_t i = 0; i < children.count; ++i, ++child) {
669         cnt += serialize(*child, pTransform);
670     }
671
672     return cnt;
673 }
674
675
676 TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform, bool compTarget)
677 {
678     if (!paint) return 0;
679
680     //Invisible paint, no point to save it if the paint is not the composition target...
681     if (!compTarget && paint->opacity() == 0) return 0;
682
683     auto transform = const_cast<Paint*>(paint)->transform();
684     if (pTransform) transform = mathMultiply(pTransform, &transform);
685
686     switch (paint->identifier()) {
687         case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast<const Shape*>(paint), pTransform, &transform);
688         case TVG_CLASS_ID_SCENE: return serializeScene(static_cast<const Scene*>(paint), pTransform, &transform);
689         case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast<const Picture*>(paint), pTransform, &transform);
690     }
691
692     return 0;
693 }
694
695
696 void TvgSaver::run(unsigned tid)
697 {
698     if (!writeHeader()) return;
699
700     //Serialize Root Paint, without its transform.
701     Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
702
703     if (paint->opacity() > 0) {
704         switch (paint->identifier()) {
705             case TVG_CLASS_ID_SHAPE: {
706                 serializeShape(static_cast<const Shape*>(paint), nullptr, &transform);
707                 break;
708             }
709             case TVG_CLASS_ID_SCENE: {
710                 serializeScene(static_cast<const Scene*>(paint), nullptr, &transform);
711                 break;
712             }
713             case TVG_CLASS_ID_PICTURE: {
714                 serializePicture(static_cast<const Picture*>(paint), nullptr, &transform);
715                 break;
716             }
717         }
718     }
719
720     if (!saveEncoding(path)) return;
721 }
722
723
724 /************************************************************************/
725 /* External Class Implementation                                        */
726 /************************************************************************/
727
728 TvgSaver::~TvgSaver()
729 {
730     close();
731 }
732
733
734 bool TvgSaver::close()
735 {
736     this->done();
737
738     if (paint) {
739         delete(paint);
740         paint = nullptr;
741     }
742     if (path) {
743         free(path);
744         path = nullptr;
745     }
746     buffer.reset();
747     return true;
748 }
749
750
751 bool TvgSaver::save(Paint* paint, const string& path, bool compress)
752 {
753     close();
754
755     float x, y;
756     x = y = 0;
757     paint->bounds(&x, &y, &vsize[0], &vsize[1], false);
758
759     //cut off the negative space
760     if (x < 0) vsize[0] += x;
761     if (y < 0) vsize[1] += y;
762
763     if (vsize[0] < FLT_EPSILON || vsize[1] < FLT_EPSILON) {
764         TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);
765         return false;
766     }
767
768     this->path = strdup(path.c_str());
769     if (!this->path) return false;
770
771     this->paint = paint;
772     this->compress = compress;
773
774     TaskScheduler::request(this);
775
776     return true;
777 }