blob: 6b8a356cfe3108cc891a7c36039a15d1193d49e4 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
//
//
#include "path_builder.h"
#include "handle.h"
//
// Verify that prim count is in sync with macro
//
#undef SPN_PATH_BUILDER_PRIM_TYPE_EXPAND_X
#define SPN_PATH_BUILDER_PRIM_TYPE_EXPAND_X(_p, _i, _n) +1
STATIC_ASSERT_MACRO_1((0 SPN_PATH_BUILDER_PRIM_TYPE_EXPAND()) == SPN_PATH_BUILDER_PRIM_TYPE_COUNT);
//
//
//
spn_result
spn_path_builder_retain(spn_path_builder_t path_builder)
{
++path_builder->refcount;
return SPN_SUCCESS;
}
spn_result
spn_path_builder_release(spn_path_builder_t path_builder)
{
SPN_ASSERT_STATE_ASSERT(SPN_PATH_BUILDER_STATE_READY, path_builder);
return path_builder->release(path_builder->impl);
}
spn_result
spn_path_builder_flush(spn_path_builder_t path_builder)
{
return path_builder->flush(path_builder->impl);
}
//
// PATH BODY
//
spn_result
spn_path_begin(spn_path_builder_t path_builder)
{
SPN_ASSERT_STATE_TRANSITION(SPN_PATH_BUILDER_STATE_READY,
SPN_PATH_BUILDER_STATE_BUILDING,
path_builder);
// begin the path
return path_builder->begin(path_builder->impl);
}
spn_result
spn_path_end(spn_path_builder_t path_builder, spn_path_t * path)
{
SPN_ASSERT_STATE_TRANSITION(SPN_PATH_BUILDER_STATE_BUILDING,
SPN_PATH_BUILDER_STATE_READY,
path_builder);
// update path header with proper counts
return path_builder->end(path_builder->impl, path);
}
//
// PATH SEGMENT OPS
//
static void
spn_path_move_to_1(spn_path_builder_t path_builder, float x0, float y0)
{
path_builder->curr[0].x = x0;
path_builder->curr[0].y = y0;
path_builder->curr[1].x = x0;
path_builder->curr[1].y = y0;
}
static void
spn_path_move_to_2(spn_path_builder_t path_builder, float x0, float y0, float x1, float y1)
{
path_builder->curr[0].x = x0;
path_builder->curr[0].y = y0;
path_builder->curr[1].x = x1;
path_builder->curr[1].y = y1;
}
spn_result
spn_path_move_to(spn_path_builder_t path_builder, float x0, float y0)
{
spn_path_move_to_1(path_builder, x0, y0);
return SPN_SUCCESS;
}
//
// Simplifying macros
//
// FIXME -- return DEVICE_LOST if a single path fills the ring
//
#define SPN_PB_CN_COORDS_APPEND(_pb, _p, _n, _c) *_pb->cn.coords._p[_n]++ = _c
#define SPN_PB_CN_ACQUIRE(_pb, _p) \
{ \
if (_pb->cn.rem._p == 0) \
{ \
spn_result const err = _pb->_p(path_builder->impl); \
if (err != SPN_SUCCESS) \
return err; \
} \
_pb->cn.rem._p -= 1; \
}
//
//
//
spn_result
spn_path_line_to(spn_path_builder_t path_builder, float x1, float y1)
{
SPN_PB_CN_ACQUIRE(path_builder, line);
SPN_PB_CN_COORDS_APPEND(path_builder, line, 0, path_builder->curr[0].x);
SPN_PB_CN_COORDS_APPEND(path_builder, line, 1, path_builder->curr[0].y);
SPN_PB_CN_COORDS_APPEND(path_builder, line, 2, x1);
SPN_PB_CN_COORDS_APPEND(path_builder, line, 3, y1);
spn_path_move_to_1(path_builder, x1, y1);
return SPN_SUCCESS;
}
spn_result
spn_path_quad_to(spn_path_builder_t path_builder, float x1, float y1, float x2, float y2)
{
SPN_PB_CN_ACQUIRE(path_builder, quad);
SPN_PB_CN_COORDS_APPEND(path_builder, quad, 0, path_builder->curr[0].x);
SPN_PB_CN_COORDS_APPEND(path_builder, quad, 1, path_builder->curr[0].y);
SPN_PB_CN_COORDS_APPEND(path_builder, quad, 2, x1);
SPN_PB_CN_COORDS_APPEND(path_builder, quad, 3, y1);
SPN_PB_CN_COORDS_APPEND(path_builder, quad, 4, x2);
SPN_PB_CN_COORDS_APPEND(path_builder, quad, 5, y2);
spn_path_move_to_2(path_builder, x2, y2, x1, y1);
return SPN_SUCCESS;
}
spn_result
spn_path_quad_smooth_to(spn_path_builder_t path_builder, float x2, float y2)
{
float const x1 = path_builder->curr[0].x * 2.0f - path_builder->curr[1].x;
float const y1 = path_builder->curr[0].y * 2.0f - path_builder->curr[1].y;
return spn_path_quad_to(path_builder, x1, y1, x2, y2);
}
spn_result
spn_path_cubic_to(
spn_path_builder_t path_builder, float x1, float y1, float x2, float y2, float x3, float y3)
{
SPN_PB_CN_ACQUIRE(path_builder, cubic);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 0, path_builder->curr[0].x);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 1, path_builder->curr[0].y);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 2, x1);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 3, y1);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 4, x2);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 5, y2);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 6, x3);
SPN_PB_CN_COORDS_APPEND(path_builder, cubic, 7, y3);
spn_path_move_to_2(path_builder, x3, y3, x2, y2);
return SPN_SUCCESS;
}
spn_result
spn_path_cubic_smooth_to(spn_path_builder_t path_builder, float x2, float y2, float x3, float y3)
{
float const x1 = path_builder->curr[0].x * 2.0f - path_builder->curr[1].x;
float const y1 = path_builder->curr[0].y * 2.0f - path_builder->curr[1].y;
return spn_path_cubic_to(path_builder, x1, y1, x2, y2, x3, y3);
}
//
//
//
spn_result
spn_path_rat_quad_to(
spn_path_builder_t path_builder, float x1, float y1, float x2, float y2, float w0)
{
SPN_PB_CN_ACQUIRE(path_builder, rat_quad);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 0, path_builder->curr[0].x);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 1, path_builder->curr[0].y);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 2, x1);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 3, y1);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 4, x2);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 5, y2);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_quad, 6, w0);
spn_path_move_to_1(path_builder, x2, y2);
return SPN_SUCCESS;
}
spn_result
spn_path_rat_cubic_to(spn_path_builder_t path_builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3,
float w0,
float w1)
{
SPN_PB_CN_ACQUIRE(path_builder, rat_cubic);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 0, path_builder->curr[0].x);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 1, path_builder->curr[0].y);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 2, x1);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 3, y1);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 4, x2);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 5, y2);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 6, x3);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 7, y3);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 8, w0);
SPN_PB_CN_COORDS_APPEND(path_builder, rat_cubic, 9, w1);
spn_path_move_to_1(path_builder, x3, y3);
return SPN_SUCCESS;
}
//
//
//
spn_result
spn_path_ellipse(spn_path_builder_t path_builder, float cx, float cy, float rx, float ry)
{
//
// FIXME -- we can implement this with rationals later...
//
//
// Approximate a circle with 4 cubics:
//
// http://en.wikipedia.org/wiki/B%C3%A9zier_spline#Approximating_circular_arcs
//
spn_path_move_to_1(path_builder, cx, cy + ry);
#define SPN_KAPPA_FLOAT 0.55228474983079339840f // moar digits!
float const kx = rx * SPN_KAPPA_FLOAT;
float const ky = ry * SPN_KAPPA_FLOAT;
spn_result err;
err = spn_path_cubic_to(path_builder, cx + kx, cy + ry, cx + rx, cy + ky, cx + rx, cy);
if (err)
return err;
err = spn_path_cubic_to(path_builder, cx + rx, cy - ky, cx + kx, cy - ry, cx, cy - ry);
if (err)
return err;
err = spn_path_cubic_to(path_builder, cx - kx, cy - ry, cx - rx, cy - ky, cx - rx, cy);
if (err)
return err;
err = spn_path_cubic_to(path_builder, cx - rx, cy + ky, cx - kx, cy + ry, cx, cy + ry);
return err;
}
//
//
//