1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/* 2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot* Copyright 2017 Google Inc. 3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot* 4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot* Use of this source code is governed by a BSD-style license that can be 5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot* found in the LICENSE file. 6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot*/ 7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkHighContrastFilter.h" 9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPM4f.h" 10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkArenaAlloc.h" 11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkRasterPipeline.h" 12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkReadBuffer.h" 13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkString.h" 14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkWriteBuffer.h" 15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "../jumper/SkJumper.h" 16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU 18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrColorSpaceInfo.h" 19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrContext.h" 20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLFragmentProcessor.h" 21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLFragmentShaderBuilder.h" 22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif 23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotusing InvertStyle = SkHighContrastConfig::InvertStyle; 25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass SkHighContrast_Filter : public SkColorFilter { 27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic: 28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkHighContrast_Filter(const SkHighContrastConfig& config) { 29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fConfig = config; 30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Clamp contrast to just inside -1 to 1 to avoid division by zero. 31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fConfig.fContrast = SkScalarPin(fConfig.fContrast, 32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot -1.0f + FLT_EPSILON, 33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 1.0f - FLT_EPSILON); 34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot ~SkHighContrast_Filter() override {} 37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU 39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot std::unique_ptr<GrFragmentProcessor> asFragmentProcessor( 40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrContext*, const GrColorSpaceInfo&) const override; 41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot #endif 42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot void onAppendStages(SkRasterPipeline* p, 44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkColorSpace* dst, 45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkArenaAlloc* scratch, 46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool shaderIsOpaque) const override; 47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_TO_STRING_OVERRIDE() 49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkHighContrast_Filter) 51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprotected: 53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot void flatten(SkWriteBuffer&) const override; 54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate: 56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkHighContrastConfig fConfig; 57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot friend class SkHighContrastFilter; 59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot typedef SkColorFilter INHERITED; 61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}; 62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p, 64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkColorSpace* dstCS, 65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkArenaAlloc* alloc, 66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool shaderIsOpaque) const { 67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (!shaderIsOpaque) { 68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::unpremul); 69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (!dstCS) { 72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // In legacy draws this effect approximately linearizes by squaring. 73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // When non-legacy, we're already (better) linearized. 74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot auto square = alloc->make<SkJumper_ParametricTransferFunction>(); 75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot square->G = 2.0f; square->A = 1.0f; 76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot square->B = square->C = square->D = square->E = square->F = 0; 77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::parametric_r, square); 79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::parametric_g, square); 80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::parametric_b, square); 81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (fConfig.fGrayscale) { 84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float r = SK_LUM_COEFF_R; 85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float g = SK_LUM_COEFF_G; 86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float b = SK_LUM_COEFF_B; 87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float* matrix = alloc->makeArray<float>(12); 88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[0] = matrix[1] = matrix[2] = r; 89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[3] = matrix[4] = matrix[5] = g; 90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[6] = matrix[7] = matrix[8] = b; 91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::matrix_3x4, matrix); 92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) { 95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float* matrix = alloc->makeArray<float>(12); 96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[0] = matrix[4] = matrix[8] = -1; 97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[9] = matrix[10] = matrix[11] = 1; 98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::matrix_3x4, matrix); 99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) { 100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::rgb_to_hsl); 101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float* matrix = alloc->makeArray<float>(12); 102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[0] = matrix[4] = matrix[11] = 1; 103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[8] = -1; 104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::matrix_3x4, matrix); 105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::hsl_to_rgb); 106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (fConfig.fContrast != 0.0) { 109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float* matrix = alloc->makeArray<float>(12); 110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float c = fConfig.fContrast; 111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float m = (1 + c) / (1 - c); 112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot float b = (-0.5f * m + 0.5f); 113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[0] = matrix[4] = matrix[8] = m; 114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot matrix[9] = matrix[10] = matrix[11] = b; 115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::matrix_3x4, matrix); 116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::clamp_0); 119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::clamp_1); 120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (!dstCS) { 122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // See the previous if(!dstCS) { ... } 123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot auto sqrt = alloc->make<SkJumper_ParametricTransferFunction>(); 124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot sqrt->G = 0.5f; sqrt->A = 1.0f; 125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot sqrt->B = sqrt->C = sqrt->D = sqrt->E = sqrt->F = 0; 126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::parametric_r, sqrt); 128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::parametric_g, sqrt); 129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::parametric_b, sqrt); 130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (!shaderIsOpaque) { 133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot p->append(SkRasterPipeline::premul); 134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const { 138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot buffer.writeBool(fConfig.fGrayscale); 139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot buffer.writeInt(static_cast<int>(fConfig.fInvertStyle)); 140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot buffer.writeScalar(fConfig.fContrast); 141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) { 144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkHighContrastConfig config; 145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot config.fGrayscale = buffer.readBool(); 146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot config.fInvertStyle = buffer.read32LE(InvertStyle::kLast); 147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot config.fContrast = buffer.readScalar(); 148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return SkHighContrastFilter::Make(config); 150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkColorFilter> SkHighContrastFilter::Make( 153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkHighContrastConfig& config) { 154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (!config.isValid()) { 155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return nullptr; 156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return sk_make_sp<SkHighContrast_Filter>(config); 158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#ifndef SK_IGNORE_TO_STRING 161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkHighContrast_Filter::toString(SkString* str) const { 162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot str->append("SkHighContrastColorFilter "); 163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif 165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter) 167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter) 168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU 171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass HighContrastFilterEffect : public GrFragmentProcessor { 172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic: 173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static std::unique_ptr<GrFragmentProcessor> Make(const SkHighContrastConfig& config, 174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool linearize) { 175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return std::unique_ptr<GrFragmentProcessor>(new HighContrastFilterEffect(config, 176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot linearize)); 177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* name() const override { return "HighContrastFilter"; } 180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkHighContrastConfig& config() const { return fConfig; } 182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool linearize() const { return fLinearize; } 183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot std::unique_ptr<GrFragmentProcessor> clone() const override { 185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return Make(fConfig, fLinearize); 186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate: 189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot HighContrastFilterEffect(const SkHighContrastConfig& config, bool linearize) 190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot : INHERITED(kHighContrastFilterEffect_ClassID, kNone_OptimizationFlags) 191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot , fConfig(config) 192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot , fLinearize(linearize) { 193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, 198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrProcessorKeyBuilder* b) const override; 199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool onIsEqual(const GrFragmentProcessor& other) const override { 201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const HighContrastFilterEffect& that = other.cast<HighContrastFilterEffect>(); 202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return fConfig.fGrayscale == that.fConfig.fGrayscale && 203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fConfig.fInvertStyle == that.fConfig.fInvertStyle && 204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fConfig.fContrast == that.fConfig.fContrast && 205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fLinearize == that.fLinearize; 206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkHighContrastConfig fConfig; 209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool fLinearize; 210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot typedef GrFragmentProcessor INHERITED; 212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}; 213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass GLHighContrastFilterEffect : public GrGLSLFragmentProcessor { 215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic: 216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); 217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprotected: 219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; 220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot void emitCode(EmitArgs& args) override; 221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate: 223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot UniformHandle fContrastUni; 224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot typedef GrGLSLFragmentProcessor INHERITED; 226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}; 227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const { 229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return new GLHighContrastFilterEffect(); 230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, 233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrProcessorKeyBuilder* b) const { 234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GLHighContrastFilterEffect::GenKey(*this, caps, b); 235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, 238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const GrFragmentProcessor& proc) { 239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); 240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot pdm.set1f(fContrastUni, hcfe.config().fContrast); 241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GLHighContrastFilterEffect::GenKey( 244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { 245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); 246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot b->add32(static_cast<uint32_t>(hcfe.config().fGrayscale)); 247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot b->add32(static_cast<uint32_t>(hcfe.config().fInvertStyle)); 248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot b->add32(hcfe.linearize() ? 1 : 0); 249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GLHighContrastFilterEffect::emitCode(EmitArgs& args) { 252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const HighContrastFilterEffect& hcfe = args.fFp.cast<HighContrastFilterEffect>(); 253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const SkHighContrastConfig& config = hcfe.config(); 254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot const char* contrast; 256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, 257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "contrast", &contrast); 258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (nullptr == args.fInputColor) { 260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot args.fInputColor = "half4(1)"; 261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor); 266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Unpremultiply. The max() is to guard against 0 / 0. 268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.00001);"); 269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);"); 270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (hcfe.linearize()) { 272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppend("color.rgb = color.rgb * color.rgb;"); 273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Grayscale. 276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (config.fGrayscale) { 277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half luma = dot(color, half4(%f, %f, %f, 0));", 278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B); 279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("color = half4(luma, luma, luma, 0);"); 280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (config.fInvertStyle == InvertStyle::kInvertBrightness) { 283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("color = half4(1, 1, 1, 1) - color;"); 284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (config.fInvertStyle == InvertStyle::kInvertLightness) { 287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Convert from RGB to HSL. 288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half fmax = max(color.r, max(color.g, color.b));"); 289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half fmin = min(color.r, min(color.g, color.b));"); 290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half l = (fmax + fmin) / 2;"); 291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half h;"); 293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("half s;"); 294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("if (fmax == fmin) {"); 296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" h = 0;"); 297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" s = 0;"); 298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("} else {"); 299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" half d = fmax - fmin;"); 300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" s = l > 0.5 ?"); 301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" d / (2 - fmax - fmin) :"); 302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" d / (fmax + fmin);"); 303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // We'd like to just write "if (color.r == fmax) { ... }". On many GPUs, running the 304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // angle_d3d9_es2 config, that failed. It seems that max(x, y) is not necessarily equal 305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // to either x or y. Tried several ways to fix it, but this was the only reasonable fix. 306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" if (color.r >= color.g && color.r >= color.b) {"); 307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" h = (color.g - color.b) / d + "); 308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" (color.g < color.b ? 6 : 0);"); 309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" } else if (color.g >= color.b) {"); 310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" h = (color.b - color.r) / d + 2;"); 311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" } else {"); 312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" h = (color.r - color.g) / d + 4;"); 313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" }"); 314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("}"); 315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("h /= 6;"); 316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("l = 1.0 - l;"); 317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Convert back from HSL to RGB. 318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SkString hue2rgbFuncName; 319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot static const GrShaderVar gHue2rgbArgs[] = { 320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrShaderVar("p", kHalf_GrSLType), 321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrShaderVar("q", kHalf_GrSLType), 322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrShaderVar("t", kHalf_GrSLType), 323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot }; 324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->emitFunction(kHalf_GrSLType, 325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "hue2rgb", 326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot SK_ARRAY_COUNT(gHue2rgbArgs), 327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot gHue2rgbArgs, 328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "if (t < 0)" 329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot " t += 1;" 330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "if (t > 1)" 331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot " t -= 1;" 332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "if (t < 1/6.)" 333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot " return p + (q - p) * 6 * t;" 334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "if (t < 1/2.)" 335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot " return q;" 336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "if (t < 2/3.)" 337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot " return p + (q - p) * (2/3. - t) * 6;" 338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot "return p;", 339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot &hue2rgbFuncName); 340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("if (s == 0) {"); 341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" color = half4(l, l, l, 0);"); 342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("} else {"); 343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" half q = l < 0.5 ? l * (1 + s) : l + s - l * s;"); 344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" half p = 2 * l - q;"); 345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" color.r = %s(p, q, h + 1/3.);", hue2rgbFuncName.c_str()); 346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" color.g = %s(p, q, h);", hue2rgbFuncName.c_str()); 347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" color.b = %s(p, q, h - 1/3.);", hue2rgbFuncName.c_str()); 348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("}"); 349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Contrast. 352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("if (%s != 0) {", contrast); 353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" half m = (1 + %s) / (1 - %s);", contrast, contrast); 354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" half off = (-0.5 * m + 0.5);"); 355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf(" color = m * color + off;"); 356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("}"); 357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Clamp. 359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("color = clamp(color, 0, 1);"); 360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot if (hcfe.linearize()) { 362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppend("color.rgb = sqrt(color.rgb);"); 363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot } 364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Restore the original alpha and premultiply. 366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("color.a = %s.a;", args.fInputColor); 367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("color.rgb *= color.a;"); 368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot // Copy to the output color. 370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot fragBuilder->codeAppendf("%s = color;", args.fOutputColor); 371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot 373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstd::unique_ptr<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor( 374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot GrContext*, const GrColorSpaceInfo& csi) const { 375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot bool linearize = !csi.isGammaCorrect(); 376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot return HighContrastFilterEffect::Make(fConfig, linearize); 377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot} 378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif 379