diff --git a/res/ex-layout/main.xml b/res/ex-layout/main.xml index 4f7bdc4..75a8d62 100644 --- a/res/ex-layout/main.xml +++ b/res/ex-layout/main.xml @@ -5,14 +5,34 @@ background="?colorSurface" padding="20dp" orientation="horizontal"> - + + + + + + + - + \ No newline at end of file diff --git a/src/br/nullexcept/mux/core/texel/CanvasTexel.java b/src/br/nullexcept/mux/core/texel/CanvasTexel.java index 0b4edf7..bad422c 100644 --- a/src/br/nullexcept/mux/core/texel/CanvasTexel.java +++ b/src/br/nullexcept/mux/core/texel/CanvasTexel.java @@ -1,7 +1,9 @@ package br.nullexcept.mux.core.texel; import br.nullexcept.mux.graphics.*; +import br.nullexcept.mux.graphics.shape.ShapeList; import br.nullexcept.mux.hardware.GLES; +import br.nullexcept.mux.utils.Log; import org.lwjgl.opengles.GLES20; class CanvasTexel implements Canvas { @@ -15,16 +17,23 @@ public CanvasTexel(int width, int height) { } public void begin() { + VgTexel.endFrame(); + VgTexel.setAlpha(alpha); + framebuffer.bind(); + VgTexel.beginFrame(framebuffer.getWidth(), framebuffer.getHeight()); + CURRENT_CANVAS = id; + } + + private void check() { if (CURRENT_CANVAS != id) { - VgTexel.endFrame(); - VgTexel.setAlpha(alpha); - framebuffer.bind(); - VgTexel.beginFrame(framebuffer.getWidth(), framebuffer.getHeight()); + Log.error("CanvasTexel", "Switching canvas!"); + begin(); } } @Override public void reset() { + check(); this.alpha = 1.0f; GLES.glClearColor(0, 0, 0, 0); GLES.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT | GLES.GL_STENCIL_BUFFER_BIT); @@ -42,14 +51,27 @@ public void alpha(float alpha){ VgTexel.setAlpha(this.alpha); } + @Override + public void clip(ShapeList list) { + check(); + VgTexel.beginClip(); + VgTexel.getPaint().setColor(Color.WHITE); + list.asArray().forEach(shape -> { + shape.draw(this, VgTexel.getPaint()); + }); + VgTexel.endClip(framebuffer.getFramebuffer()); + } + @Override public void drawColor(int color) { + check(); VgTexel.setColor(color); drawRect(0, 0, getWidth(), getHeight(), VgTexel.getPaint()); } @Override public void drawRect(int left, int top, int right, int bottom, Paint paint) { + check(); VgTexel.beginElement(); VgTexel.applyPaint(paint); VgTexel.drawRect(left, top, right - left, bottom - top); @@ -63,6 +85,7 @@ public void drawRect(Rect rect, Paint paint) { @Override public void drawText(CharSequence text, int x, int y, Paint paint) { + check(); if (text == null) { text = "[NULL]"; } @@ -74,26 +97,19 @@ public void drawText(CharSequence text, int x, int y, Paint paint) { @Override public void translate(int x, int y) { + check(); VgTexel.move(x,y); } @Override public void rotate(float angle) { + check(); VgTexel.rotate(angle); } - @Override - public void drawBitmap(Rect rect, Bitmap bitmap, Paint paint) { - drawBitmap(rect.left, rect.top, rect.width(), rect.height(), bitmap, paint); - } - - @Override - public void drawBitmap(int x, int y, Bitmap bitmap, Paint paint) { - drawBitmap(x, y, bitmap.getWidth(), bitmap.getHeight(), bitmap, paint); - } - @Override public void drawEllipse(int left, int top, int right, int bottom, Paint paint) { + check(); VgTexel.beginElement(); VgTexel.applyPaint(paint); VgTexel.drawEllipse(left, top, right-left, bottom-top); @@ -102,14 +118,21 @@ public void drawEllipse(int left, int top, int right, int bottom, Paint paint) { @Override public void drawRoundRect(int left, int top, int right, int bottom, int radius, Paint paint) { + drawRoundRect(left, top, right, bottom, radius,radius,radius,radius, paint); + } + + @Override + public void drawRoundRect(int left, int top, int right, int bottom, int radiusLeft, int radiusTop, int radiusRight, int radiusBottom, Paint paint) { + check(); VgTexel.beginElement(); VgTexel.applyPaint(paint); - VgTexel.drawRoundedRect(left,top, right-left, bottom-top, radius); + VgTexel.drawRoundedRect(left,top,right-left, bottom-top, radiusLeft, radiusTop, radiusRight, radiusBottom); VgTexel.endElement(); } @Override public void drawPath(Path path, int x, int y, Paint paint) { + check(); VgTexel.beginElement(); VgTexel.applyPaint(paint); translate(x,y); @@ -118,13 +141,24 @@ public void drawPath(Path path, int x, int y, Paint paint) { VgTexel.endElement(); } + @Override + public void drawBitmap(Rect rect, Bitmap bitmap, Paint paint) { + drawBitmap(rect.left, rect.top, rect.width(), rect.height(), bitmap, paint); + } + + @Override + public void drawBitmap(int x, int y, Bitmap bitmap, Paint paint) { + drawBitmap(x, y, bitmap.getWidth(), bitmap.getHeight(), bitmap, paint); + } + @Override public void drawBitmap(int x, int y, int width, int height, Bitmap bitmap, Paint paint) { - drawBitmap(x,y,width,height, 0,0, bitmap.getWidth(), getHeight(), bitmap,paint); + drawBitmap(x,y,width,height, 0,0, bitmap.getWidth(), bitmap.getHeight(), bitmap,paint); } @Override public void drawBitmap(int x, int y, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight, Bitmap bitmap, Paint paint) { + check(); if (!(bitmap instanceof TexelBitmap)){ throw new IllegalArgumentException("Invalid bitmap, bitmap core and canvas core is different!"); } diff --git a/src/br/nullexcept/mux/core/texel/GLTexel.java b/src/br/nullexcept/mux/core/texel/GLTexel.java index f526ec8..84179fb 100644 --- a/src/br/nullexcept/mux/core/texel/GLTexel.java +++ b/src/br/nullexcept/mux/core/texel/GLTexel.java @@ -121,4 +121,33 @@ public static void drawTexture(float x, float y, float width, float height, GLPr program.unbind(); glBindTexture(GL_TEXTURE_2D,0); } + + public static void drawTextureClip(int x, int y, int width, int height, int texture) { + GLProgram program = GLShaderList.TEXTURE; + glEnable(GL_BLEND); + glBlendFuncSeparate( + GL_ZERO, GL_SRC_ALPHA, + GL_ZERO, GL_SRC_ALPHA + ); + prepareRect(x, y, width, height); + program.bind(); + + int vPosition = program.attribute(GLProgram.ATTRIBUTE_POSITION); + int vTextureCoords = program.attribute(GLProgram.ATTRIBUTE_UV); + int uTexture = program.uniform(GLProgram.UNIFORM_TEXTURE); + + glBindTexture(GL_TEXTURE_2D, texture); + glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, bufferRect); + glVertexAttribPointer(vTextureCoords, 2, GL_FLOAT, false, 0, bufferUV); + + glEnableVertexAttribArray(vPosition); + glEnableVertexAttribArray(vTextureCoords); + + glUniform1i(uTexture, GL_TEXTURE_2D); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + program.unbind(); + glBindTexture(GL_TEXTURE_2D,0); + glDisable(GL_BLEND); + } } diff --git a/src/br/nullexcept/mux/core/texel/VgTexel.java b/src/br/nullexcept/mux/core/texel/VgTexel.java index 951d13d..e50235e 100644 --- a/src/br/nullexcept/mux/core/texel/VgTexel.java +++ b/src/br/nullexcept/mux/core/texel/VgTexel.java @@ -1,31 +1,37 @@ package br.nullexcept.mux.core.texel; import br.nullexcept.mux.C; -import br.nullexcept.mux.graphics.Color; -import br.nullexcept.mux.graphics.Paint; -import br.nullexcept.mux.graphics.Path; -import br.nullexcept.mux.graphics.Point; +import br.nullexcept.mux.graphics.*; +import br.nullexcept.mux.hardware.GLES; import br.nullexcept.mux.utils.Log; import org.lwjgl.nanovg.*; +import org.lwjgl.opengles.GLES20; import static org.lwjgl.nanovg.NanoVG.*; class VgTexel { + private static long currentContext = 0; private static long globalContext = 0; + private static long clipContext = 0; private static final Paint globalPaint = new Paint(); private static final NVGColor globalColor = NVGColor.create(); private static final NVGPaint nvgPaint = NVGPaint.create(); private static final NVGPaint tmpPaint = NVGPaint.create(); private static float alpha = 1.0f; - private static final Point VIEWPORT = new Point(); + private static final Size drawViewport = new Size(); + private static GLFramebuffer clipFbo; public static void initialize(){ try { globalContext = NanoVGGLES2.nvgCreate(NanoVGGLES2.NVG_ANTIALIAS); + clipContext = NanoVGGLES2.nvgCreate(NanoVGGLES2.NVG_ANTIALIAS); globalPaint.setTextSize(-1f); C.VG_CONTEXT = globalContext; C.BITMAP_FACTORY = new TexelBitmapFactory(); GLShaderList.build(); + + clipFbo = new GLFramebuffer(1,1); + currentContext = globalContext; } catch (Throwable e){ Log.log("TexelAPI", "Error on initialize nanovg texel."); Log.log("TexelAPI", e); @@ -35,27 +41,45 @@ public static void initialize(){ @Deprecated public static long getContext() { - return globalContext; + return currentContext; } public static void applyPaint(Paint paint){ globalPaint.from(paint); - nvgFontFaceId(globalContext, paint.getTypeface().hashCode()); + nvgFontFaceId(currentContext, paint.getTypeface().hashCode()); setColor(paint.getColor()); setTextSize(paint.getTextSize()); nvgPaint.innerColor(globalColor); nvgPaint.outerColor(globalColor); - nvgFillPaint(globalContext, nvgPaint); - nvgStrokeColor(globalContext, globalColor); + nvgFillPaint(currentContext, nvgPaint); + nvgStrokeColor(currentContext, globalColor); + } + + + public static void beginClip() { + setCurrentContext(clipContext); + clipFbo.resize(drawViewport.width, drawViewport.height); + clipFbo.bind(); + clipFbo.clear(Color.TRANSPARENT); + beginFrame(drawViewport.width, drawViewport.height); + } + + public static void endClip(int srcFBO) { + endFrame(); + clipFbo.unbind(); + GLES.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, srcFBO); + setCurrentContext(globalContext); + flushFrame(); + GLTexel.drawTextureClip(0,0, drawViewport.width, drawViewport.height,clipFbo.getTexture().getTexture()); } private static void setTextSize(float textSize) { globalPaint.setTextSize(textSize); - nvgFontSize(globalContext, globalPaint.getTextSize()); - nvgTextLetterSpacing(globalContext,0.0f); + nvgFontSize(currentContext, globalPaint.getTextSize()); + nvgTextLetterSpacing(currentContext,0.0f); } public static void setColor(int color){ @@ -77,97 +101,114 @@ public static Paint getPaint() { private static void fill(){ switch (globalPaint.getMode()){ case FILL: - nvgFill(globalContext); + nvgFill(currentContext); break; case STROKE: - nvgStroke(globalContext); + nvgStroke(currentContext); break; default: - nvgFill(globalContext); - nvgStroke(globalContext); + nvgFill(currentContext); + nvgStroke(currentContext); break; } } + public static void setCurrentContext(long context) { + currentContext = context; + } + public static void drawPath(Path path) { - nvgBeginPath(globalContext); + nvgBeginPath(currentContext); for (int i = 0; i < path.length(); i++) { - nvgPathWinding(globalContext, NVG_HOLE); + nvgPathWinding(currentContext, NVG_HOLE); Path.Segment seg = path.segment(i); - nvgMoveTo(globalContext, seg.beginX(), seg.beginY()); + nvgMoveTo(currentContext, seg.beginX(), seg.beginY()); for (int x = 0; x < seg.partCount(); x++) { float[] curves = seg.part(x); - nvgBezierTo(globalContext, curves[0], curves[1], curves[2], curves[3], curves[4], curves[5]); + nvgBezierTo(currentContext, curves[0], curves[1], curves[2], curves[3], curves[4], curves[5]); } if (seg.closed()) { - nvgLineTo(globalContext,seg.beginX(), seg.beginY()); + nvgLineTo(currentContext,seg.beginX(), seg.beginY()); } } - nvgClosePath(globalContext); + nvgClosePath(currentContext); fill(); } - public static void drawRoundedRect(int x, int y, int width, int height, int radius){ - radius = Math.min(Math.max(width,height), radius); - nvgRoundedRect(globalContext, x,y,width,height,radius); + public static void drawRoundedRect(int x, int y, int width, int height, float rl, float rt, float rr, float rb){ + if (rl == rr && rr == rt && rt == rb) { + nvgRoundedRect(currentContext, x, y, width, height, rr); + } else { + nvgRoundedRectVarying(currentContext, x, y, width, height, rl, rr, rb, rt); + } fill(); } public static void drawEllipse(int x, int y, int width, int height){ int mw = width/2; int mh = height/2; - nvgEllipse(globalContext, x+mw, y+mh, mw, mh); + nvgEllipse(currentContext, x+mw, y+mh, mw, mh); fill(); } public static void drawRect(int x, int y, int width, int height){ - nvgRect(globalContext, x, y, width, height); + nvgRect(currentContext, x, y, width, height); fill(); } public static void beginElement(){ - nvgBeginPath(globalContext); + nvgBeginPath(currentContext); } public static void endElement(){ - nvgClosePath(globalContext); + nvgClosePath(currentContext); } public static void destroy(){ - NanoVGGLES2.nvgDelete(globalContext); + NanoVGGLES2.nvgDelete(currentContext); + NanoVGGLES2.nvgDelete(clipContext); } public static void beginFrame(int w, int h) { - nvgBeginFrame(globalContext, w,h,1.0f); - VIEWPORT.set(w,h); + nvgBeginFrame(currentContext, w,h,1.0f); + drawViewport.set(w,h); + } + + public static void flushFrame() { + nvgEndFrame(currentContext); + nvgBeginFrame(currentContext, drawViewport.width, drawViewport.height, 1.0f); } public static void endFrame() { - nvgEndFrame(globalContext); - nvgResetTransform(globalContext); - nvgReset(globalContext); - nvgResetScissor(globalContext); + nvgEndFrame(currentContext); + nvgResetTransform(currentContext); + nvgReset(currentContext); + nvgResetScissor(currentContext); alpha = 1.0f; } public static void drawImage(TexelBitmap image, float destX, float destY, float destW, float destH, float srcX, float srcY, float srcW, float srcH) { - float aw = image.getWidth() / srcW; - float ah = image.getHeight() / srcH; + drawImage(image.id(), image.getWidth(), image.getHeight(), destX, destY, destW, destH, srcX, srcY, srcW, srcH); + } + + public static void drawImage(int img, int originW, int originH, float destX, float destY, float destW, float destH, float srcX, float srcY, float srcW, float srcH) { + float aw = originW / srcW; + float ah = originH / srcH; float imgH = destH * ah; float imgW = destW * aw; - float imgX = destX - ((srcX / image.getWidth()) * imgW); - float imgY = destY - ((srcY / image.getHeight()) * imgH); + float imgX = destX - ((srcX / originW) * imgW); + float imgY = destY - ((srcY / originH) * imgH); - nvgImagePattern(globalContext, imgX, imgY, imgW, imgH, 0, image.id(), alpha, tmpPaint); - nvgRect(globalContext, destX, destY, destW, destH); - nvgFillPaint(globalContext, tmpPaint); - nvgFill(globalContext); - nvgFillPaint(globalContext, nvgPaint); + nvgImagePattern(currentContext, imgX, imgY, imgW, imgH, 0, img, alpha, tmpPaint); + nvgRect(currentContext, destX, destY, destW, destH); + nvgFillPaint(currentContext, tmpPaint); + nvgFill(currentContext); + nvgFillPaint(currentContext, nvgPaint); } public static void setAlpha(float alpha) { @@ -175,14 +216,14 @@ public static void setAlpha(float alpha) { } public static void drawText(int x, int y, CharSequence line) { - nvgText(globalContext, x,y, line); + nvgText(currentContext, x,y, line); } public static void rotate(float angle){ - nvgRotate(globalContext, angle); + nvgRotate(currentContext, angle); } public static void move(int x, int y) { - nvgTranslate(globalContext, x, y); + nvgTranslate(currentContext, x, y); } } diff --git a/src/br/nullexcept/mux/graphics/Canvas.java b/src/br/nullexcept/mux/graphics/Canvas.java index dd657f4..35f1db1 100644 --- a/src/br/nullexcept/mux/graphics/Canvas.java +++ b/src/br/nullexcept/mux/graphics/Canvas.java @@ -1,6 +1,10 @@ package br.nullexcept.mux.graphics; +import br.nullexcept.mux.graphics.shape.ShapeList; + public interface Canvas { + + void clip(ShapeList list); void drawColor(int color); default void drawRect(float left, float top, float right, float bottom, Paint paint){ drawRect(Math.round(left), Math.round(top), Math.round(right), Math.round(bottom), paint); @@ -23,9 +27,13 @@ default void drawEllipse(Rect rect, Paint paint){ } void drawRoundRect(int left, int top, int right, int bottom, int radius, Paint paint); + void drawRoundRect(int left, int top, int right, int bottom, int radiusLeft, int radiusTop, int radiusRight, int radiusBottom, Paint paint); default void drawRoundRect(Rect rect, int radius, Paint paint){ drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius, paint); } + default void drawRoundRect(Rect rect, Rect radius, Paint paint){ + drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius.left, radius.top, radius.right, radius.bottom, paint); + } void drawPath(Path path, int x, int y, Paint paint); diff --git a/src/br/nullexcept/mux/graphics/shape/RoundedShape.java b/src/br/nullexcept/mux/graphics/shape/RoundedShape.java index 6117cfc..96b24e3 100644 --- a/src/br/nullexcept/mux/graphics/shape/RoundedShape.java +++ b/src/br/nullexcept/mux/graphics/shape/RoundedShape.java @@ -2,16 +2,21 @@ import br.nullexcept.mux.graphics.Canvas; import br.nullexcept.mux.graphics.Paint; +import br.nullexcept.mux.graphics.Rect; public class RoundedShape extends Shape { - private int radius = 0; + private final Rect radius = new Rect(); public void setRadius(int radius) { - this.radius = radius; + this.radius.set(radius,radius,radius,radius); + } + + public void setRadius(Rect radius) { + this.radius.set(radius); } @Override public void draw(Canvas canvas, Paint paint) { - canvas.drawRoundRect(0,0,getWidth(),getHeight(), radius,paint); + canvas.drawRoundRect(0,0,getWidth(),getHeight(), radius.left, radius.top, radius.right, radius.bottom,paint); } } diff --git a/src/br/nullexcept/mux/graphics/shape/ShapeList.java b/src/br/nullexcept/mux/graphics/shape/ShapeList.java new file mode 100644 index 0000000..046cb80 --- /dev/null +++ b/src/br/nullexcept/mux/graphics/shape/ShapeList.java @@ -0,0 +1,21 @@ +package br.nullexcept.mux.graphics.shape; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ShapeList { + private final ArrayList shapes = new ArrayList<>(); + + public ShapeList(Shape... shapes) { + this.shapes.addAll(Arrays.asList(shapes)); + } + + public List asArray() { + return new ArrayList<>(shapes); + } + + public int getLength() { + return shapes.size(); + } +} diff --git a/src/br/nullexcept/mux/res/LayoutInflater.java b/src/br/nullexcept/mux/res/LayoutInflater.java index 0fb2277..538b308 100644 --- a/src/br/nullexcept/mux/res/LayoutInflater.java +++ b/src/br/nullexcept/mux/res/LayoutInflater.java @@ -93,6 +93,7 @@ private ViewGroup.LayoutParams parseLayoutParams(AttributeList attr){ static { registerView("AbsoluteLayout", AbsoluteLayout::new); + registerView("CardLayout", CardLayout::new); registerView("LinearLayout", LinearLayout::new); registerView("FrameLayout", FrameLayout::new); registerView("ScrollView", ScrollView::new); diff --git a/src/br/nullexcept/mux/res/Parser.java b/src/br/nullexcept/mux/res/Parser.java index b56fc72..28f1f34 100644 --- a/src/br/nullexcept/mux/res/Parser.java +++ b/src/br/nullexcept/mux/res/Parser.java @@ -109,7 +109,13 @@ private static Drawable inflateXmlDrawable(Resources res, XmlElement xml) { switch (shapeType){ case "rounded": { RoundedShape shape = new RoundedShape(); - attrs.searchDimension(AttrList.radius, v -> shape.setRadius(Math.round(v))); + Rect rect = new Rect(); + attrs.searchDimension(AttrList.radius, v -> rect.set(v.intValue(),v.intValue(),v.intValue(),v.intValue())); + attrs.searchDimension("leftTop", v -> rect.left = v.intValue()); + attrs.searchDimension("rightTop", v -> rect.right = v.intValue()); + attrs.searchDimension("leftBottom", v -> rect.top = v.intValue()); + attrs.searchDimension("rightBottom", v -> rect.bottom = v.intValue()); + shape.setRadius(rect); drawable.setShape(shape); }break; case "circle": diff --git a/src/br/nullexcept/mux/widget/CardLayout.java b/src/br/nullexcept/mux/widget/CardLayout.java new file mode 100644 index 0000000..4863be6 --- /dev/null +++ b/src/br/nullexcept/mux/widget/CardLayout.java @@ -0,0 +1,33 @@ +package br.nullexcept.mux.widget; + +import br.nullexcept.mux.app.Context; +import br.nullexcept.mux.graphics.Canvas; +import br.nullexcept.mux.graphics.shape.RoundedShape; +import br.nullexcept.mux.graphics.shape.ShapeList; +import br.nullexcept.mux.res.AttributeList; +import br.nullexcept.mux.view.AttrList; + +public class CardLayout extends AbsoluteLayout { + private final RoundedShape shape = new RoundedShape(); + private final ShapeList clipArea = new ShapeList(shape); + public CardLayout(Context context) { + this(context, null); + } + + public CardLayout(Context context, AttributeList attrs) { + super(context, attrs); + initialAttributes().searchDimension(AttrList.radius,(dimen) -> shape.setRadius(dimen.intValue())); + } + + public void setRadius(int radius) { + shape.setRadius(radius); + invalidate(); + } + + @Override + public void onDrawForeground(Canvas canvas) { + super.onDrawForeground(canvas); + shape.resize(canvas.getWidth(),canvas.getHeight()); + canvas.clip(clipArea); + } +}