xref: /haiku/src/libs/icon/flat_icon/FlatIconImporter.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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
40 FlatIconImporter::FlatIconImporter()
41 #ifdef ICON_O_MATIC
42 	: Importer()
43 #endif
44 {
45 }
46 
47 // destructor
48 FlatIconImporter::~FlatIconImporter()
49 {
50 }
51 
52 // Import
53 status_t
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
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
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
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
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*
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*
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
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
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
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
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
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*
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*
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
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