#define _TVG_DECLARE_ACCESSOR(A) \
friend A
-#define _TVG_DECLARE_ACCESSORS(A, B) \
- friend A; \
- friend B
-
#define _TVG_DECALRE_IDENTIFIER() \
+ auto id() const { return _id; } \
protected: \
- unsigned id
-
+ unsigned _id
namespace tvg
{
virtual Result bounds(float* x, float* y, float* w, float* h) const = 0;
_TVG_DECALRE_IDENTIFIER();
- _TVG_DECLARE_ACCESSORS(Canvas, Scene);
};
static std::unique_ptr<Shape> gen() noexcept;
_TVG_DECLARE_PRIVATE(Shape);
- _TVG_DECLARE_ACCESSORS(Canvas, Scene);
+ _TVG_DECLARE_ACCESSOR(Canvas);
+ _TVG_DECLARE_ACCESSOR(Scene);
};
source_file = [
'tvgSwCommon.h',
+ 'tvgSwFill.cpp',
'tvgSwMath.cpp',
'tvgSwRenderer.h',
'tvgSwRaster.cpp',
using namespace tvg;
-constexpr auto SW_CURVE_TYPE_POINT = 0;
-constexpr auto SW_CURVE_TYPE_CUBIC = 1;
-
-constexpr auto SW_OUTLINE_FILL_WINDING = 0;
-constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1;
+#define SW_CURVE_TYPE_POINT 0
+#define SW_CURVE_TYPE_CUBIC 1
+#define SW_OUTLINE_FILL_WINDING 0
+#define 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 SwFixed = signed long long;
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
{
SwOutline* outline;
SwStroke* stroke;
+ SwFill* fill;
SwRleData* rle;
SwRleData* strokeRle;
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)
{
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
}
+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 mathDivide(int64_t a, int64_t b);
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
void shapeResetStroke(SwShape& shape, const Shape& sdata);
bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip);
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);
bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(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);
void rleFree(SwRleData* rle);
-bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterGradientShape(Surface& surface, SwShape& shape);
+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_ */
--- /dev/null
+/*
+ * 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_ */
\ No newline at end of file
auto tmp = v.y;
v.y = -v.x;
v.x = tmp;
- theta = ANGLE_PI2;
+ theta = SW_ANGLE_PI2;
} else {
- theta = v.y > 0 ? ANGLE_PI : -ANGLE_PI;
+ theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
v.x = -v.x;
v.y = -v.y;
}
} else {
if (v.y < -v.x) {
- theta = -ANGLE_PI2;
+ theta = -SW_ANGLE_PI2;
auto tmp = -v.y;
v.y = v.x;
v.x = tmp;
SwFixed y = pt.y;
//Rotate inside [-PI/4, PI/4] sector
- while (theta < -ANGLE_PI4) {
+ while (theta < -SW_ANGLE_PI4) {
auto tmp = y;
y = -x;
x = tmp;
- theta += ANGLE_PI2;
+ theta += SW_ANGLE_PI2;
}
- while (theta > ANGLE_PI4) {
+ while (theta > SW_ANGLE_PI4) {
auto tmp = -y;
y = x;
x = tmp;
- theta -= ANGLE_PI2;
+ theta -= SW_ANGLE_PI2;
}
auto atan = ATAN_TBL;
auto theta1 = abs(mathDiff(angleIn, angleMid));
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;
}
SwFixed mathSin(SwFixed angle)
{
- return mathCos(ANGLE_PI2 - angle);
+ return mathCos(SW_ANGLE_PI2 - angle);
}
{
auto delta = angle2 - angle1;
- delta %= ANGLE_2PI;
- if (delta < 0) delta += ANGLE_2PI;
- if (delta > ANGLE_PI) delta -= ANGLE_2PI;
+ delta %= SW_ANGLE_2PI;
+ if (delta < 0) delta += SW_ANGLE_2PI;
+ if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
return delta;
}
/* 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;
-}
-
-
-static inline uint32_t COLOR_ALPHA_BLEND(uint32_t color, uint32_t alpha)
-{
- return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) +
- ((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff));
-}
+ if (!rle) return false;
+ auto span = rle->spans;
+ auto stride = surface.stride;
+ uint32_t tmp;
-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);
+ for (uint32_t i = 0; i < rle->size; ++i) {
+ auto dst = &surface.buffer[span->y * stride + span->x];
+ if (span->coverage < 255) tmp = COLOR_ALPHA_BLEND(color, span->coverage);
+ else tmp = color;
+ for (uint32_t i = 0; i < span->len; ++i) {
+ dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
+ }
+ ++span;
+ }
+ return true;
}
-static void
-_rasterTranslucent(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
+static bool _rasterSolidRle(Surface& surface, SwRleData* rle, uint32_t color)
{
- //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);
- }
-}
-
+ if (!rle) return false;
-static void
-_rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
-{
- //OPTIMIZE ME: SIMD
+ auto span = rle->spans;
+ auto stride = surface.stride;
- //Fully Opaque
- if (cov == 255) {
- for (uint32_t i = 0; i < len; ++i) {
- dst[i] = color;
- }
- } else {
- 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);
+ for (uint32_t i = 0; i < rle->size; ++i) {
+ auto dst = &surface.buffer[span->y * stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t i = 0; i < span->len; ++i) {
+ 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;
}
- }
+ return true;
}
-static bool
-_rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
+static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
{
- if (!rle) return false;
+ 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;
- for (uint32_t i = 0; i < rle->size; ++i) {
- assert(span);
-
- auto dst = &surface.buffer[span->y * stride + span->x];
-
- if (a == 255) _rasterSolid(dst, span->len, color, span->coverage);
- else _rasterTranslucent(dst, span->len, color, span->coverage);
-
- ++span;
+ //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;
}
/* 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 _rasterGradientRle(surface, shape.rle, shape.fill);
+}
+
+
+bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
- return _rasterRle(surface, sdata.rle, 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& sdata, 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)
{
- return _rasterRle(surface, sdata.strokeRle, COLOR_ARGB_JOIN(r, g, b, a), 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));
}
return true;
}
-bool SwRenderer::render(const Shape& shape, void *data)
+bool SwRenderer::render(const Shape& sdata, void *data)
{
- SwShape* sdata = static_cast<SwShape*>(data);
- if (!sdata) return false;
+ SwShape* shape = static_cast<SwShape*>(data);
+ if (!shape) return false;
uint8_t r, g, b, a;
- shape.fill(&r, &g, &b, &a);
- if (a > 0) rasterShape(surface, *sdata, r, g, b, a);
+ if (sdata.fill()) {
+ 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);
- if (a > 0) rasterStroke(surface, *sdata, r, g, b, a);
+ sdata.strokeColor(&r, &g, &b, &a);
+ if (a > 0) rasterStroke(surface, *shape, r, g, b, a);
return true;
}
-bool SwRenderer::dispose(const Shape& shape, void *data)
+bool SwRenderer::dispose(const Shape& sdata, void *data)
{
- auto sdata = static_cast<SwShape*>(data);
- if (!sdata) return true;
- shapeFree(sdata);
+ auto shape = static_cast<SwShape*>(data);
+ if (!shape) return true;
+ shapeFree(shape);
return true;
}
shapeReset(*shape);
uint8_t alpha = 0;
sdata.fill(nullptr, nullptr, nullptr, &alpha);
- if (alpha > 0) {
+ if (alpha > 0 || sdata.fill()) {
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
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
if (sdata.strokeWidth() > 0.5) {
if (alpha > 0) {
if (!shapeGenStrokeRle(*shape, sdata, clip)) return shape;
}
+ } else {
+ shapeDelStroke(*shape);
}
}
}
-void shapeFree(SwShape* sdata)
+void shapeFree(SwShape* shape)
{
- assert(sdata);
+ assert(shape);
- shapeDelOutline(*sdata);
- rleFree(sdata->rle);
+ shapeDelOutline(*shape);
+ rleFree(shape->rle);
- if (sdata->stroke) {
- rleFree(sdata->strokeRle);
- strokeFree(sdata->stroke);
+ if (shape->stroke) {
+ rleFree(shape->strokeRle);
+ 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;
}
}
+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_ */
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
{
- return (ANGLE_PI2 - (s) * ANGLE_PI);
+ return (SW_ANGLE_PI2 - (s) * SW_ANGLE_PI);
}
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};
mathRotate(a, angleStart);
a += center;
auto total = angleDiff;
auto angle = angleStart;
- auto rotate = (angleDiff >= 0) ? ANGLE_PI2 : -ANGLE_PI2;
+ auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
while (total != 0) {
auto step = total;
auto border = stroke.borders + side;
auto rotate = SIDE_TO_ROTATE(side);
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);
border->movable = false;
if (!bevel) {
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
- if (theta == ANGLE_PI) {
+ if (theta == SW_ANGLE_PI) {
theta = rotate;
phi = stroke.angleIn;
} else {
void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
{
SwPoint delta = {stroke.width, 0};
- mathRotate(delta, startAngle + ANGLE_PI2);
+ mathRotate(delta, startAngle + SW_ANGLE_PI2);
auto pt = stroke.center + delta;
auto border = stroke.borders;
auto angle = mathAtan(delta);
delta = {stroke.width, 0};
- mathRotate(delta, angle + ANGLE_PI2);
+ mathRotate(delta, angle + SW_ANGLE_PI2);
//process corner if necessary
if (stroke.firstPt) {
stroke.angleOut = angleIn;
_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
stroke.center = arc[3];
stroke.angleOut = angleIn;
auto alpha1 = mathAtan(_end - _start);
//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
auto beta = mathAtan(arc[3] - _start);
} else if (stroke.cap == StrokeCap::Round) {
stroke.angleIn = angle;
- stroke.angleOut = angle + ANGLE_PI;
+ stroke.angleOut = angle + SW_ANGLE_PI;
_arcTo(stroke, side);
return;
//now add the final cap
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
and deosn't need further processing */
assert(renderer);
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.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->clear(*renderer)) return Result::InsufficientCondition;
assert(renderer);
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.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->update(*renderer, nullptr)) return Result::InsufficientCondition;
{
assert(renderer);
- if (paint->id == PAINT_ID_SCENE) {
+ if (paint->id() == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->update(*renderer)) return Result::InsufficientCondition;
if (!renderer->clear()) return Result::InsufficientCondition;
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.
auto scene = static_cast<Scene*>(paint);
if(!SCENE_IMPL->render(*renderer)) return Result::InsufficientCondition;
LinearGradient::LinearGradient():pImpl(make_unique<Impl>())
{
- id = FILL_ID_LINEAR;
+ _id = FILL_ID_LINEAR;
}
RadialGradient::RadialGradient():pImpl(make_unique<Impl>())
{
- id = FILL_ID_RADIAL;
+ _id = FILL_ID_RADIAL;
}
Scene::Scene() : pImpl(make_unique<Impl>())
{
- id = PAINT_ID_SCENE;
+ _id = PAINT_ID_SCENE;
}
bool clear(RenderMethod& renderer)
{
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.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->clear(renderer)) return false;
bool updateInternal(RenderMethod &renderer, const RenderTransform* transform, uint32_t flag)
{
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.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->update(renderer, transform, flag)) return false;
bool render(RenderMethod &renderer)
{
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.
auto scene = static_cast<Scene*>(paint);
if(!SCENE_IMPL->render(renderer)) return false;
auto w2 = 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.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false;
Shape :: Shape() : pImpl(make_unique<Impl>())
{
- id = PAINT_ID_SHAPE;
+ _id = PAINT_ID_SHAPE;
}
canvas->target(buffer, WIDTH, WIDTH, HEIGHT);
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
auto shape1 = tvg::Shape::gen();
- shape1->appendRect(0, 0, 400, 400, 50); //x, y, w, h, cornerRadius
- shape1->stroke(255, 255, 255, 255);
- shape1->stroke(2);
+ shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, cornerRadius
//LinearGradient
auto fill = tvg::LinearGradient::gen();
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));
canvas->push(move(shape1));
-
//Prepare Circle
auto shape2 = tvg::Shape::gen();
shape2->appendCircle(400, 400, 200, 200); //cx, cy, radiusW, radiusH
- shape2->stroke(255, 255, 255, 255);
- shape2->stroke(2);
//LinearGradient
auto fill2 = tvg::LinearGradient::gen();
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));
canvas->push(move(shape2));
//Prepare Ellipse
auto shape3 = tvg::Shape::gen();
shape3->appendCircle(600, 600, 150, 100); //cx, cy, radiusW, radiusH
- shape3->stroke(255, 255, 255, 255);
- shape3->stroke(2);
//LinearGradient
auto fill3 = tvg::LinearGradient::gen();
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));
canvas->push(move(shape3));