Пример #1
0
    ~BufferManager() {
        // nullptr out the entries that are really free list links rather than ptrs before deleting.
        intptr_t curr = fFreeListHead;
        while (kFreeListEnd != curr) {
            intptr_t next = reinterpret_cast<intptr_t>(fBuffers[SkToS32(curr)]);
            fBuffers[SkToS32(curr)] = nullptr;
            curr = next;
        }

        fBuffers.deleteAll();
    }
Пример #2
0
void SkBinaryWriteBuffer::writeTypeface(SkTypeface* obj) {
    if (fDeduper) {
        this->write32(fDeduper->findOrDefineTypeface(obj));
        return;
    }

    // Write 32 bits (signed)
    //   0 -- default font
    //  >0 -- index
    //  <0 -- custom (serial procs)

    if (obj == nullptr) {
        fWriter.write32(0);
    } else if (fProcs.fTypefaceProc) {
        auto data = fProcs.fTypefaceProc(obj, fProcs.fTypefaceCtx);
        if (data) {
            size_t size = data->size();
            if (!SkTFitsIn<int32_t>(size)) {
                size = 0;               // fall back to default font
            }
            int32_t ssize = SkToS32(size);
            fWriter.write32(-ssize);    // negative to signal custom
            if (size) {
                this->writePad32(data->data(), size);
            }
            return;
        }
        // no data means fall through for std behavior
    }
    fWriter.write32(fTFSet ? fTFSet->add(obj) : 0);
}
Пример #3
0
/*  Format:
 *  (subset) bounds
 *  size (31bits)
 *  data [ encoded, with raw width/height ]
 */
