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
34using std::nothrow;
35
36// constructor
37FlatIconImporter::FlatIconImporter()
38#ifdef ICON_O_MATIC
39	: Importer()
40#endif
41{
42}
43
44// destructor
45FlatIconImporter::~FlatIconImporter()
46{
47}
48
49// Import
50status_t
51FlatIconImporter::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
84status_t
85FlatIconImporter::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
105status_t
106FlatIconImporter::_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
143static 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
159static 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
172static 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
203static 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
277status_t
278FlatIconImporter::_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
334static bool
335read_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
351static bool
352read_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
378static bool
379read_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
388status_t
389FlatIconImporter::_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
438static 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
522Shape*
523FlatIconImporter::_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
618status_t
619FlatIconImporter::_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