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
34using std::nothrow;
35
36_USING_ICON_NAMESPACE
37
38
39// constructor
40FlatIconImporter::FlatIconImporter()
41#ifdef ICON_O_MATIC
42	: Importer()
43#endif
44{
45}
46
47// destructor
48FlatIconImporter::~FlatIconImporter()
49{
50}
51
52// Import
53status_t
54FlatIconImporter::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
87status_t
88FlatIconImporter::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
108status_t
109FlatIconImporter::_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
146static 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
162static 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
175static 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
206static 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
280status_t
281FlatIconImporter::_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
337static bool
338read_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
354static bool
355read_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
381static bool
382read_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
391status_t
392FlatIconImporter::_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
441static 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
535Shape*
536FlatIconImporter::_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
631status_t
632FlatIconImporter::_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