mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
sw_engine: implement linear gradient feature
also added testLinearGradient Change-Id: I9cce74b9fc40c4ebd978939ee50955e44e7f44f2
This commit is contained in:
parent
f3afd2a636
commit
5c988d01a5
16 changed files with 544 additions and 150 deletions
|
@ -51,14 +51,10 @@ protected: \
|
||||||
#define _TVG_DECLARE_ACCESSOR(A) \
|
#define _TVG_DECLARE_ACCESSOR(A) \
|
||||||
friend A
|
friend A
|
||||||
|
|
||||||
#define _TVG_DECLARE_ACCESSORS(A, B) \
|
|
||||||
friend A; \
|
|
||||||
friend B
|
|
||||||
|
|
||||||
#define _TVG_DECALRE_IDENTIFIER() \
|
#define _TVG_DECALRE_IDENTIFIER() \
|
||||||
|
auto id() const { return _id; } \
|
||||||
protected: \
|
protected: \
|
||||||
unsigned id
|
unsigned _id
|
||||||
|
|
||||||
|
|
||||||
namespace tvg
|
namespace tvg
|
||||||
{
|
{
|
||||||
|
@ -101,7 +97,6 @@ public:
|
||||||
virtual Result bounds(float* x, float* y, float* w, float* h) const = 0;
|
virtual Result bounds(float* x, float* y, float* w, float* h) const = 0;
|
||||||
|
|
||||||
_TVG_DECALRE_IDENTIFIER();
|
_TVG_DECALRE_IDENTIFIER();
|
||||||
_TVG_DECLARE_ACCESSORS(Canvas, Scene);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -264,7 +259,8 @@ public:
|
||||||
static std::unique_ptr<Shape> gen() noexcept;
|
static std::unique_ptr<Shape> gen() noexcept;
|
||||||
|
|
||||||
_TVG_DECLARE_PRIVATE(Shape);
|
_TVG_DECLARE_PRIVATE(Shape);
|
||||||
_TVG_DECLARE_ACCESSORS(Canvas, Scene);
|
_TVG_DECLARE_ACCESSOR(Canvas);
|
||||||
|
_TVG_DECLARE_ACCESSOR(Scene);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
source_file = [
|
source_file = [
|
||||||
'tvgSwCommon.h',
|
'tvgSwCommon.h',
|
||||||
|
'tvgSwFill.cpp',
|
||||||
'tvgSwMath.cpp',
|
'tvgSwMath.cpp',
|
||||||
'tvgSwRenderer.h',
|
'tvgSwRenderer.h',
|
||||||
'tvgSwRaster.cpp',
|
'tvgSwRaster.cpp',
|
||||||
|
|
|
@ -21,11 +21,14 @@
|
||||||
|
|
||||||
using namespace tvg;
|
using namespace tvg;
|
||||||
|
|
||||||
constexpr auto SW_CURVE_TYPE_POINT = 0;
|
#define SW_CURVE_TYPE_POINT 0
|
||||||
constexpr auto SW_CURVE_TYPE_CUBIC = 1;
|
#define SW_CURVE_TYPE_CUBIC 1
|
||||||
|
#define SW_OUTLINE_FILL_WINDING 0
|
||||||
constexpr auto SW_OUTLINE_FILL_WINDING = 0;
|
#define SW_OUTLINE_FILL_EVEN_ODD 1
|
||||||
constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1;
|
#define SW_ANGLE_PI (180L << 16)
|
||||||
|
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
|
||||||
|
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
|
||||||
|
#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
|
||||||
|
|
||||||
using SwCoord = signed long;
|
using SwCoord = signed long;
|
||||||
using SwFixed = signed long long;
|
using SwFixed = signed long long;
|
||||||
|
@ -153,22 +156,28 @@ struct SwDashStroke
|
||||||
bool curOpGap;
|
bool curOpGap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SwFill
|
||||||
|
{
|
||||||
|
uint32_t* ctable;
|
||||||
|
float x1, y1, x2, y2;
|
||||||
|
float dx, dy;
|
||||||
|
float len;
|
||||||
|
float offset;
|
||||||
|
FillSpread spread;
|
||||||
|
bool translucent;
|
||||||
|
};
|
||||||
|
|
||||||
struct SwShape
|
struct SwShape
|
||||||
{
|
{
|
||||||
SwOutline* outline;
|
SwOutline* outline;
|
||||||
SwStroke* stroke;
|
SwStroke* stroke;
|
||||||
|
SwFill* fill;
|
||||||
SwRleData* rle;
|
SwRleData* rle;
|
||||||
SwRleData* strokeRle;
|
SwRleData* strokeRle;
|
||||||
SwBBox bbox;
|
SwBBox bbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
constexpr static SwFixed ANGLE_PI = (180L << 16);
|
|
||||||
constexpr static SwFixed ANGLE_2PI = (ANGLE_PI << 1);
|
|
||||||
constexpr static SwFixed ANGLE_PI2 = (ANGLE_PI >> 1);
|
|
||||||
constexpr static SwFixed ANGLE_PI4 = (ANGLE_PI >> 2);
|
|
||||||
|
|
||||||
|
|
||||||
static inline SwPoint TO_SWPOINT(const Point* pt)
|
static inline SwPoint TO_SWPOINT(const Point* pt)
|
||||||
{
|
{
|
||||||
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
|
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
|
||||||
|
@ -181,6 +190,33 @@ static inline SwCoord TO_SWCOORD(float val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t COLOR_ALPHA(uint32_t rgba)
|
||||||
|
{
|
||||||
|
return (rgba >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t COLOR_ALPHA_BLEND(uint32_t rgba, uint32_t alpha)
|
||||||
|
{
|
||||||
|
return (((((rgba >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) +
|
||||||
|
((((rgba & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t COLOR_INTERPOLATE(uint32_t rgba1, uint32_t a, uint32_t rgba2, uint32_t b)
|
||||||
|
{
|
||||||
|
auto t = (((rgba1 & 0xff00ff) * a + (rgba2 & 0xff00ff) * b) >> 8) & 0xff00ff;
|
||||||
|
rgba1 = (((rgba1 >> 8) & 0xff00ff) * a + ((rgba2 >> 8) & 0xff00ff) * b) & 0xff00ff00;
|
||||||
|
return (rgba1 |= t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||||
|
{
|
||||||
|
return (a << 24 | r << 16 | g << 8 | b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int64_t mathMultiply(int64_t a, int64_t b);
|
int64_t mathMultiply(int64_t a, int64_t b);
|
||||||
int64_t mathDivide(int64_t a, int64_t b);
|
int64_t mathDivide(int64_t a, int64_t b);
|
||||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
|
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
|
||||||
|
@ -202,16 +238,26 @@ void shapeDelOutline(SwShape& shape);
|
||||||
void shapeResetStroke(SwShape& shape, const Shape& sdata);
|
void shapeResetStroke(SwShape& shape, const Shape& sdata);
|
||||||
bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip);
|
bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip);
|
||||||
void shapeFree(SwShape* shape);
|
void shapeFree(SwShape* shape);
|
||||||
|
void shapeDelStroke(SwShape& shape);
|
||||||
|
bool shapeGenFillColors(SwShape& shape, const Fill* fill);
|
||||||
|
void shapeResetFill(SwShape& shape, const Fill* fill);
|
||||||
|
void shapeDelFill(SwShape& shape);
|
||||||
|
|
||||||
void strokeReset(SwStroke& stroke, const Shape& shape);
|
void strokeReset(SwStroke& stroke, const Shape& shape);
|
||||||
bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline);
|
bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline);
|
||||||
SwOutline* strokeExportOutline(SwStroke& stroke);
|
SwOutline* strokeExportOutline(SwStroke& stroke);
|
||||||
void strokeFree(SwStroke* stroke);
|
void strokeFree(SwStroke* stroke);
|
||||||
|
|
||||||
|
bool fillGenColorTable(SwFill* fill, const Fill* fdata);
|
||||||
|
void fillReset(SwFill* fill, const Fill* fdata);
|
||||||
|
void fillFree(SwFill* fill);
|
||||||
|
void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
|
||||||
|
|
||||||
SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
|
SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
|
||||||
void rleFree(SwRleData* rle);
|
void rleFree(SwRleData* rle);
|
||||||
|
|
||||||
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
bool rasterGradientShape(Surface& surface, SwShape& shape);
|
||||||
bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||||
|
bool rasterStroke(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||||
|
|
||||||
#endif /* _TVG_SW_COMMON_H_ */
|
#endif /* _TVG_SW_COMMON_H_ */
|
||||||
|
|
262
src/lib/sw_engine/tvgSwFill.cpp
Normal file
262
src/lib/sw_engine/tvgSwFill.cpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _TVG_SW_FILL_CPP_
|
||||||
|
#define _TVG_SW_FILL_CPP_
|
||||||
|
|
||||||
|
#include "tvgSwCommon.h"
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* Internal Class Implementation */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
#define GRADIENT_STOP_SIZE 1024
|
||||||
|
#define FIXPT_BITS 8
|
||||||
|
#define FIXPT_SIZE (1<<FIXPT_BITS)
|
||||||
|
|
||||||
|
|
||||||
|
static bool _updateColorTable(SwFill* fill, const LinearGradient* linear)
|
||||||
|
{
|
||||||
|
assert(fill && linear);
|
||||||
|
|
||||||
|
if (!fill->ctable) {
|
||||||
|
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
|
||||||
|
assert(fill->ctable);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Fill::ColorStop* colors;
|
||||||
|
auto cnt = linear->colorStops(&colors);
|
||||||
|
if (cnt == 0 || !colors) return false;
|
||||||
|
|
||||||
|
auto pColors = colors;
|
||||||
|
|
||||||
|
if (pColors->a < 255) fill->translucent = true;
|
||||||
|
|
||||||
|
auto rgba = COLOR_ARGB_JOIN(pColors->r, pColors->g, pColors->b, pColors->a);
|
||||||
|
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
|
||||||
|
auto pos = 1.5f * inc;
|
||||||
|
uint32_t i = 0;
|
||||||
|
|
||||||
|
fill->ctable[i++] = rgba;
|
||||||
|
|
||||||
|
while (pos <= pColors->offset) {
|
||||||
|
fill->ctable[i] = fill->ctable[i - 1];
|
||||||
|
++i;
|
||||||
|
pos += inc;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < cnt - 1; ++j) {
|
||||||
|
auto curr = colors + j;
|
||||||
|
auto next = curr + 1;
|
||||||
|
assert(curr && next);
|
||||||
|
auto delta = 1.0f / (next->offset - curr->offset);
|
||||||
|
if (next->a < 255) fill->translucent = true;
|
||||||
|
auto rgba2 = COLOR_ARGB_JOIN(next->r, next->g, next->b, next->a);
|
||||||
|
|
||||||
|
while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
|
||||||
|
auto t = (pos - curr->offset) * delta;
|
||||||
|
auto dist = static_cast<int32_t>(256 * t);
|
||||||
|
auto dist2 = 256 - dist;
|
||||||
|
fill->ctable[i] = COLOR_INTERPOLATE(rgba, dist2, rgba2, dist);
|
||||||
|
++i;
|
||||||
|
pos += inc;
|
||||||
|
}
|
||||||
|
rgba = rgba2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < GRADIENT_STOP_SIZE; ++i)
|
||||||
|
fill->ctable[i] = rgba;
|
||||||
|
|
||||||
|
//Make sure the lat color stop is represented at the end of the table
|
||||||
|
fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool _prepareLinear(SwFill* fill, const LinearGradient* linear)
|
||||||
|
{
|
||||||
|
assert(fill && linear);
|
||||||
|
|
||||||
|
if (linear->linear(&fill->x1, &fill->y1, &fill->x2, &fill->y2) != Result::Success) return false;
|
||||||
|
|
||||||
|
fill->dx = fill->x2 - fill->x1;
|
||||||
|
fill->dy = fill->y2 - fill->y1;
|
||||||
|
fill->len = fill->dx * fill->dx + fill->dy * fill->dy;
|
||||||
|
fill->offset = 0;
|
||||||
|
|
||||||
|
if (fill->len < FLT_EPSILON) return true;
|
||||||
|
|
||||||
|
fill->dx /= fill->len;
|
||||||
|
fill->dy /= fill->len;
|
||||||
|
fill->offset = -fill->dx * fill->x1 - fill->dy * fill->y1;
|
||||||
|
|
||||||
|
return _updateColorTable(fill, linear);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool _prepareRadial(SwFill* fill, const RadialGradient* radial)
|
||||||
|
{
|
||||||
|
assert(fill && radial);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t _clamp(const SwFill* fill, uint32_t pos)
|
||||||
|
{
|
||||||
|
switch (fill->spread) {
|
||||||
|
case FillSpread::Pad: {
|
||||||
|
if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FillSpread::Repeat: {
|
||||||
|
pos = pos % GRADIENT_STOP_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FillSpread::Reflect: {
|
||||||
|
auto limit = GRADIENT_STOP_SIZE * 2;
|
||||||
|
pos = pos % limit;
|
||||||
|
if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t _fixedPixel(const SwFill* fill, uint32_t pos)
|
||||||
|
{
|
||||||
|
auto i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
|
||||||
|
return fill->ctable[_clamp(fill, i)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t _pixel(const SwFill* fill, float pos)
|
||||||
|
{
|
||||||
|
auto i = static_cast<uint32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
|
||||||
|
return fill->ctable[_clamp(fill, i)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void _write(uint32_t *dst, uint32_t val, uint32_t len)
|
||||||
|
{
|
||||||
|
if (len <= 0) return;
|
||||||
|
|
||||||
|
// Cute hack to align future memcopy operation
|
||||||
|
// and do unroll the loop a bit. Not sure it is
|
||||||
|
// the most efficient, but will do for now.
|
||||||
|
auto n = (len + 7) / 8;
|
||||||
|
|
||||||
|
switch (len & 0x07) {
|
||||||
|
case 0: do { *dst++ = val;
|
||||||
|
case 7: *dst++ = val;
|
||||||
|
case 6: *dst++ = val;
|
||||||
|
case 5: *dst++ = val;
|
||||||
|
case 4: *dst++ = val;
|
||||||
|
case 3: *dst++ = val;
|
||||||
|
case 2: *dst++ = val;
|
||||||
|
case 1: *dst++ = val;
|
||||||
|
} while (--n > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* External Class Implementation */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
|
||||||
|
{
|
||||||
|
assert(fill->len > 0);
|
||||||
|
|
||||||
|
//TODO: Rotation???
|
||||||
|
auto rx = x + 0.5f;
|
||||||
|
auto ry = y + 0.5f;
|
||||||
|
auto t = (fill->dx * rx + fill->dy * ry + fill->offset) * (GRADIENT_STOP_SIZE - 1);
|
||||||
|
auto inc = (fill->dx) * (GRADIENT_STOP_SIZE - 1);
|
||||||
|
|
||||||
|
if (fabsf(inc) < FLT_EPSILON) {
|
||||||
|
auto color = _fixedPixel(fill, static_cast<uint32_t>(t * FIXPT_SIZE));
|
||||||
|
_write(dst, color, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||||
|
auto vMin = -vMax;
|
||||||
|
auto v = t + (inc * len);
|
||||||
|
|
||||||
|
//we can use fixed point math
|
||||||
|
if (v < vMax && v > vMin) {
|
||||||
|
auto t2 = static_cast<uint32_t>(t * FIXPT_SIZE);
|
||||||
|
auto inc2 = static_cast<uint32_t>(inc * FIXPT_SIZE);
|
||||||
|
for (uint32_t j = 0; j < len; ++j) {
|
||||||
|
*dst = _fixedPixel(fill, t2);
|
||||||
|
++dst;
|
||||||
|
t2 += inc2;
|
||||||
|
}
|
||||||
|
//we have to fallback to float math
|
||||||
|
} else {
|
||||||
|
while (dst < dst + len) {
|
||||||
|
*dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
|
||||||
|
++dst;
|
||||||
|
t += inc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool fillGenColorTable(SwFill* fill, const Fill* fdata)
|
||||||
|
{
|
||||||
|
if (!fill) return false;
|
||||||
|
|
||||||
|
assert(fdata);
|
||||||
|
|
||||||
|
fill->spread = fdata->spread();
|
||||||
|
|
||||||
|
if (fdata->id() == FILL_ID_LINEAR) {
|
||||||
|
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata));
|
||||||
|
} else if (fdata->id() == FILL_ID_RADIAL) {
|
||||||
|
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata));
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "What type of gradient?!" << endl;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fillReset(SwFill* fill, const Fill* fdata)
|
||||||
|
{
|
||||||
|
if (fill->ctable) {
|
||||||
|
free(fill->ctable);
|
||||||
|
fill->ctable = nullptr;
|
||||||
|
}
|
||||||
|
fill->translucent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fillFree(SwFill* fill)
|
||||||
|
{
|
||||||
|
if (!fill) return;
|
||||||
|
|
||||||
|
if (fill->ctable) free(fill->ctable);
|
||||||
|
|
||||||
|
free(fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _TVG_SW_FILL_CPP_ */
|
|
@ -93,15 +93,15 @@ static void _polarize(SwPoint& pt)
|
||||||
auto tmp = v.y;
|
auto tmp = v.y;
|
||||||
v.y = -v.x;
|
v.y = -v.x;
|
||||||
v.x = tmp;
|
v.x = tmp;
|
||||||
theta = ANGLE_PI2;
|
theta = SW_ANGLE_PI2;
|
||||||
} else {
|
} else {
|
||||||
theta = v.y > 0 ? ANGLE_PI : -ANGLE_PI;
|
theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
|
||||||
v.x = -v.x;
|
v.x = -v.x;
|
||||||
v.y = -v.y;
|
v.y = -v.y;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (v.y < -v.x) {
|
if (v.y < -v.x) {
|
||||||
theta = -ANGLE_PI2;
|
theta = -SW_ANGLE_PI2;
|
||||||
auto tmp = -v.y;
|
auto tmp = -v.y;
|
||||||
v.y = v.x;
|
v.y = v.x;
|
||||||
v.x = tmp;
|
v.x = tmp;
|
||||||
|
@ -144,18 +144,18 @@ static void _rotate(SwPoint& pt, SwFixed theta)
|
||||||
SwFixed y = pt.y;
|
SwFixed y = pt.y;
|
||||||
|
|
||||||
//Rotate inside [-PI/4, PI/4] sector
|
//Rotate inside [-PI/4, PI/4] sector
|
||||||
while (theta < -ANGLE_PI4) {
|
while (theta < -SW_ANGLE_PI4) {
|
||||||
auto tmp = y;
|
auto tmp = y;
|
||||||
y = -x;
|
y = -x;
|
||||||
x = tmp;
|
x = tmp;
|
||||||
theta += ANGLE_PI2;
|
theta += SW_ANGLE_PI2;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (theta > ANGLE_PI4) {
|
while (theta > SW_ANGLE_PI4) {
|
||||||
auto tmp = -y;
|
auto tmp = -y;
|
||||||
y = x;
|
y = x;
|
||||||
x = tmp;
|
x = tmp;
|
||||||
theta -= ANGLE_PI2;
|
theta -= SW_ANGLE_PI2;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto atan = ATAN_TBL;
|
auto atan = ATAN_TBL;
|
||||||
|
@ -236,7 +236,7 @@ bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed&
|
||||||
auto theta1 = abs(mathDiff(angleIn, angleMid));
|
auto theta1 = abs(mathDiff(angleIn, angleMid));
|
||||||
auto theta2 = abs(mathDiff(angleMid, angleOut));
|
auto theta2 = abs(mathDiff(angleMid, angleOut));
|
||||||
|
|
||||||
if ((theta1 < (ANGLE_PI / 8)) && (theta2 < (ANGLE_PI / 8))) return true;
|
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +346,7 @@ SwFixed mathAtan(const SwPoint& pt)
|
||||||
|
|
||||||
SwFixed mathSin(SwFixed angle)
|
SwFixed mathSin(SwFixed angle)
|
||||||
{
|
{
|
||||||
return mathCos(ANGLE_PI2 - angle);
|
return mathCos(SW_ANGLE_PI2 - angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,9 +408,9 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
|
||||||
{
|
{
|
||||||
auto delta = angle2 - angle1;
|
auto delta = angle2 - angle1;
|
||||||
|
|
||||||
delta %= ANGLE_2PI;
|
delta %= SW_ANGLE_2PI;
|
||||||
if (delta < 0) delta += ANGLE_2PI;
|
if (delta < 0) delta += SW_ANGLE_2PI;
|
||||||
if (delta > ANGLE_PI) delta -= ANGLE_2PI;
|
if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
|
||||||
|
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,60 +24,28 @@
|
||||||
/* Internal Class Implementation */
|
/* Internal Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
static inline uint32_t COLOR_ALPHA(uint32_t color)
|
static bool _rasterTranslucentRle(Surface& surface, SwRleData* rle, uint32_t color)
|
||||||
{
|
{
|
||||||
return (color >> 24) & 0xff;
|
if (!rle) return false;
|
||||||
}
|
|
||||||
|
|
||||||
|
auto span = rle->spans;
|
||||||
|
auto stride = surface.stride;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
static inline uint32_t COLOR_ALPHA_BLEND(uint32_t color, uint32_t alpha)
|
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||||
{
|
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||||
return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) +
|
if (span->coverage < 255) tmp = COLOR_ALPHA_BLEND(color, span->coverage);
|
||||||
((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff));
|
else tmp = color;
|
||||||
}
|
for (uint32_t i = 0; i < span->len; ++i) {
|
||||||
|
dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
|
||||||
|
}
|
||||||
static inline uint32_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
++span;
|
||||||
{
|
|
||||||
return (a << 24 | r << 16 | g << 8 | b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
_rasterTranslucent(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
|
|
||||||
{
|
|
||||||
//OPTIMIZE ME: SIMD
|
|
||||||
|
|
||||||
if (cov < 255) color = COLOR_ALPHA_BLEND(color, cov);
|
|
||||||
auto ialpha = 255 - COLOR_ALPHA(color);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
dst[i] = color + COLOR_ALPHA_BLEND(dst[i], ialpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
_rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
|
|
||||||
{
|
|
||||||
//OPTIMIZE ME: SIMD
|
|
||||||
|
|
||||||
//Fully Opaque
|
|
||||||
if (cov == 255) {
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
dst[i] = color;
|
|
||||||
}
|
}
|
||||||
} else {
|
return true;
|
||||||
auto ialpha = 255 - cov;
|
|
||||||
for (uint32_t i = 0; i < len; ++i) {
|
|
||||||
dst[i] = COLOR_ALPHA_BLEND(color, cov) + COLOR_ALPHA_BLEND(dst[i], ialpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool _rasterSolidRle(Surface& surface, SwRleData* rle, uint32_t color)
|
||||||
_rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
|
|
||||||
{
|
{
|
||||||
if (!rle) return false;
|
if (!rle) return false;
|
||||||
|
|
||||||
|
@ -85,16 +53,66 @@ _rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
|
||||||
auto stride = surface.stride;
|
auto stride = surface.stride;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||||
assert(span);
|
|
||||||
|
|
||||||
auto dst = &surface.buffer[span->y * stride + span->x];
|
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||||
|
if (span->coverage == 255) {
|
||||||
if (a == 255) _rasterSolid(dst, span->len, color, span->coverage);
|
for (uint32_t i = 0; i < span->len; ++i) {
|
||||||
else _rasterTranslucent(dst, span->len, color, span->coverage);
|
dst[i] = color;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < span->len; ++i) {
|
||||||
|
dst[i] = COLOR_ALPHA_BLEND(color, span->coverage) + COLOR_ALPHA_BLEND(dst[i], 255 - span->coverage);
|
||||||
|
}
|
||||||
|
}
|
||||||
++span;
|
++span;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
|
||||||
|
{
|
||||||
|
if (!rle || !fill) return false;
|
||||||
|
|
||||||
|
auto buf = static_cast<uint32_t*>(alloca(surface.w * sizeof(uint32_t)));
|
||||||
|
if (!buf) return false;
|
||||||
|
|
||||||
|
auto span = rle->spans;
|
||||||
|
auto stride = surface.stride;
|
||||||
|
|
||||||
|
//Translucent Gradient
|
||||||
|
if (fill->translucent) {
|
||||||
|
uint32_t tmp;
|
||||||
|
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||||
|
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||||
|
fillFetch(fill, buf, span->y, span->x, span->len);
|
||||||
|
if (span->coverage == 255) {
|
||||||
|
for (uint32_t i = 0; i < span->len; ++i) {
|
||||||
|
dst[i] = buf[i] + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(buf[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < span->len; ++i) {
|
||||||
|
tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
|
||||||
|
dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++span;
|
||||||
|
}
|
||||||
|
//Opaque Gradient
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||||
|
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||||
|
if (span->coverage == 255) {
|
||||||
|
fillFetch(fill, dst, span->y, span->x, span->len);
|
||||||
|
} else {
|
||||||
|
fillFetch(fill, buf, span->y, span->x, span->len);
|
||||||
|
auto ialpha = 255 - span->coverage;
|
||||||
|
for (uint32_t i = 0; i < span->len; ++i) {
|
||||||
|
dst[i] = COLOR_ALPHA_BLEND(buf[i], span->coverage) + COLOR_ALPHA_BLEND(dst[i], ialpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++span;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,15 +121,23 @@ _rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
bool rasterGradientShape(Surface& surface, SwShape& shape)
|
||||||
{
|
{
|
||||||
return _rasterRle(surface, sdata.rle, COLOR_ARGB_JOIN(r, g, b, a), a);
|
return _rasterGradientRle(surface, shape.rle, shape.fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||||
{
|
{
|
||||||
return _rasterRle(surface, sdata.strokeRle, COLOR_ARGB_JOIN(r, g, b, a), a);
|
if (a == 255) return _rasterSolidRle(surface, shape.rle, COLOR_ARGB_JOIN(r, g, b, a));
|
||||||
|
return _rasterTranslucentRle(surface, shape.rle, COLOR_ARGB_JOIN(r, g, b, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool rasterStroke(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||||
|
{
|
||||||
|
if (a == 255) return _rasterSolidRle(surface, shape.strokeRle, COLOR_ARGB_JOIN(r, g, b, a));
|
||||||
|
return _rasterTranslucentRle(surface, shape.strokeRle, COLOR_ARGB_JOIN(r, g, b, a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -57,28 +57,32 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SwRenderer::render(const Shape& shape, void *data)
|
bool SwRenderer::render(const Shape& sdata, void *data)
|
||||||
{
|
{
|
||||||
SwShape* sdata = static_cast<SwShape*>(data);
|
SwShape* shape = static_cast<SwShape*>(data);
|
||||||
if (!sdata) return false;
|
if (!shape) return false;
|
||||||
|
|
||||||
uint8_t r, g, b, a;
|
uint8_t r, g, b, a;
|
||||||
|
|
||||||
shape.fill(&r, &g, &b, &a);
|
if (sdata.fill()) {
|
||||||
if (a > 0) rasterShape(surface, *sdata, r, g, b, a);
|
rasterGradientShape(surface, *shape);
|
||||||
|
} else {
|
||||||
|
sdata.fill(&r, &g, &b, &a);
|
||||||
|
if (a > 0) rasterSolidShape(surface, *shape, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
shape.strokeColor(&r, &g, &b, &a);
|
sdata.strokeColor(&r, &g, &b, &a);
|
||||||
if (a > 0) rasterStroke(surface, *sdata, r, g, b, a);
|
if (a > 0) rasterStroke(surface, *shape, r, g, b, a);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SwRenderer::dispose(const Shape& shape, void *data)
|
bool SwRenderer::dispose(const Shape& sdata, void *data)
|
||||||
{
|
{
|
||||||
auto sdata = static_cast<SwShape*>(data);
|
auto shape = static_cast<SwShape*>(data);
|
||||||
if (!sdata) return true;
|
if (!shape) return true;
|
||||||
shapeFree(sdata);
|
shapeFree(shape);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +107,22 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
|
||||||
shapeReset(*shape);
|
shapeReset(*shape);
|
||||||
uint8_t alpha = 0;
|
uint8_t alpha = 0;
|
||||||
sdata.fill(nullptr, nullptr, nullptr, &alpha);
|
sdata.fill(nullptr, nullptr, nullptr, &alpha);
|
||||||
if (alpha > 0) {
|
if (alpha > 0 || sdata.fill()) {
|
||||||
if (!shapeGenRle(*shape, sdata, clip, transform)) return shape;
|
if (!shapeGenRle(*shape, sdata, clip, transform)) return shape;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Fill
|
||||||
|
if (flags & (RenderUpdateFlag::Gradient)) {
|
||||||
|
auto fill = sdata.fill();
|
||||||
|
if (fill) {
|
||||||
|
shapeResetFill(*shape, fill);
|
||||||
|
if (!shapeGenFillColors(*shape, fill)) return shape;
|
||||||
|
} else {
|
||||||
|
shapeDelFill(*shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Stroke
|
//Stroke
|
||||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||||
if (sdata.strokeWidth() > 0.5) {
|
if (sdata.strokeWidth() > 0.5) {
|
||||||
|
@ -117,6 +132,8 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
|
||||||
if (alpha > 0) {
|
if (alpha > 0) {
|
||||||
if (!shapeGenStrokeRle(*shape, sdata, clip)) return shape;
|
if (!shapeGenStrokeRle(*shape, sdata, clip)) return shape;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
shapeDelStroke(*shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -666,19 +666,29 @@ bool shapeGenOutline(SwShape& shape, const Shape& sdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void shapeFree(SwShape* sdata)
|
void shapeFree(SwShape* shape)
|
||||||
{
|
{
|
||||||
assert(sdata);
|
assert(shape);
|
||||||
|
|
||||||
shapeDelOutline(*sdata);
|
shapeDelOutline(*shape);
|
||||||
rleFree(sdata->rle);
|
rleFree(shape->rle);
|
||||||
|
|
||||||
if (sdata->stroke) {
|
if (shape->stroke) {
|
||||||
rleFree(sdata->strokeRle);
|
rleFree(shape->strokeRle);
|
||||||
strokeFree(sdata->stroke);
|
strokeFree(shape->stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(sdata);
|
free(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void shapeDelStroke(SwShape& shape)
|
||||||
|
{
|
||||||
|
if (!shape.stroke) return;
|
||||||
|
rleFree(shape.strokeRle);
|
||||||
|
shape.strokeRle = nullptr;
|
||||||
|
strokeFree(shape.stroke);
|
||||||
|
shape.stroke = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -730,4 +740,32 @@ bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool shapeGenFillColors(SwShape& shape, const Fill* fill)
|
||||||
|
{
|
||||||
|
assert(fill);
|
||||||
|
|
||||||
|
fillGenColorTable(shape.fill, fill);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void shapeResetFill(SwShape& shape, const Fill* fill)
|
||||||
|
{
|
||||||
|
assert(fill);
|
||||||
|
|
||||||
|
if (!shape.fill) shape.fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
|
||||||
|
assert(shape.fill);
|
||||||
|
|
||||||
|
fillReset(shape.fill, fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void shapeDelFill(SwShape& shape)
|
||||||
|
{
|
||||||
|
if (!shape.fill) return;
|
||||||
|
fillFree(shape.fill);
|
||||||
|
shape.fill = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif /* _TVG_SW_SHAPE_H_ */
|
#endif /* _TVG_SW_SHAPE_H_ */
|
||||||
|
|
|
@ -30,7 +30,7 @@ static constexpr auto SW_STROKE_TAG_END = 8;
|
||||||
|
|
||||||
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
|
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
|
||||||
{
|
{
|
||||||
return (ANGLE_PI2 - (s) * ANGLE_PI);
|
return (SW_ANGLE_PI2 - (s) * SW_ANGLE_PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,14 +134,14 @@ static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl
|
||||||
|
|
||||||
static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff)
|
static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff)
|
||||||
{
|
{
|
||||||
constexpr SwFixed ARC_CUBIC_ANGLE = ANGLE_PI / 2;
|
constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
|
||||||
SwPoint a = {radius, 0};
|
SwPoint a = {radius, 0};
|
||||||
mathRotate(a, angleStart);
|
mathRotate(a, angleStart);
|
||||||
a += center;
|
a += center;
|
||||||
|
|
||||||
auto total = angleDiff;
|
auto total = angleDiff;
|
||||||
auto angle = angleStart;
|
auto angle = angleStart;
|
||||||
auto rotate = (angleDiff >= 0) ? ANGLE_PI2 : -ANGLE_PI2;
|
auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
|
||||||
|
|
||||||
while (total != 0) {
|
while (total != 0) {
|
||||||
auto step = total;
|
auto step = total;
|
||||||
|
@ -222,7 +222,7 @@ static void _arcTo(SwStroke& stroke, int32_t side)
|
||||||
auto border = stroke.borders + side;
|
auto border = stroke.borders + side;
|
||||||
auto rotate = SIDE_TO_ROTATE(side);
|
auto rotate = SIDE_TO_ROTATE(side);
|
||||||
auto total = mathDiff(stroke.angleIn, stroke.angleOut);
|
auto total = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||||
if (total == ANGLE_PI) total = -rotate * 2;
|
if (total == SW_ANGLE_PI) total = -rotate * 2;
|
||||||
|
|
||||||
_borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total);
|
_borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total);
|
||||||
border->movable = false;
|
border->movable = false;
|
||||||
|
@ -249,7 +249,7 @@ static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||||
|
|
||||||
if (!bevel) {
|
if (!bevel) {
|
||||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
|
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||||
if (theta == ANGLE_PI) {
|
if (theta == SW_ANGLE_PI) {
|
||||||
theta = rotate;
|
theta = rotate;
|
||||||
phi = stroke.angleIn;
|
phi = stroke.angleIn;
|
||||||
} else {
|
} else {
|
||||||
|
@ -354,7 +354,7 @@ void _processCorner(SwStroke& stroke, SwFixed lineLength)
|
||||||
void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
|
void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
|
||||||
{
|
{
|
||||||
SwPoint delta = {stroke.width, 0};
|
SwPoint delta = {stroke.width, 0};
|
||||||
mathRotate(delta, startAngle + ANGLE_PI2);
|
mathRotate(delta, startAngle + SW_ANGLE_PI2);
|
||||||
|
|
||||||
auto pt = stroke.center + delta;
|
auto pt = stroke.center + delta;
|
||||||
auto border = stroke.borders;
|
auto border = stroke.borders;
|
||||||
|
@ -384,7 +384,7 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
|
||||||
auto angle = mathAtan(delta);
|
auto angle = mathAtan(delta);
|
||||||
|
|
||||||
delta = {stroke.width, 0};
|
delta = {stroke.width, 0};
|
||||||
mathRotate(delta, angle + ANGLE_PI2);
|
mathRotate(delta, angle + SW_ANGLE_PI2);
|
||||||
|
|
||||||
//process corner if necessary
|
//process corner if necessary
|
||||||
if (stroke.firstPt) {
|
if (stroke.firstPt) {
|
||||||
|
@ -460,7 +460,7 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
|
||||||
stroke.angleOut = angleIn;
|
stroke.angleOut = angleIn;
|
||||||
_processCorner(stroke, 0);
|
_processCorner(stroke, 0);
|
||||||
}
|
}
|
||||||
} else if (abs(mathDiff(stroke.angleIn, angleIn)) > (ANGLE_PI / 8)) {
|
} else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8)) {
|
||||||
//if the deviation from one arc to the next is too great add a round corner
|
//if the deviation from one arc to the next is too great add a round corner
|
||||||
stroke.center = arc[3];
|
stroke.center = arc[3];
|
||||||
stroke.angleOut = angleIn;
|
stroke.angleOut = angleIn;
|
||||||
|
@ -515,7 +515,7 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
|
||||||
auto alpha1 = mathAtan(_end - _start);
|
auto alpha1 = mathAtan(_end - _start);
|
||||||
|
|
||||||
//is the direction of the border arc opposite to that of the original arc?
|
//is the direction of the border arc opposite to that of the original arc?
|
||||||
if (abs(mathDiff(alpha0, alpha1)) > ANGLE_PI / 2) {
|
if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
|
||||||
|
|
||||||
//use the sine rule to find the intersection point
|
//use the sine rule to find the intersection point
|
||||||
auto beta = mathAtan(arc[3] - _start);
|
auto beta = mathAtan(arc[3] - _start);
|
||||||
|
@ -583,7 +583,7 @@ static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
|
||||||
} else if (stroke.cap == StrokeCap::Round) {
|
} else if (stroke.cap == StrokeCap::Round) {
|
||||||
|
|
||||||
stroke.angleIn = angle;
|
stroke.angleIn = angle;
|
||||||
stroke.angleOut = angle + ANGLE_PI;
|
stroke.angleOut = angle + SW_ANGLE_PI;
|
||||||
_arcTo(stroke, side);
|
_arcTo(stroke, side);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -692,7 +692,7 @@ static void _endSubPath(SwStroke& stroke)
|
||||||
|
|
||||||
//now add the final cap
|
//now add the final cap
|
||||||
stroke.center = stroke.ptStartSubPath;
|
stroke.center = stroke.ptStartSubPath;
|
||||||
_addCap(stroke, stroke.subPathAngle + ANGLE_PI, 0);
|
_addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
|
||||||
|
|
||||||
/* now end the right subpath accordingly. The left one is rewind
|
/* now end the right subpath accordingly. The left one is rewind
|
||||||
and deosn't need further processing */
|
and deosn't need further processing */
|
||||||
|
|
|
@ -53,7 +53,7 @@ struct Canvas::Impl
|
||||||
assert(renderer);
|
assert(renderer);
|
||||||
|
|
||||||
for (auto paint : paints) {
|
for (auto paint : paints) {
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if (!SCENE_IMPL->clear(*renderer)) return Result::InsufficientCondition;
|
if (!SCENE_IMPL->clear(*renderer)) return Result::InsufficientCondition;
|
||||||
|
@ -73,7 +73,7 @@ struct Canvas::Impl
|
||||||
assert(renderer);
|
assert(renderer);
|
||||||
|
|
||||||
for(auto paint: paints) {
|
for(auto paint: paints) {
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if (!SCENE_IMPL->update(*renderer, nullptr)) return Result::InsufficientCondition;
|
if (!SCENE_IMPL->update(*renderer, nullptr)) return Result::InsufficientCondition;
|
||||||
|
@ -89,7 +89,7 @@ struct Canvas::Impl
|
||||||
{
|
{
|
||||||
assert(renderer);
|
assert(renderer);
|
||||||
|
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if (!SCENE_IMPL->update(*renderer)) return Result::InsufficientCondition;
|
if (!SCENE_IMPL->update(*renderer)) return Result::InsufficientCondition;
|
||||||
|
@ -108,7 +108,7 @@ struct Canvas::Impl
|
||||||
if (!renderer->clear()) return Result::InsufficientCondition;
|
if (!renderer->clear()) return Result::InsufficientCondition;
|
||||||
|
|
||||||
for(auto paint: paints) {
|
for(auto paint: paints) {
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if(!SCENE_IMPL->render(*renderer)) return Result::InsufficientCondition;
|
if(!SCENE_IMPL->render(*renderer)) return Result::InsufficientCondition;
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct LinearGradient::Impl
|
||||||
|
|
||||||
LinearGradient::LinearGradient():pImpl(make_unique<Impl>())
|
LinearGradient::LinearGradient():pImpl(make_unique<Impl>())
|
||||||
{
|
{
|
||||||
id = FILL_ID_LINEAR;
|
_id = FILL_ID_LINEAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct RadialGradient::Impl
|
||||||
|
|
||||||
RadialGradient::RadialGradient():pImpl(make_unique<Impl>())
|
RadialGradient::RadialGradient():pImpl(make_unique<Impl>())
|
||||||
{
|
{
|
||||||
id = FILL_ID_RADIAL;
|
_id = FILL_ID_RADIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
Scene::Scene() : pImpl(make_unique<Impl>())
|
Scene::Scene() : pImpl(make_unique<Impl>())
|
||||||
{
|
{
|
||||||
id = PAINT_ID_SCENE;
|
_id = PAINT_ID_SCENE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ struct Scene::Impl
|
||||||
bool clear(RenderMethod& renderer)
|
bool clear(RenderMethod& renderer)
|
||||||
{
|
{
|
||||||
for (auto paint : paints) {
|
for (auto paint : paints) {
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if (!SCENE_IMPL->clear(renderer)) return false;
|
if (!SCENE_IMPL->clear(renderer)) return false;
|
||||||
|
@ -57,7 +57,7 @@ struct Scene::Impl
|
||||||
bool updateInternal(RenderMethod &renderer, const RenderTransform* transform, uint32_t flag)
|
bool updateInternal(RenderMethod &renderer, const RenderTransform* transform, uint32_t flag)
|
||||||
{
|
{
|
||||||
for(auto paint: paints) {
|
for(auto paint: paints) {
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if (!SCENE_IMPL->update(renderer, transform, flag)) return false;
|
if (!SCENE_IMPL->update(renderer, transform, flag)) return false;
|
||||||
|
@ -97,7 +97,7 @@ struct Scene::Impl
|
||||||
bool render(RenderMethod &renderer)
|
bool render(RenderMethod &renderer)
|
||||||
{
|
{
|
||||||
for(auto paint: paints) {
|
for(auto paint: paints) {
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if(!SCENE_IMPL->render(renderer)) return false;
|
if(!SCENE_IMPL->render(renderer)) return false;
|
||||||
|
@ -122,7 +122,7 @@ struct Scene::Impl
|
||||||
auto w2 = 0.0f;
|
auto w2 = 0.0f;
|
||||||
auto h2 = 0.0f;
|
auto h2 = 0.0f;
|
||||||
|
|
||||||
if (paint->id == PAINT_ID_SCENE) {
|
if (paint->id() == PAINT_ID_SCENE) {
|
||||||
//We know renderer type, avoid dynamic_cast for performance.
|
//We know renderer type, avoid dynamic_cast for performance.
|
||||||
auto scene = static_cast<Scene*>(paint);
|
auto scene = static_cast<Scene*>(paint);
|
||||||
if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false;
|
if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false;
|
||||||
|
|
|
@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
|
||||||
|
|
||||||
Shape :: Shape() : pImpl(make_unique<Impl>())
|
Shape :: Shape() : pImpl(make_unique<Impl>())
|
||||||
{
|
{
|
||||||
id = PAINT_ID_SHAPE;
|
_id = PAINT_ID_SHAPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,37 +18,39 @@ void tvgtest()
|
||||||
canvas->target(buffer, WIDTH, WIDTH, HEIGHT);
|
canvas->target(buffer, WIDTH, WIDTH, HEIGHT);
|
||||||
canvas->reserve(3); //reserve 3 shape nodes (optional)
|
canvas->reserve(3); //reserve 3 shape nodes (optional)
|
||||||
|
|
||||||
//Linear Gradient Color Stops
|
|
||||||
tvg::Fill::ColorStop colorStops[3];
|
|
||||||
colorStops[0] = {0, 255, 0, 0, 255};
|
|
||||||
colorStops[1] = {0.5, 255, 255, 255, 255};
|
|
||||||
colorStops[2] = {1, 0, 0, 255, 255};
|
|
||||||
|
|
||||||
//Prepare Round Rectangle
|
//Prepare Round Rectangle
|
||||||
auto shape1 = tvg::Shape::gen();
|
auto shape1 = tvg::Shape::gen();
|
||||||
shape1->appendRect(0, 0, 400, 400, 50); //x, y, w, h, cornerRadius
|
shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, cornerRadius
|
||||||
shape1->stroke(255, 255, 255, 255);
|
|
||||||
shape1->stroke(2);
|
|
||||||
|
|
||||||
//LinearGradient
|
//LinearGradient
|
||||||
auto fill = tvg::LinearGradient::gen();
|
auto fill = tvg::LinearGradient::gen();
|
||||||
fill->linear(0, 0, 400, 400);
|
fill->linear(0, 0, 400, 400);
|
||||||
fill->colorStops(colorStops, 3);
|
|
||||||
|
//Linear Gradient Color Stops
|
||||||
|
tvg::Fill::ColorStop colorStops[2];
|
||||||
|
colorStops[0] = {0, 0, 0, 0, 255};
|
||||||
|
colorStops[1] = {1, 255, 255, 255, 255};
|
||||||
|
|
||||||
|
fill->colorStops(colorStops, 2);
|
||||||
|
|
||||||
shape1->fill(move(fill));
|
shape1->fill(move(fill));
|
||||||
canvas->push(move(shape1));
|
canvas->push(move(shape1));
|
||||||
|
|
||||||
|
|
||||||
//Prepare Circle
|
//Prepare Circle
|
||||||
auto shape2 = tvg::Shape::gen();
|
auto shape2 = tvg::Shape::gen();
|
||||||
shape2->appendCircle(400, 400, 200, 200); //cx, cy, radiusW, radiusH
|
shape2->appendCircle(400, 400, 200, 200); //cx, cy, radiusW, radiusH
|
||||||
shape2->stroke(255, 255, 255, 255);
|
|
||||||
shape2->stroke(2);
|
|
||||||
|
|
||||||
//LinearGradient
|
//LinearGradient
|
||||||
auto fill2 = tvg::LinearGradient::gen();
|
auto fill2 = tvg::LinearGradient::gen();
|
||||||
fill2->linear(400, 200, 400, 600);
|
fill2->linear(400, 200, 400, 600);
|
||||||
fill2->colorStops(colorStops, 3);
|
|
||||||
|
//Linear Gradient Color Stops
|
||||||
|
tvg::Fill::ColorStop colorStops2[3];
|
||||||
|
colorStops2[0] = {0, 255, 0, 0, 255};
|
||||||
|
colorStops2[1] = {0.5, 255, 255, 0, 255};
|
||||||
|
colorStops2[2] = {1, 255, 255, 255, 255};
|
||||||
|
|
||||||
|
fill2->colorStops(colorStops2, 3);
|
||||||
|
|
||||||
shape2->fill(move(fill2));
|
shape2->fill(move(fill2));
|
||||||
canvas->push(move(shape2));
|
canvas->push(move(shape2));
|
||||||
|
@ -57,13 +59,19 @@ void tvgtest()
|
||||||
//Prepare Ellipse
|
//Prepare Ellipse
|
||||||
auto shape3 = tvg::Shape::gen();
|
auto shape3 = tvg::Shape::gen();
|
||||||
shape3->appendCircle(600, 600, 150, 100); //cx, cy, radiusW, radiusH
|
shape3->appendCircle(600, 600, 150, 100); //cx, cy, radiusW, radiusH
|
||||||
shape3->stroke(255, 255, 255, 255);
|
|
||||||
shape3->stroke(2);
|
|
||||||
|
|
||||||
//LinearGradient
|
//LinearGradient
|
||||||
auto fill3 = tvg::LinearGradient::gen();
|
auto fill3 = tvg::LinearGradient::gen();
|
||||||
fill3->linear(450, 600, 750, 600);
|
fill3->linear(450, 600, 750, 600);
|
||||||
fill3->colorStops(colorStops, 3);
|
|
||||||
|
//Linear Gradient Color Stops
|
||||||
|
tvg::Fill::ColorStop colorStops3[4];
|
||||||
|
colorStops3[0] = {0, 0, 127, 0, 127};
|
||||||
|
colorStops3[1] = {0.25, 0, 170, 170, 170};
|
||||||
|
colorStops3[2] = {0.5, 200, 0, 200, 200};
|
||||||
|
colorStops3[3] = {1, 255, 255, 255, 255};
|
||||||
|
|
||||||
|
fill3->colorStops(colorStops3, 4);
|
||||||
|
|
||||||
shape3->fill(move(fill3));
|
shape3->fill(move(fill3));
|
||||||
canvas->push(move(shape3));
|
canvas->push(move(shape3));
|
||||||
|
|
Loading…
Add table
Reference in a new issue