1 /*
2 * Copyright 2006, 2023, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 * Zardshard
8 */
9
10 #include "FlatIconImporter.h"
11
12 #include <new>
13 #include <stdio.h>
14
15 #include <Archivable.h>
16 #include <DataIO.h>
17 #include <Message.h>
18
19 #include "AffineTransformer.h"
20 #include "AutoDeleter.h"
21 #include "ContourTransformer.h"
22 #include "FlatIconFormat.h"
23 #include "GradientTransformable.h"
24 #include "Icon.h"
25 #include "LittleEndianBuffer.h"
26 #include "PathCommandQueue.h"
27 #include "PathSourceShape.h"
28 #include "PerspectiveTransformer.h"
29 #include "Shape.h"
30 #include "StrokeTransformer.h"
31 #include "Style.h"
32 #include "VectorPath.h"
33
34 using std::nothrow;
35
36 _USING_ICON_NAMESPACE
37
38
39 // constructor
FlatIconImporter()40 FlatIconImporter::FlatIconImporter()
41 #ifdef ICON_O_MATIC
42 : Importer()
43 #endif
44 {
45 }
46
47 // destructor
~FlatIconImporter()48 FlatIconImporter::~FlatIconImporter()
49 {
50 }
51
52 // Import
53 status_t
Import(Icon * icon,BPositionIO * stream)54 FlatIconImporter::Import(Icon* icon, BPositionIO* stream)
55 {
56 #ifdef ICON_O_MATIC
57 status_t ret = Init(icon);
58 if (ret < B_OK)
59 return ret;
60 #else
61 status_t ret;
62 #endif
63
64 // seek around in the stream to figure out the size
65 off_t size = stream->Seek(0, SEEK_END);
66 if (stream->Seek(0, SEEK_SET) != 0)
67 return B_ERROR;
68
69 // we chicken out on anything larger than 256k
70 if (size <= 0 || size > 256 * 1024)
71 return B_BAD_VALUE;
72
73 // read the entire stream into a buffer
74 LittleEndianBuffer buffer(size);
75 if (!buffer.Buffer())
76 return B_NO_MEMORY;
77
78 if (stream->Read(buffer.Buffer(), size) != size)
79 return B_ERROR;
80
81 ret = _ParseSections(buffer, icon);
82
83 return ret;
84 }
85
86 // Import
87 status_t
Import(Icon * icon,uint8 * _buffer,size_t size)88 FlatIconImporter::Import(Icon* icon, uint8* _buffer, size_t size)
89 {
90 #ifdef ICON_O_MATIC
91 status_t ret = Init(icon);
92 if (ret < B_OK)
93 return ret;
94 #endif
95
96 if (!_buffer)
97 return B_BAD_VALUE;
98
99 // attach LittleEndianBuffer to buffer
100 LittleEndianBuffer buffer(_buffer, size);
101
102 return _ParseSections(buffer, icon);
103 }
104
105 // #pragma mark -
106
107 // _ParseSections
108 status_t
_ParseSections(LittleEndianBuffer & buffer,Icon * icon)109 FlatIconImporter::_ParseSections(LittleEndianBuffer& buffer, Icon* icon)
110 {
111 // test if this is an icon at all
112 uint32 magic;
113 if (!buffer.Read(magic) || magic != FLAT_ICON_MAGIC)
114 return B_BAD_TYPE;
115
116 // styles
117 Container<Style>* styles = icon->Styles();
118 status_t ret = _ParseStyles(buffer, styles);
119 if (ret < B_OK) {
120 printf("FlatIconImporter::_ParseSections() - "
121 "error parsing styles: %s\n", strerror(ret));
122 return ret;
123 }
124
125 // paths
126 Container<VectorPath>* paths = icon->Paths();
127 ret = _ParsePaths(buffer, paths);
128 if (ret < B_OK) {
129 printf("FlatIconImporter::_ParseSections() - "
130 "error parsing paths: %s\n", strerror(ret));
131 return ret;
132 }
133
134 // shapes
135 ret = _ParseShapes(buffer, styles, paths, icon->Shapes());
136 if (ret < B_OK) {
137 printf("FlatIconImporter::_ParseSections() - "
138 "error parsing shapes: %s\n", strerror(ret));
139 return ret;
140 }
141
142 return B_OK;
143 }
144
145 // _ReadTransformable
146 static bool
_ReadTransformable(LittleEndianBuffer & buffer,Transformable * transformable)147 _ReadTransformable(LittleEndianBuffer& buffer, Transformable* transformable)
148 {
149 int32 matrixSize = Transformable::matrix_size;
150 double matrix[matrixSize];
151 for (int32 i = 0; i < matrixSize; i++) {
152 float value;
153 if (!read_float_24(buffer, value))
154 return false;
155 matrix[i] = value;
156 }
157 transformable->LoadFrom(matrix);
158 return true;
159 }
160
161 // _ReadTranslation
162 static bool
_ReadTranslation(LittleEndianBuffer & buffer,Transformable * transformable)163 _ReadTranslation(LittleEndianBuffer& buffer, Transformable* transformable)
164 {
165 BPoint t;
166 if (read_coord(buffer, t.x) && read_coord(buffer, t.y)) {
167 transformable->TranslateBy(t);
168 return true;
169 }
170
171 return false;
172 }
173
174 // _ReadColorStyle
175 static Style*
_ReadColorStyle(LittleEndianBuffer & buffer,bool alpha,bool gray)176 _ReadColorStyle(LittleEndianBuffer& buffer, bool alpha, bool gray)
177 {
178 rgb_color color;
179 if (alpha) {
180 if (gray) {
181 if (!buffer.Read(color.red)
182 || !buffer.Read(color.alpha))
183 return NULL;
184 color.green = color.blue = color.red;
185 } else {
186 if (!buffer.Read((uint32&)color))
187 return NULL;
188 }
189 } else {
190 color.alpha = 255;
191 if (gray) {
192 if (!buffer.Read(color.red))
193 return NULL;
194 color.green = color.blue = color.red;
195 } else {
196 if (!buffer.Read(color.red)
197 || !buffer.Read(color.green)
198 || !buffer.Read(color.blue))
199 return NULL;
200 }
201 }
202 return new (nothrow) Style(color);
203 }
204
205 // _ReadGradientStyle
206 static Style*
_ReadGradientStyle(LittleEndianBuffer & buffer)207 _ReadGradientStyle(LittleEndianBuffer& buffer)
208 {
209 Style* style = new (nothrow) Style();
210 if (!style)
211 return NULL;
212
213 ObjectDeleter<Style> styleDeleter(style);
214
215 uint8 gradientType;
216 uint8 gradientFlags;
217 uint8 gradientStopCount;
218 if (!buffer.Read(gradientType)
219 || !buffer.Read(gradientFlags)
220 || !buffer.Read(gradientStopCount)) {
221 return NULL;
222 }
223
224 Gradient gradient(true);
225 // empty gradient
226
227 gradient.SetType((gradients_type)gradientType);
228 // TODO: support more stuff with flags
229 // ("inherits transformation" and so on)
230 if (gradientFlags & GRADIENT_FLAG_TRANSFORM) {
231 if (!_ReadTransformable(buffer, &gradient))
232 return NULL;
233 }
234
235 bool alpha = !(gradientFlags & GRADIENT_FLAG_NO_ALPHA);
236 bool gray = gradientFlags & GRADIENT_FLAG_GRAYS;
237
238 for (int32 i = 0; i < gradientStopCount; i++) {
239 uint8 stopOffset;
240 rgb_color color;
241
242 if (!buffer.Read(stopOffset))
243 return NULL;
244
245 if (alpha) {
246 if (gray) {
247 if (!buffer.Read(color.red)
248 || !buffer.Read(color.alpha))
249 return NULL;
250 color.green = color.blue = color.red;
251 } else {
252 if (!buffer.Read((uint32&)color))
253 return NULL;
254 }
255 } else {
256 color.alpha = 255;
257 if (gray) {
258 if (!buffer.Read(color.red))
259 return NULL;
260 color.green = color.blue = color.red;
261 } else {
262 if (!buffer.Read(color.red)
263 || !buffer.Read(color.green)
264 || !buffer.Read(color.blue)) {
265 return NULL;
266 }
267 }
268 }
269
270 gradient.AddColor(color, stopOffset / 255.0);
271 }
272
273 style->SetGradient(&gradient);
274
275 styleDeleter.Detach();
276 return style;
277 }
278
279 // _ParseStyles
280 status_t
_ParseStyles(LittleEndianBuffer & buffer,Container<Style> * styles)281 FlatIconImporter::_ParseStyles(LittleEndianBuffer& buffer,
282 Container<Style>* styles)
283 {
284 uint8 styleCount;
285 if (!buffer.Read(styleCount))
286 return B_ERROR;
287
288 for (int32 i = 0; i < styleCount; i++) {
289 uint8 styleType;
290 if (!buffer.Read(styleType))
291 return B_ERROR;
292 Style* style = NULL;
293 if (styleType == STYLE_TYPE_SOLID_COLOR) {
294 // solid color
295 style = _ReadColorStyle(buffer, true, false);
296 if (!style)
297 return B_NO_MEMORY;
298 } else if (styleType == STYLE_TYPE_SOLID_COLOR_NO_ALPHA) {
299 // solid color without alpha
300 style = _ReadColorStyle(buffer, false, false);
301 if (!style)
302 return B_NO_MEMORY;
303 } else if (styleType == STYLE_TYPE_SOLID_GRAY) {
304 // solid gray plus alpha
305 style = _ReadColorStyle(buffer, true, true);
306 if (!style)
307 return B_NO_MEMORY;
308 } else if (styleType == STYLE_TYPE_SOLID_GRAY_NO_ALPHA) {
309 // solid gray without alpha
310 style = _ReadColorStyle(buffer, false, true);
311 if (!style)
312 return B_NO_MEMORY;
313 } else if (styleType == STYLE_TYPE_GRADIENT) {
314 // gradient
315 style = _ReadGradientStyle(buffer);
316 if (!style)
317 return B_NO_MEMORY;
318 } else {
319 // unkown style type, skip tag
320 uint16 tagLength;
321 if (!buffer.Read(tagLength))
322 return B_ERROR;
323 buffer.Skip(tagLength);
324 continue;
325 }
326 // add style if we were able to read one
327 if (style && !styles->AddItem(style)) {
328 delete style;
329 return B_NO_MEMORY;
330 }
331 }
332
333 return B_OK;
334 }
335
336 // read_path_no_curves
337 static bool
read_path_no_curves(LittleEndianBuffer & buffer,VectorPath * path,uint8 pointCount)338 read_path_no_curves(LittleEndianBuffer& buffer, VectorPath* path,
339 uint8 pointCount)
340 {
341 for (uint32 p = 0; p < pointCount; p++) {
342 BPoint point;
343 if (!read_coord(buffer, point.x)
344 || !read_coord(buffer, point.y))
345 return false;
346
347 if (!path->AddPoint(point))
348 return false;
349 }
350 return true;
351 }
352
353 // read_path_curves
354 static bool
read_path_curves(LittleEndianBuffer & buffer,VectorPath * path,uint8 pointCount)355 read_path_curves(LittleEndianBuffer& buffer, VectorPath* path,
356 uint8 pointCount)
357 {
358 for (uint32 p = 0; p < pointCount; p++) {
359 BPoint point;
360 if (!read_coord(buffer, point.x)
361 || !read_coord(buffer, point.y))
362 return false;
363
364 BPoint pointIn;
365 if (!read_coord(buffer, pointIn.x)
366 || !read_coord(buffer, pointIn.y))
367 return false;
368
369 BPoint pointOut;
370 if (!read_coord(buffer, pointOut.x)
371 || !read_coord(buffer, pointOut.y))
372 return false;
373
374 if (!path->AddPoint(point, pointIn, pointOut, false))
375 return false;
376 }
377 return true;
378 }
379
380 // read_path_with_commands
381 static bool
read_path_with_commands(LittleEndianBuffer & buffer,VectorPath * path,uint8 pointCount)382 read_path_with_commands(LittleEndianBuffer& buffer, VectorPath* path,
383 uint8 pointCount)
384 {
385 PathCommandQueue queue;
386 return queue.Read(buffer, path, pointCount);
387 }
388
389
390 // _ParsePaths
391 status_t
_ParsePaths(LittleEndianBuffer & buffer,Container<VectorPath> * paths)392 FlatIconImporter::_ParsePaths(LittleEndianBuffer& buffer,
393 Container<VectorPath>* paths)
394 {
395 uint8 pathCount;
396 if (!buffer.Read(pathCount))
397 return B_ERROR;
398
399 for (int32 i = 0; i < pathCount; i++) {
400 uint8 pathFlags;
401 uint8 pointCount;
402 if (!buffer.Read(pathFlags) || !buffer.Read(pointCount))
403 return B_ERROR;
404
405 VectorPath* path = new (nothrow) VectorPath();
406 if (!path)
407 return B_NO_MEMORY;
408
409 // chose path reading strategy depending on path flags
410 bool error = false;
411 if (pathFlags & PATH_FLAG_NO_CURVES) {
412 if (!read_path_no_curves(buffer, path, pointCount))
413 error = true;
414 } else if (pathFlags & PATH_FLAG_USES_COMMANDS) {
415 if (!read_path_with_commands(buffer, path, pointCount))
416 error = true;
417 } else {
418 if (!read_path_curves(buffer, path, pointCount))
419 error = true;
420 }
421
422 if (error) {
423 delete path;
424 return B_ERROR;
425 }
426 // post process path to clean it up
427 path->CleanUp();
428 if (pathFlags & PATH_FLAG_CLOSED)
429 path->SetClosed(true);
430 // add path to container
431 if (!paths->AddItem(path)) {
432 delete path;
433 return B_NO_MEMORY;
434 }
435 }
436
437 return B_OK;
438 }
439
440 // _ReadTransformer
441 static Transformer*
_ReadTransformer(LittleEndianBuffer & buffer,VertexSource & source,Shape * shape)442 _ReadTransformer(LittleEndianBuffer& buffer, VertexSource& source, Shape* shape)
443 {
444 uint8 transformerType;
445 if (!buffer.Read(transformerType))
446 return NULL;
447
448 switch (transformerType) {
449 case TRANSFORMER_TYPE_AFFINE: {
450 AffineTransformer* affine
451 = new (nothrow) AffineTransformer(source);
452 if (!affine)
453 return NULL;
454 double matrix[6];
455 for (int32 i = 0; i < 6; i++) {
456 float value;
457 if (!buffer.Read(value)) {
458 delete affine;
459 return NULL;
460 }
461 matrix[i] = value;
462 }
463 affine->load_from(matrix);
464 return affine;
465 }
466 case TRANSFORMER_TYPE_CONTOUR: {
467 ContourTransformer* contour
468 = new (nothrow) ContourTransformer(source);
469 uint8 width;
470 uint8 lineJoin;
471 uint8 miterLimit;
472 if (!contour
473 || !buffer.Read(width)
474 || !buffer.Read(lineJoin)
475 || !buffer.Read(miterLimit)) {
476 delete contour;
477 return NULL;
478 }
479 contour->width(width - 128.0);
480 contour->line_join((agg::line_join_e)lineJoin);
481 contour->miter_limit(miterLimit);
482 return contour;
483 }
484 case TRANSFORMER_TYPE_PERSPECTIVE: {
485 PerspectiveTransformer* perspective
486 = new (nothrow) PerspectiveTransformer(source, shape);
487 if (!perspective)
488 return NULL;
489 double matrix[9];
490 for (int32 i = 0; i < 9; i++) {
491 float value;
492 if (!read_float_24(buffer, value)) {
493 delete perspective;
494 return NULL;
495 }
496 matrix[i] = value;
497 }
498 perspective->load_from(matrix);
499 return perspective;
500 }
501 case TRANSFORMER_TYPE_STROKE: {
502 StrokeTransformer* stroke
503 = new (nothrow) StrokeTransformer(source);
504 uint8 width;
505 uint8 lineOptions;
506 uint8 miterLimit;
507 // uint8 shorten;
508 if (!stroke
509 || !buffer.Read(width)
510 || !buffer.Read(lineOptions)
511 || !buffer.Read(miterLimit)) {
512 delete stroke;
513 return NULL;
514 }
515 stroke->width(width - 128.0);
516 uint8 lineJoin = lineOptions & 15;
517 stroke->line_join((agg::line_join_e)lineJoin);
518 uint8 lineCap = lineOptions >> 4;
519 stroke->line_cap((agg::line_cap_e)lineCap);
520 stroke->miter_limit(miterLimit);
521 return stroke;
522 }
523 default: {
524 // unkown transformer, skip tag
525 uint16 tagLength;
526 if (!buffer.Read(tagLength))
527 return NULL;
528 buffer.Skip(tagLength);
529 return NULL;
530 }
531 }
532 }
533
534 // _ReadPathSourceShape
535 Shape*
_ReadPathSourceShape(LittleEndianBuffer & buffer,Container<Style> * styles,Container<VectorPath> * paths)536 FlatIconImporter::_ReadPathSourceShape(LittleEndianBuffer& buffer,
537 Container<Style>* styles,
538 Container<VectorPath>* paths)
539 {
540 // find out which style this shape uses
541 uint8 styleIndex;
542 uint8 pathCount;
543 if (!buffer.Read(styleIndex) || !buffer.Read(pathCount))
544 return NULL;
545
546 #ifdef ICON_O_MATIC
547 Style* style = styles->ItemAt(StyleIndexFor(styleIndex));
548 #else
549 Style* style = styles->ItemAt(styleIndex);
550 #endif
551
552 if (!style) {
553 printf("_ReadPathSourceShape() - "
554 "shape references non-existing style %d\n", styleIndex);
555 return NULL;
556 }
557
558 // create the shape
559 PathSourceShape* shape = new (nothrow) PathSourceShape(style);
560 ObjectDeleter<Shape> shapeDeleter(shape);
561
562 if (!shape || shape->InitCheck() < B_OK)
563 return NULL;
564
565 // find out which paths this shape uses
566 for (uint32 i = 0; i < pathCount; i++) {
567 uint8 pathIndex;
568 if (!buffer.Read(pathIndex))
569 return NULL;
570
571 #ifdef ICON_O_MATIC
572 VectorPath* path = paths->ItemAt(PathIndexFor(pathIndex));
573 #else
574 VectorPath* path = paths->ItemAt(pathIndex);
575 #endif
576 if (!path) {
577 printf("_ReadPathSourceShape() - "
578 "shape references non-existing path %d\n", pathIndex);
579 continue;
580 }
581 shape->Paths()->AddItem(path);
582 }
583
584 // shape flags
585 uint8 shapeFlags;
586 if (!buffer.Read(shapeFlags))
587 return NULL;
588
589 shape->SetHinting(shapeFlags & SHAPE_FLAG_HINTING);
590
591 if (shapeFlags & SHAPE_FLAG_TRANSFORM) {
592 // transformation
593 if (!_ReadTransformable(buffer, shape))
594 return NULL;
595 } else if (shapeFlags & SHAPE_FLAG_TRANSLATION) {
596 // translation
597 if (!_ReadTranslation(buffer, shape))
598 return NULL;
599 }
600
601 if (shapeFlags & SHAPE_FLAG_LOD_SCALE) {
602 // min max visibility scale
603 uint8 minScale;
604 uint8 maxScale;
605 if (!buffer.Read(minScale) || !buffer.Read(maxScale))
606 return NULL;
607 shape->SetMinVisibilityScale(minScale / 63.75);
608 shape->SetMaxVisibilityScale(maxScale / 63.75);
609 }
610
611 // transformers
612 if (shapeFlags & SHAPE_FLAG_HAS_TRANSFORMERS) {
613 uint8 transformerCount;
614 if (!buffer.Read(transformerCount))
615 return NULL;
616 for (uint32 i = 0; i < transformerCount; i++) {
617 Transformer* transformer
618 = _ReadTransformer(buffer, shape->VertexSource(), shape);
619 if (transformer && !shape->Transformers()->AddItem(transformer)) {
620 delete transformer;
621 return NULL;
622 }
623 }
624 }
625
626 shapeDeleter.Detach();
627 return shape;
628 }
629
630 // _ParseShapes
631 status_t
_ParseShapes(LittleEndianBuffer & buffer,Container<Style> * styles,Container<VectorPath> * paths,Container<Shape> * shapes)632 FlatIconImporter::_ParseShapes(LittleEndianBuffer& buffer,
633 Container<Style>* styles,
634 Container<VectorPath>* paths,
635 Container<Shape>* shapes)
636 {
637 uint8 shapeCount;
638 if (!buffer.Read(shapeCount))
639 return B_ERROR;
640
641 for (uint32 i = 0; i < shapeCount; i++) {
642 uint8 shapeType;
643 if (!buffer.Read(shapeType))
644 return B_ERROR;
645 Shape* shape = NULL;
646 if (shapeType == SHAPE_TYPE_PATH_SOURCE) {
647 // path source shape
648 shape = _ReadPathSourceShape(buffer, styles, paths);
649 if (!shape)
650 return B_NO_MEMORY;
651 } else {
652 // unkown shape type, skip tag
653 uint16 tagLength;
654 if (!buffer.Read(tagLength))
655 return B_ERROR;
656 buffer.Skip(tagLength);
657 continue;
658 }
659 // add shape if we were able to read one
660 if (shape && !shapes->AddItem(shape)) {
661 delete shape;
662 return B_NO_MEMORY;
663 }
664 }
665
666 return B_OK;
667 }
668