TestUtils.cpp revision 1bc4ee4506b0a113d447d54909e1f8f5c1205563
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "TestUtils.h"
18
19#include "DeferredLayerUpdater.h"
20#include "LayerRenderer.h"
21
22#include <unistd.h>
23#include <signal.h>
24
25namespace android {
26namespace uirenderer {
27
28SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
29    int startA = (start >> 24) & 0xff;
30    int startR = (start >> 16) & 0xff;
31    int startG = (start >> 8) & 0xff;
32    int startB = start & 0xff;
33
34    int endA = (end >> 24) & 0xff;
35    int endR = (end >> 16) & 0xff;
36    int endG = (end >> 8) & 0xff;
37    int endB = end & 0xff;
38
39    return (int)((startA + (int)(fraction * (endA - startA))) << 24)
40            | (int)((startR + (int)(fraction * (endR - startR))) << 16)
41            | (int)((startG + (int)(fraction * (endG - startG))) << 8)
42            | (int)((startB + (int)(fraction * (endB - startB))));
43}
44
45sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
46        renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
47        std::function<void(Matrix4*)> transformSetupCallback) {
48    bool isOpaque = true;
49    bool forceFilter = true;
50    GLenum renderTarget = GL_TEXTURE_EXTERNAL_OES;
51
52    Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
53    LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, forceFilter,
54            renderTarget, Matrix4::identity().data);
55    transformSetupCallback(&(layer->getTransform()));
56
57    sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
58    return layerUpdater;
59}
60
61void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text,
62        std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
63        float* outTotalAdvance, Rect* outBounds) {
64    Rect bounds;
65    float totalAdvance = 0;
66    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
67    SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
68    while (*text != '\0') {
69        SkUnichar unichar = SkUTF8_NextUnichar(&text);
70        glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
71        autoCache.getCache()->unicharToGlyph(unichar);
72
73        // push glyph and its relative position
74        outGlyphs->push_back(glyph);
75        outPositions->push_back(totalAdvance);
76        outPositions->push_back(0);
77
78        // compute bounds
79        SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
80        Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight);
81        glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop);
82        bounds.unionWith(glyphBounds);
83
84        // advance next character
85        SkScalar skWidth;
86        paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
87        totalAdvance += skWidth;
88    }
89    *outBounds = bounds;
90    *outTotalAdvance = totalAdvance;
91}
92
93void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
94        const SkPaint& paint, float x, float y) {
95    // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
96    LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
97            "must use glyph encoding");
98    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
99    SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
100
101    std::vector<glyph_t> glyphs;
102    std::vector<float> positions;
103    float totalAdvance;
104    Rect bounds;
105    layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
106
107    // apply alignment via x parameter (which JNI layer would have done)
108    if (paint.getTextAlign() == SkPaint::kCenter_Align) {
109        x -= totalAdvance / 2;
110    } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
111        x -= totalAdvance;
112    }
113
114    bounds.translate(x, y);
115
116    // Force left alignment, since alignment offset is already baked in
117    SkPaint alignPaintCopy(paint);
118    alignPaintCopy.setTextAlign(SkPaint::kLeft_Align);
119    canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
120                bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
121}
122
123void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
124        const SkPaint& paint, const SkPath& path) {
125    // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
126    LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
127            "must use glyph encoding");
128    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
129    SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
130
131    std::vector<glyph_t> glyphs;
132    while (*text != '\0') {
133        SkUnichar unichar = SkUTF8_NextUnichar(&text);
134        glyphs.push_back(autoCache.getCache()->unicharToGlyph(unichar));
135    }
136    canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
137}
138
139static void defaultCrashHandler() {
140    fprintf(stderr, "RenderThread crashed!");
141}
142
143static std::function<void()> gCrashHandler = defaultCrashHandler;
144static sighandler_t gPreviousSignalHandler;
145
146static void signalHandler(int sig) {
147    gCrashHandler();
148    if (gPreviousSignalHandler) {
149        gPreviousSignalHandler(sig);
150    }
151}
152
153void TestUtils::setRenderThreadCrashHandler(std::function<void()> crashHandler) {
154    gCrashHandler = crashHandler;
155}
156
157void TestUtils::TestTask::run() {
158    gPreviousSignalHandler = signal(SIGABRT, signalHandler);
159
160    // RenderState only valid once RenderThread is running, so queried here
161    RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
162
163    renderState.onGLContextCreated();
164    rtCallback(renderthread::RenderThread::getInstance());
165    renderState.flush(Caches::FlushMode::Full);
166    renderState.onGLContextDestroyed();
167
168    // Restore the previous signal handler
169    signal(SIGABRT, gPreviousSignalHandler);
170}
171
172} /* namespace uirenderer */
173} /* namespace android */
174