diff --git a/example/demo.c b/example/demo.c index 0ee2acc..8d0d1ce 100644 --- a/example/demo.c +++ b/example/demo.c @@ -1017,6 +1017,42 @@ void drawCaps(NVGcontext* vg, float x, float y, float width) nvgRestore(vg); } +void drawScissor(NVGcontext* vg, float x, float y, float t) +{ + nvgSave(vg); + + // Draw first rect and set scissor to it's area. + nvgTranslate(vg, x, y); + nvgRotate(vg, nvgDegToRad(5)); + nvgBeginPath(vg); + nvgRect(vg, -20,-20,60,40); + nvgFillColor(vg, nvgRGBA(255,0,0,255)); + nvgFill(vg); + nvgScissor(vg, -20,-20,60,40); + + // Draw second rectangle with offset and rotation. + nvgTranslate(vg, 40,0); + nvgRotate(vg, t); + + // Draw the intended second rectangle without any scissoring. + nvgSave(vg); + nvgResetScissor(vg); + nvgBeginPath(vg); + nvgRect(vg, -20,-10,60,30); + nvgFillColor(vg, nvgRGBA(255,128,0,64)); + nvgFill(vg); + nvgRestore(vg); + + // Draw second rectangle with combined scissoring. + nvgIntersectScissor(vg, -20,-10,60,30); + nvgBeginPath(vg); + nvgRect(vg, -20,-10,60,30); + nvgFillColor(vg, nvgRGBA(255,128,0,255)); + nvgFill(vg); + + nvgRestore(vg); +} + void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, float t, int blowup, DemoData* data) { @@ -1028,7 +1064,7 @@ void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, drawColorwheel(vg, width - 300, height - 300, 250.0f, 250.0f, t); // Line joints - drawLines(vg, 50, height-50, 600, 50, t); + drawLines(vg, 120, height-50, 600, 50, t); // Line caps drawWidths(vg, 10, 50, 30); @@ -1036,6 +1072,8 @@ void renderDemo(NVGcontext* vg, float mx, float my, float width, float height, // Line caps drawCaps(vg, 10, 300, 30); + drawScissor(vg, 50, height-80, t); + nvgSave(vg); if (blowup) { nvgRotate(vg, sinf(t*0.3f)*5.0f/180.0f*NVG_PI); diff --git a/src/nanovg.c b/src/nanovg.c index a0b9d17..1e4afb5 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -881,6 +881,49 @@ void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h) state->scissor.extent[1] = h*0.5f; } +static void nvg__isectRects(float* dst, + float ax, float ay, float aw, float ah, + float bx, float by, float bw, float bh) +{ + float minx = nvg__maxf(ax, bx); + float miny = nvg__maxf(ay, by); + float maxx = nvg__minf(ax+aw, bx+bw); + float maxy = nvg__minf(ay+ah, by+bh); + dst[0] = minx; + dst[1] = miny; + dst[2] = nvg__maxf(0.0f, maxx - minx); + dst[3] = nvg__maxf(0.0f, maxy - miny); +} + +void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h) +{ + NVGstate* state = nvg__getState(ctx); + float pxform[6], invxorm[6]; + float rect[4]; + float ex, ey, tex, tey; + + // If no previous scissor has been set, set the scissor as current scissor. + if (state->scissor.extent[0] < 0) { + nvgScissor(ctx, x, y, w, h); + return; + } + + // Transform the current scissor rect into current transform space. + // If there is difference in rotation, this will be approximation. + memcpy(pxform, state->scissor.xform, sizeof(float)*6); + ex = state->scissor.extent[0]; + ey = state->scissor.extent[1]; + nvgTransformInverse(invxorm, state->xform); + nvgTransformMultiply(pxform, invxorm); + tex = ex*nvg__absf(pxform[0]) + ey*nvg__absf(pxform[2]); + tey = ex*nvg__absf(pxform[1]) + ey*nvg__absf(pxform[3]); + + // Intersect rects. + nvg__isectRects(rect, pxform[4]-tex,pxform[5]-tey,tex*2,tey*2, x,y,w,h); + + nvgScissor(ctx, rect[0], rect[1], rect[2], rect[3]); +} + void nvgResetScissor(NVGcontext* ctx) { NVGstate* state = nvg__getState(ctx); diff --git a/src/nanovg.h b/src/nanovg.h index 23d9e37..9bb517f 100644 --- a/src/nanovg.h +++ b/src/nanovg.h @@ -370,10 +370,18 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey // Scissoring allows you to clip the rendering into a rectangle. This is useful for varius // user interface cases like rendering a text edit or a timeline. -// Sets the current +// Sets the current scissor rectangle. // The scissor rectangle is transformed by the current transform. void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); +// Intersects current scissor rectangle with the specified rectangle. +// The scissor rectangle is transformed by the current transform. +// Note: in case the rotation of previous scissor rect differs from +// the current one, the intersection will be done between the specified +// rectangle and the previous scissor rectangle transformed in the current +// transform space. The resulting shape is always rectangle. +void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); + // Reset and disables scissoring. void nvgResetScissor(NVGcontext* ctx);