void SkBinaryWriteBuffer::writeImage(const SkImage* image) {
    if (fDeduper) {
        this->write32(fDeduper->findOrDefineImage(const_cast<SkImage*>(image)));
        return;
    }

    const SkIRect bounds = SkImage_getSubset(image);
    this->writeIRect(bounds);

    sk_sp<SkData> data;
    if (fProcs.fImageProc) {
        data = fProcs.fImageProc(const_cast<SkImage*>(image), fProcs.fImageCtx);
    }
    if (!data) {
        data = image->encodeToData();
    }

    size_t size = data ? data->size() : 0;
    if (!SkTFitsIn<int32_t>(size)) {
        size = 0;   // too big to store
    }
    this->write32(SkToS32(size));   // writing 0 signals failure
    if (size) {
        this->writePad32(data->data(), size);
    }
}
Пример #4
0
// Xref table and footer
void SkPDFObjectSerializer::serializeFooter(SkWStream* wStream,
                                            const sk_sp<SkPDFObject> docCatalog,
                                            sk_sp<SkPDFObject> id) {
    this->serializeObjects(wStream);
    int32_t xRefFileOffset = this->offset(wStream);
    // Include the special zeroth object in the count.
    int32_t objCount = SkToS32(fOffsets.count() + 1);
    wStream->writeText("xref\n0 ");
    wStream->writeDecAsText(objCount);
    wStream->writeText("\n0000000000 65535 f \n");
    for (int i = 0; i < fOffsets.count(); i++) {
        wStream->writeBigDecAsText(fOffsets[i], 10);
        wStream->writeText(" 00000 n \n");
    }
    SkPDFDict trailerDict;
    trailerDict.insertInt("Size", objCount);
    SkASSERT(docCatalog);
    trailerDict.insertObjRef("Root", docCatalog);
    SkASSERT(fInfoDict);
    trailerDict.insertObjRef("Info", std::move(fInfoDict));
    if (id) {
        trailerDict.insertObject("ID", std::move(id));
    }
    wStream->writeText("trailer\n");
    trailerDict.emitObject(wStream, fObjNumMap);
    wStream->writeText("\nstartxref\n");
    wStream->writeBigDecAsText(xRefFileOffset);
    wStream->writeText("\n%%EOF");
}
Пример #5
0
size_t SkMask::computeTotalImageSize() const {
    size_t size = this->computeImageSize();
    if (fFormat == SkMask::k3D_Format) {
        size = safeMul32(SkToS32(size), 3);
    }
    return size;
}
Пример #6
0
SkString* SkObjectParser::TextToString(const void* text, size_t byteLength,
                                       SkPaint::TextEncoding encoding) {

    SkString* decodedText = new SkString();
    switch (encoding) {
        case SkPaint::kUTF8_TextEncoding: {
            decodedText->append("UTF-8: ");
            decodedText->append((const char*)text, byteLength);
            break;
        }
        case SkPaint::kUTF16_TextEncoding: {
            decodedText->append("UTF-16: ");
            size_t sizeNeeded = SkUTF16_ToUTF8((uint16_t*)text,
                                                SkToS32(byteLength / 2),
                                                nullptr);
            SkAutoSTMalloc<0x100, char> utf8(sizeNeeded);
            SkUTF16_ToUTF8((uint16_t*)text, SkToS32(byteLength / 2), utf8);
            decodedText->append(utf8, sizeNeeded);
            break;
        }
        case SkPaint::kUTF32_TextEncoding: {
            decodedText->append("UTF-32: ");
            const SkUnichar* begin = (const SkUnichar*)text;
            const SkUnichar* end = (const SkUnichar*)((const char*)text + byteLength);
            for (const SkUnichar* unichar = begin; unichar < end; ++unichar) {
                decodedText->appendUnichar(*unichar);
            }
            break;
        }
        case SkPaint::kGlyphID_TextEncoding: {
            decodedText->append("GlyphID: ");
            const uint16_t* begin = (const uint16_t*)text;
            const uint16_t* end = (const uint16_t*)((const char*)text + byteLength);
            for (const uint16_t* glyph = begin; glyph < end; ++glyph) {
                decodedText->append("0x");
                decodedText->appendHex(*glyph);
                decodedText->append(" ");
            }
            break;
        }
        default:
            decodedText->append("Unknown text encoding.");
            break;
    }

    return decodedText;
}
Пример #7
0
void* ThreadSafePipeController::requestBlock(size_t minRequest, size_t *actual) {
    if (fBlock) {
        // Save the previous block for later
        PipeBlock previousBloc(fBlock, fBytesWritten);
        fBlockList.push(previousBloc);
    }
    int32_t blockSize = SkMax32(SkToS32(minRequest), kMinBlockSize);
    fBlock = fAllocator.allocThrow(blockSize);
    fBytesWritten = 0;
    *actual = blockSize;
    return fBlock;
}
Пример #8
0
std::unique_ptr<SkAdvancedTypefaceMetrics> SkTestTypeface::onGetAdvancedMetrics() const { // pdf only
    std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
    info->fFontName.set(fTestFont->fName);
    int glyphCount = this->onCountGlyphs();

    SkTDArray<SkUnichar>& toUnicode = info->fGlyphToUnicode;
    toUnicode.setCount(glyphCount);
    SkASSERT(glyphCount == SkToInt(fTestFont->fCharCodesCount));
    for (int gid = 0; gid < glyphCount; ++gid) {
        toUnicode[gid] = SkToS32(fTestFont->fCharCodes[gid]);
    }
    return info;
}
Пример #9
0
SkMallocPixelRef* SkMallocPixelRef::NewAllocate(const SkImageInfo& info,
                                                size_t requestedRowBytes,
                                                SkColorTable* ctable) {
    if (!is_valid(info, ctable)) {
        return NULL;
    }

    int32_t minRB = SkToS32(info.minRowBytes());
    if (minRB < 0) {
        return NULL;    // allocation will be too large
    }
    if (requestedRowBytes > 0 && (int32_t)requestedRowBytes < minRB) {
        return NULL;    // cannot meet requested rowbytes
    }

    int32_t rowBytes;
    if (requestedRowBytes) {
        rowBytes = SkToS32(requestedRowBytes);
    } else {
        rowBytes = minRB;
    }

    int64_t bigSize = (int64_t)info.fHeight * rowBytes;
    if (!sk_64_isS32(bigSize)) {
        return NULL;
    }

    size_t size = sk_64_asS32(bigSize);
    SkASSERT(size >= info.getSafeSize(rowBytes));
    void* addr = sk_malloc_flags(size, 0);
    if (NULL == addr) {
        return NULL;
    }

    return SkNEW_ARGS(SkMallocPixelRef,
                      (info, addr, rowBytes, ctable,
                       sk_free_releaseproc, NULL));
}
Пример #10
0
 sk_sp<SkPixelRef> SkMallocPixelRef::MakeUsing(void*(*alloc)(size_t),
                                               const SkImageInfo& info,
                                               size_t requestedRowBytes,
                                               sk_sp<SkColorTable> ctable) {
    if (!is_valid(info, ctable.get())) {
        return nullptr;
    }

    // only want to permit 31bits of rowBytes
    int64_t minRB = (int64_t)info.minRowBytes64();
    if (minRB < 0 || !sk_64_isS32(minRB)) {
        return nullptr;    // allocation will be too large
    }
    if (requestedRowBytes > 0 && (int32_t)requestedRowBytes < minRB) {
        return nullptr;    // cannot meet requested rowbytes
    }

    int32_t rowBytes;
    if (requestedRowBytes) {
        rowBytes = SkToS32(requestedRowBytes);
    } else {
        rowBytes = minRB;
    }

    int64_t bigSize = (int64_t)info.height() * rowBytes;
    if (!sk_64_isS32(bigSize)) {
        return nullptr;
    }

    size_t size = sk_64_asS32(bigSize);
    SkASSERT(size >= info.getSafeSize(rowBytes));
    void* addr = alloc(size);
    if (nullptr == addr) {
        return nullptr;
    }

     return sk_sp<SkPixelRef>(new SkMallocPixelRef(info, addr, rowBytes, std::move(ctable),
                                                   sk_free_releaseproc, nullptr));
}
Пример #11
0
bool SkXMLParser::parse(SkStream& docStream)
{
    ParsingContext ctx(this);
    if (!ctx.fXMLParser) {
        SkDebugf("could not create XML parser\n");
        return false;
    }

    XML_SetUserData(ctx.fXMLParser, &ctx);
    XML_SetElementHandler(ctx.fXMLParser, start_element_handler, end_element_handler);
    XML_SetCharacterDataHandler(ctx.fXMLParser, text_handler);

    // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340.
    XML_SetEntityDeclHandler(ctx.fXMLParser, entity_decl_handler);

    static const int kBufferSize = 512 SkDEBUGCODE( - 507);
    bool done = false;
    do {
        void* buffer = XML_GetBuffer(ctx.fXMLParser, kBufferSize);
        if (!buffer) {
            SkDebugf("could not buffer enough to continue\n");
            return false;
        }

        size_t len = docStream.read(buffer, kBufferSize);
        done = docStream.isAtEnd();
        XML_Status status = XML_ParseBuffer(ctx.fXMLParser, SkToS32(len), done);
        if (XML_STATUS_ERROR == status) {
            XML_Error error = XML_GetErrorCode(ctx.fXMLParser);
            int line = XML_GetCurrentLineNumber(ctx.fXMLParser);
            int column = XML_GetCurrentColumnNumber(ctx.fXMLParser);
            const XML_LChar* errorString = XML_ErrorString(error);
            SkDebugf("parse error @%d:%d: %d (%s).\n", line, column, error, errorString);
            return false;
        }
    } while (!done);

    return true;
}
Пример #12
0
static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) {
    sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
    sk_bzero(drawFilters, sizeof(drawFilters));

    if (FLAGS_filter.count() > 0) {
        const char* filters = FLAGS_filter[0];
        const char* colon = strchr(filters, ':');
        if (colon) {
            int32_t type = -1;
            size_t typeLen = colon - filters;
            for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
                if (typeLen == strlen(gFilterTypes[tIndex])
                        && !strncmp(filters, gFilterTypes[tIndex], typeLen)) {
                    type = SkToS32(tIndex);
                    break;
                }
            }
            if (type < 0) {
                SkString err;
                err.printf("Unknown type for --filter %s\n", filters);
                gLogger.logError(err);
                exit(-1);
            }
            int flag = -1;
            size_t flagLen = strlen(filters) - typeLen - 1;
            for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
                if (flagLen == strlen(gFilterFlags[fIndex])
                        && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
                    flag = 1 << fIndex;
                    break;
                }
            }
            if (flag < 0) {
                SkString err;
                err.printf("Unknown flag for --filter %s\n", filters);
                gLogger.logError(err);
                exit(-1);
            }
            for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
                if (type != SkDrawFilter::kTypeCount && index != type) {
                    continue;
                }
                drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
                        (drawFilters[index] | flag);
            }
        } else {
            SkString err;
            err.printf("Unknown arg for --filter %s : missing colon\n", filters);
            gLogger.logError(err);
            exit(-1);
        }
    }

    if (FLAGS_timers.count() > 0) {
        size_t index = 0;
        bool timerWall = false;
        bool truncatedTimerWall = false;
        bool timerCpu = false;
        bool truncatedTimerCpu = false;
        bool timerGpu = false;
        while (index < strlen(FLAGS_timers[0])) {
            switch (FLAGS_timers[0][index]) {
                case 'w':
                    timerWall = true;
                    break;
                case 'c':
                    timerCpu = true;
                    break;
                case 'W':
                    truncatedTimerWall = true;
                    break;
                case 'C':
                    truncatedTimerCpu = true;
                    break;
                case 'g':
                    timerGpu = true;
                    break;
                default:
                    SkDebugf("mystery character\n");
                    break;
            }
            index++;
        }
        benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu,
                                  timerGpu);
    }

    SkString errorString;
    SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
                                                                   kBench_PictureTool));

    if (errorString.size() > 0) {
        gLogger.logError(errorString);
    }

    if (NULL == renderer.get()) {
        exit(-1);
    }

    if (FLAGS_timeIndividualTiles) {
        sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer();
        if (NULL == tiledRenderer) {
            gLogger.logError("--timeIndividualTiles requires tiled rendering.\n");
            exit(-1);
        }
        if (!tiledRenderer->supportsTimingIndividualTiles()) {
            gLogger.logError("This renderer does not support --timeIndividualTiles.\n");
            exit(-1);
        }
        benchmark->setTimeIndividualTiles(true);
    }

    benchmark->setPurgeDecodedTex(FLAGS_purgeDecodedTex);
    benchmark->setPreprocess(FLAGS_preprocess);

    if (FLAGS_readPath.count() < 1) {
        gLogger.logError(".skp files or directories are required.\n");
        exit(-1);
    }

    renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
    if (FLAGS_logPerIter) {
        benchmark->setTimerResultType(TimerData::kPerIter_Result);
    } else if (FLAGS_min) {
        benchmark->setTimerResultType(TimerData::kMin_Result);
    } else {
        benchmark->setTimerResultType(TimerData::kAvg_Result);
    }
    benchmark->setRenderer(renderer);
    benchmark->setRepeats(FLAGS_repeat);
    benchmark->setWriter(&gWriter);
}
Пример #13
0
static bool emit_pdf_document(const SkTDArray<const SkPDFDevice*>& pageDevices,
                              SkWStream* stream) {
    if (pageDevices.isEmpty()) {
        return false;
    }

    SkTDArray<SkPDFDict*> pages;
    SkAutoTUnref<SkPDFDict> dests(SkNEW(SkPDFDict));

    for (int i = 0; i < pageDevices.count(); i++) {
        SkASSERT(pageDevices[i]);
        SkASSERT(i == 0 ||
                 pageDevices[i - 1]->getCanon() == pageDevices[i]->getCanon());
        SkAutoTUnref<SkPDFDict> page(create_pdf_page(pageDevices[i]));
        pageDevices[i]->appendDestinations(dests, page.get());
        pages.push(page.detach());
    }

    SkTDArray<SkPDFDict*> pageTree;
    SkAutoTUnref<SkPDFDict> docCatalog(SkNEW_ARGS(SkPDFDict, ("Catalog")));

    SkPDFDict* pageTreeRoot;
    generate_page_tree(pages, &pageTree, &pageTreeRoot);
    docCatalog->insertObjRef("Pages", SkRef(pageTreeRoot));

    if (dests->size() > 0) {
        docCatalog->insertObjRef("Dests", dests.detach());
    }

    /* TODO(vandebo): output intent
    SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
    outputIntent->insertName("S", "GTS_PDFA1");
    outputIntent->insertString("OutputConditionIdentifier", "sRGB");
    SkAutoTUnref<SkPDFArray> intentArray(new SkPDFArray);
    intentArray->appendObject(SkRef(outputIntent.get()));
    docCatalog->insertObject("OutputIntent", intentArray.detach());
    */

    // Build font subsetting info before proceeding.
    SkPDFSubstituteMap substitutes;
    perform_font_subsetting(pageDevices, &substitutes);

    SkPDFObjNumMap objNumMap;
    if (objNumMap.addObject(docCatalog.get())) {
        docCatalog->addResources(&objNumMap, substitutes);
    }
    size_t baseOffset = stream->bytesWritten();
    emit_pdf_header(stream);
    SkTDArray<int32_t> offsets;
    for (int i = 0; i < objNumMap.objects().count(); ++i) {
        SkPDFObject* object = objNumMap.objects()[i];
        size_t offset = stream->bytesWritten();
        // This assert checks that size(pdf_header) > 0 and that
        // the output stream correctly reports bytesWritten().
        SkASSERT(offset > baseOffset);
        offsets.push(SkToS32(offset - baseOffset));
        SkASSERT(object == substitutes.getSubstitute(object));
        SkASSERT(objNumMap.getObjectNumber(object) == i + 1);
        stream->writeDecAsText(i + 1);
        stream->writeText(" 0 obj\n");  // Generation number is always 0.
        object->emitObject(stream, objNumMap, substitutes);
        stream->writeText("\nendobj\n");
    }
    int32_t xRefFileOffset = SkToS32(stream->bytesWritten() - baseOffset);

    // Include the zeroth object in the count.
    int32_t objCount = SkToS32(offsets.count() + 1);

    stream->writeText("xref\n0 ");
    stream->writeDecAsText(objCount);
    stream->writeText("\n0000000000 65535 f \n");
    for (int i = 0; i < offsets.count(); i++) {
        SkASSERT(offsets[i] > 0);
        stream->writeBigDecAsText(offsets[i], 10);
        stream->writeText(" 00000 n \n");
    }
    emit_pdf_footer(stream, objNumMap, substitutes, docCatalog.get(), objCount,
                    xRefFileOffset);

    // The page tree has both child and parent pointers, so it creates a
    // reference cycle.  We must clear that cycle to properly reclaim memory.
    for (int i = 0; i < pageTree.count(); i++) {
        pageTree[i]->clear();
    }
    pageTree.safeUnrefAll();
    pages.unrefAll();
    return true;
}
Пример #14
0
int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
    size_t offset = wStream->bytesWritten();
    SkASSERT(offset > fBaseOffset);
    return SkToS32(offset - fBaseOffset);
}
Пример #15
0
bool SkPatchUtils::getVertexData(SkPatchUtils::VertexData* data, const SkPoint cubics[12],
                   const SkColor colors[4], const SkPoint texCoords[4], int lodX, int lodY) {
    if (lodX < 1 || lodY < 1 || NULL == cubics || NULL == data) {
        return false;
    }

    // check for overflow in multiplication
    const int64_t lodX64 = (lodX + 1),
                   lodY64 = (lodY + 1),
                   mult64 = lodX64 * lodY64;
    if (mult64 > SK_MaxS32) {
        return false;
    }
    data->fVertexCount = SkToS32(mult64);

    // it is recommended to generate draw calls of no more than 65536 indices, so we never generate
    // more than 60000 indices. To accomplish that we resize the LOD and vertex count
    if (data->fVertexCount > 10000 || lodX > 200 || lodY > 200) {
        SkScalar weightX = static_cast<SkScalar>(lodX) / (lodX + lodY);
        SkScalar weightY = static_cast<SkScalar>(lodY) / (lodX + lodY);

        // 200 comes from the 100 * 2 which is the max value of vertices because of the limit of
        // 60000 indices ( sqrt(60000 / 6) that comes from data->fIndexCount = lodX * lodY * 6)
        lodX = static_cast<int>(weightX * 200);
        lodY = static_cast<int>(weightY * 200);
        data->fVertexCount = (lodX + 1) * (lodY + 1);
    }
    data->fIndexCount = lodX * lodY * 6;
    
    data->fPoints = SkNEW_ARRAY(SkPoint, data->fVertexCount);
    data->fIndices = SkNEW_ARRAY(uint16_t, data->fIndexCount);
    
    // if colors is not null then create array for colors
    SkPMColor colorsPM[kNumCorners];
    if (NULL != colors) {
        // premultiply colors to avoid color bleeding.
        for (int i = 0; i < kNumCorners; i++) {
            colorsPM[i] = SkPreMultiplyColor(colors[i]);
        }
        data->fColors = SkNEW_ARRAY(uint32_t, data->fVertexCount);
    }
    
    // if texture coordinates are not null then create array for them
    if (NULL != texCoords) {
        data->fTexCoords = SkNEW_ARRAY(SkPoint, data->fVertexCount);
    }
    
    SkPoint pts[kNumPtsCubic];
    SkPatchUtils::getBottomCubic(cubics, pts);
    FwDCubicEvaluator fBottom(pts);
    SkPatchUtils::getTopCubic(cubics, pts);
    FwDCubicEvaluator fTop(pts);
    SkPatchUtils::getLeftCubic(cubics, pts);
    FwDCubicEvaluator fLeft(pts);
    SkPatchUtils::getRightCubic(cubics, pts);
    FwDCubicEvaluator fRight(pts);
    
    fBottom.restart(lodX);
    fTop.restart(lodX);
    
    SkScalar u = 0.0f;
    int stride = lodY + 1;
    for (int x = 0; x <= lodX; x++) {
        SkPoint bottom = fBottom.next(), top = fTop.next();
        fLeft.restart(lodY);
        fRight.restart(lodY);
        SkScalar v = 0.f;
        for (int y = 0; y <= lodY; y++) {
            int dataIndex = x * (lodY + 1) + y;
            
            SkPoint left = fLeft.next(), right = fRight.next();
            
            SkPoint s0 = SkPoint::Make((1.0f - v) * top.x() + v * bottom.x(),
                                       (1.0f - v) * top.y() + v * bottom.y());
            SkPoint s1 = SkPoint::Make((1.0f - u) * left.x() + u * right.x(),
                                       (1.0f - u) * left.y() + u * right.y());
            SkPoint s2 = SkPoint::Make(
                                       (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].x()
                                                     + u * fTop.getCtrlPoints()[3].x())
                                       + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].x()
                                              + u * fBottom.getCtrlPoints()[3].x()),
                                       (1.0f - v) * ((1.0f - u) * fTop.getCtrlPoints()[0].y()
                                                     + u * fTop.getCtrlPoints()[3].y())
                                       + v * ((1.0f - u) * fBottom.getCtrlPoints()[0].y()
                                              + u * fBottom.getCtrlPoints()[3].y()));
            data->fPoints[dataIndex] = s0 + s1 - s2;
            
            if (NULL != colors) {
                uint8_t a = uint8_t(bilerp(u, v,
                                   SkScalar(SkColorGetA(colorsPM[kTopLeft_Corner])),
                                   SkScalar(SkColorGetA(colorsPM[kTopRight_Corner])),
                                   SkScalar(SkColorGetA(colorsPM[kBottomLeft_Corner])),
                                   SkScalar(SkColorGetA(colorsPM[kBottomRight_Corner]))));
                uint8_t r = uint8_t(bilerp(u, v,
                                   SkScalar(SkColorGetR(colorsPM[kTopLeft_Corner])),
                                   SkScalar(SkColorGetR(colorsPM[kTopRight_Corner])),
                                   SkScalar(SkColorGetR(colorsPM[kBottomLeft_Corner])),
                                   SkScalar(SkColorGetR(colorsPM[kBottomRight_Corner]))));
                uint8_t g = uint8_t(bilerp(u, v,
                                   SkScalar(SkColorGetG(colorsPM[kTopLeft_Corner])),
                                   SkScalar(SkColorGetG(colorsPM[kTopRight_Corner])),
                                   SkScalar(SkColorGetG(colorsPM[kBottomLeft_Corner])),
                                   SkScalar(SkColorGetG(colorsPM[kBottomRight_Corner]))));
                uint8_t b = uint8_t(bilerp(u, v,
                                   SkScalar(SkColorGetB(colorsPM[kTopLeft_Corner])),
                                   SkScalar(SkColorGetB(colorsPM[kTopRight_Corner])),
                                   SkScalar(SkColorGetB(colorsPM[kBottomLeft_Corner])),
                                   SkScalar(SkColorGetB(colorsPM[kBottomRight_Corner]))));
                data->fColors[dataIndex] = SkPackARGB32(a,r,g,b);
            }
            
            if (NULL != texCoords) {
                data->fTexCoords[dataIndex] = SkPoint::Make(
                                            bilerp(u, v, texCoords[kTopLeft_Corner].x(),
                                                   texCoords[kTopRight_Corner].x(),
                                                   texCoords[kBottomLeft_Corner].x(),
                                                   texCoords[kBottomRight_Corner].x()),
                                            bilerp(u, v, texCoords[kTopLeft_Corner].y(),
                                                   texCoords[kTopRight_Corner].y(),
                                                   texCoords[kBottomLeft_Corner].y(),
                                                   texCoords[kBottomRight_Corner].y()));
                
            }
            
            if(x < lodX && y < lodY) {
                int i = 6 * (x * lodY + y);
                data->fIndices[i] = x * stride + y;
                data->fIndices[i + 1] = x * stride + 1 + y;
                data->fIndices[i + 2] = (x + 1) * stride + 1 + y;
                data->fIndices[i + 3] = data->fIndices[i];
                data->fIndices[i + 4] = data->fIndices[i + 2];
                data->fIndices[i + 5] = (x + 1) * stride + y;
            }
            v = SkScalarClampMax(v + 1.f / lodY, 1);
        }
        u = SkScalarClampMax(u + 1.f / lodX, 1);
    }
    return true;

}