vfs: check userland buffers before reading them.
[haiku.git] / src / apps / icon-o-matic / import_export / flat_icon / FlatIconExporter.cpp
bloba0eb7b8c2474d7c64d4724dc819bbb72e111590f
1 /*
2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
9 #include "FlatIconExporter.h"
11 #include <new>
12 #include <stdio.h>
14 #include <Archivable.h>
15 #include <DataIO.h>
16 #include <Message.h>
17 #include <Node.h>
19 #include "AffineTransformer.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"
34 using std::nothrow;
36 // constructor
37 FlatIconExporter::FlatIconExporter()
38 #if PRINT_STATISTICS
39 : fStyleSectionSize(0)
40 , fGradientSize(0)
41 , fGradientTransformSize(0)
42 , fPathSectionSize(0)
43 , fShapeSectionSize(0)
44 , fPointCount(0)
45 #endif // PRINT_STATISTICS
49 // destructor
50 FlatIconExporter::~FlatIconExporter()
52 #if PRINT_STATISTICS
53 printf("Statistics\n"
54 "--style section size: %" B_PRId32 "\n"
55 " gradients: %" B_PRId32 "\n"
56 " gradient transforms: %" B_PRId32 "\n"
57 "---path section size: %" B_PRId32 "\n"
58 "--shape section size: %" B_PRId32 "\n"
59 "---total/different points: %" B_PRId32 "/%" B_PRIdSSIZE "\n",
60 fStyleSectionSize,
61 fGradientSize,
62 fGradientTransformSize,
63 fPathSectionSize,
64 fShapeSectionSize,
65 fPointCount,
66 fUsedPoints.size());
67 #endif // PRINT_STATISTICS
70 // Export
71 status_t
72 FlatIconExporter::Export(const Icon* icon, BPositionIO* stream)
74 LittleEndianBuffer buffer;
76 // flatten icon
77 status_t ret = _Export(buffer, icon);
78 if (ret < B_OK)
79 return ret;
81 // write buffer to stream
82 ssize_t written = stream->Write(buffer.Buffer(), buffer.SizeUsed());
83 if (written != (ssize_t)buffer.SizeUsed()) {
84 if (written < 0)
85 return (status_t)written;
86 return B_ERROR;
89 return B_OK;
92 // MIMEType
93 const char*
94 FlatIconExporter::MIMEType()
96 return NULL;
99 // Export
100 status_t
101 FlatIconExporter::Export(const Icon* icon, BNode* node,
102 const char* attrName)
104 LittleEndianBuffer buffer;
106 // flatten icon
107 status_t ret = _Export(buffer, icon);
108 if (ret < B_OK) {
109 printf("failed to export to buffer: %s\n", strerror(ret));
110 return ret;
113 #ifndef __HAIKU__
114 // work arround a BFS bug, attributes with the same name but different
115 // type fail to be written
116 node->RemoveAttr(attrName);
117 #endif
119 // write buffer to attribute
120 ssize_t written = node->WriteAttr(attrName, B_VECTOR_ICON_TYPE, 0,
121 buffer.Buffer(), buffer.SizeUsed());
122 if (written != (ssize_t)buffer.SizeUsed()) {
123 if (written < 0) {
124 printf("failed to write attribute: %s\n",
125 strerror((status_t)written));
126 return (status_t)written;
128 printf("failed to write attribute\n");
129 return B_ERROR;
132 return B_OK;
135 // #pragma mark -
137 // _Export
138 status_t
139 FlatIconExporter::_Export(LittleEndianBuffer& buffer, const Icon* icon)
141 if (!buffer.Write(FLAT_ICON_MAGIC))
142 return B_NO_MEMORY;
144 #if PRINT_STATISTICS
145 fGradientSize = 0;
146 fGradientTransformSize = 0;
147 fPointCount = 0;
148 #endif
150 // styles
151 StyleContainer* styles = icon->Styles();
152 status_t ret = _WriteStyles(buffer, styles);
153 if (ret < B_OK)
154 return ret;
156 #if PRINT_STATISTICS
157 fStyleSectionSize = buffer.SizeUsed();
158 #endif
160 // paths
161 PathContainer* paths = icon->Paths();
162 ret = _WritePaths(buffer, paths);
163 if (ret < B_OK)
164 return ret;
166 #if PRINT_STATISTICS
167 fPathSectionSize = buffer.SizeUsed() - fStyleSectionSize;
168 #endif
170 // shapes
171 ret = _WriteShapes(buffer, styles, paths, icon->Shapes());
172 if (ret < B_OK)
173 return ret;
175 #if PRINT_STATISTICS
176 fShapeSectionSize = buffer.SizeUsed()
177 - (fStyleSectionSize + fPathSectionSize);
178 #endif
180 return B_OK;
183 // _WriteTransformable
184 static bool
185 _WriteTransformable(LittleEndianBuffer& buffer,
186 const Transformable* transformable)
188 int32 matrixSize = Transformable::matrix_size;
189 double matrix[matrixSize];
190 transformable->StoreTo(matrix);
191 for (int32 i = 0; i < matrixSize; i++) {
192 // if (!buffer.Write((float)matrix[i]))
193 // return false;
194 if (!write_float_24(buffer, (float)matrix[i]))
195 return false;
197 return true;
200 // _WriteTranslation
201 static bool
202 _WriteTranslation(LittleEndianBuffer& buffer,
203 const Transformable* transformable)
205 BPoint t(B_ORIGIN);
206 transformable->Transform(&t);
207 return write_coord(buffer, t.x) && write_coord(buffer, t.y);
210 // _WriteStyles
211 status_t
212 FlatIconExporter::_WriteStyles(LittleEndianBuffer& buffer,
213 StyleContainer* styles)
215 uint8 styleCount = min_c(255, styles->CountStyles());
216 if (!buffer.Write(styleCount))
217 return B_NO_MEMORY;
219 for (int32 i = 0; i < styleCount; i++) {
220 Style* style = styles->StyleAtFast(i);
222 // style type
223 uint8 styleType;
225 const Gradient* gradient = style->Gradient();
226 if (gradient) {
227 styleType = STYLE_TYPE_GRADIENT;
228 } else {
229 rgb_color color = style->Color();
230 if (color.red == color.green && color.red == color.blue) {
231 // gray
232 if (style->Color().alpha == 255)
233 styleType = STYLE_TYPE_SOLID_GRAY_NO_ALPHA;
234 else
235 styleType = STYLE_TYPE_SOLID_GRAY;
236 } else {
237 // color
238 if (style->Color().alpha == 255)
239 styleType = STYLE_TYPE_SOLID_COLOR_NO_ALPHA;
240 else
241 styleType = STYLE_TYPE_SOLID_COLOR;
245 if (!buffer.Write(styleType))
246 return B_NO_MEMORY;
248 if (styleType == STYLE_TYPE_SOLID_COLOR) {
249 // solid color
250 rgb_color color = style->Color();
251 if (!buffer.Write(*(uint32*)&color))
252 return B_NO_MEMORY;
253 } else if (styleType == STYLE_TYPE_SOLID_COLOR_NO_ALPHA) {
254 // solid color without alpha
255 rgb_color color = style->Color();
256 if (!buffer.Write(color.red)
257 || !buffer.Write(color.green)
258 || !buffer.Write(color.blue))
259 return B_NO_MEMORY;
260 } else if (styleType == STYLE_TYPE_SOLID_GRAY) {
261 // solid gray
262 rgb_color color = style->Color();
263 if (!buffer.Write(color.red)
264 || !buffer.Write(color.alpha))
265 return B_NO_MEMORY;
266 } else if (styleType == STYLE_TYPE_SOLID_GRAY_NO_ALPHA) {
267 // solid gray without alpha
268 if (!buffer.Write(style->Color().red))
269 return B_NO_MEMORY;
270 } else if (styleType == STYLE_TYPE_GRADIENT) {
271 // gradient
272 if (!_WriteGradient(buffer, gradient))
273 return B_NO_MEMORY;
277 return B_OK;
280 // _AnalysePath
281 bool
282 FlatIconExporter::_AnalysePath(VectorPath* path, uint8 pointCount,
283 int32& straightCount, int32& lineCount, int32& curveCount)
285 straightCount = 0;
286 lineCount = 0;
287 curveCount = 0;
289 BPoint lastPoint(B_ORIGIN);
290 for (uint32 p = 0; p < pointCount; p++) {
291 BPoint point;
292 BPoint pointIn;
293 BPoint pointOut;
294 if (!path->GetPointsAt(p, point, pointIn, pointOut))
295 return B_ERROR;
296 if (point == pointIn && point == pointOut) {
297 if (point.x == lastPoint.x || point.y == lastPoint.y)
298 straightCount++;
299 else
300 lineCount++;
301 } else
302 curveCount++;
303 lastPoint = point;
305 #if PRINT_STATISTICS
306 fUsedPoints.insert(point);
307 fUsedPoints.insert(pointIn);
308 fUsedPoints.insert(pointOut);
309 fPointCount += 3;
310 #endif
313 return true;
317 // write_path_no_curves
318 static bool
319 write_path_no_curves(LittleEndianBuffer& buffer, VectorPath* path,
320 uint8 pointCount)
322 //printf("write_path_no_curves()\n");
323 for (uint32 p = 0; p < pointCount; p++) {
324 BPoint point;
325 if (!path->GetPointAt(p, point))
326 return false;
327 if (!write_coord(buffer, point.x) || !write_coord(buffer, point.y))
328 return false;
330 return true;
333 // write_path_curves
334 static bool
335 write_path_curves(LittleEndianBuffer& buffer, VectorPath* path,
336 uint8 pointCount)
338 //printf("write_path_curves()\n");
339 for (uint32 p = 0; p < pointCount; p++) {
340 BPoint point;
341 BPoint pointIn;
342 BPoint pointOut;
343 if (!path->GetPointsAt(p, point, pointIn, pointOut))
344 return false;
345 if (!write_coord(buffer, point.x)
346 || !write_coord(buffer, point.y))
347 return false;
348 if (!write_coord(buffer, pointIn.x)
349 || !write_coord(buffer, pointIn.y))
350 return false;
351 if (!write_coord(buffer, pointOut.x)
352 || !write_coord(buffer, pointOut.y))
353 return false;
355 return true;
358 // write_path_with_commands
359 static bool
360 write_path_with_commands(LittleEndianBuffer& buffer, VectorPath* path,
361 uint8 pointCount)
363 PathCommandQueue queue;
364 return queue.Write(buffer, path, pointCount);
368 // _WritePaths
369 status_t
370 FlatIconExporter::_WritePaths(LittleEndianBuffer& buffer, PathContainer* paths)
372 uint8 pathCount = min_c(255, paths->CountPaths());
373 if (!buffer.Write(pathCount))
374 return B_NO_MEMORY;
376 for (uint32 i = 0; i < pathCount; i++) {
377 VectorPath* path = paths->PathAtFast(i);
378 uint8 pathFlags = 0;
379 if (path->IsClosed())
380 pathFlags |= PATH_FLAG_CLOSED;
382 uint8 pointCount = min_c(255, path->CountPoints());
384 // see if writing segments with commands is more efficient
385 // than writing all segments as curves with no commands
386 int32 straightCount;
387 int32 lineCount;
388 int32 curveCount;
389 if (!_AnalysePath(path, pointCount,
390 straightCount, lineCount, curveCount))
391 return B_ERROR;
393 int32 commandPathLength
394 = pointCount + straightCount * 2 + lineCount * 4 + curveCount * 12;
395 int32 plainPathLength
396 = pointCount * 12;
397 //printf("segments: %d, command length: %ld, plain length: %ld\n",
398 // pointCount, commandPathLength, plainPathLength);
400 if (commandPathLength < plainPathLength) {
401 if (curveCount == 0)
402 pathFlags |= PATH_FLAG_NO_CURVES;
403 else
404 pathFlags |= PATH_FLAG_USES_COMMANDS;
407 if (!buffer.Write(pathFlags) || !buffer.Write(pointCount))
408 return B_NO_MEMORY;
410 if (pathFlags & PATH_FLAG_NO_CURVES) {
411 if (!write_path_no_curves(buffer, path, pointCount))
412 return B_ERROR;
413 } else if (pathFlags & PATH_FLAG_USES_COMMANDS) {
414 if (!write_path_with_commands(buffer, path, pointCount))
415 return B_ERROR;
416 } else {
417 if (!write_path_curves(buffer, path, pointCount))
418 return B_ERROR;
422 return B_OK;
425 // _WriteTransformer
426 static bool
427 _WriteTransformer(LittleEndianBuffer& buffer, Transformer* t)
429 if (AffineTransformer* affine
430 = dynamic_cast<AffineTransformer*>(t)) {
431 // affine
432 if (!buffer.Write((uint8)TRANSFORMER_TYPE_AFFINE))
433 return false;
434 double matrix[6];
435 affine->store_to(matrix);
436 for (int32 i = 0; i < 6; i++) {
437 if (!write_float_24(buffer, (float)matrix[i]))
438 return false;
441 } else if (ContourTransformer* contour
442 = dynamic_cast<ContourTransformer*>(t)) {
443 // contour
444 if (!buffer.Write((uint8)TRANSFORMER_TYPE_CONTOUR))
445 return false;
446 uint8 width = (uint8)((int8)contour->width() + 128);
447 uint8 lineJoin = (uint8)contour->line_join();
448 uint8 miterLimit = (uint8)contour->miter_limit();
449 if (!buffer.Write(width)
450 || !buffer.Write(lineJoin)
451 || !buffer.Write(miterLimit))
452 return false;
454 } else if (dynamic_cast<PerspectiveTransformer*>(t)) {
455 // perspective
456 if (!buffer.Write((uint8)TRANSFORMER_TYPE_PERSPECTIVE))
457 return false;
458 // TODO: ... (upgrade AGG for storage support of trans_perspective)
460 } else if (StrokeTransformer* stroke
461 = dynamic_cast<StrokeTransformer*>(t)) {
462 // stroke
463 if (!buffer.Write((uint8)TRANSFORMER_TYPE_STROKE))
464 return false;
465 uint8 width = (uint8)((int8)stroke->width() + 128);
466 uint8 lineOptions = (uint8)stroke->line_join();
467 lineOptions |= ((uint8)stroke->line_cap()) << 4;
468 uint8 miterLimit = (uint8)stroke->miter_limit();
470 if (!buffer.Write(width)
471 || !buffer.Write(lineOptions)
472 || !buffer.Write(miterLimit))
473 return false;
476 return true;
479 // _WritePathSourceShape
480 static bool
481 _WritePathSourceShape(LittleEndianBuffer& buffer, Shape* shape,
482 StyleContainer* styles, PathContainer* paths)
484 // find out which style this shape uses
485 Style* style = shape->Style();
486 if (!style)
487 return false;
489 int32 styleIndex = styles->IndexOf(style);
490 if (styleIndex < 0 || styleIndex > 255)
491 return false;
493 uint8 pathCount = min_c(255, shape->Paths()->CountPaths());
495 // write shape type and style index
496 if (!buffer.Write((uint8)SHAPE_TYPE_PATH_SOURCE)
497 || !buffer.Write((uint8)styleIndex)
498 || !buffer.Write(pathCount))
499 return false;
501 // find out which paths this shape uses
502 for (uint32 i = 0; i < pathCount; i++) {
503 VectorPath* path = shape->Paths()->PathAtFast(i);
504 int32 pathIndex = paths->IndexOf(path);
505 if (pathIndex < 0 || pathIndex > 255)
506 return false;
508 if (!buffer.Write((uint8)pathIndex))
509 return false;
512 uint8 transformerCount = min_c(255, shape->CountTransformers());
514 // shape flags
515 uint8 shapeFlags = 0;
516 if (!shape->IsIdentity()) {
517 if (shape->IsTranslationOnly())
518 shapeFlags |= SHAPE_FLAG_TRANSLATION;
519 else
520 shapeFlags |= SHAPE_FLAG_TRANSFORM;
522 if (shape->Hinting())
523 shapeFlags |= SHAPE_FLAG_HINTING;
524 if (shape->MinVisibilityScale() != 0.0
525 || shape->MaxVisibilityScale() != 4.0)
526 shapeFlags |= SHAPE_FLAG_LOD_SCALE;
527 if (transformerCount > 0)
528 shapeFlags |= SHAPE_FLAG_HAS_TRANSFORMERS;
530 if (!buffer.Write((uint8)shapeFlags))
531 return false;
533 if (shapeFlags & SHAPE_FLAG_TRANSFORM) {
534 // transformation
535 if (!_WriteTransformable(buffer, shape))
536 return false;
537 } else if (shapeFlags & SHAPE_FLAG_TRANSLATION) {
538 // translation
539 if (!_WriteTranslation(buffer, shape))
540 return false;
543 if (shapeFlags & SHAPE_FLAG_LOD_SCALE) {
544 // min max visibility scale
545 if (!buffer.Write(
546 (uint8)(shape->MinVisibilityScale() * 63.75 + 0.5))
547 || !buffer.Write(
548 (uint8)(shape->MaxVisibilityScale() * 63.75 + 0.5))) {
549 return false;
553 if (shapeFlags & SHAPE_FLAG_HAS_TRANSFORMERS) {
554 // transformers
555 if (!buffer.Write(transformerCount))
556 return false;
558 for (uint32 i = 0; i < transformerCount; i++) {
559 Transformer* transformer = shape->TransformerAtFast(i);
560 if (!_WriteTransformer(buffer, transformer))
561 return false;
565 return true;
568 // _WriteShapes
569 status_t
570 FlatIconExporter::_WriteShapes(LittleEndianBuffer& buffer,
571 StyleContainer* styles,
572 PathContainer* paths,
573 ShapeContainer* shapes)
575 uint8 shapeCount = min_c(255, shapes->CountShapes());
576 if (!buffer.Write(shapeCount))
577 return B_NO_MEMORY;
579 for (uint32 i = 0; i < shapeCount; i++) {
580 Shape* shape = shapes->ShapeAtFast(i);
581 if (!_WritePathSourceShape(buffer, shape, styles, paths))
582 return B_ERROR;
585 return B_OK;
588 // _WriteGradient
589 bool
590 FlatIconExporter::_WriteGradient(LittleEndianBuffer& buffer,
591 const Gradient* gradient)
593 #if PRINT_STATISTICS
594 size_t currentSize = buffer.SizeUsed();
595 #endif
597 uint8 gradientType = (uint8)gradient->Type();
598 uint8 gradientFlags = 0;
599 uint8 gradientStopCount = (uint8)gradient->CountColors();
601 // figure out flags
602 if (!gradient->IsIdentity())
603 gradientFlags |= GRADIENT_FLAG_TRANSFORM;
605 bool alpha = false;
606 bool gray = true;
607 for (int32 i = 0; i < gradientStopCount; i++) {
608 BGradient::ColorStop* step = gradient->ColorAtFast(i);
609 if (step->color.alpha < 255)
610 alpha = true;
611 if (step->color.red != step->color.green
612 || step->color.red != step->color.blue)
613 gray = false;
615 if (!alpha)
616 gradientFlags |= GRADIENT_FLAG_NO_ALPHA;
617 if (gray)
618 gradientFlags |= GRADIENT_FLAG_GRAYS;
620 if (!buffer.Write(gradientType)
621 || !buffer.Write(gradientFlags)
622 || !buffer.Write(gradientStopCount))
623 return false;
625 if (gradientFlags & GRADIENT_FLAG_TRANSFORM) {
626 if (!_WriteTransformable(buffer, gradient))
627 return false;
628 #if PRINT_STATISTICS
629 fGradientTransformSize += Transformable::matrix_size * sizeof(float);
630 #endif
633 for (int32 i = 0; i < gradientStopCount; i++) {
634 BGradient::ColorStop* step = gradient->ColorAtFast(i);
635 uint8 stopOffset = (uint8)(step->offset * 255.0);
636 uint32 color = (uint32&)step->color;
637 if (!buffer.Write(stopOffset))
638 return false;
639 if (alpha) {
640 if (gray) {
641 if (!buffer.Write(step->color.red)
642 || !buffer.Write(step->color.alpha))
643 return false;
644 } else {
645 if (!buffer.Write(color))
646 return false;
648 } else {
649 if (gray) {
650 if (!buffer.Write(step->color.red))
651 return false;
652 } else {
653 if (!buffer.Write(step->color.red)
654 || !buffer.Write(step->color.green)
655 || !buffer.Write(step->color.blue))
656 return false;
661 #if PRINT_STATISTICS
662 fGradientSize += buffer.SizeUsed() - currentSize;
663 #endif
665 return true;