int SkPaintPriv::ValidCountText(const void* text, size_t length, SkPaint::TextEncoding encoding) { if (length == 0) { return 0; } switch (encoding) { case SkPaint::kUTF8_TextEncoding: return SkUTF8_CountUnichars(text, length); case SkPaint::kUTF16_TextEncoding: return SkUTF16_CountUnichars(text, length); case SkPaint::kUTF32_TextEncoding: return SkUTF32_CountUnichars(text, length); case SkPaint::kGlyphID_TextEncoding: if (SkIsAlign2(intptr_t(text)) && SkIsAlign2(length)) { return length >> 1; } break; } return 0; }
SkPathEffect* SkDashPathEffect::Create(const SkScalar intervals[], int count, SkScalar phase) { if ((count < 2) || !SkIsAlign2(count)) { return nullptr; } for (int i = 0; i < count; i++) { if (intervals[i] < 0) { return nullptr; } } return new SkDashPathEffect(intervals, count, phase); }
bool SkDashPath::ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count) { if (count < 2 || !SkIsAlign2(count)) { return false; } SkScalar length = 0; for (int i = 0; i < count; i++) { if (intervals[i] < 0) { return false; } length += intervals[i]; } // watch out for values that might make us go out of bounds return length > 0 && SkScalarIsFinite(phase) && SkScalarIsFinite(length); }
void SkPipeCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], SK_XFERMODE_PARAM xmode, const uint16_t indices[], int indexCount, const SkPaint& paint) { SkASSERT(vertexCount > 0); unsigned extra = 0; if (vertexCount <= kVCount_DrawVerticesMask) { extra |= vertexCount; } extra |= (unsigned)vmode << kVMode_DrawVerticesShift; #ifdef SK_SUPPORT_LEGACY_XFERMODE_PARAM SkBlendMode bmode = xmode ? xmode->blend() : SkBlendMode::kModulate; #else SkBlendMode bmode = xmode; #endif extra |= (unsigned)bmode << kXMode_DrawVerticesShift; if (texs) { extra |= kHasTex_DrawVerticesMask; } if (colors) { extra |= kHasColors_DrawVerticesMask; } if (indexCount > 0) { extra |= kHasIndices_DrawVerticesMask; } SkPipeWriter writer(this); writer.write32(pack_verb(SkPipeVerb::kDrawVertices, extra)); if (vertexCount > kVCount_DrawVerticesMask) { writer.write32(vertexCount); } writer.write(vertices, vertexCount * sizeof(SkPoint)); if (texs) { writer.write(texs, vertexCount * sizeof(SkPoint)); } if (colors) { writer.write(colors, vertexCount * sizeof(SkColor)); } if (indexCount > 0) { writer.write32(indexCount); SkASSERT(SkIsAlign2(indexCount)); writer.write(indices, indexCount * sizeof(uint16_t)); } write_paint(writer, paint, kVertices_PaintUsage); }
SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase) : fPhase(0) , fInitialDashLength(-1) , fInitialDashIndex(0) , fIntervalLength(0) { SkASSERT(intervals); SkASSERT(count > 1 && SkIsAlign2(count)); fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); fCount = count; for (int i = 0; i < count; i++) { fIntervals[i] = intervals[i]; } // set the internal data members SkDashPath::CalcDashParameters(phase, fIntervals, fCount, &fInitialDashLength, &fInitialDashIndex, &fIntervalLength, &fPhase); }
static void check(skiatest::Reporter* r, const char path[], SkISize size, bool supportsScanlineDecoding, bool supportsSubsetDecoding, bool supportsIncomplete = true) { SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkCodec> codec(nullptr); bool isIncomplete = supportsIncomplete; if (isIncomplete) { size_t size = stream->getLength(); SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3))); codec.reset(SkCodec::NewFromData(data)); } else { codec.reset(SkCodec::NewFromStream(stream.detach())); } if (!codec) { ERRORF(r, "Unable to decode '%s'", path); return; } // Test full image decodes with SkCodec SkMD5::Digest codecDigest; SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); SkBitmap bm; SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess; test_codec(r, codec.get(), bm, info, size, expectedResult, &codecDigest, nullptr); // Scanline decoding follows. // Need to call startScanlineDecode() first. REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0); REPORTER_ASSERT(r, codec->skipScanlines(1) == 0); const SkCodec::Result startResult = codec->startScanlineDecode(info); if (supportsScanlineDecoding) { bm.eraseColor(SK_ColorYELLOW); REPORTER_ASSERT(r, startResult == SkCodec::kSuccess); for (int y = 0; y < info.height(); y++) { const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); if (!isIncomplete) { REPORTER_ASSERT(r, 1 == lines); } } // verify that scanline decoding gives the same result. if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) { compare_to_good_digest(r, codecDigest, bm); } // Cannot continue to decode scanlines beyond the end REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0); // Interrupting a scanline decode with a full decode starts from // scratch REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess); const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0); if (!isIncomplete) { REPORTER_ASSERT(r, lines == 1); } REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()) == expectedResult); REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0); REPORTER_ASSERT(r, codec->skipScanlines(1) == 0); // Test partial scanline decodes if (supports_scaled_codec(path) && info.width() >= 3) { SkCodec::Options options; int width = info.width(); int height = info.height(); SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height); options.fSubset = ⊂ const SkCodec::Result partialStartResult = codec->startScanlineDecode(info, &options, nullptr, nullptr); REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess); for (int y = 0; y < height; y++) { const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); if (!isIncomplete) { REPORTER_ASSERT(r, 1 == lines); } } } } else { REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented); } // The rest of this function tests decoding subsets, and will decode an arbitrary number of // random subsets. // Do not attempt to decode subsets of an image of only once pixel, since there is no // meaningful subset. if (size.width() * size.height() == 1) { return; } SkRandom rand; SkIRect subset; SkCodec::Options opts; opts.fSubset = ⊂ for (int i = 0; i < 5; i++) { subset = generate_random_subset(&rand, size.width(), size.height()); SkASSERT(!subset.isEmpty()); const bool supported = codec->getValidSubset(&subset); REPORTER_ASSERT(r, supported == supportsSubsetDecoding); SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height()); SkBitmap bm; bm.allocPixels(subsetInfo); const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts, nullptr, nullptr); if (supportsSubsetDecoding) { REPORTER_ASSERT(r, result == expectedResult); // Webp is the only codec that supports subsets, and it will have modified the subset // to have even left/top. REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); } else { // No subsets will work. REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); } } // SkScaledCodec tests if ((supportsScanlineDecoding || supportsSubsetDecoding) && supports_scaled_codec(path)) { SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkAndroidCodec> androidCodec(nullptr); if (isIncomplete) { size_t size = stream->getLength(); SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3))); androidCodec.reset(SkAndroidCodec::NewFromData(data)); } else { androidCodec.reset(SkAndroidCodec::NewFromStream(stream.detach())); } if (!androidCodec) { ERRORF(r, "Unable to decode '%s'", path); return; } SkBitmap bm; SkMD5::Digest scaledCodecDigest; test_codec(r, androidCodec.get(), bm, info, size, expectedResult, &scaledCodecDigest, &codecDigest); } // Test SkCodecImageGenerator if (!isIncomplete) { SkAutoTDelete<SkStream> stream(resource(path)); SkAutoTUnref<SkData> fullData(SkData::NewFromStream(stream, stream->getLength())); SkAutoTDelete<SkImageGenerator> gen(SkCodecImageGenerator::NewFromEncodedCodec(fullData)); SkBitmap bm; bm.allocPixels(info); SkAutoLockPixels autoLockPixels(bm); REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes())); compare_to_good_digest(r, codecDigest, bm); } // If we've just tested incomplete decodes, let's run the same test again on full decodes. if (isIncomplete) { check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, false); } }
/* * Performs the bitmap decoding for RLE input format * RLE decoding is performed all at once, rather than a one row at a time */ SkCodec::Result SkBmpRLECodec::decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) { // Set RLE flags static const uint8_t RLE_ESCAPE = 0; static const uint8_t RLE_EOL = 0; static const uint8_t RLE_EOF = 1; static const uint8_t RLE_DELTA = 2; // Set constant values const int width = dstInfo.width(); const int height = dstInfo.height(); // Destination parameters int x = 0; int y = 0; // Set the background as transparent. Then, if the RLE code skips pixels, // the skipped pixels will be transparent. // Because of the need for transparent pixels, kN32 is the only color // type that makes sense for the destination format. SkASSERT(kN32_SkColorType == dstInfo.colorType()); if (kNo_ZeroInitialized == opts.fZeroInitialized) { SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL); } while (true) { // If we have reached a row that is beyond the requested height, we have // succeeded. if (y >= height) { // It would be better to check for the EOF marker before returning // success, but we may be performing a scanline decode, which // may require us to stop before decoding the full height. return kSuccess; } // Every entry takes at least two bytes if ((int) fRLEBytes - fCurrRLEByte < 2) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < 2) { return kIncompleteInput; } } // Read the next two bytes. These bytes have different meanings // depending on their values. In the first interpretation, the first // byte is an escape flag and the second byte indicates what special // task to perform. const uint8_t flag = fStreamBuffer.get()[fCurrRLEByte++]; const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++]; // Perform decoding if (RLE_ESCAPE == flag) { switch (task) { case RLE_EOL: x = 0; y++; break; case RLE_EOF: return kSuccess; case RLE_DELTA: { // Two bytes are needed to specify delta if ((int) fRLEBytes - fCurrRLEByte < 2) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < 2) { return kIncompleteInput; } } // Modify x and y const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++]; const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++]; x += dx; y += dy; if (x > width || y > height) { SkCodecPrintf("Warning: invalid RLE input.\n"); return kInvalidInput; } break; } default: { // If task does not match any of the above signals, it // indicates that we have a sequence of non-RLE pixels. // Furthermore, the value of task is equal to the number // of pixels to interpret. uint8_t numPixels = task; const size_t rowBytes = compute_row_bytes(numPixels, this->bitsPerPixel()); // Abort if setting numPixels moves us off the edge of the // image. if (x + numPixels > width) { SkCodecPrintf("Warning: invalid RLE input.\n"); return kInvalidInput; } // Also abort if there are not enough bytes // remaining in the stream to set numPixels. if ((int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < SkAlign2(rowBytes)) { return kIncompleteInput; } } // Set numPixels number of pixels while (numPixels > 0) { switch(this->bitsPerPixel()) { case 4: { SkASSERT(fCurrRLEByte < fRLEBytes); uint8_t val = fStreamBuffer.get()[fCurrRLEByte++]; setPixel(dst, dstRowBytes, dstInfo, x++, y, val >> 4); numPixels--; if (numPixels != 0) { setPixel(dst, dstRowBytes, dstInfo, x++, y, val & 0xF); numPixels--; } break; } case 8: SkASSERT(fCurrRLEByte < fRLEBytes); setPixel(dst, dstRowBytes, dstInfo, x++, y, fStreamBuffer.get()[fCurrRLEByte++]); numPixels--; break; case 24: { SkASSERT(fCurrRLEByte + 2 < fRLEBytes); uint8_t blue = fStreamBuffer.get()[fCurrRLEByte++]; uint8_t green = fStreamBuffer.get()[fCurrRLEByte++]; uint8_t red = fStreamBuffer.get()[fCurrRLEByte++]; setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue); numPixels--; } default: SkASSERT(false); return kInvalidInput; } } // Skip a byte if necessary to maintain alignment if (!SkIsAlign2(rowBytes)) { fCurrRLEByte++; } break; } } } else { // If the first byte read is not a flag, it indicates the number of // pixels to set in RLE mode. const uint8_t numPixels = flag; const int endX = SkTMin<int>(x + numPixels, width); if (24 == this->bitsPerPixel()) { // In RLE24, the second byte read is part of the pixel color. // There are two more required bytes to finish encoding the // color. if ((int) fRLEBytes - fCurrRLEByte < 2) { SkCodecPrintf("Warning: might be incomplete RLE input.\n"); if (this->checkForMoreData() < 2) { return kIncompleteInput; } } // Fill the pixels up to endX with the specified color uint8_t blue = task; uint8_t green = fStreamBuffer.get()[fCurrRLEByte++]; uint8_t red = fStreamBuffer.get()[fCurrRLEByte++]; while (x < endX) { setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red, green, blue); } } else { // In RLE8 or RLE4, the second byte read gives the index in the // color table to look up the pixel color. // RLE8 has one color index that gets repeated // RLE4 has two color indexes in the upper and lower 4 bits of // the bytes, which are alternated uint8_t indices[2] = { task, task }; if (4 == this->bitsPerPixel()) { indices[0] >>= 4; indices[1] &= 0xf; } // Set the indicated number of pixels for (int which = 0; x < endX; x++) { setPixel(dst, dstRowBytes, dstInfo, x, y, indices[which]); which = !which; } } }
static void check(skiatest::Reporter* r, const char path[], SkISize size, bool supportsScanlineDecoding, bool supportsSubsetDecoding, bool supports565 = true) { SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); if (!codec) { ERRORF(r, "Unable to decode '%s'", path); return; } // This test is used primarily to verify rewinding works properly. Using kN32 allows // us to test this without the added overhead of creating different bitmaps depending // on the color type (ex: building a color table for kIndex8). DM is where we test // decodes to all possible destination color types. SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); REPORTER_ASSERT(r, info.dimensions() == size); { // Test decoding to 565 SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType); SkCodec::Result expected = (supports565 && info.alphaType() == kOpaque_SkAlphaType) ? SkCodec::kSuccess : SkCodec::kInvalidConversion; test_info(r, codec, info565, expected, NULL); } SkBitmap bm; bm.allocPixels(info); SkAutoLockPixels autoLockPixels(bm); SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), NULL, NULL, NULL); REPORTER_ASSERT(r, result == SkCodec::kSuccess); SkMD5::Digest digest; md5(bm, &digest); // verify that re-decoding gives the same result. test_info(r, codec, info, SkCodec::kSuccess, &digest); { // Check alpha type conversions if (info.alphaType() == kOpaque_SkAlphaType) { test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType), SkCodec::kInvalidConversion, NULL); test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType), SkCodec::kInvalidConversion, NULL); } else { // Decoding to opaque should fail test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType), SkCodec::kInvalidConversion, NULL); SkAlphaType otherAt = info.alphaType(); if (kPremul_SkAlphaType == otherAt) { otherAt = kUnpremul_SkAlphaType; } else { otherAt = kPremul_SkAlphaType; } // The other non-opaque alpha type should always succeed, but not match. test_info(r, codec, info.makeAlphaType(otherAt), SkCodec::kSuccess, NULL); } } // Scanline decoding follows. stream.reset(resource(path)); SkAutoTDelete<SkScanlineDecoder> scanlineDecoder( SkScanlineDecoder::NewFromStream(stream.detach())); if (supportsScanlineDecoding) { bm.eraseColor(SK_ColorYELLOW); REPORTER_ASSERT(r, scanlineDecoder); REPORTER_ASSERT(r, scanlineDecoder->start(info) == SkCodec::kSuccess); for (int y = 0; y < info.height(); y++) { result = scanlineDecoder->getScanlines(bm.getAddr(0, y), 1, 0); REPORTER_ASSERT(r, result == SkCodec::kSuccess); } // verify that scanline decoding gives the same result. compare_to_good_digest(r, digest, bm); } else { REPORTER_ASSERT(r, !scanlineDecoder); } // The rest of this function tests decoding subsets, and will decode an arbitrary number of // random subsets. // Do not attempt to decode subsets of an image of only once pixel, since there is no // meaningful subset. if (size.width() * size.height() == 1) { return; } SkRandom rand; SkIRect subset; SkCodec::Options opts; opts.fSubset = ⊂ for (int i = 0; i < 5; i++) { subset = generate_random_subset(&rand, size.width(), size.height()); SkASSERT(!subset.isEmpty()); const bool supported = codec->getValidSubset(&subset); REPORTER_ASSERT(r, supported == supportsSubsetDecoding); SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height()); SkBitmap bm; bm.allocPixels(subsetInfo); const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts, NULL, NULL); if (supportsSubsetDecoding) { REPORTER_ASSERT(r, result == SkCodec::kSuccess); // Webp is the only codec that supports subsets, and it will have modified the subset // to have even left/top. REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); } else { // No subsets will work. REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); } } }