/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gl/GrGLExtensions.h" #include "gl/GrGLDefines.h" #include "gl/GrGLUtil.h" #include "SkTSearch.h" #include "SkTSort.h" namespace { // This cannot be static because it is used as a template parameter. inline bool extension_compare(const SkString& a, const SkString& b) { return strcmp(a.c_str(), b.c_str()) < 0; } } // finds the index of ext in strings or a negative result if ext is not found. static int find_string(const SkTArray& strings, const char ext[]) { if (strings.empty()) { return -1; } SkString extensionStr(ext); int idx = SkTSearch(&strings.front(), strings.count(), extensionStr, sizeof(SkString)); return idx; } GrGLExtensions::GrGLExtensions(const GrGLExtensions& that) : fStrings(new SkTArray) { *this = that; } GrGLExtensions& GrGLExtensions::operator=(const GrGLExtensions& that) { *fStrings = *that.fStrings; fInitialized = that.fInitialized; return *this; } bool GrGLExtensions::init(GrGLStandard standard, GrGLGetStringProc getString, GrGLGetStringiProc getStringi, GrGLGetIntegervProc getIntegerv) { fInitialized = false; fStrings->reset(); if (nullptr == getString) { return false; } // glGetStringi and indexed extensions were added in version 3.0 of desktop GL and ES. const GrGLubyte* verString = getString(GR_GL_VERSION); GrGLVersion version = GrGLGetVersionFromString((const char*) verString); if (GR_GL_INVALID_VER == version) { return false; } bool indexed = version >= GR_GL_VER(3, 0); if (indexed) { if (nullptr == getStringi || nullptr == getIntegerv) { return false; } GrGLint extensionCnt = 0; getIntegerv(GR_GL_NUM_EXTENSIONS, &extensionCnt); fStrings->push_back_n(extensionCnt); for (int i = 0; i < extensionCnt; ++i) { const char* ext = (const char*) getStringi(GR_GL_EXTENSIONS, i); (*fStrings)[i] = ext; } } else { const char* extensions = (const char*) getString(GR_GL_EXTENSIONS); if (nullptr == extensions) { return false; } while (true) { // skip over multiple spaces between extensions while (' ' == *extensions) { ++extensions; } // quit once we reach the end of the string. if ('\0' == *extensions) { break; } // we found an extension size_t length = strcspn(extensions, " "); fStrings->push_back().set(extensions, length); extensions += length; } } if (!fStrings->empty()) { SkTLessFunctionToFunctorAdaptor cmp; SkTQSort(&fStrings->front(), &fStrings->back(), cmp); } fInitialized = true; return true; } bool GrGLExtensions::has(const char ext[]) const { SkASSERT(fInitialized); return find_string(*fStrings, ext) >= 0; } bool GrGLExtensions::remove(const char ext[]) { SkASSERT(fInitialized); int idx = find_string(*fStrings, ext); if (idx >= 0) { // This is not terribly effecient but we really only expect this function to be called at // most a handful of times when our test programs start. SkAutoTDelete< SkTArray > oldStrings(fStrings.detach()); fStrings.reset(new SkTArray(oldStrings->count() - 1)); fStrings->push_back_n(idx, &oldStrings->front()); fStrings->push_back_n(oldStrings->count() - idx - 1, &(*oldStrings)[idx] + 1); return true; } else { return false; } } void GrGLExtensions::add(const char ext[]) { int idx = find_string(*fStrings, ext); if (idx < 0) { // This is not the most effecient approach since we end up doing a full sort of the // extensions after the add fStrings->push_back().set(ext); SkTLessFunctionToFunctorAdaptor cmp; SkTQSort(&fStrings->front(), &fStrings->back(), cmp); } } void GrGLExtensions::print(const char* sep) const { if (nullptr == sep) { sep = " "; } int cnt = fStrings->count(); for (int i = 0; i < cnt; ++i) { SkDebugf("%s%s", (*fStrings)[i].c_str(), (i < cnt - 1) ? sep : ""); } }