return dstBuffer;
}
- inline char* refText(const char* text, size_t byteLength) {
- return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
- }
-
inline const SkPath* refPath(const SkPath* path) {
if (!path) return nullptr;
}
/**
- * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in (with deduping
- * based on paint generation ID)
- *
- * Note that this forces Left_Align, since drawText glyph rendering expects left alignment,
- * since alignment offsetting has been done at a higher level. This is done to essentially all
- * copied paints, since the deduping can mean a paint is shared by drawText commands and other
- * types (which wouldn't care about alignment).
+ * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in
+ * (with deduping based on paint hash / equality check)
*/
inline const SkPaint* refPaint(const SkPaint* paint) {
if (!paint) return nullptr;
// In the unlikely event that 2 unique paints have the same hash we do a
// object equality check to ensure we don't erroneously dedup them.
if (cachedPaint == nullptr || *cachedPaint != *paint) {
- SkPaint* copy = new SkPaint(*paint);
- copy->setTextAlign(SkPaint::kLeft_Align);
-
- cachedPaint = copy;
- mDisplayList->paints.emplace_back(copy);
+ cachedPaint = new SkPaint(*paint);
+ mDisplayList->paints.emplace_back(cachedPaint);
// replaceValueFor() performs an add if the entry doesn't exist
mPaintMap.replaceValueFor(key, cachedPaint);
refBitmapsInShader(cachedPaint->getShader());
textPaint.setAntiAlias(true);
textPaint.setTextSize(20);
textPaint.setStrikeThruText(true);
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
for (int i = 0; i < LOOPS; i++) {
TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
}
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
});
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
paint.setUnderlineText(i != 0);
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setTextAlign(SkPaint::kLeft_Align);
TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25);
paint.setTextAlign(SkPaint::kCenter_Align);
});
int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
+ float lastX = FLT_MAX;
+ playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
count++;
ASSERT_EQ(RecordedOpId::TextOp, op.opId);
EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
<< "recorded drawText commands must force kLeft_Align on their paint";
- EXPECT_EQ(SkPaint::kGlyphID_TextEncoding, op.paint->getTextEncoding()); // verify TestUtils
+
+ // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
+ EXPECT_GT(lastX, ((const TextOp&)op).x)
+ << "x coordinate should reduce across each of the draw commands, from alignment";
+ lastX = ((const TextOp&)op).x;
});
ASSERT_EQ(3, count);
}
EXPECT_TRUE(chunks[1].reorderChildren);
}
+TEST(RecordingCanvas, refPaint) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(20);
+ paint.setTextAlign(SkPaint::kLeft_Align);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
+ paint.setColor(SK_ColorBLUE);
+ // first three should use same paint
+ canvas.drawRect(0, 0, 200, 10, paint);
+ SkPaint paintCopy(paint);
+ canvas.drawRect(0, 10, 200, 20, paintCopy);
+ TestUtils::drawTextToCanvas(&canvas, "helloworld", paint, 50, 25);
+
+ // only here do we use different paint ptr
+ paint.setColor(SK_ColorRED);
+ canvas.drawRect(0, 20, 200, 30, paint);
+ });
+ auto ops = dl->getOps();
+ ASSERT_EQ(4u, ops.size());
+
+ // first three are the same
+ EXPECT_NE(nullptr, ops[0]->paint);
+ EXPECT_NE(&paint, ops[0]->paint);
+ EXPECT_EQ(ops[0]->paint, ops[1]->paint);
+ EXPECT_EQ(ops[0]->paint, ops[2]->paint);
+
+ // last is different, but still copied / non-null
+ EXPECT_NE(nullptr, ops[3]->paint);
+ EXPECT_NE(ops[0]->paint, ops[3]->paint);
+ EXPECT_NE(&paint, ops[3]->paint);
+}
+
} // namespace uirenderer
} // namespace android
}
void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
- const SkPaint& inPaint, float x, float y) {
- // copy to force TextEncoding (which JNI layer would have done)
- SkPaint paint(inPaint);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ const SkPaint& paint, float x, float y) {
+ // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
+ LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
+ "must use glyph encoding");
- SkMatrix identity;
- identity.setIdentity();
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &identity);
+ SkMatrix identity;
+ identity.setIdentity();
+ SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+ SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &identity);
- float totalAdvance = 0;
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- Rect bounds;
- while (*text != '\0') {
- SkUnichar unichar = SkUTF8_NextUnichar(&text);
- glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
- autoCache.getCache()->unicharToGlyph(unichar);
+ float totalAdvance = 0;
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ Rect bounds;
+ while (*text != '\0') {
+ SkUnichar unichar = SkUTF8_NextUnichar(&text);
+ glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
+ autoCache.getCache()->unicharToGlyph(unichar);
- // push glyph and its relative position
- glyphs.push_back(glyph);
- positions.push_back(totalAdvance);
- positions.push_back(0);
+ // push glyph and its relative position
+ glyphs.push_back(glyph);
+ positions.push_back(totalAdvance);
+ positions.push_back(0);
- // compute bounds
- SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
- Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight);
- glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop);
- bounds.unionWith(glyphBounds);
+ // compute bounds
+ SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
+ Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight);
+ glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop);
+ bounds.unionWith(glyphBounds);
- // advance next character
- SkScalar skWidth;
- paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
- totalAdvance += skWidth;
- }
- bounds.translate(x, y);
- canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), paint, x, y,
- bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
+ // advance next character
+ SkScalar skWidth;
+ paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
+ totalAdvance += skWidth;
+ }
+
+ // apply alignment via x parameter (which JNI layer would have done)
+ if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+ x -= totalAdvance / 2;
+ } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
+ x -= totalAdvance;
+ }
+
+ bounds.translate(x, y);
+
+ // Force left alignment, since alignment offset is already baked in
+ SkPaint alignPaintCopy(paint);
+ alignPaintCopy.setTextAlign(SkPaint::kLeft_Align);
+ canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
+ bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
}
} /* namespace uirenderer */
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
static void drawTextToCanvas(TestCanvas* canvas, const char* text,
- const SkPaint& inPaint, float x, float y);
+ const SkPaint& paint, float x, float y);
private:
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {