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