Loading core/jni/android_graphics_Canvas.cpp +4 −41 Original line number Diff line number Diff line Loading @@ -544,39 +544,6 @@ private: float totalAdvance; }; // Same values used by Skia #define kStdStrikeThru_Offset (-6.0f / 21.0f) #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) { uint32_t flags; SkDrawFilter* drawFilter = canvas->getDrawFilter(); if (drawFilter) { SkPaint paintCopy(paint); drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); flags = paintCopy.getFlags(); } else { flags = paint.getFlags(); } if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { SkScalar left = x; SkScalar right = x + length; float textSize = paint.getTextSize(); float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); if (flags & SkPaint::kUnderlineText_Flag) { SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; canvas->drawRect(left, top, right, bottom, paint); } if (flags & SkPaint::kStrikeThruText_Flag) { SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; canvas->drawRect(left, top, right, bottom, paint); } } } void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount, float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) { // minikin may modify the original paint Loading @@ -586,8 +553,8 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); uint16_t* glyphs = new uint16_t[nGlyphs]; float* pos = new float[nGlyphs * 2]; std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); x += MinikinUtils::xOffsetForTextAlign(&paint, layout); Loading @@ -597,13 +564,9 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co bounds.offset(x, y); } DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance()); DrawTextFunctor f(layout, canvas, glyphs.get(), pos.get(), paint, x, y, bounds, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); drawTextDecorations(canvas, x, y, layout.getAdvance(), paint); delete[] glyphs; delete[] pos; } static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, Loading libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ hwui_src_files := \ AnimatorManager.cpp \ AssetAtlas.cpp \ Caches.cpp \ Canvas.cpp \ CanvasState.cpp \ ClipArea.cpp \ DamageAccumulator.cpp \ Loading libs/hwui/BakedOpRenderer.cpp +92 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ #include "utils/GLUtils.h" #include "VertexBuffer.h" #include <algorithm> #include <math.h> namespace android { namespace uirenderer { Loading Loading @@ -183,6 +186,10 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op renderer.renderGlop(state, glop); } void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { LOG_ALWAYS_FATAL("todo"); } void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) { Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) Loading Loading @@ -270,6 +277,91 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR renderer.renderGlop(state, glop); } static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, const TextOp& op, const BakedOpState& state) { renderer.caches().textureState().activateTexture(0); PaintUtils::TextShadow textShadow; if (!PaintUtils::getTextShadow(op.paint, &textShadow)) { LOG_ALWAYS_FATAL("failed to query shadow attributes"); } renderer.caches().dropShadowCache.setFontRenderer(fontRenderer); ShadowTexture* texture = renderer.caches().dropShadowCache.get( op.paint, (const char*) op.glyphs, op.glyphCount, textShadow.radius, op.positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!texture) return; const AutoTexture autoCleanup(texture); const float sx = op.x - texture->left + textShadow.dx; const float sy = op.y - texture->top + textShadow.dy; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUnitQuad(nullptr) .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { fontRenderer.setFont(op.paint, SkMatrix::I()); renderTextShadow(renderer, fontRenderer, op, state); } float x = op.x; float y = op.y; const Matrix4& transform = state.computedState.transform; const bool pureTranslate = transform.isPureTranslate(); if (CC_LIKELY(pureTranslate)) { x = floorf(x + transform.getTranslateX() + 0.5f); y = floorf(y + transform.getTranslateY() + 0.5f); fontRenderer.setFont(op.paint, SkMatrix::I()); fontRenderer.setTextureFiltering(false); } else if (CC_UNLIKELY(transform.isPerspective())) { fontRenderer.setFont(op.paint, SkMatrix::I()); fontRenderer.setTextureFiltering(true); } else { // We only pass a partial transform to the font renderer. That partial // matrix defines how glyphs are rasterized. Typically we want glyphs // to be rasterized at their final size on screen, which means the partial // matrix needs to take the scale factor into account. // When a partial matrix is used to transform glyphs during rasterization, // the mesh is generated with the inverse transform (in the case of scale, // the mesh is generated at 1.0 / scale for instance.) This allows us to // apply the full transform matrix at draw time in the vertex shader. // Applying the full matrix in the shader is the easiest way to handle // rotation and perspective and allows us to always generated quads in the // font renderer which greatly simplifies the code, clipping in particular. float sx, sy; transform.decomposeScale(sx, sy); fontRenderer.setFont(op.paint, SkMatrix::MakeScale( roundf(std::max(1.0f, sx)), roundf(std::max(1.0f, sy)))); fontRenderer.setTextureFiltering(true); } // TODO: Implement better clipping for scaled/rotated text const Rect* clip = !pureTranslate ? nullptr : &state.computedState.clipRect; Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); TextDrawFunctor functor(&renderer, &state, x, y, pureTranslate, alpha, mode, op.paint); bool hasActiveLayer = false; // TODO fontRenderer.renderPosText(op.paint, clip, (const char*) op.glyphs, op.glyphCount, x, y, op.positions, hasActiveLayer ? &layerBounds : nullptr, &functor, true); // TODO: merging } void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { OffscreenBuffer* buffer = *op.layerHandle; Loading libs/hwui/Canvas.cpp 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * 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. */ #include "Canvas.h" #include <SkDrawFilter.h> namespace android { void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { uint32_t flags; SkDrawFilter* drawFilter = getDrawFilter(); if (drawFilter) { SkPaint paintCopy(paint); drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); flags = paintCopy.getFlags(); } else { flags = paint.getFlags(); } if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { // Same values used by Skia const float kStdStrikeThru_Offset = (-6.0f / 21.0f); const float kStdUnderline_Offset = (1.0f / 9.0f); const float kStdUnderline_Thickness = (1.0f / 18.0f); SkScalar left = x; SkScalar right = x + length; float textSize = paint.getTextSize(); float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); if (flags & SkPaint::kUnderlineText_Flag) { SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; drawRect(left, top, right, bottom, paint); } if (flags & SkPaint::kStrikeThruText_Flag) { SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; drawRect(left, top, right, bottom, paint); } } } } // namespace android libs/hwui/Canvas.h +4 −5 Original line number Diff line number Diff line Loading @@ -149,16 +149,12 @@ public: // Text /** * drawText: count is of glyphs * totalAdvance is ignored in software renderering, used by hardware renderer for * text decorations (underlines, strikethroughs). * totalAdvance: used to define width of text decorations (underlines, strikethroughs). */ virtual void drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) = 0; /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */ virtual void drawPosText(const uint16_t* text, const float* positions, int count, int posCount, const SkPaint& paint) = 0; /** drawTextOnPath: count is of glyphs */ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) = 0; Loading @@ -171,6 +167,9 @@ public: * to be added to each glyph's position to get its absolute position. */ virtual bool drawTextAbsolutePos() const = 0; protected: void drawTextDecorations(float x, float y, float length, const SkPaint& paint); }; }; // namespace android Loading Loading
core/jni/android_graphics_Canvas.cpp +4 −41 Original line number Diff line number Diff line Loading @@ -544,39 +544,6 @@ private: float totalAdvance; }; // Same values used by Skia #define kStdStrikeThru_Offset (-6.0f / 21.0f) #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) { uint32_t flags; SkDrawFilter* drawFilter = canvas->getDrawFilter(); if (drawFilter) { SkPaint paintCopy(paint); drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); flags = paintCopy.getFlags(); } else { flags = paint.getFlags(); } if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { SkScalar left = x; SkScalar right = x + length; float textSize = paint.getTextSize(); float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); if (flags & SkPaint::kUnderlineText_Flag) { SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; canvas->drawRect(left, top, right, bottom, paint); } if (flags & SkPaint::kStrikeThruText_Flag) { SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; canvas->drawRect(left, top, right, bottom, paint); } } } void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount, float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) { // minikin may modify the original paint Loading @@ -586,8 +553,8 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); uint16_t* glyphs = new uint16_t[nGlyphs]; float* pos = new float[nGlyphs * 2]; std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); x += MinikinUtils::xOffsetForTextAlign(&paint, layout); Loading @@ -597,13 +564,9 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co bounds.offset(x, y); } DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance()); DrawTextFunctor f(layout, canvas, glyphs.get(), pos.get(), paint, x, y, bounds, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); drawTextDecorations(canvas, x, y, layout.getAdvance(), paint); delete[] glyphs; delete[] pos; } static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, Loading
libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ hwui_src_files := \ AnimatorManager.cpp \ AssetAtlas.cpp \ Caches.cpp \ Canvas.cpp \ CanvasState.cpp \ ClipArea.cpp \ DamageAccumulator.cpp \ Loading
libs/hwui/BakedOpRenderer.cpp +92 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ #include "utils/GLUtils.h" #include "VertexBuffer.h" #include <algorithm> #include <math.h> namespace android { namespace uirenderer { Loading Loading @@ -183,6 +186,10 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op renderer.renderGlop(state, glop); } void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { LOG_ALWAYS_FATAL("todo"); } void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) { Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) Loading Loading @@ -270,6 +277,91 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR renderer.renderGlop(state, glop); } static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, const TextOp& op, const BakedOpState& state) { renderer.caches().textureState().activateTexture(0); PaintUtils::TextShadow textShadow; if (!PaintUtils::getTextShadow(op.paint, &textShadow)) { LOG_ALWAYS_FATAL("failed to query shadow attributes"); } renderer.caches().dropShadowCache.setFontRenderer(fontRenderer); ShadowTexture* texture = renderer.caches().dropShadowCache.get( op.paint, (const char*) op.glyphs, op.glyphCount, textShadow.radius, op.positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!texture) return; const AutoTexture autoCleanup(texture); const float sx = op.x - texture->left + textShadow.dx; const float sy = op.y - texture->top + textShadow.dy; Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) .setRoundRectClipState(state.roundRectClipState) .setMeshTexturedUnitQuad(nullptr) .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) .build(); renderer.renderGlop(state, glop); } void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { fontRenderer.setFont(op.paint, SkMatrix::I()); renderTextShadow(renderer, fontRenderer, op, state); } float x = op.x; float y = op.y; const Matrix4& transform = state.computedState.transform; const bool pureTranslate = transform.isPureTranslate(); if (CC_LIKELY(pureTranslate)) { x = floorf(x + transform.getTranslateX() + 0.5f); y = floorf(y + transform.getTranslateY() + 0.5f); fontRenderer.setFont(op.paint, SkMatrix::I()); fontRenderer.setTextureFiltering(false); } else if (CC_UNLIKELY(transform.isPerspective())) { fontRenderer.setFont(op.paint, SkMatrix::I()); fontRenderer.setTextureFiltering(true); } else { // We only pass a partial transform to the font renderer. That partial // matrix defines how glyphs are rasterized. Typically we want glyphs // to be rasterized at their final size on screen, which means the partial // matrix needs to take the scale factor into account. // When a partial matrix is used to transform glyphs during rasterization, // the mesh is generated with the inverse transform (in the case of scale, // the mesh is generated at 1.0 / scale for instance.) This allows us to // apply the full transform matrix at draw time in the vertex shader. // Applying the full matrix in the shader is the easiest way to handle // rotation and perspective and allows us to always generated quads in the // font renderer which greatly simplifies the code, clipping in particular. float sx, sy; transform.decomposeScale(sx, sy); fontRenderer.setFont(op.paint, SkMatrix::MakeScale( roundf(std::max(1.0f, sx)), roundf(std::max(1.0f, sy)))); fontRenderer.setTextureFiltering(true); } // TODO: Implement better clipping for scaled/rotated text const Rect* clip = !pureTranslate ? nullptr : &state.computedState.clipRect; Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); TextDrawFunctor functor(&renderer, &state, x, y, pureTranslate, alpha, mode, op.paint); bool hasActiveLayer = false; // TODO fontRenderer.renderPosText(op.paint, clip, (const char*) op.glyphs, op.glyphCount, x, y, op.positions, hasActiveLayer ? &layerBounds : nullptr, &functor, true); // TODO: merging } void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { OffscreenBuffer* buffer = *op.layerHandle; Loading
libs/hwui/Canvas.cpp 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * 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. */ #include "Canvas.h" #include <SkDrawFilter.h> namespace android { void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { uint32_t flags; SkDrawFilter* drawFilter = getDrawFilter(); if (drawFilter) { SkPaint paintCopy(paint); drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); flags = paintCopy.getFlags(); } else { flags = paint.getFlags(); } if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { // Same values used by Skia const float kStdStrikeThru_Offset = (-6.0f / 21.0f); const float kStdUnderline_Offset = (1.0f / 9.0f); const float kStdUnderline_Thickness = (1.0f / 18.0f); SkScalar left = x; SkScalar right = x + length; float textSize = paint.getTextSize(); float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); if (flags & SkPaint::kUnderlineText_Flag) { SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; drawRect(left, top, right, bottom, paint); } if (flags & SkPaint::kStrikeThruText_Flag) { SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; drawRect(left, top, right, bottom, paint); } } } } // namespace android
libs/hwui/Canvas.h +4 −5 Original line number Diff line number Diff line Loading @@ -149,16 +149,12 @@ public: // Text /** * drawText: count is of glyphs * totalAdvance is ignored in software renderering, used by hardware renderer for * text decorations (underlines, strikethroughs). * totalAdvance: used to define width of text decorations (underlines, strikethroughs). */ virtual void drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) = 0; /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */ virtual void drawPosText(const uint16_t* text, const float* positions, int count, int posCount, const SkPaint& paint) = 0; /** drawTextOnPath: count is of glyphs */ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) = 0; Loading @@ -171,6 +167,9 @@ public: * to be added to each glyph's position to get its absolute position. */ virtual bool drawTextAbsolutePos() const = 0; protected: void drawTextDecorations(float x, float y, float length, const SkPaint& paint); }; }; // namespace android Loading