DEF_TEST(TextBlob_extended, reporter) { SkTextBlobBuilder textBlobBuilder; SkFont font; const char text1[] = "Foo"; const char text2[] = "Bar"; int glyphCount = font.countText(text1, strlen(text1), SkTextEncoding::kUTF8); SkAutoTMalloc<uint16_t> glyphs(glyphCount); (void)font.textToGlyphs(text1, strlen(text1), SkTextEncoding::kUTF8, glyphs.get(), glyphCount); auto run = SkTextBlobBuilderPriv::AllocRunText(&textBlobBuilder, font, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr); memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount); memcpy(run.utf8text, text2, strlen(text2)); for (int i = 0; i < glyphCount; ++i) { run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2))); } sk_sp<SkTextBlob> blob(textBlobBuilder.make()); REPORTER_ASSERT(reporter, blob); for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount); for (uint32_t i = 0; i < it.glyphCount(); ++i) { REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]); } REPORTER_ASSERT(reporter, SkTextBlobRunIterator::kDefault_Positioning == it.positioning()); REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset()); REPORTER_ASSERT(reporter, it.textSize() > 0); REPORTER_ASSERT(reporter, it.clusters()); for (uint32_t i = 0; i < it.glyphCount(); ++i) { REPORTER_ASSERT(reporter, i == it.clusters()[i]); } REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize())); } }
void GrVkPipelineState::BuildStateKey(const GrPipeline& pipeline, GrPrimitiveType primitiveType, SkTArray<uint8_t, true>* key) { // Save room for the key length and key header key->reset(); key->push_back_n(kData_StateKeyOffset); GrProcessorKeyBuilder b(key); GrVkRenderTarget* vkRT = (GrVkRenderTarget*)pipeline.getRenderTarget(); vkRT->simpleRenderPass()->genKey(&b); pipeline.getStencil().genKey(&b); SkASSERT(sizeof(GrPipelineBuilder::DrawFace) <= sizeof(uint32_t)); b.add32(pipeline.getDrawFace()); b.add32(get_blend_info_key(pipeline)); b.add32(primitiveType); // Set key length int keyLength = key->count(); SkASSERT(0 == (keyLength % 4)); *reinterpret_cast<uint32_t*>(key->begin()) = SkToU32(keyLength); }
static void read_string(SkStream* stream, SkString* string) { const uint32_t length = SkToU32(stream->readPackedUInt()); if (length > 0) { string->resize(length); stream->read(string->writable_str(), length); } }
int SkPipeDeduper::findOrDefineFactory(SkFlattenable* flattenable) { if (!flattenable) { return 0; } int index = fFactories.find(flattenable->getFactory()); SkASSERT(index >= 0); if (index) { if (show_deduper_traffic) { SkDebugf(" reuseFactory(%d)\n", index - 1); } return index; } index = fFactories.add(flattenable->getFactory()); ASSERT_FITS_IN(index, kIndex_DefineFactoryExtraBits); const char* name = flattenable->getTypeName(); size_t len = strlen(name); ASSERT_FITS_IN(len, kNameLength_DefineFactoryExtraBits); unsigned extra = (index << kNameLength_DefineFactoryExtraBits) | len; size_t prevWritten = fStream->bytesWritten(); fStream->write32(pack_verb(SkPipeVerb::kDefineFactory, extra)); write_pad(fStream, name, len + 1); if (false) { SkDebugf(" defineFactory(%d) %d %s\n", index - 1, SkToU32(fStream->bytesWritten() - prevWritten), name); } return index; }
static void write_encoded_bitmap(SkWriteBuffer* buffer, SkData* data, const SkIPoint& origin) { buffer->writeUInt(SkToU32(data->size())); buffer->getWriter32()->writePad(data->data(), data->size()); buffer->write32(origin.fX); buffer->write32(origin.fY); }
void SkPipeCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], const SkRect* cull, const SkPaint& paint) { SkASSERT(byteLength); bool compact = fits_in(byteLength, 23); unsigned extra = compact ? (byteLength << 1) : 0; if (cull) { extra |= 1; } SkPipeWriter writer(this); writer.write32(pack_verb(SkPipeVerb::kDrawTextRSXform, extra)); if (!compact) { writer.write32(SkToU32(byteLength)); } write_pad(&writer, text, byteLength); int count = paint.countText(text, byteLength); writer.write32(count); // maybe we can/should store this in extra as well? writer.write(xform, count * sizeof(SkRSXform)); if (cull) { writer.writeRect(*cull); } write_paint(writer, paint, kText_PaintUsage); }
// Asserts that asset == expected and is peekable. static void stream_peek_test(skiatest::Reporter* rep, SkStreamAsset* asset, const SkData* expected) { if (asset->getLength() != expected->size()) { ERRORF(rep, "Unexpected length."); return; } SkRandom rand; uint8_t buffer[4096]; const uint8_t* expect = expected->bytes(); for (size_t i = 0; i < asset->getLength(); ++i) { uint32_t maxSize = SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i)); size_t size = rand.nextRangeU(1, maxSize); SkASSERT(size >= 1); SkASSERT(size <= sizeof(buffer)); SkASSERT(size + i <= asset->getLength()); if (asset->peek(buffer, size) < size) { ERRORF(rep, "Peek Failed!"); return; } if (0 != memcmp(buffer, &expect[i], size)) { ERRORF(rep, "Peek returned wrong bytes!"); return; } uint8_t value; REPORTER_ASSERT(rep, 1 == asset->read(&value, 1)); if (value != expect[i]) { ERRORF(rep, "Read Failed!"); return; } } }
int SkPipeDeduper::findOrDefinePicture(SkPicture* picture) { int index = fPictures.find(picture->uniqueID()); SkASSERT(index >= 0); if (index) { if (show_deduper_traffic) { SkDebugf(" reusePicture(%d)\n", index - 1); } return index; } size_t prevWritten = fStream->bytesWritten(); unsigned extra = 0; // 0 means we're defining a new picture, non-zero means undef_index + 1 fStream->write32(pack_verb(SkPipeVerb::kDefinePicture, extra)); const SkRect cull = picture->cullRect(); fStream->write(&cull, sizeof(cull)); picture->playback(fPipeCanvas); // call fPictures.add *after* we're written the picture, so that any nested pictures will have // already been defined, and we get the "last" index value. index = fPictures.add(picture->uniqueID()); ASSERT_FITS_IN(index, kObjectDefinitionBits); fStream->write32(pack_verb(SkPipeVerb::kEndPicture, index)); SkDebugf(" definePicture(%d) %d\n", index - 1, SkToU32(fStream->bytesWritten() - prevWritten)); return index; }
int SkPipeDeduper::findOrDefineImage(SkImage* image) { int index = fImages.find(image->uniqueID()); SkASSERT(index >= 0); if (index) { if (show_deduper_traffic) { SkDebugf(" reuseImage(%d)\n", index - 1); } return index; } sk_sp<SkData> data = fIMSerializer ? fIMSerializer->serialize(image) : default_image_serializer(image); if (data) { index = fImages.add(image->uniqueID()); SkASSERT(index > 0); SkASSERT(fits_in(index, 24)); fStream->write32(pack_verb(SkPipeVerb::kDefineImage, index)); uint32_t len = SkToU32(data->size()); fStream->write32(SkAlign4(len)); write_pad(fStream, data->data(), len); if (show_deduper_traffic) { int size = image->width() * image->height() << 2; SkDebugf(" defineImage(%d) %d -> %d\n", index - 1, size, len); } return index; } SkDebugf("+++ failed to encode image [%d %d]\n", image->width(), image->height()); return 0; // failed to encode }
int SkPipeDeduper::findOrDefineTypeface(SkTypeface* typeface) { if (!typeface) { return 0; // default } int index = fTypefaces.find(typeface->uniqueID()); SkASSERT(index >= 0); if (index) { if (show_deduper_traffic) { SkDebugf(" reuseTypeface(%d)\n", index - 1); } return index; } sk_sp<SkData> data = fTFSerializer ? fTFSerializer->serialize(typeface) : encode(typeface); if (data) { index = fTypefaces.add(typeface->uniqueID()); SkASSERT(index > 0); SkASSERT(fits_in(index, 24)); fStream->write32(pack_verb(SkPipeVerb::kDefineTypeface, index)); uint32_t len = SkToU32(data->size()); fStream->write32(SkAlign4(len)); write_pad(fStream, data->data(), len); if (show_deduper_traffic) { SkDebugf(" defineTypeface(%d) %d\n", index - 1, len); } return index; } SkDebugf("+++ failed to encode typeface %d\n", typeface->uniqueID()); return 0; // failed to encode }
size_t SkPictureRecord::recordRestoreOffsetPlaceholder(SkClipOp op) { if (fRestoreOffsetStack.isEmpty()) { return -1; } // The RestoreOffset field is initially filled with a placeholder // value that points to the offset of the previous RestoreOffset // in the current stack level, thus forming a linked list so that // the restore offsets can be filled in when the corresponding // restore command is recorded. int32_t prevOffset = fRestoreOffsetStack.top(); if (clipOpExpands(op)) { // Run back through any previous clip ops, and mark their offset to // be 0, disabling their ability to trigger a jump-to-restore, otherwise // they could hide this clips ability to expand the clip (i.e. go from // empty to non-empty). this->fillRestoreOffsetPlaceholdersForCurrentStackLevel(0); // Reset the pointer back to the previous clip so that subsequent // restores don't overwrite the offsets we just cleared. prevOffset = 0; } size_t offset = fWriter.bytesWritten(); this->addInt(prevOffset); fRestoreOffsetStack.top() = SkToU32(offset); return offset; }
void SkPipeCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { SkPipeWriter writer(this); writer.write32(pack_verb(SkPipeVerb::kDrawPoints, mode)); writer.write32(SkToU32(count)); writer.write(pts, count * sizeof(SkPoint)); write_paint(writer, paint, kGeometry_PaintUsage | kRespectsStroke_PaintUsage); }
size_t SkBinaryWriteBuffer::writeStream(SkStream* stream, size_t length) { fWriter.write32(SkToU32(length)); size_t bytesWritten = fWriter.readFromStream(stream, length); if (bytesWritten < length) { fWriter.reservePad(length - bytesWritten); } return bytesWritten; }
void SkPictureStateTree::appendNode(size_t offset) { Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node))); n->fOffset = SkToU32(offset); n->fFlags = 0; n->fParent = fCurrentState.fNode; n->fLevel = fCurrentState.fNode->fLevel + 1; n->fMatrix = fCurrentState.fMatrix; fCurrentState.fNode = n; }
/*static*/ bool SkBitmapHasher::ComputeDigestInternal(const SkBitmap& bitmap, uint64_t *result) { SkMD5 out; // start with the x/y dimensions write_int32_to_buffer(SkToU32(bitmap.width()), &out); write_int32_to_buffer(SkToU32(bitmap.height()), &out); // add all the pixel data SkAutoTDelete<SkImageEncoder> enc(CreateARGBImageEncoder()); if (!enc->encodeStream(&out, bitmap, SkImageEncoder::kDefaultQuality)) { return false; } SkMD5::Digest digest; out.finish(digest); *result = first_8_bytes_as_uint64(digest.data); return true; }
void GrGLProgramDesc::finalize() { int keyLength = fKey.count(); SkASSERT(0 == (keyLength % 4)); *this->atOffset<uint32_t, kLengthOffset>() = SkToU32(keyLength); uint32_t* checksum = this->atOffset<uint32_t, kChecksumOffset>(); *checksum = 0; *checksum = SkChecksum::Compute(reinterpret_cast<uint32_t*>(fKey.begin()), keyLength); }
void SkPipeCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* data) { const size_t len = strlen(key) + 1; // must write the trailing 0 bool compact = fits_in(len, 23); uint32_t extra = compact ? (unsigned)len : 0; extra <<= 1; // make room for has_data_sentinel if (data) { extra |= 1; } fStream->write32(pack_verb(SkPipeVerb::kDrawAnnotation, extra)); fStream->write(&rect, sizeof(SkRect)); if (!compact) { fStream->write32(SkToU32(len)); } write_pad(fStream, key, len); if (data) { fStream->write32(SkToU32(data->size())); write_pad(fStream, data->data(), data->size()); } }
void SkGPipeCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { if (count) { NOTIFY_SETUP(this); this->writePaint(paint); if (this->needOpBytes(4 + count * sizeof(SkPoint))) { this->writeOp(kDrawPoints_DrawOp, mode, 0); fWriter.write32(SkToU32(count)); fWriter.write(pts, count * sizeof(SkPoint)); } } }
static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) { SkASSERT(typeface); SkDynamicMemoryWStream stream; typeface->serialize(&stream); size_t size = stream.getOffset(); if (writer) { writer->write32(SkToU32(size)); SkAutoDataUnref data(stream.copyToData()); writer->writePad(data->data(), size); } return 4 + SkAlign4(size); }
/*static*/ bool SkBitmapHasher::ComputeDigestInternal(const SkBitmap& bitmap, SkHashDigest *result) { size_t pixelBufferSize = bitmap.width() * bitmap.height() * 4; size_t totalBufferSize = pixelBufferSize + 2 * sizeof(uint32_t); SkAutoMalloc bufferManager(totalBufferSize); char *bufferStart = static_cast<char *>(bufferManager.get()); SkMemoryWStream out(bufferStart, totalBufferSize); // start with the x/y dimensions write_int_to_buffer(SkToU32(bitmap.width()), &out); write_int_to_buffer(SkToU32(bitmap.height()), &out); // add all the pixel data SkAutoTDelete<SkImageEncoder> enc(CreateARGBImageEncoder()); if (!enc->encodeStream(&out, bitmap, SkImageEncoder::kDefaultQuality)) { return false; } *result = SkCityHash::Compute64(bufferStart, totalBufferSize); return true; }
void SkGPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { if (byteLength) { NOTIFY_SETUP(this); this->writePaint(paint); if (this->needOpBytes(4 + SkAlign4(byteLength) + 2 * sizeof(SkScalar))) { this->writeOp(kDrawText_DrawOp); fWriter.write32(SkToU32(byteLength)); fWriter.writePad(text, byteLength); fWriter.writeScalar(x); fWriter.writeScalar(y); } } }
void SkMallocPixelRef::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); buffer.write32(SkToU32(fRB)); // TODO: replace this bulk write with a chunky one that can trim off any // trailing bytes on each scanline (in case rowbytes > width*size) size_t size = this->info().getSafeSize(fRB); buffer.writeByteArray(fStorage, size); buffer.writeBool(fCTable != NULL); if (fCTable) { fCTable->writeToBuffer(buffer); } }
void SkPipeCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) { SkASSERT(byteLength); bool compact = fits_in(byteLength, 24); SkPipeWriter writer(this); writer.write32(pack_verb(SkPipeVerb::kDrawPosText, compact ? (unsigned)byteLength : 0)); if (!compact) { writer.write32(SkToU32(byteLength)); } write_pad(&writer, text, byteLength); writer.writePointArray(pos, paint.countText(text, byteLength)); write_paint(writer, paint, kText_PaintUsage); }
void SkGPipeCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], const SkPaint& paint) { if (byteLength) { NOTIFY_SETUP(this); this->writePaint(paint); int count = paint.textToGlyphs(text, byteLength, nullptr); if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkPoint))) { this->writeOp(kDrawPosText_DrawOp); fWriter.write32(SkToU32(byteLength)); fWriter.writePad(text, byteLength); fWriter.write32(count); fWriter.write(pos, count * sizeof(SkPoint)); } } }
void SkGPipeCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, const SkPaint& paint) { if (byteLength) { NOTIFY_SETUP(this); this->writePaint(paint); int count = paint.textToGlyphs(text, byteLength, NULL); if (this->needOpBytes(4 + SkAlign4(byteLength) + 4 + count * sizeof(SkScalar) + 4)) { this->writeOp(kDrawPosTextH_DrawOp); fWriter.write32(SkToU32(byteLength)); fWriter.writePad(text, byteLength); fWriter.write32(count); fWriter.write(xpos, count * sizeof(SkScalar)); fWriter.writeScalar(constY); } } }
void SkPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) { SkASSERT(byteLength); bool compact = fits_in(byteLength, 24); SkPipeWriter writer(this); writer.write32(pack_verb(SkPipeVerb::kDrawText, compact ? (unsigned)byteLength : 0)); if (!compact) { writer.write32(SkToU32(byteLength)); } write_pad(&writer, text, byteLength); writer.writeScalar(x); writer.writeScalar(y); write_paint(writer, paint, kText_PaintUsage); }
void SkWriter32::writeString(const char str[], size_t len) { if (NULL == str) { str = ""; len = 0; } if ((long)len < 0) { len = strlen(str); } // [ 4 byte len ] [ str ... ] [1 - 4 \0s] uint32_t* ptr = this->reservePad(sizeof(uint32_t) + len + 1); *ptr = SkToU32(len); char* chars = (char*)(ptr + 1); memcpy(chars, str, len); chars[len] = '\0'; }
bool SkWStream::writePackedUInt(size_t value) { uint8_t data[5]; size_t len = 1; if (value <= SK_MAX_BYTE_FOR_U8) { data[0] = value; len = 1; } else if (value <= 0xFFFF) { uint16_t value16 = value; data[0] = SK_BYTE_SENTINEL_FOR_U16; memcpy(&data[1], &value16, 2); len = 3; } else { uint32_t value32 = SkToU32(value); data[0] = SK_BYTE_SENTINEL_FOR_U32; memcpy(&data[1], &value32, 4); len = 5; } return this->write(data, len); }
/** * A function which emits a meta key into the key builder. This is required because shader code may * be dependent on properties of the effect that the effect itself doesn't use * in its key (e.g. the pixel format of textures used). So we create a meta-key for * every effect using this function. It is also responsible for inserting the effect's class ID * which must be different for every GrProcessor subclass. It can fail if an effect uses too many * transforms, etc, for the space allotted in the meta-key. NOTE, both FPs and GPs share this * function because it is hairy, though FPs do not have attribs, and GPs do not have transforms */ static bool gen_meta_key(const GrProcessor& proc, const GrGLSLCaps& glslCaps, uint32_t transformKey, GrProcessorKeyBuilder* b) { size_t processorKeySize = b->size(); uint32_t classID = proc.classID(); // Currently we allow 16 bits for the class id and the overall processor key size. static const uint32_t kMetaKeyInvalidMask = ~((uint32_t)SK_MaxU16); if ((processorKeySize | classID) & kMetaKeyInvalidMask) { return false; } add_texture_key(b, proc, glslCaps); uint32_t* key = b->add32n(2); key[0] = (classID << 16) | SkToU32(processorKeySize); key[1] = transformKey; return true; }
SkPath ValueTraits<ShapeValue>::As<SkPath>(const ShapeValue& shape) { SkPath path; if (!shape.fVertices.empty()) { // conservatively assume all cubics path.incReserve(1 + SkToU32(shape.fVertices.size() * 3)); path.moveTo(shape.fVertices.front().fVertex); } const auto& addCubic = [&](size_t from, size_t to) { const auto c0 = shape.fVertices[from].fVertex + shape.fVertices[from].fOutPoint, c1 = shape.fVertices[to].fVertex + shape.fVertices[to].fInPoint; if (c0 == shape.fVertices[from].fVertex && c1 == shape.fVertices[to].fVertex) { // If the control points are coincident, we can power-reduce to a straight line. // TODO: we could also do that when the controls are on the same line as the // vertices, but it's unclear how common that case is. path.lineTo(shape.fVertices[to].fVertex); } else { path.cubicTo(c0, c1, shape.fVertices[to].fVertex); } }; for (size_t i = 1; i < shape.fVertices.size(); ++i) { addCubic(i - 1, i); } if (!shape.fVertices.empty() && shape.fClosed) { addCubic(shape.fVertices.size() - 1, 0); path.close(); } path.setIsVolatile(shape.fVolatile); path.shrinkToFit(); return path; }