1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/text/GrAtlasManager.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "src/codec/SkMasks.h" 11cb93a386Sopenharmony_ci#include "src/core/SkAutoMalloc.h" 12cb93a386Sopenharmony_ci#include "src/gpu/GrGlyph.h" 13cb93a386Sopenharmony_ci#include "src/gpu/GrImageInfo.h" 14cb93a386Sopenharmony_ci#include "src/gpu/text/GrStrikeCache.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ciGrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider, 17cb93a386Sopenharmony_ci size_t maxTextureBytes, 18cb93a386Sopenharmony_ci GrDrawOpAtlas::AllowMultitexturing allowMultitexturing) 19cb93a386Sopenharmony_ci : fAllowMultitexturing{allowMultitexturing} 20cb93a386Sopenharmony_ci , fProxyProvider{proxyProvider} 21cb93a386Sopenharmony_ci , fCaps{fProxyProvider->refCaps()} 22cb93a386Sopenharmony_ci , fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { } 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ciGrAtlasManager::~GrAtlasManager() = default; 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_civoid GrAtlasManager::freeAll() { 27cb93a386Sopenharmony_ci for (int i = 0; i < kMaskFormatCount; ++i) { 28cb93a386Sopenharmony_ci fAtlases[i] = nullptr; 29cb93a386Sopenharmony_ci } 30cb93a386Sopenharmony_ci} 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_cibool GrAtlasManager::hasGlyph(GrMaskFormat format, GrGlyph* glyph) { 33cb93a386Sopenharmony_ci SkASSERT(glyph); 34cb93a386Sopenharmony_ci return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator()); 35cb93a386Sopenharmony_ci} 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_citemplate <typename INT_TYPE> 38cb93a386Sopenharmony_cistatic void expand_bits(INT_TYPE* dst, 39cb93a386Sopenharmony_ci const uint8_t* src, 40cb93a386Sopenharmony_ci int width, 41cb93a386Sopenharmony_ci int height, 42cb93a386Sopenharmony_ci int dstRowBytes, 43cb93a386Sopenharmony_ci int srcRowBytes) { 44cb93a386Sopenharmony_ci for (int y = 0; y < height; ++y) { 45cb93a386Sopenharmony_ci int rowWritesLeft = width; 46cb93a386Sopenharmony_ci const uint8_t* s = src; 47cb93a386Sopenharmony_ci INT_TYPE* d = dst; 48cb93a386Sopenharmony_ci while (rowWritesLeft > 0) { 49cb93a386Sopenharmony_ci unsigned mask = *s++; 50cb93a386Sopenharmony_ci for (int x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) { 51cb93a386Sopenharmony_ci *d++ = (mask & (1 << x)) ? (INT_TYPE)(~0UL) : 0; 52cb93a386Sopenharmony_ci } 53cb93a386Sopenharmony_ci } 54cb93a386Sopenharmony_ci dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes); 55cb93a386Sopenharmony_ci src += srcRowBytes; 56cb93a386Sopenharmony_ci } 57cb93a386Sopenharmony_ci} 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_cistatic void get_packed_glyph_image( 60cb93a386Sopenharmony_ci const SkGlyph& glyph, int dstRB, GrMaskFormat expectedMaskFormat, void* dst) { 61cb93a386Sopenharmony_ci const int width = glyph.width(); 62cb93a386Sopenharmony_ci const int height = glyph.height(); 63cb93a386Sopenharmony_ci const void* src = glyph.image(); 64cb93a386Sopenharmony_ci SkASSERT(src != nullptr); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci GrMaskFormat grMaskFormat = GrGlyph::FormatFromSkGlyph(glyph.maskFormat()); 67cb93a386Sopenharmony_ci if (grMaskFormat == expectedMaskFormat) { 68cb93a386Sopenharmony_ci int srcRB = glyph.rowBytes(); 69cb93a386Sopenharmony_ci // Notice this comparison is with the glyphs raw mask format, and not its GrMaskFormat. 70cb93a386Sopenharmony_ci if (glyph.maskFormat() != SkMask::kBW_Format) { 71cb93a386Sopenharmony_ci if (srcRB != dstRB) { 72cb93a386Sopenharmony_ci const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat); 73cb93a386Sopenharmony_ci for (int y = 0; y < height; y++) { 74cb93a386Sopenharmony_ci memcpy(dst, src, width * bbp); 75cb93a386Sopenharmony_ci src = (const char*) src + srcRB; 76cb93a386Sopenharmony_ci dst = (char*) dst + dstRB; 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci } else { 79cb93a386Sopenharmony_ci memcpy(dst, src, dstRB * height); 80cb93a386Sopenharmony_ci } 81cb93a386Sopenharmony_ci } else { 82cb93a386Sopenharmony_ci // Handle 8-bit format by expanding the mask to the expected format. 83cb93a386Sopenharmony_ci const uint8_t* bits = reinterpret_cast<const uint8_t*>(src); 84cb93a386Sopenharmony_ci switch (expectedMaskFormat) { 85cb93a386Sopenharmony_ci case kA8_GrMaskFormat: { 86cb93a386Sopenharmony_ci uint8_t* bytes = reinterpret_cast<uint8_t*>(dst); 87cb93a386Sopenharmony_ci expand_bits(bytes, bits, width, height, dstRB, srcRB); 88cb93a386Sopenharmony_ci break; 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci case kA565_GrMaskFormat: { 91cb93a386Sopenharmony_ci uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst); 92cb93a386Sopenharmony_ci expand_bits(rgb565, bits, width, height, dstRB, srcRB); 93cb93a386Sopenharmony_ci break; 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci default: 96cb93a386Sopenharmony_ci SK_ABORT("Invalid GrMaskFormat"); 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci } 99cb93a386Sopenharmony_ci } else if (grMaskFormat == kA565_GrMaskFormat && expectedMaskFormat == kARGB_GrMaskFormat) { 100cb93a386Sopenharmony_ci // Convert if the glyph uses a 565 mask format since it is using LCD text rendering 101cb93a386Sopenharmony_ci // but the expected format is 8888 (will happen on macOS with Metal since that 102cb93a386Sopenharmony_ci // combination does not support 565). 103cb93a386Sopenharmony_ci static constexpr SkMasks masks{ 104cb93a386Sopenharmony_ci {0b1111'1000'0000'0000, 11, 5}, // Red 105cb93a386Sopenharmony_ci {0b0000'0111'1110'0000, 5, 6}, // Green 106cb93a386Sopenharmony_ci {0b0000'0000'0001'1111, 0, 5}, // Blue 107cb93a386Sopenharmony_ci {0, 0, 0} // Alpha 108cb93a386Sopenharmony_ci }; 109cb93a386Sopenharmony_ci constexpr int a565Bpp = GrMaskFormatBytesPerPixel(kA565_GrMaskFormat); 110cb93a386Sopenharmony_ci constexpr int argbBpp = GrMaskFormatBytesPerPixel(kARGB_GrMaskFormat); 111cb93a386Sopenharmony_ci for (int y = 0; y < height; y++) { 112cb93a386Sopenharmony_ci for (int x = 0; x < width; x++) { 113cb93a386Sopenharmony_ci uint16_t color565 = 0; 114cb93a386Sopenharmony_ci memcpy(&color565, src, a565Bpp); 115cb93a386Sopenharmony_ci uint32_t colorRGBA = GrColorPackRGBA(masks.getRed(color565), 116cb93a386Sopenharmony_ci masks.getGreen(color565), 117cb93a386Sopenharmony_ci masks.getBlue(color565), 118cb93a386Sopenharmony_ci 0xFF); 119cb93a386Sopenharmony_ci memcpy(dst, &colorRGBA, argbBpp); 120cb93a386Sopenharmony_ci src = (char*)src + a565Bpp; 121cb93a386Sopenharmony_ci dst = (char*)dst + argbBpp; 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci } 124cb93a386Sopenharmony_ci } else { 125cb93a386Sopenharmony_ci // crbug:510931 126cb93a386Sopenharmony_ci // Retrieving the image from the cache can actually change the mask format. This case is 127cb93a386Sopenharmony_ci // very uncommon so for now we just draw a clear box for these glyphs. 128cb93a386Sopenharmony_ci const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat); 129cb93a386Sopenharmony_ci for (int y = 0; y < height; y++) { 130cb93a386Sopenharmony_ci sk_bzero(dst, width * bpp); 131cb93a386Sopenharmony_ci dst = (char*)dst + dstRB; 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci } 134cb93a386Sopenharmony_ci} 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci// returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's 137cb93a386Sopenharmony_ci// mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never 138cb93a386Sopenharmony_ci// happen. 139cb93a386Sopenharmony_ci// TODO we can handle some of these cases if we really want to, but the long term solution is to 140cb93a386Sopenharmony_ci// get the actual glyph image itself when we get the glyph metrics. 141cb93a386Sopenharmony_ciGrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph, 142cb93a386Sopenharmony_ci GrGlyph* grGlyph, 143cb93a386Sopenharmony_ci int srcPadding, 144cb93a386Sopenharmony_ci GrResourceProvider* resourceProvider, 145cb93a386Sopenharmony_ci GrDeferredUploadTarget* uploadTarget, 146cb93a386Sopenharmony_ci bool bilerpPadding) { 147cb93a386Sopenharmony_ci if (skGlyph.image() == nullptr) { 148cb93a386Sopenharmony_ci return GrDrawOpAtlas::ErrorCode::kError; 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci SkASSERT(grGlyph != nullptr); 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ci GrMaskFormat glyphFormat = GrGlyph::FormatFromSkGlyph(skGlyph.maskFormat()); 153cb93a386Sopenharmony_ci GrMaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat); 154cb93a386Sopenharmony_ci int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat); 155cb93a386Sopenharmony_ci 156cb93a386Sopenharmony_ci // Add 1 pixel padding around grGlyph if needed. 157cb93a386Sopenharmony_ci int padding = bilerpPadding ? 1 : 0; 158cb93a386Sopenharmony_ci const int width = skGlyph.width() + 2*padding; 159cb93a386Sopenharmony_ci const int height = skGlyph.height() + 2*padding; 160cb93a386Sopenharmony_ci int rowBytes = width * bytesPerPixel; 161cb93a386Sopenharmony_ci size_t size = height * rowBytes; 162cb93a386Sopenharmony_ci 163cb93a386Sopenharmony_ci // Temporary storage for normalizing grGlyph image. 164cb93a386Sopenharmony_ci SkAutoSMalloc<1024> storage(size); 165cb93a386Sopenharmony_ci void* dataPtr = storage.get(); 166cb93a386Sopenharmony_ci if (padding > 0) { 167cb93a386Sopenharmony_ci sk_bzero(dataPtr, size); 168cb93a386Sopenharmony_ci // Advance in one row and one column. 169cb93a386Sopenharmony_ci dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr); 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_ci auto errorCode = this->addToAtlas(resourceProvider, 175cb93a386Sopenharmony_ci uploadTarget, 176cb93a386Sopenharmony_ci expectedMaskFormat, 177cb93a386Sopenharmony_ci width, 178cb93a386Sopenharmony_ci height, 179cb93a386Sopenharmony_ci storage.get(), 180cb93a386Sopenharmony_ci &grGlyph->fAtlasLocator); 181cb93a386Sopenharmony_ci 182cb93a386Sopenharmony_ci if (errorCode == GrDrawOpAtlas::ErrorCode::kSucceeded) { 183cb93a386Sopenharmony_ci grGlyph->fAtlasLocator.insetSrc(srcPadding); 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci return errorCode; 187cb93a386Sopenharmony_ci} 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci// add to texture atlas that matches this format 190cb93a386Sopenharmony_ciGrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider, 191cb93a386Sopenharmony_ci GrDeferredUploadTarget* target, 192cb93a386Sopenharmony_ci GrMaskFormat format, 193cb93a386Sopenharmony_ci int width, int height, const void* image, 194cb93a386Sopenharmony_ci GrDrawOpAtlas::AtlasLocator* atlasLocator) { 195cb93a386Sopenharmony_ci return this->getAtlas(format)->addToAtlas(resourceProvider, target, width, height, image, 196cb93a386Sopenharmony_ci atlasLocator); 197cb93a386Sopenharmony_ci} 198cb93a386Sopenharmony_ci 199cb93a386Sopenharmony_civoid GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater, 200cb93a386Sopenharmony_ci GrMaskFormat format, GrGlyph* glyph, 201cb93a386Sopenharmony_ci GrDeferredUploadToken token) { 202cb93a386Sopenharmony_ci SkASSERT(glyph); 203cb93a386Sopenharmony_ci if (updater->add(glyph->fAtlasLocator)) { 204cb93a386Sopenharmony_ci this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token); 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci} 207cb93a386Sopenharmony_ci 208cb93a386Sopenharmony_ci#ifdef SK_DEBUG 209cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h" 210cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h" 211cb93a386Sopenharmony_ci#include "src/gpu/GrSurfaceProxy.h" 212cb93a386Sopenharmony_ci#include "src/gpu/GrTextureProxy.h" 213cb93a386Sopenharmony_ci#include "src/gpu/SurfaceContext.h" 214cb93a386Sopenharmony_ci 215cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h" 216cb93a386Sopenharmony_ci#include "include/core/SkImageEncoder.h" 217cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 218cb93a386Sopenharmony_ci#include <stdio.h> 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ci/** 221cb93a386Sopenharmony_ci * Write the contents of the surface proxy to a PNG. Returns true if successful. 222cb93a386Sopenharmony_ci * @param filename Full path to desired file 223cb93a386Sopenharmony_ci */ 224cb93a386Sopenharmony_cistatic bool save_pixels(GrDirectContext* dContext, GrSurfaceProxyView view, GrColorType colorType, 225cb93a386Sopenharmony_ci const char* filename) { 226cb93a386Sopenharmony_ci if (!view.proxy()) { 227cb93a386Sopenharmony_ci return false; 228cb93a386Sopenharmony_ci } 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_ci auto ii = SkImageInfo::Make(view.proxy()->dimensions(), kRGBA_8888_SkColorType, 231cb93a386Sopenharmony_ci kPremul_SkAlphaType); 232cb93a386Sopenharmony_ci SkBitmap bm; 233cb93a386Sopenharmony_ci if (!bm.tryAllocPixels(ii)) { 234cb93a386Sopenharmony_ci return false; 235cb93a386Sopenharmony_ci } 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ci auto sContext = dContext->priv().makeSC(std::move(view), 238cb93a386Sopenharmony_ci {colorType, kUnknown_SkAlphaType, nullptr}); 239cb93a386Sopenharmony_ci if (!sContext || !sContext->asTextureProxy()) { 240cb93a386Sopenharmony_ci return false; 241cb93a386Sopenharmony_ci } 242cb93a386Sopenharmony_ci 243cb93a386Sopenharmony_ci bool result = sContext->readPixels(dContext, bm.pixmap(), {0, 0}); 244cb93a386Sopenharmony_ci if (!result) { 245cb93a386Sopenharmony_ci SkDebugf("------ failed to read pixels for %s\n", filename); 246cb93a386Sopenharmony_ci return false; 247cb93a386Sopenharmony_ci } 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_ci // remove any previous version of this file 250cb93a386Sopenharmony_ci remove(filename); 251cb93a386Sopenharmony_ci 252cb93a386Sopenharmony_ci SkFILEWStream file(filename); 253cb93a386Sopenharmony_ci if (!file.isValid()) { 254cb93a386Sopenharmony_ci SkDebugf("------ failed to create file: %s\n", filename); 255cb93a386Sopenharmony_ci remove(filename); // remove any partial file 256cb93a386Sopenharmony_ci return false; 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) { 260cb93a386Sopenharmony_ci SkDebugf("------ failed to encode %s\n", filename); 261cb93a386Sopenharmony_ci remove(filename); // remove any partial file 262cb93a386Sopenharmony_ci return false; 263cb93a386Sopenharmony_ci } 264cb93a386Sopenharmony_ci 265cb93a386Sopenharmony_ci return true; 266cb93a386Sopenharmony_ci} 267cb93a386Sopenharmony_ci 268cb93a386Sopenharmony_civoid GrAtlasManager::dump(GrDirectContext* context) const { 269cb93a386Sopenharmony_ci static int gDumpCount = 0; 270cb93a386Sopenharmony_ci for (int i = 0; i < kMaskFormatCount; ++i) { 271cb93a386Sopenharmony_ci if (fAtlases[i]) { 272cb93a386Sopenharmony_ci const GrSurfaceProxyView* views = fAtlases[i]->getViews(); 273cb93a386Sopenharmony_ci for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) { 274cb93a386Sopenharmony_ci SkASSERT(views[pageIdx].proxy()); 275cb93a386Sopenharmony_ci SkString filename; 276cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_ANDROID 277cb93a386Sopenharmony_ci filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx); 278cb93a386Sopenharmony_ci#else 279cb93a386Sopenharmony_ci filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx); 280cb93a386Sopenharmony_ci#endif 281cb93a386Sopenharmony_ci auto ct = GrMaskFormatToColorType(AtlasIndexToMaskFormat(i)); 282cb93a386Sopenharmony_ci save_pixels(context, views[pageIdx], ct, filename.c_str()); 283cb93a386Sopenharmony_ci } 284cb93a386Sopenharmony_ci } 285cb93a386Sopenharmony_ci } 286cb93a386Sopenharmony_ci ++gDumpCount; 287cb93a386Sopenharmony_ci} 288cb93a386Sopenharmony_ci#endif 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_civoid GrAtlasManager::setAtlasDimensionsToMinimum_ForTesting() { 291cb93a386Sopenharmony_ci // Delete any old atlases. 292cb93a386Sopenharmony_ci // This should be safe to do as long as we are not in the middle of a flush. 293cb93a386Sopenharmony_ci for (int i = 0; i < kMaskFormatCount; i++) { 294cb93a386Sopenharmony_ci fAtlases[i] = nullptr; 295cb93a386Sopenharmony_ci } 296cb93a386Sopenharmony_ci 297cb93a386Sopenharmony_ci // Set all the atlas sizes to 1x1 plot each. 298cb93a386Sopenharmony_ci new (&fAtlasConfig) GrDrawOpAtlasConfig{}; 299cb93a386Sopenharmony_ci} 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_cibool GrAtlasManager::initAtlas(GrMaskFormat format) { 302cb93a386Sopenharmony_ci int index = MaskFormatToAtlasIndex(format); 303cb93a386Sopenharmony_ci if (fAtlases[index] == nullptr) { 304cb93a386Sopenharmony_ci GrColorType grColorType = GrMaskFormatToColorType(format); 305cb93a386Sopenharmony_ci#ifdef SK_ENABLE_SMALL_PAGE 306cb93a386Sopenharmony_ci int pageNum = 4; // The maximum number of texture pages in the original skia code is 4 307cb93a386Sopenharmony_ci if ((format == kA8_GrMaskFormat) && (fAtlasConfig.getARGBDimensions().width() > 512)) { 308cb93a386Sopenharmony_ci // reset fAtlasConfig to suit small page. 309cb93a386Sopenharmony_ci pageNum = fAtlasConfig.resetAsSmallPage(); 310cb93a386Sopenharmony_ci } 311cb93a386Sopenharmony_ci#endif 312cb93a386Sopenharmony_ci SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format); 313cb93a386Sopenharmony_ci SkISize plotDimensions = fAtlasConfig.plotDimensions(format); 314cb93a386Sopenharmony_ci 315cb93a386Sopenharmony_ci const GrBackendFormat backendFormat = 316cb93a386Sopenharmony_ci fCaps->getDefaultBackendFormat(grColorType, GrRenderable::kNo); 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ci fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, backendFormat, grColorType, 319cb93a386Sopenharmony_ci atlasDimensions.width(), atlasDimensions.height(), 320cb93a386Sopenharmony_ci plotDimensions.width(), plotDimensions.height(), 321cb93a386Sopenharmony_ci this, fAllowMultitexturing, 322cb93a386Sopenharmony_ci#ifdef SK_ENABLE_SMALL_PAGE 323cb93a386Sopenharmony_ci pageNum, 324cb93a386Sopenharmony_ci#endif 325cb93a386Sopenharmony_ci nullptr); 326cb93a386Sopenharmony_ci if (!fAtlases[index]) { 327cb93a386Sopenharmony_ci return false; 328cb93a386Sopenharmony_ci } 329cb93a386Sopenharmony_ci } 330cb93a386Sopenharmony_ci return true; 331cb93a386Sopenharmony_ci} 332