2018-09-19 11:21:12 +08:00
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <agge/path.h>
|
|
|
|
#include <agge/stroke.h>
|
|
|
|
#include <agge/bitmap.h>
|
|
|
|
#include <agge/clipper.h>
|
|
|
|
#include <agge/renderer.h>
|
|
|
|
#include <agge/rasterizer.h>
|
|
|
|
#include <agge/raw_bitmap.h>
|
|
|
|
#include <agge/filling_rules.h>
|
|
|
|
#include <agge/blenders_generic.h>
|
|
|
|
#include <agge/vector_rasterizer.h>
|
|
|
|
#include <agge/stroke_features.h>
|
|
|
|
#include <agge/nanovg_image_blender.h>
|
|
|
|
#include <agge/nanovg_vertex.h>
|
|
|
|
#include <agge/nanovg_agge.h>
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
agge::rect<T> mkrect(T x1, T y1, T x2, T y2) {
|
|
|
|
agge::rect<T> r = {x1, y1, x2, y2};
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct AGGENVGtexture {
|
|
|
|
int id;
|
|
|
|
int tex;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
int type;
|
|
|
|
int flags;
|
|
|
|
const uint8_t* data;
|
|
|
|
};
|
|
|
|
typedef struct AGGENVGtexture AGGENVGtexture;
|
|
|
|
|
|
|
|
struct AGGENVGcontext {
|
|
|
|
AGGENVGcontext() {
|
|
|
|
this->w = 0;
|
|
|
|
this->h = 0;
|
|
|
|
this->data = NULL;
|
|
|
|
this->textures = NULL;
|
|
|
|
this->ntextures = 0;
|
|
|
|
this->ctextures = 0;
|
|
|
|
this->textureId = 0;
|
|
|
|
|
|
|
|
this->line_style.set_cap(agge::caps::butt());
|
|
|
|
this->line_style.set_join(agge::joins::bevel());
|
|
|
|
}
|
|
|
|
|
|
|
|
~AGGENVGcontext() {
|
|
|
|
free(this->textures);
|
|
|
|
this->textures = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ntextures;
|
|
|
|
int ctextures;
|
|
|
|
int textureId;
|
|
|
|
AGGENVGtexture* textures;
|
|
|
|
|
|
|
|
/*fill/stroke color*/
|
|
|
|
uint8_t r;
|
|
|
|
uint8_t g;
|
|
|
|
uint8_t b;
|
|
|
|
uint8_t a;
|
|
|
|
|
|
|
|
/*frame buffer*/
|
2018-10-25 17:46:30 +08:00
|
|
|
uint32_t w;
|
|
|
|
uint32_t h;
|
|
|
|
uint32_t stride;
|
2018-09-19 11:21:12 +08:00
|
|
|
uint8_t* data;
|
2018-10-15 16:19:26 +08:00
|
|
|
enum NVGtexture format;
|
2018-09-19 11:21:12 +08:00
|
|
|
|
|
|
|
/*agge related*/
|
|
|
|
agge::renderer ren;
|
|
|
|
agge::stroke line_style;
|
|
|
|
agge::rasterizer<agge::clipper<int> > ras;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int aggenvg__maxi(int a, int b) {
|
|
|
|
return a > b ? a : b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static AGGENVGtexture* aggenvg__allocTexture(AGGENVGcontext* agge) {
|
|
|
|
int i;
|
|
|
|
AGGENVGtexture* tex = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < agge->ntextures; i++) {
|
|
|
|
if (agge->textures[i].id == 0) {
|
|
|
|
tex = &agge->textures[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tex == NULL) {
|
|
|
|
if (agge->ntextures + 1 > agge->ctextures) {
|
|
|
|
AGGENVGtexture* textures;
|
|
|
|
int ctextures =
|
|
|
|
aggenvg__maxi(agge->ntextures + 1, 4) + agge->ctextures / 2; // 1.5x Overallocate
|
|
|
|
textures = (AGGENVGtexture*)realloc(agge->textures, sizeof(AGGENVGtexture) * ctextures);
|
|
|
|
if (textures == NULL) return NULL;
|
|
|
|
agge->textures = textures;
|
|
|
|
agge->ctextures = ctextures;
|
|
|
|
}
|
|
|
|
tex = &agge->textures[agge->ntextures++];
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(tex, 0, sizeof(*tex));
|
|
|
|
tex->id = ++agge->textureId;
|
|
|
|
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
|
|
|
|
static AGGENVGtexture* aggenvg__findTexture(AGGENVGcontext* agge, int id) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < agge->ntextures; i++)
|
|
|
|
if (agge->textures[i].id == id) return &agge->textures[i];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aggenvg__deleteTexture(AGGENVGcontext* agge, int id) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < agge->ntextures; i++) {
|
|
|
|
if (agge->textures[i].id == id) {
|
|
|
|
memset(&agge->textures[i], 0, sizeof(agge->textures[i]));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aggenvg__renderCreate(void* uptr) {
|
|
|
|
NVG_NOTUSED(uptr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aggenvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags,
|
|
|
|
const unsigned char* data) {
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
AGGENVGtexture* tex = aggenvg__allocTexture(agge);
|
|
|
|
|
|
|
|
if (tex == NULL) return 0;
|
|
|
|
|
|
|
|
tex->width = w;
|
|
|
|
tex->height = h;
|
|
|
|
tex->type = type;
|
|
|
|
tex->data = data;
|
|
|
|
tex->flags = imageFlags;
|
|
|
|
|
|
|
|
return tex->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aggenvg__renderDeleteTexture(void* uptr, int image) {
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
return aggenvg__deleteTexture(agge, image);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aggenvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h,
|
|
|
|
const unsigned char* data) {
|
|
|
|
NVG_NOTUSED(x);
|
|
|
|
NVG_NOTUSED(y);
|
|
|
|
NVG_NOTUSED(w);
|
|
|
|
NVG_NOTUSED(h);
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
AGGENVGtexture* tex = aggenvg__findTexture(agge, image);
|
|
|
|
if (tex == NULL) return 0;
|
|
|
|
|
|
|
|
tex->data = data;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int aggenvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) {
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
AGGENVGtexture* tex = aggenvg__findTexture(agge, image);
|
|
|
|
if (tex == NULL) return 0;
|
|
|
|
*w = tex->width;
|
|
|
|
*h = tex->height;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-10-13 18:40:00 +08:00
|
|
|
static void aggenvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) {
|
2018-09-19 11:21:12 +08:00
|
|
|
NVG_NOTUSED(uptr);
|
|
|
|
NVG_NOTUSED(width);
|
|
|
|
NVG_NOTUSED(height);
|
|
|
|
NVG_NOTUSED(devicePixelRatio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void aggenvg__renderCancel(void* uptr) {
|
|
|
|
NVG_NOTUSED(uptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void aggenvg__renderFlush(void* uptr) {
|
|
|
|
NVG_NOTUSED(uptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prepareRasterizer(AGGENVGcontext* agge, NVGscissor* scissor, NVGpaint* paint) {
|
|
|
|
agge::rasterizer<agge::clipper<int> >& ras = agge->ras;
|
|
|
|
|
|
|
|
agge::real_t clip_w = scissor->extent[0] * 2;
|
|
|
|
agge::real_t clip_h = scissor->extent[1] * 2;
|
|
|
|
agge::real_t clip_x = scissor->xform[4] - scissor->extent[0];
|
|
|
|
agge::real_t clip_y = scissor->xform[5] - scissor->extent[1];
|
|
|
|
agge::rect<agge::real_t> clip_r = mkrect(clip_x, clip_y, clip_x + clip_w, clip_y + clip_h);
|
|
|
|
|
|
|
|
ras.reset();
|
|
|
|
if (clip_w > 0 && clip_h > 0) {
|
|
|
|
ras.set_clipping(clip_r);
|
|
|
|
}
|
|
|
|
|
|
|
|
agge->r = paint->innerColor.r * 0xff;
|
|
|
|
agge->g = paint->innerColor.g * 0xff;
|
|
|
|
agge->b = paint->innerColor.b * 0xff;
|
|
|
|
agge->a = paint->innerColor.a * 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename PixelT>
|
2018-09-26 15:40:55 +08:00
|
|
|
void renderPaint(AGGENVGcontext* agge, NVGpaint* paint) {
|
2018-09-19 11:21:12 +08:00
|
|
|
agge::renderer& ren = agge->ren;
|
|
|
|
agge::rasterizer<agge::clipper<int> >& ras = agge->ras;
|
2018-10-25 17:46:30 +08:00
|
|
|
agge::bitmap<PixelT, agge::raw_bitmap> surface(agge->w, agge->h, agge->stride, agge->data);
|
2018-09-19 11:21:12 +08:00
|
|
|
|
|
|
|
if (paint->image > 0) {
|
|
|
|
float invxform[6];
|
|
|
|
AGGENVGtexture* tex = aggenvg__findTexture(agge, paint->image);
|
|
|
|
nvgTransformInverse(invxform, paint->xform);
|
|
|
|
|
|
|
|
switch (tex->type) {
|
|
|
|
case NVG_TEXTURE_RGBA: {
|
|
|
|
typedef agge::bitmap<agge::pixel32_rgba, agge::raw_bitmap> rgba_bitmap_t;
|
2018-10-25 17:46:30 +08:00
|
|
|
rgba_bitmap_t src(tex->width, tex->height, tex->width*4, (uint8_t*)(tex->data));
|
2018-09-19 11:21:12 +08:00
|
|
|
agge::nanovg_image_blender<PixelT, rgba_bitmap_t> color(&src, (float*)invxform);
|
|
|
|
ren(surface, 0, ras, color, agge::winding<>());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NVG_TEXTURE_BGRA: {
|
|
|
|
typedef agge::bitmap<agge::pixel32_bgra, agge::raw_bitmap> bgra_bitmap_t;
|
2018-10-25 17:46:30 +08:00
|
|
|
bgra_bitmap_t src(tex->width, tex->height, tex->width*4, (uint8_t*)(tex->data));
|
2018-09-19 11:21:12 +08:00
|
|
|
agge::nanovg_image_blender<PixelT, bgra_bitmap_t> color(&src, (float*)invxform);
|
|
|
|
ren(surface, 0, ras, color, agge::winding<>());
|
|
|
|
break;
|
|
|
|
}
|
2018-10-10 17:30:59 +08:00
|
|
|
case NVG_TEXTURE_BGR565: {
|
2018-09-20 10:28:31 +08:00
|
|
|
typedef agge::bitmap<agge::pixel16_bgr565, agge::raw_bitmap> bgr565_bitmap_t;
|
2018-10-25 17:46:30 +08:00
|
|
|
bgr565_bitmap_t src(tex->width, tex->height, tex->width*2, (uint8_t*)(tex->data));
|
2018-09-20 10:28:31 +08:00
|
|
|
agge::nanovg_image_blender<PixelT, bgr565_bitmap_t> color(&src, (float*)invxform);
|
2018-09-19 11:21:12 +08:00
|
|
|
ren(surface, 0, ras, color, agge::winding<>());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NVG_TEXTURE_RGB: {
|
|
|
|
typedef agge::bitmap<agge::pixel24_rgb, agge::raw_bitmap> rgb_bitmap_t;
|
2018-10-25 17:46:30 +08:00
|
|
|
rgb_bitmap_t src(tex->width, tex->height, tex->width*3, (uint8_t*)(tex->data));
|
2018-09-19 11:21:12 +08:00
|
|
|
agge::nanovg_image_blender<PixelT, rgb_bitmap_t> color(&src, (float*)invxform);
|
|
|
|
ren(surface, 0, ras, color, agge::winding<>());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
assert(!"not supported format");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
agge::blender_solid_color_rgb<PixelT> color(agge->r, agge->g, agge->b, agge->a);
|
|
|
|
|
|
|
|
ren(surface, 0, ras, color, agge::winding<>());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 15:40:55 +08:00
|
|
|
template <typename PixelT>
|
|
|
|
void renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation,
|
|
|
|
NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths,
|
|
|
|
int npaths) {
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
agge::rasterizer<agge::clipper<int> >& ras = agge->ras;
|
|
|
|
|
|
|
|
prepareRasterizer(agge, scissor, paint);
|
|
|
|
|
|
|
|
for (int i = 0; i < npaths; i++) {
|
|
|
|
const NVGpath* p = paths + i;
|
|
|
|
for (int j = 0; j < p->nfill; j++) {
|
|
|
|
const NVGvertex* v = p->fill + j;
|
|
|
|
if (j == 0) {
|
|
|
|
ras.move_to(v->x, v->y);
|
|
|
|
} else {
|
|
|
|
ras.line_to(v->x, v->y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ras.close_polygon();
|
|
|
|
}
|
|
|
|
|
|
|
|
ras.sort();
|
|
|
|
renderPaint<PixelT>(agge, paint);
|
|
|
|
}
|
|
|
|
|
2018-09-19 11:21:12 +08:00
|
|
|
template <typename PixelT>
|
|
|
|
void renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation,
|
|
|
|
NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths,
|
|
|
|
int npaths) {
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
agge::stroke& line_style = agge->line_style;
|
|
|
|
agge::rasterizer<agge::clipper<int> >& ras = agge->ras;
|
|
|
|
|
|
|
|
line_style.width(strokeWidth);
|
|
|
|
prepareRasterizer(agge, scissor, paint);
|
|
|
|
|
|
|
|
for (int i = 0; i < npaths; i++) {
|
|
|
|
const NVGpath* p = paths + i;
|
|
|
|
agge::nanovg_vertex::iterator iter(p->stroke, p->nstroke);
|
|
|
|
agge::path_generator_adapter<agge::nanovg_vertex::iterator, agge::stroke> path_gen(iter,
|
|
|
|
line_style);
|
|
|
|
add_path(ras, path_gen);
|
|
|
|
}
|
|
|
|
|
|
|
|
ras.sort();
|
2018-09-26 15:40:55 +08:00
|
|
|
renderPaint<PixelT>(agge, paint);
|
2018-09-19 11:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename PixelT>
|
|
|
|
void renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation,
|
|
|
|
NVGscissor* scissor, const NVGvertex* verts, int nverts) {
|
2018-09-26 15:40:55 +08:00
|
|
|
/*XXX: not used yet*/
|
2018-09-19 11:21:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void aggenvg__renderDelete(void* uptr) {
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)uptr;
|
|
|
|
if (agge == NULL) return;
|
|
|
|
|
|
|
|
delete agge;
|
|
|
|
}
|
|
|
|
|
2018-10-25 17:46:30 +08:00
|
|
|
static void nvgInitAGGE(AGGENVGcontext* agge, NVGparams* params, uint32_t w, uint32_t h, uint32_t stride,
|
2018-10-15 16:19:26 +08:00
|
|
|
enum NVGtexture format, uint8_t* data) {
|
2018-09-19 11:21:12 +08:00
|
|
|
agge->w = w;
|
|
|
|
agge->h = h;
|
|
|
|
agge->data = data;
|
2018-10-25 17:46:30 +08:00
|
|
|
agge->stride = stride;
|
2018-09-19 11:21:12 +08:00
|
|
|
agge->format = format;
|
2018-10-25 17:46:30 +08:00
|
|
|
params->renderTriangles = NULL;
|
2018-09-19 11:21:12 +08:00
|
|
|
|
|
|
|
switch (agge->format) {
|
2018-10-15 16:19:26 +08:00
|
|
|
case NVG_TEXTURE_RGBA: {
|
2018-09-21 12:31:38 +08:00
|
|
|
params->renderStroke = renderStroke<agge::pixel32_rgba>;
|
|
|
|
params->renderFill = renderFill<agge::pixel32_rgba>;
|
2018-09-19 11:21:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-10-15 16:19:26 +08:00
|
|
|
case NVG_TEXTURE_BGRA: {
|
2018-09-21 12:31:38 +08:00
|
|
|
params->renderStroke = renderStroke<agge::pixel32_bgra>;
|
|
|
|
params->renderFill = renderFill<agge::pixel32_bgra>;
|
2018-09-19 11:21:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-10-15 16:19:26 +08:00
|
|
|
case NVG_TEXTURE_RGB: {
|
2018-09-21 12:31:38 +08:00
|
|
|
params->renderStroke = renderStroke<agge::pixel24_rgb>;
|
|
|
|
params->renderFill = renderFill<agge::pixel24_rgb>;
|
2018-09-19 11:21:12 +08:00
|
|
|
break;
|
|
|
|
}
|
2018-10-15 16:19:26 +08:00
|
|
|
case NVG_TEXTURE_BGR: {
|
2018-10-10 17:30:59 +08:00
|
|
|
params->renderStroke = renderStroke<agge::pixel24_bgr>;
|
|
|
|
params->renderFill = renderFill<agge::pixel24_bgr>;
|
|
|
|
break;
|
|
|
|
}
|
2018-10-15 16:19:26 +08:00
|
|
|
case NVG_TEXTURE_BGR565: {
|
2018-09-21 12:31:38 +08:00
|
|
|
params->renderStroke = renderStroke<agge::pixel16_bgr565>;
|
|
|
|
params->renderFill = renderFill<agge::pixel16_bgr565>;
|
2018-09-19 11:21:12 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
assert(!"not supported format");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-09-21 12:31:38 +08:00
|
|
|
}
|
|
|
|
|
2018-10-25 17:46:30 +08:00
|
|
|
void nvgReinitAgge(NVGcontext* ctx, uint32_t w, uint32_t h, uint32_t stride, enum NVGtexture format,
|
2018-09-21 12:31:38 +08:00
|
|
|
uint8_t* data) {
|
|
|
|
NVGparams* params = nvgGetParams(ctx);
|
|
|
|
AGGENVGcontext* agge = (AGGENVGcontext*)(params->userPtr);
|
|
|
|
|
2018-10-25 17:46:30 +08:00
|
|
|
nvgInitAGGE(agge, params, w, h, stride, format, data);
|
2018-09-21 12:31:38 +08:00
|
|
|
}
|
|
|
|
|
2018-10-25 17:46:30 +08:00
|
|
|
NVGcontext* nvgCreateAGGE(uint32_t w, uint32_t h, uint32_t stride, enum NVGtexture format, uint8_t* data) {
|
2018-09-21 12:31:38 +08:00
|
|
|
NVGparams params;
|
|
|
|
NVGcontext* ctx = NULL;
|
|
|
|
AGGENVGcontext* agge = new AGGENVGcontext();
|
|
|
|
|
|
|
|
if (agge == NULL) goto error;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
params.renderCreate = aggenvg__renderCreate;
|
|
|
|
params.renderCreateTexture = aggenvg__renderCreateTexture;
|
|
|
|
params.renderDeleteTexture = aggenvg__renderDeleteTexture;
|
|
|
|
params.renderUpdateTexture = aggenvg__renderUpdateTexture;
|
|
|
|
params.renderGetTextureSize = aggenvg__renderGetTextureSize;
|
|
|
|
params.renderViewport = aggenvg__renderViewport;
|
|
|
|
params.renderCancel = aggenvg__renderCancel;
|
|
|
|
params.renderFlush = aggenvg__renderFlush;
|
|
|
|
params.renderDelete = aggenvg__renderDelete;
|
|
|
|
params.userPtr = agge;
|
|
|
|
params.edgeAntiAlias = 1;
|
|
|
|
|
2018-10-25 17:46:30 +08:00
|
|
|
nvgInitAGGE(agge, ¶ms, w, h, stride, format, data);
|
2018-09-19 11:21:12 +08:00
|
|
|
|
|
|
|
ctx = nvgCreateInternal(¶ms);
|
|
|
|
if (ctx == NULL) goto error;
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (ctx != NULL) nvgDeleteInternal(ctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nvgDeleteAGGE(NVGcontext* ctx) {
|
|
|
|
nvgDeleteInternal(ctx);
|
|
|
|
}
|