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