* Copyright 2006, 2023, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Zardshard
*/
#include "FlatIconImporter.h"
#include <new>
#include <stdio.h>
#include <Archivable.h>
#include <DataIO.h>
#include <Message.h>
#include "AffineTransformer.h"
#include "AutoDeleter.h"
#include "ContourTransformer.h"
#include "FlatIconFormat.h"
#include "GradientTransformable.h"
#include "Icon.h"
#include "LittleEndianBuffer.h"
#include "PathCommandQueue.h"
#include "PathSourceShape.h"
#include "PerspectiveTransformer.h"
#include "Shape.h"
#include "StrokeTransformer.h"
#include "Style.h"
#include "VectorPath.h"
using std::nothrow;
_USING_ICON_NAMESPACE
FlatIconImporter::FlatIconImporter()
#ifdef ICON_O_MATIC
: Importer()
#endif
{
}
FlatIconImporter::~FlatIconImporter()
{
}
status_t
FlatIconImporter::Import(Icon* icon, BPositionIO* stream)
{
#ifdef ICON_O_MATIC
status_t ret = Init(icon);
if (ret < B_OK)
return ret;
#else
status_t ret;
#endif
off_t size = stream->Seek(0, SEEK_END);
if (stream->Seek(0, SEEK_SET) != 0)
return B_ERROR;
if (size <= 0 || size > 256 * 1024)
return B_BAD_VALUE;
LittleEndianBuffer buffer(size);
if (!buffer.Buffer())
return B_NO_MEMORY;
if (stream->Read(buffer.Buffer(), size) != size)
return B_ERROR;
ret = _ParseSections(buffer, icon);
return ret;
}
status_t
FlatIconImporter::Import(Icon* icon, uint8* _buffer, size_t size)
{
#ifdef ICON_O_MATIC
status_t ret = Init(icon);
if (ret < B_OK)
return ret;
#endif
if (!_buffer)
return B_BAD_VALUE;
LittleEndianBuffer buffer(_buffer, size);
return _ParseSections(buffer, icon);
}
status_t
FlatIconImporter::_ParseSections(LittleEndianBuffer& buffer, Icon* icon)
{
uint32 magic;
if (!buffer.Read(magic) || magic != FLAT_ICON_MAGIC)
return B_BAD_TYPE;
Container<Style>* styles = icon->Styles();
status_t ret = _ParseStyles(buffer, styles);
if (ret < B_OK) {
printf("FlatIconImporter::_ParseSections() - "
"error parsing styles: %s\n", strerror(ret));
return ret;
}
Container<VectorPath>* paths = icon->Paths();
ret = _ParsePaths(buffer, paths);
if (ret < B_OK) {
printf("FlatIconImporter::_ParseSections() - "
"error parsing paths: %s\n", strerror(ret));
return ret;
}
ret = _ParseShapes(buffer, styles, paths, icon->Shapes());
if (ret < B_OK) {
printf("FlatIconImporter::_ParseSections() - "
"error parsing shapes: %s\n", strerror(ret));
return ret;
}
return B_OK;
}
static bool
_ReadTransformable(LittleEndianBuffer& buffer, Transformable* transformable)
{
int32 matrixSize = Transformable::matrix_size;
double matrix[matrixSize];
for (int32 i = 0; i < matrixSize; i++) {
float value;
if (!read_float_24(buffer, value))
return false;
matrix[i] = value;
}
transformable->LoadFrom(matrix);
return true;
}
static bool
_ReadTranslation(LittleEndianBuffer& buffer, Transformable* transformable)
{
BPoint t;
if (read_coord(buffer, t.x) && read_coord(buffer, t.y)) {
transformable->TranslateBy(t);
return true;
}
return false;
}
static Style*
_ReadColorStyle(LittleEndianBuffer& buffer, bool alpha, bool gray)
{
rgb_color color;
if (alpha) {
if (gray) {
if (!buffer.Read(color.red)
|| !buffer.Read(color.alpha))
return NULL;
color.green = color.blue = color.red;
} else {
if (!buffer.Read((uint32&)color))
return NULL;
}
} else {
color.alpha = 255;
if (gray) {
if (!buffer.Read(color.red))
return NULL;
color.green = color.blue = color.red;
} else {
if (!buffer.Read(color.red)
|| !buffer.Read(color.green)
|| !buffer.Read(color.blue))
return NULL;
}
}
return new (nothrow) Style(color);
}
static Style*
_ReadGradientStyle(LittleEndianBuffer& buffer)
{
Style* style = new (nothrow) Style();
if (!style)
return NULL;
ObjectDeleter<Style> styleDeleter(style);
uint8 gradientType;
uint8 gradientFlags;
uint8 gradientStopCount;
if (!buffer.Read(gradientType)
|| !buffer.Read(gradientFlags)
|| !buffer.Read(gradientStopCount)) {
return NULL;
}
Gradient gradient(true);
gradient.SetType((gradients_type)gradientType);
if (gradientFlags & GRADIENT_FLAG_TRANSFORM) {
if (!_ReadTransformable(buffer, &gradient))
return NULL;
}
bool alpha = !(gradientFlags & GRADIENT_FLAG_NO_ALPHA);
bool gray = gradientFlags & GRADIENT_FLAG_GRAYS;
for (int32 i = 0; i < gradientStopCount; i++) {
uint8 stopOffset;
rgb_color color;
if (!buffer.Read(stopOffset))
return NULL;
if (alpha) {
if (gray) {
if (!buffer.Read(color.red)
|| !buffer.Read(color.alpha))
return NULL;
color.green = color.blue = color.red;
} else {
if (!buffer.Read((uint32&)color))
return NULL;
}
} else {
color.alpha = 255;
if (gray) {
if (!buffer.Read(color.red))
return NULL;
color.green = color.blue = color.red;
} else {
if (!buffer.Read(color.red)
|| !buffer.Read(color.green)
|| !buffer.Read(color.blue)) {
return NULL;
}
}
}
gradient.AddColor(color, stopOffset / 255.0);
}
style->SetGradient(&gradient);
styleDeleter.Detach();
return style;
}
status_t
FlatIconImporter::_ParseStyles(LittleEndianBuffer& buffer,
Container<Style>* styles)
{
uint8 styleCount;
if (!buffer.Read(styleCount))
return B_ERROR;
for (int32 i = 0; i < styleCount; i++) {
uint8 styleType;
if (!buffer.Read(styleType))
return B_ERROR;
Style* style = NULL;
if (styleType == STYLE_TYPE_SOLID_COLOR) {
style = _ReadColorStyle(buffer, true, false);
if (!style)
return B_NO_MEMORY;
} else if (styleType == STYLE_TYPE_SOLID_COLOR_NO_ALPHA) {
style = _ReadColorStyle(buffer, false, false);
if (!style)
return B_NO_MEMORY;
} else if (styleType == STYLE_TYPE_SOLID_GRAY) {
style = _ReadColorStyle(buffer, true, true);
if (!style)
return B_NO_MEMORY;
} else if (styleType == STYLE_TYPE_SOLID_GRAY_NO_ALPHA) {
style = _ReadColorStyle(buffer, false, true);
if (!style)
return B_NO_MEMORY;
} else if (styleType == STYLE_TYPE_GRADIENT) {
style = _ReadGradientStyle(buffer);
if (!style)
return B_NO_MEMORY;
} else {
uint16 tagLength;
if (!buffer.Read(tagLength))
return B_ERROR;
buffer.Skip(tagLength);
continue;
}
if (style && !styles->AddItem(style)) {
delete style;
return B_NO_MEMORY;
}
}
return B_OK;
}
static bool
read_path_no_curves(LittleEndianBuffer& buffer, VectorPath* path,
uint8 pointCount)
{
for (uint32 p = 0; p < pointCount; p++) {
BPoint point;
if (!read_coord(buffer, point.x)
|| !read_coord(buffer, point.y))
return false;
if (!path->AddPoint(point))
return false;
}
return true;
}
static bool
read_path_curves(LittleEndianBuffer& buffer, VectorPath* path,
uint8 pointCount)
{
for (uint32 p = 0; p < pointCount; p++) {
BPoint point;
if (!read_coord(buffer, point.x)
|| !read_coord(buffer, point.y))
return false;
BPoint pointIn;
if (!read_coord(buffer, pointIn.x)
|| !read_coord(buffer, pointIn.y))
return false;
BPoint pointOut;
if (!read_coord(buffer, pointOut.x)
|| !read_coord(buffer, pointOut.y))
return false;
if (!path->AddPoint(point, pointIn, pointOut, false))
return false;
}
return true;
}
static bool
read_path_with_commands(LittleEndianBuffer& buffer, VectorPath* path,
uint8 pointCount)
{
PathCommandQueue queue;
return queue.Read(buffer, path, pointCount);
}
status_t
FlatIconImporter::_ParsePaths(LittleEndianBuffer& buffer,
Container<VectorPath>* paths)
{
uint8 pathCount;
if (!buffer.Read(pathCount))
return B_ERROR;
for (int32 i = 0; i < pathCount; i++) {
uint8 pathFlags;
uint8 pointCount;
if (!buffer.Read(pathFlags) || !buffer.Read(pointCount))
return B_ERROR;
VectorPath* path = new (nothrow) VectorPath();
if (!path)
return B_NO_MEMORY;
bool error = false;
if (pathFlags & PATH_FLAG_NO_CURVES) {
if (!read_path_no_curves(buffer, path, pointCount))
error = true;
} else if (pathFlags & PATH_FLAG_USES_COMMANDS) {
if (!read_path_with_commands(buffer, path, pointCount))
error = true;
} else {
if (!read_path_curves(buffer, path, pointCount))
error = true;
}
if (error) {
delete path;
return B_ERROR;
}
path->CleanUp();
if (pathFlags & PATH_FLAG_CLOSED)
path->SetClosed(true);
if (!paths->AddItem(path)) {
delete path;
return B_NO_MEMORY;
}
}
return B_OK;
}
static Transformer*
_ReadTransformer(LittleEndianBuffer& buffer, VertexSource& source, Shape* shape)
{
uint8 transformerType;
if (!buffer.Read(transformerType))
return NULL;
switch (transformerType) {
case TRANSFORMER_TYPE_AFFINE: {
AffineTransformer* affine
= new (nothrow) AffineTransformer(source);
if (!affine)
return NULL;
double matrix[6];
for (int32 i = 0; i < 6; i++) {
float value;
if (!buffer.Read(value)) {
delete affine;
return NULL;
}
matrix[i] = value;
}
affine->load_from(matrix);
return affine;
}
case TRANSFORMER_TYPE_CONTOUR: {
ContourTransformer* contour
= new (nothrow) ContourTransformer(source);
uint8 width;
uint8 lineJoin;
uint8 miterLimit;
if (!contour
|| !buffer.Read(width)
|| !buffer.Read(lineJoin)
|| !buffer.Read(miterLimit)) {
delete contour;
return NULL;
}
contour->width(width - 128.0);
contour->line_join((agg::line_join_e)lineJoin);
contour->miter_limit(miterLimit);
return contour;
}
case TRANSFORMER_TYPE_PERSPECTIVE: {
PerspectiveTransformer* perspective
= new (nothrow) PerspectiveTransformer(source, shape);
if (!perspective)
return NULL;
double matrix[9];
for (int32 i = 0; i < 9; i++) {
float value;
if (!read_float_24(buffer, value)) {
delete perspective;
return NULL;
}
matrix[i] = value;
}
perspective->load_from(matrix);
return perspective;
}
case TRANSFORMER_TYPE_STROKE: {
StrokeTransformer* stroke
= new (nothrow) StrokeTransformer(source);
uint8 width;
uint8 lineOptions;
uint8 miterLimit;
if (!stroke
|| !buffer.Read(width)
|| !buffer.Read(lineOptions)
|| !buffer.Read(miterLimit)) {
delete stroke;
return NULL;
}
stroke->width(width - 128.0);
uint8 lineJoin = lineOptions & 15;
stroke->line_join((agg::line_join_e)lineJoin);
uint8 lineCap = lineOptions >> 4;
stroke->line_cap((agg::line_cap_e)lineCap);
stroke->miter_limit(miterLimit);
return stroke;
}
default: {
uint16 tagLength;
if (!buffer.Read(tagLength))
return NULL;
buffer.Skip(tagLength);
return NULL;
}
}
}
Shape*
FlatIconImporter::_ReadPathSourceShape(LittleEndianBuffer& buffer,
Container<Style>* styles,
Container<VectorPath>* paths)
{
uint8 styleIndex;
uint8 pathCount;
if (!buffer.Read(styleIndex) || !buffer.Read(pathCount))
return NULL;
#ifdef ICON_O_MATIC
Style* style = styles->ItemAt(StyleIndexFor(styleIndex));
#else
Style* style = styles->ItemAt(styleIndex);
#endif
if (!style) {
printf("_ReadPathSourceShape() - "
"shape references non-existing style %d\n", styleIndex);
return NULL;
}
PathSourceShape* shape = new (nothrow) PathSourceShape(style);
ObjectDeleter<Shape> shapeDeleter(shape);
if (!shape || shape->InitCheck() < B_OK)
return NULL;
for (uint32 i = 0; i < pathCount; i++) {
uint8 pathIndex;
if (!buffer.Read(pathIndex))
return NULL;
#ifdef ICON_O_MATIC
VectorPath* path = paths->ItemAt(PathIndexFor(pathIndex));
#else
VectorPath* path = paths->ItemAt(pathIndex);
#endif
if (!path) {
printf("_ReadPathSourceShape() - "
"shape references non-existing path %d\n", pathIndex);
continue;
}
shape->Paths()->AddItem(path);
}
uint8 shapeFlags;
if (!buffer.Read(shapeFlags))
return NULL;
shape->SetHinting(shapeFlags & SHAPE_FLAG_HINTING);
if (shapeFlags & SHAPE_FLAG_TRANSFORM) {
if (!_ReadTransformable(buffer, shape))
return NULL;
} else if (shapeFlags & SHAPE_FLAG_TRANSLATION) {
if (!_ReadTranslation(buffer, shape))
return NULL;
}
if (shapeFlags & SHAPE_FLAG_LOD_SCALE) {
uint8 minScale;
uint8 maxScale;
if (!buffer.Read(minScale) || !buffer.Read(maxScale))
return NULL;
shape->SetMinVisibilityScale(minScale / 63.75);
shape->SetMaxVisibilityScale(maxScale / 63.75);
}
if (shapeFlags & SHAPE_FLAG_HAS_TRANSFORMERS) {
uint8 transformerCount;
if (!buffer.Read(transformerCount))
return NULL;
for (uint32 i = 0; i < transformerCount; i++) {
Transformer* transformer
= _ReadTransformer(buffer, shape->VertexSource(), shape);
if (transformer && !shape->Transformers()->AddItem(transformer)) {
delete transformer;
return NULL;
}
}
}
shapeDeleter.Detach();
return shape;
}
status_t
FlatIconImporter::_ParseShapes(LittleEndianBuffer& buffer,
Container<Style>* styles,
Container<VectorPath>* paths,
Container<Shape>* shapes)
{
uint8 shapeCount;
if (!buffer.Read(shapeCount))
return B_ERROR;
for (uint32 i = 0; i < shapeCount; i++) {
uint8 shapeType;
if (!buffer.Read(shapeType))
return B_ERROR;
Shape* shape = NULL;
if (shapeType == SHAPE_TYPE_PATH_SOURCE) {
shape = _ReadPathSourceShape(buffer, styles, paths);
if (!shape)
return B_NO_MEMORY;
} else {
uint16 tagLength;
if (!buffer.Read(tagLength))
return B_ERROR;
buffer.Skip(tagLength);
continue;
}
if (shape && !shapes->AddItem(shape)) {
delete shape;
return B_NO_MEMORY;
}
}
return B_OK;
}