static bool verify_query(SkIRect query, DataRect rects[], SkTDArray<void*>& found) { SkTDArray<void*> expected; // manually intersect with every rectangle for (int i = 0; i < NUM_RECTS; ++i) { if (SkIRect::IntersectsNoEmptyCheck(query, rects[i].rect)) { expected.push(rects[i].data); } } if (expected.count() != found.count()) { return false; } if (0 == expected.count()) { return true; } // Just cast to long since sorting by the value of the void*'s was being problematic... SkTQSort(reinterpret_cast<long*>(expected.begin()), reinterpret_cast<long*>(expected.end() - 1)); SkTQSort(reinterpret_cast<long*>(found.begin()), reinterpret_cast<long*>(found.end() - 1)); return found == expected; }
void CommandSet::drawHelp(SkCanvas* canvas) { if (kNone_HelpMode == fHelpMode) { return; } // Sort commands for current mode: SkTQSort(fCommands.begin(), fCommands.end() - 1, kAlphabetical_HelpMode == fHelpMode ? compareCommandKey : compareCommandGroup); SkPaint bgPaint; bgPaint.setColor(0xC0000000); canvas->drawPaint(bgPaint); SkPaint paint; paint.setTextSize(16); paint.setAntiAlias(true); paint.setColor(0xFFFFFFFF); SkPaint groupPaint; groupPaint.setTextSize(18); groupPaint.setUnderlineText(true); groupPaint.setAntiAlias(true); groupPaint.setColor(0xFFFFFFFF); SkScalar x = SkIntToScalar(10); SkScalar y = SkIntToScalar(10); // Measure all key strings: SkScalar keyWidth = 0; for (Command& cmd : fCommands) { keyWidth = SkMaxScalar(keyWidth, paint.measureText(cmd.fKeyName.c_str(), cmd.fKeyName.size())); } keyWidth += paint.measureText(" ", 1); // If we're grouping by category, we'll be adding text height on every new group (including the // first), so no need to do that here. Otherwise, skip down so the first line is where we want. if (kGrouped_HelpMode != fHelpMode) { y += paint.getTextSize(); } // Print everything: SkString lastGroup; for (Command& cmd : fCommands) { if (kGrouped_HelpMode == fHelpMode && lastGroup != cmd.fGroup) { // Group change. Advance and print header: y += paint.getTextSize(); canvas->drawText(cmd.fGroup.c_str(), cmd.fGroup.size(), x, y, groupPaint); y += groupPaint.getTextSize() + 2; lastGroup = cmd.fGroup; } canvas->drawText(cmd.fKeyName.c_str(), cmd.fKeyName.size(), x, y, paint); SkString text = SkStringPrintf(": %s", cmd.fDescription.c_str()); canvas->drawText(text.c_str(), text.size(), x + keyWidth, y, paint); y += paint.getTextSize() + 2; } }
bool GrGLExtensions::init(GrGLStandard standard, GrGLGetStringProc getString, GrGLGetStringiProc getStringi, GrGLGetIntegervProc getIntegerv) { fInitialized = false; fStrings->reset(); if (NULL == 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 (NULL == getStringi || NULL == 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 (NULL == 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<SkString, extension_compare> cmp; SkTQSort(&fStrings->front(), &fStrings->back(), cmp); } fInitialized = true; return true; }
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<SkString, extension_compare> cmp; SkTQSort(&fStrings->front(), &fStrings->back(), cmp); } }
bool GrGLExtensions::init(GrGLBinding binding, GrGLGetStringProc getString, GrGLGetStringiProc getStringi, GrGLGetIntegervProc getIntegerv) { fStrings.reset(); if (NULL == getString) { return false; } bool indexed = false; if (kDesktop_GrGLBinding == binding) { const GrGLubyte* verString = getString(GR_GL_VERSION); if (NULL == verString) { return false; } GrGLVersion version = GrGLGetVersionFromString((const char*) verString); indexed = version >= GR_GL_VER(3, 0); } if (indexed) { if (NULL == getStringi || NULL == 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 (NULL == 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 (0 != fStrings.count()) { SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp; SkTQSort(&fStrings.front(), &fStrings.back(), cmp); } return true; }
static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) { SkTQSort(list, list + count - 1); // now make the edges linked in sorted order for (int i = 1; i < count; i++) { list[i - 1]->fNext = list[i]; list[i]->fPrev = list[i - 1]; } *last = list[count - 1]; return list[0]; }
bool GrGLExtensions::init(GrGLStandard standard, GrGLFunction<GrGLGetStringProc> getString, GrGLFunction<GrGLGetStringiProc> getStringi, GrGLFunction<GrGLGetIntegervProc> getIntegerv, GrGLFunction<GrEGLQueryStringProc> queryString, GrEGLDisplay eglDisplay) { fInitialized = false; fStrings->reset(); if (!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 (!getStringi || !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 (!extensions) { return false; } eat_space_sep_strings(fStrings.get(), extensions); } if (queryString) { const char* extensions = queryString(eglDisplay, GR_EGL_EXTENSIONS); eat_space_sep_strings(fStrings.get(), extensions); } if (!fStrings->empty()) { SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp; SkTQSort(&fStrings->front(), &fStrings->back(), cmp); } fInitialized = true; return true; }
int SkWGLExtensions::selectFormat(const int formats[], int formatCount, HDC dc, int desiredSampleCount) { PixelFormat desiredFormat = { 0, desiredSampleCount, 0, 0, }; SkTDArray<PixelFormat> rankedFormats; rankedFormats.setCount(formatCount); bool supportsCoverage = this->hasExtension(dc, "WGL_NV_multisample_coverage"); for (int i = 0; i < formatCount; ++i) { static const int queryAttrs[] = { SK_WGL_COVERAGE_SAMPLES, // Keep COLOR_SAMPLES at the end so it can be skipped SK_WGL_COLOR_SAMPLES, }; int answers[2]; int queryAttrCnt = supportsCoverage ? SK_ARRAY_COUNT(queryAttrs) : SK_ARRAY_COUNT(queryAttrs) - 1; this->getPixelFormatAttribiv(dc, formats[i], 0, queryAttrCnt, queryAttrs, answers); rankedFormats[i].fFormat = formats[i]; rankedFormats[i].fCoverageSamples = answers[0]; rankedFormats[i].fColorSamples = answers[supportsCoverage ? 1 : 0]; rankedFormats[i].fChoosePixelFormatRank = i; } SkTQSort(rankedFormats.begin(), rankedFormats.begin() + rankedFormats.count() - 1, SkTLessFunctionToFunctorAdaptor<PixelFormat, pf_less>()); int idx = SkTSearch<PixelFormat, pf_less>(rankedFormats.begin(), rankedFormats.count(), desiredFormat, sizeof(PixelFormat)); if (idx < 0) { idx = ~idx; } return rankedFormats[idx].fFormat; }
int SkWGLExtensions::selectFormat(const int formats[], int formatCount, HDC dc, int desiredSampleCount) const { if (formatCount <= 0) { return -1; } PixelFormat desiredFormat = { 0, desiredSampleCount, 0, }; SkTDArray<PixelFormat> rankedFormats; rankedFormats.setCount(formatCount); for (int i = 0; i < formatCount; ++i) { static const int kQueryAttr = SK_WGL_SAMPLES; int numSamples; this->getPixelFormatAttribiv(dc, formats[i], 0, 1, &kQueryAttr, &numSamples); rankedFormats[i].fFormat = formats[i]; rankedFormats[i].fSampleCnt = numSamples; rankedFormats[i].fChoosePixelFormatRank = i; } SkTQSort(rankedFormats.begin(), rankedFormats.begin() + rankedFormats.count() - 1, SkTLessFunctionToFunctorAdaptor<PixelFormat, pf_less>()); int idx = SkTSearch<PixelFormat, pf_less>(rankedFormats.begin(), rankedFormats.count(), desiredFormat, sizeof(PixelFormat)); if (idx < 0) { idx = ~idx; } return rankedFormats[idx].fFormat; }
void SkRecordDraw(const SkRecord& record, SkCanvas* canvas, const SkBBoxHierarchy* bbh, SkDrawPictureCallback* callback) { SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); if (NULL != bbh) { // Draw only ops that affect pixels in the canvas's current clip. SkIRect devBounds; canvas->getClipDeviceBounds(&devBounds); SkTDArray<void*> ops; bbh->search(devBounds, &ops); // FIXME: QuadTree doesn't send these back in the order we inserted them. :( // Also remove the sort in SkPictureData::getActiveOps()? if (ops.count() > 0) { SkTQSort(ops.begin(), ops.end() - 1, SkTCompareLT<void*>()); } SkRecords::Draw draw(canvas); for (int i = 0; i < ops.count(); i++) { if (NULL != callback && callback->abortDrawing()) { return; } record.visit<void>((uintptr_t)ops[i], draw); // See FillBounds below. } } else { // Draw all ops. for (SkRecords::Draw draw(canvas); draw.index() < record.count(); draw.next()) { if (NULL != callback && callback->abortDrawing()) { return; } record.visit<void>(draw.index(), draw); } } }
int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept, SearchAxis xAxis, double* validRoots) const { extrema += findInflections(&extremeTs[extrema]); extremeTs[extrema++] = 0; extremeTs[extrema] = 1; SkASSERT(extrema < 6); SkTQSort(extremeTs, extremeTs + extrema); int validCount = 0; for (int index = 0; index < extrema; ) { double min = extremeTs[index]; double max = extremeTs[++index]; if (min == max) { continue; } double newT = binarySearch(min, max, axisIntercept, xAxis); if (newT >= 0) { if (validCount >= 3) { return 0; } validRoots[validCount++] = newT; } } return validCount; }
bool SkOpEdgeBuilder::walk() { uint8_t* verbPtr = fPathVerbs.begin(); uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf]; SkPoint* pointsPtr = fPathPts.begin() - 1; SkScalar* weightPtr = fWeights.begin(); SkPath::Verb verb; SkOpContour* contour = fContourBuilder.contour(); while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) { if (verbPtr == endOfFirstHalf) { fOperand = true; } verbPtr++; switch (verb) { case SkPath::kMove_Verb: if (contour && contour->count()) { if (fAllowOpenContours) { complete(); } else if (!close()) { return false; } } if (!contour) { fContourBuilder.setContour(contour = fContoursHead->appendContour()); } contour->init(fGlobalState, fOperand, fXorMask[fOperand] == kEvenOdd_PathOpsMask); pointsPtr += 1; continue; case SkPath::kLine_Verb: fContourBuilder.addLine(pointsPtr); break; case SkPath::kQuad_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; if (v1.dot(v2) < 0) { SkPoint pair[5]; if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { goto addOneQuad; } if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { return false; } for (unsigned index = 0; index < SK_ARRAY_COUNT(pair); ++index) { force_small_to_zero(&pair[index]); } SkPoint cStorage[2][2]; SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? &pair[0] : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? &pair[2] : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fContourBuilder.addCurve(v1, curve1); fContourBuilder.addCurve(v2, curve2); break; } } } addOneQuad: fContourBuilder.addQuad(pointsPtr); break; case SkPath::kConic_Verb: { SkVector v1 = pointsPtr[1] - pointsPtr[0]; SkVector v2 = pointsPtr[2] - pointsPtr[1]; SkScalar weight = *weightPtr++; if (v1.dot(v2) < 0) { // FIXME: max curvature for conics hasn't been implemented; use placeholder SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); if (maxCurvature > 0) { SkConic conic(pointsPtr, weight); SkConic pair[2]; if (!conic.chopAt(maxCurvature, pair)) { // if result can't be computed, use original fContourBuilder.addConic(pointsPtr, weight); break; } SkPoint cStorage[2][3]; SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); SkPoint* curve1 = v1 != SkPath::kLine_Verb ? pair[0].fPts : cStorage[0]; SkPoint* curve2 = v2 != SkPath::kLine_Verb ? pair[1].fPts : cStorage[1]; if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { fContourBuilder.addCurve(v1, curve1, pair[0].fW); fContourBuilder.addCurve(v2, curve2, pair[1].fW); break; } } } fContourBuilder.addConic(pointsPtr, weight); } break; case SkPath::kCubic_Verb: { // Split complex cubics (such as self-intersecting curves or // ones with difficult curvature) in two before proceeding. // This can be required for intersection to succeed. SkScalar splitT[3]; int breaks = SkDCubic::ComplexBreak(pointsPtr, splitT); if (!breaks) { fContourBuilder.addCubic(pointsPtr); break; } SkASSERT(breaks <= (int) SK_ARRAY_COUNT(splitT)); struct Splitsville { double fT[2]; SkPoint fPts[4]; SkPoint fReduced[4]; SkPath::Verb fVerb; bool fCanAdd; } splits[4]; SkASSERT(SK_ARRAY_COUNT(splits) == SK_ARRAY_COUNT(splitT) + 1); SkTQSort(splitT, &splitT[breaks - 1]); for (int index = 0; index <= breaks; ++index) { Splitsville* split = &splits[index]; split->fT[0] = index ? splitT[index - 1] : 0; split->fT[1] = index < breaks ? splitT[index] : 1; SkDCubic part = SkDCubic::SubDivide(pointsPtr, split->fT[0], split->fT[1]); if (!part.toFloatPoints(split->fPts)) { return false; } split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); SkPoint* curve = SkPath::kCubic_Verb == verb ? split->fPts : split->fReduced; split->fCanAdd = can_add_curve(split->fVerb, curve); } for (int index = 0; index <= breaks; ++index) { Splitsville* split = &splits[index]; if (!split->fCanAdd) { continue; } int prior = index; while (prior > 0 && !splits[prior - 1].fCanAdd) { --prior; } if (prior < index) { split->fT[0] = splits[prior].fT[0]; split->fPts[0] = splits[prior].fPts[0]; } int next = index; int breakLimit = SkTMin(breaks, (int) SK_ARRAY_COUNT(splits) - 1); while (next < breakLimit && !splits[next + 1].fCanAdd) { ++next; } if (next > index) { split->fT[1] = splits[next].fT[1]; split->fPts[3] = splits[next].fPts[3]; } if (prior < index || next > index) { split->fVerb = SkReduceOrder::Cubic(split->fPts, split->fReduced); } SkPoint* curve = SkPath::kCubic_Verb == split->fVerb ? split->fPts : split->fReduced; if (!can_add_curve(split->fVerb, curve)) { return false; } fContourBuilder.addCurve(split->fVerb, curve); } } break; case SkPath::kClose_Verb: SkASSERT(contour); if (!close()) { return false; } contour = nullptr; continue; default: SkDEBUGFAIL("bad verb"); return false; } SkASSERT(contour); if (contour->count()) { contour->debugValidate(); } pointsPtr += SkPathOpsVerbToPoints(verb); } fContourBuilder.flush(); if (contour && contour->count() &&!fAllowOpenContours && !close()) { return false; } return true; }
SkCodec* SkIcoCodec::NewFromStream(SkStream* stream, Result* result) { // Ensure that we do not leak the input stream std::unique_ptr<SkStream> inputStream(stream); // Header size constants static const uint32_t kIcoDirectoryBytes = 6; static const uint32_t kIcoDirEntryBytes = 16; // Read the directory header std::unique_ptr<uint8_t[]> dirBuffer(new uint8_t[kIcoDirectoryBytes]); if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) { SkCodecPrintf("Error: unable to read ico directory header.\n"); *result = kIncompleteInput; return nullptr; } // Process the directory header const uint16_t numImages = get_short(dirBuffer.get(), 4); if (0 == numImages) { SkCodecPrintf("Error: No images embedded in ico.\n"); *result = kInvalidInput; return nullptr; } // This structure is used to represent the vital information about entries // in the directory header. We will obtain this information for each // directory entry. struct Entry { uint32_t offset; uint32_t size; }; SkAutoFree dirEntryBuffer(sk_malloc_flags(sizeof(Entry) * numImages, SK_MALLOC_TEMP)); if (!dirEntryBuffer) { SkCodecPrintf("Error: OOM allocating ICO directory for %i images.\n", numImages); *result = kInternalError; return nullptr; } auto* directoryEntries = reinterpret_cast<Entry*>(dirEntryBuffer.get()); // Iterate over directory entries for (uint32_t i = 0; i < numImages; i++) { uint8_t entryBuffer[kIcoDirEntryBytes]; if (inputStream->read(entryBuffer, kIcoDirEntryBytes) != kIcoDirEntryBytes) { SkCodecPrintf("Error: Dir entries truncated in ico.\n"); *result = kIncompleteInput; return nullptr; } // The directory entry contains information such as width, height, // bits per pixel, and number of colors in the color palette. We will // ignore these fields since they are repeated in the header of the // embedded image. In the event of an inconsistency, we would always // defer to the value in the embedded header anyway. // Specifies the size of the embedded image, including the header uint32_t size = get_int(entryBuffer, 8); // Specifies the offset of the embedded image from the start of file. // It does not indicate the start of the pixel data, but rather the // start of the embedded image header. uint32_t offset = get_int(entryBuffer, 12); // Save the vital fields directoryEntries[i].offset = offset; directoryEntries[i].size = size; } // Default Result, if no valid embedded codecs are found. *result = kInvalidInput; // It is "customary" that the embedded images will be stored in order of // increasing offset. However, the specification does not indicate that // they must be stored in this order, so we will not trust that this is the // case. Here we sort the embedded images by increasing offset. struct EntryLessThan { bool operator() (Entry a, Entry b) const { return a.offset < b.offset; } }; EntryLessThan lessThan; SkTQSort(directoryEntries, &directoryEntries[numImages - 1], lessThan); // Now will construct a candidate codec for each of the embedded images uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; std::unique_ptr<SkTArray<std::unique_ptr<SkCodec>, true>> codecs( new (SkTArray<std::unique_ptr<SkCodec>, true>)(numImages)); for (uint32_t i = 0; i < numImages; i++) { uint32_t offset = directoryEntries[i].offset; uint32_t size = directoryEntries[i].size; // Ensure that the offset is valid if (offset < bytesRead) { SkCodecPrintf("Warning: invalid ico offset.\n"); continue; } // If we cannot skip, assume we have reached the end of the stream and // stop trying to make codecs if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) { SkCodecPrintf("Warning: could not skip to ico offset.\n"); break; } bytesRead = offset; // Create a new stream for the embedded codec SkAutoFree buffer(sk_malloc_flags(size, 0)); if (!buffer) { SkCodecPrintf("Warning: OOM trying to create embedded stream.\n"); break; } if (inputStream->read(buffer.get(), size) != size) { SkCodecPrintf("Warning: could not create embedded stream.\n"); *result = kIncompleteInput; break; } sk_sp<SkData> data(SkData::MakeFromMalloc(buffer.release(), size)); std::unique_ptr<SkMemoryStream> embeddedStream(new SkMemoryStream(data)); bytesRead += size; // Check if the embedded codec is bmp or png and create the codec SkCodec* codec = nullptr; Result dummyResult; if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { codec = SkPngCodec::NewFromStream(embeddedStream.release(), &dummyResult); } else { codec = SkBmpCodec::NewFromIco(embeddedStream.release(), &dummyResult); } // Save a valid codec if (nullptr != codec) { codecs->push_back().reset(codec); } } // Recognize if there are no valid codecs if (0 == codecs->count()) { SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); return nullptr; } // Use the largest codec as a "suggestion" for image info size_t maxSize = 0; int maxIndex = 0; for (int i = 0; i < codecs->count(); i++) { SkImageInfo info = codecs->operator[](i)->getInfo(); size_t size = info.getSafeSize(info.minRowBytes()); if (size > maxSize) { maxSize = size; maxIndex = i; } } int width = codecs->operator[](maxIndex)->getInfo().width(); int height = codecs->operator[](maxIndex)->getInfo().height(); SkEncodedInfo info = codecs->operator[](maxIndex)->getEncodedInfo(); SkColorSpace* colorSpace = codecs->operator[](maxIndex)->getInfo().colorSpace(); *result = kSuccess; // The original stream is no longer needed, because the embedded codecs own their // own streams. return new SkIcoCodec(width, height, info, codecs.release(), sk_ref_sp(colorSpace)); }
bool GrVkExtensions::initInstance(uint32_t specVersion) { if (fGetProc == nullptr) { return false; } uint32_t nonPatchVersion = remove_patch_version(specVersion); GET_PROC_LOCAL(EnumerateInstanceExtensionProperties, VK_NULL_HANDLE, VK_NULL_HANDLE); GET_PROC_LOCAL(EnumerateInstanceLayerProperties, VK_NULL_HANDLE, VK_NULL_HANDLE); SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp; if (!EnumerateInstanceExtensionProperties || !EnumerateInstanceLayerProperties) { return false; } // instance layers uint32_t layerCount = 0; VkResult res = EnumerateInstanceLayerProperties(&layerCount, nullptr); if (VK_SUCCESS != res) { return false; } VkLayerProperties* layers = new VkLayerProperties[layerCount]; res = EnumerateInstanceLayerProperties(&layerCount, layers); if (VK_SUCCESS != res) { delete[] layers; return false; } for (uint32_t i = 0; i < layerCount; ++i) { if (nonPatchVersion <= remove_patch_version(layers[i].specVersion)) { fInstanceLayerStrings->push_back() = layers[i].layerName; } } delete[] layers; if (!fInstanceLayerStrings->empty()) { SkTQSort(&fInstanceLayerStrings->front(), &fInstanceLayerStrings->back(), cmp); } // instance extensions // via Vulkan implementation and implicitly enabled layers uint32_t extensionCount = 0; res = EnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); if (VK_SUCCESS != res) { return false; } VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount]; res = EnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions); if (VK_SUCCESS != res) { delete[] extensions; return false; } for (uint32_t i = 0; i < extensionCount; ++i) { if (nonPatchVersion <= remove_patch_version(extensions[i].specVersion)) { fInstanceExtensionStrings->push_back() = extensions[i].extensionName; } } delete [] extensions; // sort so we can search if (!fInstanceExtensionStrings->empty()) { SkTQSort(&fInstanceExtensionStrings->front(), &fInstanceExtensionStrings->back(), cmp); } // via explicitly enabled layers layerCount = fInstanceLayerStrings->count(); for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) { uint32_t extensionCount = 0; res = EnumerateInstanceExtensionProperties((*fInstanceLayerStrings)[layerIndex].c_str(), &extensionCount, nullptr); if (VK_SUCCESS != res) { return false; } VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount]; res = EnumerateInstanceExtensionProperties((*fInstanceLayerStrings)[layerIndex].c_str(), &extensionCount, extensions); if (VK_SUCCESS != res) { delete[] extensions; return false; } for (uint32_t i = 0; i < extensionCount; ++i) { // if not already in the list, add it if (nonPatchVersion <= remove_patch_version(extensions[i].specVersion) && find_string(*fInstanceExtensionStrings, extensions[i].extensionName) < 0) { fInstanceExtensionStrings->push_back() = extensions[i].extensionName; SkTQSort(&fInstanceExtensionStrings->front(), &fInstanceExtensionStrings->back(), cmp); } } delete[] extensions; } return true; }
SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { if (branches->count() == 1) { // Only one branch: it will be the root Branch out = (*branches)[0]; branches->rewind(); return out; } else { // We sort the whole list by y coordinates, if we are told to do so. // // We expect Webkit / Blink to give us a reasonable x,y order. // Avoiding this call resulted in a 17% win for recording with // negligible difference in playback speed. if (fSortWhenBulkLoading) { SkTQSort(branches->begin(), branches->end() - 1, RectLessY()); } int numBranches = branches->count() / fMaxChildren; int remainder = branches->count() % fMaxChildren; int newBranches = 0; if (0 != remainder) { ++numBranches; // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to // some other branches to make up for it if (remainder >= fMinChildren) { remainder = 0; } else { remainder = fMinChildren - remainder; } } int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) * SkScalarInvert(fAspectRatio))); int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); int currentBranch = 0; for (int i = 0; i < numStrips; ++i) { // Once again, if we are told to do so, we sort by x. if (fSortWhenBulkLoading) { int begin = currentBranch; int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder, (fMaxChildren - fMinChildren) * numTiles); if (end > branches->count()) { end = branches->count(); } // Now we sort horizontal strips of rectangles by their x coords SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX()); } for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) { int incrementBy = fMaxChildren; if (remainder != 0) { // if need be, omit some nodes to make up for remainder if (remainder <= fMaxChildren - fMinChildren) { incrementBy -= remainder; remainder = 0; } else { incrementBy = fMinChildren; remainder -= fMaxChildren - fMinChildren; } } Node* n = allocateNode(level); n->fNumChildren = 1; *n->child(0) = (*branches)[currentBranch]; Branch b; b.fBounds = (*branches)[currentBranch].fBounds; b.fChild.subtree = n; ++currentBranch; for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) { b.fBounds.join((*branches)[currentBranch].fBounds); *n->child(k) = (*branches)[currentBranch]; ++n->fNumChildren; ++currentBranch; } (*branches)[newBranches] = b; ++newBranches; } } branches->setCount(newBranches); return this->bulkLoad(branches, level + 1); } }
int SkRTree::distributeChildren(Branch* children) { // We have two sides to sort by on each of two axes: const static SortSide sorts[2][2] = { {&SkIRect::fLeft, &SkIRect::fRight}, {&SkIRect::fTop, &SkIRect::fBottom} }; // We want to choose an axis to split on, then a distribution along that axis; we'll need // three pieces of info: the split axis, the side to sort by on that axis, and the index // to split the sorted array on. int32_t sortSide = -1; int32_t k = -1; int32_t axis = -1; int32_t bestS = SK_MaxS32; // Evaluate each axis, we want the min summed margin-value (s) over all distributions for (int i = 0; i < 2; ++i) { int32_t minOverlap = SK_MaxS32; int32_t minArea = SK_MaxS32; int32_t axisBestK = 0; int32_t axisBestSide = 0; int32_t s = 0; // Evaluate each sort for (int j = 0; j < 2; ++j) { SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[i][j])); // Evaluate each split index for (int32_t k = 1; k <= fMaxChildren - 2 * fMinChildren + 2; ++k) { SkIRect r1 = children[0].fBounds; SkIRect r2 = children[fMinChildren + k - 1].fBounds; for (int32_t l = 1; l < fMinChildren - 1 + k; ++l) { join_no_empty_check(children[l].fBounds, &r1); } for (int32_t l = fMinChildren + k; l < fMaxChildren + 1; ++l) { join_no_empty_check(children[l].fBounds, &r2); } int32_t area = get_area(r1) + get_area(r2); int32_t overlap = get_overlap(r1, r2); s += get_margin(r1) + get_margin(r2); if (overlap < minOverlap || (overlap == minOverlap && area < minArea)) { minOverlap = overlap; minArea = area; axisBestSide = j; axisBestK = k; } } } if (s < bestS) { bestS = s; axis = i; sortSide = axisBestSide; k = axisBestK; } } // replicate the sort of the winning distribution, (we can skip this if the last // sort ended up being best) if (!(axis == 1 && sortSide == 1)) { SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[axis][sortSide])); } return fMinChildren - 1 + k; }
/* * Assumes IsIco was called and returned true * Creates an Ico decoder * Reads enough of the stream to determine the image format */ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { // Ensure that we do not leak the input stream SkAutoTDelete<SkStream> inputStream(stream); // Header size constants static const uint32_t kIcoDirectoryBytes = 6; static const uint32_t kIcoDirEntryBytes = 16; // Read the directory header SkAutoTDeleteArray<uint8_t> dirBuffer(new uint8_t[kIcoDirectoryBytes]); if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) { SkCodecPrintf("Error: unable to read ico directory header.\n"); return nullptr; } // Process the directory header const uint16_t numImages = get_short(dirBuffer.get(), 4); if (0 == numImages) { SkCodecPrintf("Error: No images embedded in ico.\n"); return nullptr; } // Ensure that we can read all of indicated directory entries SkAutoTDeleteArray<uint8_t> entryBuffer(new uint8_t[numImages * kIcoDirEntryBytes]); if (inputStream.get()->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) != numImages*kIcoDirEntryBytes) { SkCodecPrintf("Error: unable to read ico directory entries.\n"); return nullptr; } // This structure is used to represent the vital information about entries // in the directory header. We will obtain this information for each // directory entry. struct Entry { uint32_t offset; uint32_t size; }; SkAutoTDeleteArray<Entry> directoryEntries(new Entry[numImages]); // Iterate over directory entries for (uint32_t i = 0; i < numImages; i++) { // The directory entry contains information such as width, height, // bits per pixel, and number of colors in the color palette. We will // ignore these fields since they are repeated in the header of the // embedded image. In the event of an inconsistency, we would always // defer to the value in the embedded header anyway. // Specifies the size of the embedded image, including the header uint32_t size = get_int(entryBuffer.get(), 8 + i*kIcoDirEntryBytes); // Specifies the offset of the embedded image from the start of file. // It does not indicate the start of the pixel data, but rather the // start of the embedded image header. uint32_t offset = get_int(entryBuffer.get(), 12 + i*kIcoDirEntryBytes); // Save the vital fields directoryEntries.get()[i].offset = offset; directoryEntries.get()[i].size = size; } // It is "customary" that the embedded images will be stored in order of // increasing offset. However, the specification does not indicate that // they must be stored in this order, so we will not trust that this is the // case. Here we sort the embedded images by increasing offset. struct EntryLessThan { bool operator() (Entry a, Entry b) const { return a.offset < b.offset; } }; EntryLessThan lessThan; SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1, lessThan); // Now will construct a candidate codec for each of the embedded images uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; SkAutoTDelete<SkTArray<SkAutoTDelete<SkCodec>, true>> codecs( new (SkTArray<SkAutoTDelete<SkCodec>, true>)(numImages)); for (uint32_t i = 0; i < numImages; i++) { uint32_t offset = directoryEntries.get()[i].offset; uint32_t size = directoryEntries.get()[i].size; // Ensure that the offset is valid if (offset < bytesRead) { SkCodecPrintf("Warning: invalid ico offset.\n"); continue; } // If we cannot skip, assume we have reached the end of the stream and // stop trying to make codecs if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) { SkCodecPrintf("Warning: could not skip to ico offset.\n"); break; } bytesRead = offset; // Create a new stream for the embedded codec SkAutoTUnref<SkData> data( SkData::NewFromStream(inputStream.get(), size)); if (nullptr == data.get()) { SkCodecPrintf("Warning: could not create embedded stream.\n"); break; } SkAutoTDelete<SkMemoryStream> embeddedStream(new SkMemoryStream(data.get())); bytesRead += size; // Check if the embedded codec is bmp or png and create the codec SkCodec* codec = nullptr; if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { codec = SkPngCodec::NewFromStream(embeddedStream.detach()); } else { codec = SkBmpCodec::NewFromIco(embeddedStream.detach()); } // Save a valid codec if (nullptr != codec) { codecs->push_back().reset(codec); } } // Recognize if there are no valid codecs if (0 == codecs->count()) { SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); return nullptr; } // Use the largest codec as a "suggestion" for image info uint32_t maxSize = 0; uint32_t maxIndex = 0; for (int32_t i = 0; i < codecs->count(); i++) { SkImageInfo info = codecs->operator[](i)->getInfo(); uint32_t size = info.width() * info.height(); if (size > maxSize) { maxSize = size; maxIndex = i; } } SkImageInfo info = codecs->operator[](maxIndex)->getInfo(); // ICOs contain an alpha mask after the image which means we cannot // guarantee that an image is opaque, even if the sub-codec thinks it // is. // FIXME (msarett): The BMP decoder depends on the alpha type in order // to decode correctly, otherwise it could report kUnpremul and we would // not have to correct it here. Is there a better way? // FIXME (msarett): This is only true for BMP in ICO - could a PNG in ICO // be opaque? Is it okay that we missed out on the opportunity to mark // such an image as opaque? info = info.makeAlphaType(kUnpremul_SkAlphaType); // Note that stream is owned by the embedded codec, the ico does not need // direct access to the stream. return new SkIcoCodec(info, codecs.detach()); }
void SkCommandLineFlags::Parse(int argc, char** argv) { // Only allow calling this function once. static bool gOnce; if (gOnce) { SkDebugf("Parse should only be called once at the beginning of main!\n"); SkASSERT(false); return; } gOnce = true; bool helpPrinted = false; // Loop over argv, starting with 1, since the first is just the name of the program. for (int i = 1; i < argc; i++) { if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) { // Print help message. SkTDArray<const char*> helpFlags; for (int j = i + 1; j < argc; j++) { if (SkStrStartsWith(argv[j], '-')) { break; } helpFlags.append(1, &argv[j]); } if (0 == helpFlags.count()) { // Only print general help message if help for specific flags is not requested. SkDebugf("%s\n%s\n", argv[0], gUsage.c_str()); } SkDebugf("Flags:\n"); if (0 == helpFlags.count()) { // If no flags followed --help, print them all SkTDArray<SkFlagInfo*> allFlags; for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag; flag = flag->next()) { allFlags.push(flag); } SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1], CompareFlagsByName()); for (int i = 0; i < allFlags.count(); ++i) { print_help_for_flag(allFlags[i]); if (allFlags[i]->extendedHelp().size() > 0) { SkDebugf(" Use '--help %s' for more information.\n", allFlags[i]->name().c_str()); } } } else { for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag; flag = flag->next()) { for (int k = 0; k < helpFlags.count(); k++) { if (flag->name().equals(helpFlags[k]) || flag->shortName().equals(helpFlags[k])) { print_extended_help_for_flag(flag); helpFlags.remove(k); break; } } } } if (helpFlags.count() > 0) { SkDebugf("Requested help for unrecognized flags:\n"); for (int k = 0; k < helpFlags.count(); k++) { SkDebugf(" --%s\n", helpFlags[k]); } } helpPrinted = true; } if (!helpPrinted) { bool flagMatched = false; SkFlagInfo* flag = gHead; while (flag != nullptr) { if (flag->match(argv[i])) { flagMatched = true; switch (flag->getFlagType()) { case SkFlagInfo::kBool_FlagType: // Can be handled by match, above, but can also be set by the next // string. if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) { i++; bool value; if (parse_bool_arg(argv[i], &value)) { flag->setBool(value); } } break; case SkFlagInfo::kString_FlagType: flag->resetStrings(); // Add all arguments until another flag is reached. while (i+1 < argc) { char* end = nullptr; // Negative numbers aren't flags. ignore_result(strtod(argv[i+1], &end)); if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) { break; } i++; flag->append(argv[i]); } break; case SkFlagInfo::kInt_FlagType: i++; flag->setInt(atoi(argv[i])); break; case SkFlagInfo::kDouble_FlagType: i++; flag->setDouble(atof(argv[i])); break; default: SkDEBUGFAIL("Invalid flag type"); } break; } flag = flag->next(); } if (!flagMatched) { #if SK_BUILD_FOR_MAC if (SkStrStartsWith(argv[i], "NSDocumentRevisions") || SkStrStartsWith(argv[i], "-NSDocumentRevisions")) { i++; // skip YES } else #endif if (FLAGS_undefok) { SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]); } else { SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]); exit(-1); } } } } // Since all of the flags have been set, release the memory used by each // flag. FLAGS_x can still be used after this. SkFlagInfo* flag = gHead; gHead = nullptr; while (flag != nullptr) { SkFlagInfo* next = flag->next(); delete flag; flag = next; } if (helpPrinted) { exit(0); } }
/* * Assumes IsIco was called and returned true * Creates an Ico decoder * Reads enough of the stream to determine the image format */ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { // Ensure that we do not leak the input stream SkAutoTDelete<SkStream> inputStream(stream); // Header size constants static const uint32_t kIcoDirectoryBytes = 6; static const uint32_t kIcoDirEntryBytes = 16; // Read the directory header SkAutoTDeleteArray<uint8_t> dirBuffer( SkNEW_ARRAY(uint8_t, kIcoDirectoryBytes)); if (inputStream.get()->read(dirBuffer.get(), kIcoDirectoryBytes) != kIcoDirectoryBytes) { SkCodecPrintf("Error: unable to read ico directory header.\n"); return NULL; } // Process the directory header const uint16_t numImages = get_short(dirBuffer.get(), 4); if (0 == numImages) { SkCodecPrintf("Error: No images embedded in ico.\n"); return NULL; } // Ensure that we can read all of indicated directory entries SkAutoTDeleteArray<uint8_t> entryBuffer( SkNEW_ARRAY(uint8_t, numImages*kIcoDirEntryBytes)); if (inputStream.get()->read(entryBuffer.get(), numImages*kIcoDirEntryBytes) != numImages*kIcoDirEntryBytes) { SkCodecPrintf("Error: unable to read ico directory entries.\n"); return NULL; } // This structure is used to represent the vital information about entries // in the directory header. We will obtain this information for each // directory entry. struct Entry { uint32_t offset; uint32_t size; }; SkAutoTDeleteArray<Entry> directoryEntries(SkNEW_ARRAY(Entry, numImages)); // Iterate over directory entries for (uint32_t i = 0; i < numImages; i++) { // The directory entry contains information such as width, height, // bits per pixel, and number of colors in the color palette. We will // ignore these fields since they are repeated in the header of the // embedded image. In the event of an inconsistency, we would always // defer to the value in the embedded header anyway. // Specifies the size of the embedded image, including the header uint32_t size = get_int(entryBuffer.get(), 8 + i*kIcoDirEntryBytes); // Specifies the offset of the embedded image from the start of file. // It does not indicate the start of the pixel data, but rather the // start of the embedded image header. uint32_t offset = get_int(entryBuffer.get(), 12 + i*kIcoDirEntryBytes); // Save the vital fields directoryEntries.get()[i].offset = offset; directoryEntries.get()[i].size = size; } // It is "customary" that the embedded images will be stored in order of // increasing offset. However, the specification does not indicate that // they must be stored in this order, so we will not trust that this is the // case. Here we sort the embedded images by increasing offset. struct EntryLessThan { bool operator() (Entry a, Entry b) const { return a.offset < b.offset; } }; EntryLessThan lessThan; SkTQSort(directoryEntries.get(), directoryEntries.get() + numImages - 1, lessThan); // Now will construct a candidate codec for each of the embedded images uint32_t bytesRead = kIcoDirectoryBytes + numImages * kIcoDirEntryBytes; SkAutoTDelete<SkTArray<SkAutoTDelete<SkCodec>, true>> codecs( SkNEW_ARGS((SkTArray<SkAutoTDelete<SkCodec>, true>), (numImages))); for (uint32_t i = 0; i < numImages; i++) { uint32_t offset = directoryEntries.get()[i].offset; uint32_t size = directoryEntries.get()[i].size; // Ensure that the offset is valid if (offset < bytesRead) { SkCodecPrintf("Warning: invalid ico offset.\n"); continue; } // If we cannot skip, assume we have reached the end of the stream and // stop trying to make codecs if (inputStream.get()->skip(offset - bytesRead) != offset - bytesRead) { SkCodecPrintf("Warning: could not skip to ico offset.\n"); break; } bytesRead = offset; // Create a new stream for the embedded codec SkAutoTUnref<SkData> data( SkData::NewFromStream(inputStream.get(), size)); if (NULL == data.get()) { SkCodecPrintf("Warning: could not create embedded stream.\n"); break; } SkAutoTDelete<SkMemoryStream> embeddedStream(SkNEW_ARGS(SkMemoryStream, (data.get()))); bytesRead += size; // Check if the embedded codec is bmp or png and create the codec const bool isPng = SkPngCodec::IsPng(embeddedStream); SkAssertResult(embeddedStream->rewind()); SkCodec* codec = NULL; if (isPng) { codec = SkPngCodec::NewFromStream(embeddedStream.detach()); } else { codec = SkBmpCodec::NewFromIco(embeddedStream.detach()); } // Save a valid codec if (NULL != codec) { codecs->push_back().reset(codec); } } // Recognize if there are no valid codecs if (0 == codecs->count()) { SkCodecPrintf("Error: could not find any valid embedded ico codecs.\n"); return NULL; } // Use the largest codec as a "suggestion" for image info uint32_t maxSize = 0; uint32_t maxIndex = 0; for (int32_t i = 0; i < codecs->count(); i++) { SkImageInfo info = codecs->operator[](i)->getInfo(); uint32_t size = info.width() * info.height(); if (size > maxSize) { maxSize = size; maxIndex = i; } } SkImageInfo info = codecs->operator[](maxIndex)->getInfo(); // Note that stream is owned by the embedded codec, the ico does not need // direct access to the stream. return SkNEW_ARGS(SkIcoCodec, (info, codecs.detach())); }