1907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org/* 2907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * Copyright 2012 Google Inc. 3907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * 4907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * Use of this source code is governed by a BSD-style license that can be 5907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * found in the LICENSE file. 6907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 7907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 8907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org#ifndef GrTextureDomainEffect_DEFINED 9907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org#define GrTextureDomainEffect_DEFINED 10907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 11907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org#include "GrSingleTextureEffect.h" 12b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt#include "gl/GrGLProcessor.h" 13907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 1430ba436f04e61d4505fb854d5fc56079636e0788joshualittclass GrGLProgramBuilder; 15907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgclass GrGLShaderBuilder; 16605dd0fbce9dbb2a0d3313e13e161f2bd54870d7egdanielclass GrInvariantOutput; 17907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgstruct SkRect; 18907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 19907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org/** 20907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped 21907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * the edge of the domain or result in a vec4 of zeros (decal mode). The domain is clipped to 22907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the 23907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * domain to affect the read value unless the caller considers this when calculating the domain. 24907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 25907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgclass GrTextureDomain { 26907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgpublic: 27907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org enum Mode { 285ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt // Ignore the texture domain rectangle. 295ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt kIgnore_Mode, 305ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt // Clamp texture coords to the domain rectangle. 315ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt kClamp_Mode, 325ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt // Treat the area outside the domain rectangle as fully transparent. 335ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt kDecal_Mode, 345ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt // Wrap texture coordinates. NOTE: filtering may not work as expected because Bilerp will 355ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt // read texels outside of the domain. We could perform additional texture reads and filter 365ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt // in the shader, but are not currently doing this for performance reasons 375ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt kRepeat_Mode, 38907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 395ae5fc59b27a48711e514b3ede548b228e393e9bjoshualitt kLastMode = kRepeat_Mode 40907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org }; 41907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org static const int kModeCount = kLastMode + 1; 42907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 437d7f31433b627e62f518e9186d3f2d9bd44662e0commit-bot@chromium.org static const GrTextureDomain& IgnoredDomain() { 447d7f31433b627e62f518e9186d3f2d9bd44662e0commit-bot@chromium.org static const SkRect gDummyRect = {0, 0, 0, 0}; 457d7f31433b627e62f518e9186d3f2d9bd44662e0commit-bot@chromium.org static const GrTextureDomain gDomain(gDummyRect, kIgnore_Mode); 467d7f31433b627e62f518e9186d3f2d9bd44662e0commit-bot@chromium.org return gDomain; 477d7f31433b627e62f518e9186d3f2d9bd44662e0commit-bot@chromium.org } 487d7f31433b627e62f518e9186d3f2d9bd44662e0commit-bot@chromium.org 49907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org /** 50907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * @param index Pass a value >= 0 if using multiple texture domains in the same effect. 51907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * It is used to keep inserted variables from causing name collisions. 52907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 53907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrTextureDomain(const SkRect& domain, Mode, int index = -1); 54907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 55907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const SkRect& domain() const { return fDomain; } 56907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org Mode mode() const { return fMode; } 57907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 58907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled 59907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org texels neighboring the domain may be read. */ 60907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) { 61907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org SkScalar wInv = SK_Scalar1 / texture->width(); 62907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org SkScalar hInv = SK_Scalar1 / texture->height(); 63907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org SkRect result = { 64907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org texelRect.fLeft * wInv, 65907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org texelRect.fTop * hInv, 66907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org texelRect.fRight * wInv, 67907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org texelRect.fBottom * hInv 68907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org }; 69907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org return result; 70907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org } 71907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 72d0d37cace08f12abf8d316e6949e947551d418e6senorblanco static const SkRect MakeTexelDomainForMode(const GrTexture* texture, const SkIRect& texelRect, Mode mode) { 73d0d37cace08f12abf8d316e6949e947551d418e6senorblanco // For Clamp mode, inset by half a texel. 74d0d37cace08f12abf8d316e6949e947551d418e6senorblanco SkScalar wInv = SK_Scalar1 / texture->width(); 75d0d37cace08f12abf8d316e6949e947551d418e6senorblanco SkScalar hInv = SK_Scalar1 / texture->height(); 76d0d37cace08f12abf8d316e6949e947551d418e6senorblanco SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0; 77d0d37cace08f12abf8d316e6949e947551d418e6senorblanco return SkRect::MakeLTRB( 78d0d37cace08f12abf8d316e6949e947551d418e6senorblanco (texelRect.fLeft + inset) * wInv, 79d0d37cace08f12abf8d316e6949e947551d418e6senorblanco (texelRect.fTop + inset) * hInv, 80d0d37cace08f12abf8d316e6949e947551d418e6senorblanco (texelRect.fRight - inset) * wInv, 81d0d37cace08f12abf8d316e6949e947551d418e6senorblanco (texelRect.fBottom - inset) * hInv 82d0d37cace08f12abf8d316e6949e947551d418e6senorblanco ); 83d0d37cace08f12abf8d316e6949e947551d418e6senorblanco } 84d0d37cace08f12abf8d316e6949e947551d418e6senorblanco 85907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org bool operator== (const GrTextureDomain& that) const { 8601a492f959e886f89995c6d1bbcd9f7bb7639726egdaniel return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain); 87907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org } 88907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 89907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org /** 90b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * A GrGLProcessor subclass that corresponds to a GrProcessor subclass that uses GrTextureDomain 91907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * should include this helper. It generates the texture domain GLSL, produces the part of the 92907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * effect key that reflects the texture domain code, and performs the uniform uploads necessary 93907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * for texture domains. 94907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 95907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org class GLDomain { 96907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org public: 97907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GLDomain() { 98907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org fPrevDomain[0] = SK_FloatNaN; 99907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org SkDEBUGCODE(fMode = (Mode) -1;) 100907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org } 101907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 102907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org /** 103b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * Call this from GrGLProcessor::emitCode() to sample the texture W.R.T. the domain and 104b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * mode. 105907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * 106907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * @param outcolor name of vec4 variable to hold the sampled color. 107907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * @param inCoords name of vec2 variable containing the coords to be used with the domain. 108907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * It is assumed that this is a variable and not an expression. 109907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * @param inModulateColor if non-NULL the sampled color will be modulated with this 110907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * expression before being written to outColor. 111907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 112907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org void sampleTexture(GrGLShaderBuilder* builder, 113907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const GrTextureDomain& textureDomain, 114907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const char* outColor, 115907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const SkString& inCoords, 116b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt const GrGLProcessor::TextureSampler sampler, 117907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const char* inModulateColor = NULL); 118907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 119907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org /** 120b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * Call this from GrGLProcessor::setData() to upload uniforms necessary for the texture 121b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * domain. The rectangle is automatically adjusted to account for the texture's origin. 122907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 1237510b224e52b9518a8ddf7418db0e9c258f79539kkinnunen void setData(const GrGLProgramDataManager& pdman, const GrTextureDomain& textureDomain, 124907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrSurfaceOrigin textureOrigin); 125907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 126907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org enum { 127907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org kDomainKeyBits = 2, // See DomainKey(). 128907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org }; 129907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 130907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org /** 131b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * GrGLProcessor::GenKey() must call this and include the returned value in it's computed 132b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt * key. The returned will be limited to the lower kDomainKeyBits bits. 133907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 13463e99f7a03b2ac90ae7a00232674fd39c0bdcc68bsalomon static uint32_t DomainKey(const GrTextureDomain& domain) { 135907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GR_STATIC_ASSERT(kModeCount <= 4); 136907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org return domain.mode(); 137907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org } 138907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 139907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org private: 1407510b224e52b9518a8ddf7418db0e9c258f79539kkinnunen SkDEBUGCODE(Mode fMode;) 1417510b224e52b9518a8ddf7418db0e9c258f79539kkinnunen GrGLProgramDataManager::UniformHandle fDomainUni; 1427510b224e52b9518a8ddf7418db0e9c258f79539kkinnunen SkString fDomainName; 1437510b224e52b9518a8ddf7418db0e9c258f79539kkinnunen GrGLfloat fPrevDomain[4]; 144907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org }; 145907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 146907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgprotected: 147907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org Mode fMode; 148907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org SkRect fDomain; 149907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org int fIndex; 150907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 151907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org typedef GrSingleTextureEffect INHERITED; 152907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org}; 153907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 154907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org/** 155907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org * A basic texture effect that uses GrTextureDomain. 156907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org */ 157907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgclass GrTextureDomainEffect : public GrSingleTextureEffect { 158907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 159907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgpublic: 160b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt static GrFragmentProcessor* Create(GrTexture*, 161b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt const SkMatrix&, 162b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt const SkRect& domain, 163b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt GrTextureDomain::Mode, 164b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt GrTextureParams::FilterMode filterMode, 165b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt GrCoordSet = kLocal_GrCoordSet); 166907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 167907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org virtual ~GrTextureDomainEffect(); 168907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 16936352bf5e38f45a70ee4f4fc132a38048d38206dmtklein const char* name() const override { return "TextureDomain"; } 170907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 171cfc18867d982119d9dc2888bf09f1093012daaddjvanverth void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override; 172907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 17336352bf5e38f45a70ee4f4fc132a38048d38206dmtklein GrGLFragmentProcessor* createGLInstance() const override; 174907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 175907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const GrTextureDomain& textureDomain() const { return fTextureDomain; } 176907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 177907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgprotected: 178907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrTextureDomain fTextureDomain; 179907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 180907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.orgprivate: 181907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrTextureDomainEffect(GrTexture*, 182907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const SkMatrix&, 183907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org const SkRect& domain, 184907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrTextureDomain::Mode, 185907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrTextureParams::FilterMode, 186907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org GrCoordSet); 187907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 18836352bf5e38f45a70ee4f4fc132a38048d38206dmtklein bool onIsEqual(const GrFragmentProcessor&) const override; 189907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 19036352bf5e38f45a70ee4f4fc132a38048d38206dmtklein void onComputeInvariantOutput(GrInvariantOutput* inout) const override; 1911a8ecdfb73a15de600d5779b75d7c4b61863c50begdaniel 192b0a8a377f832c59cee939ad721e1f87d378b7142joshualitt GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 193907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 194907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org typedef GrSingleTextureEffect INHERITED; 195907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org}; 196907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org 197907fbd53c5e5dd4cbde7b72f9242b51febd7ef95commit-bot@chromium.org#endif 198