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