| 1 | /* |
| 2 | * Copyright 2018 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/core/SkData.h" |
| 9 | #include "include/core/SkMath.h" |
| 10 | #include "include/private/SkPathRef.h" |
| 11 | #include "include/private/SkTo.h" |
| 12 | #include "src/core/SkBuffer.h" |
| 13 | #include "src/core/SkPathPriv.h" |
| 14 | #include "src/core/SkRRectPriv.h" |
| 15 | #include "src/core/SkSafeMath.h" |
| 16 | |
| 17 | #include <cmath> |
| 18 | |
| 19 | enum SerializationOffsets { |
| 20 | kType_SerializationShift = 28, // requires 4 bits |
| 21 | kDirection_SerializationShift = 26, // requires 2 bits |
| 22 | kFillType_SerializationShift = 8, // requires 8 bits |
| 23 | // low-8-bits are version |
| 24 | kVersion_SerializationMask = 0xFF, |
| 25 | }; |
| 26 | |
| 27 | enum SerializationVersions { |
| 28 | // kPathPrivFirstDirection_Version = 1, |
| 29 | // kPathPrivLastMoveToIndex_Version = 2, |
| 30 | // kPathPrivTypeEnumVersion = 3, |
| 31 | kJustPublicData_Version = 4, // introduced Feb/2018 |
| 32 | kVerbsAreStoredForward_Version = 5, // introduced Sept/2019 |
| 33 | |
| 34 | kMin_Version = kJustPublicData_Version, |
| 35 | kCurrent_Version = kVerbsAreStoredForward_Version |
| 36 | }; |
| 37 | |
| 38 | enum SerializationType { |
| 39 | kGeneral = 0, |
| 40 | kRRect = 1 |
| 41 | }; |
| 42 | |
| 43 | static unsigned (uint32_t packed) { |
| 44 | return packed & kVersion_SerializationMask; |
| 45 | } |
| 46 | |
| 47 | static SkPathFillType (uint32_t packed) { |
| 48 | return static_cast<SkPathFillType>((packed >> kFillType_SerializationShift) & 0x3); |
| 49 | } |
| 50 | |
| 51 | static SerializationType (uint32_t packed) { |
| 52 | return static_cast<SerializationType>((packed >> kType_SerializationShift) & 0xF); |
| 53 | } |
| 54 | |
| 55 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
| 56 | |
| 57 | size_t SkPath::writeToMemoryAsRRect(void* storage) const { |
| 58 | SkRect oval; |
| 59 | SkRRect rrect; |
| 60 | bool isCCW; |
| 61 | unsigned start; |
| 62 | if (fPathRef->isOval(&oval, &isCCW, &start)) { |
| 63 | rrect.setOval(oval); |
| 64 | // Convert to rrect start indices. |
| 65 | start *= 2; |
| 66 | } else if (!fPathRef->isRRect(&rrect, &isCCW, &start)) { |
| 67 | return 0; |
| 68 | } |
| 69 | |
| 70 | // packed header, rrect, start index. |
| 71 | const size_t sizeNeeded = sizeof(int32_t) + SkRRect::kSizeInMemory + sizeof(int32_t); |
| 72 | if (!storage) { |
| 73 | return sizeNeeded; |
| 74 | } |
| 75 | |
| 76 | int firstDir = isCCW ? SkPathPriv::kCCW_FirstDirection : SkPathPriv::kCW_FirstDirection; |
| 77 | int32_t packed = (fFillType << kFillType_SerializationShift) | |
| 78 | (firstDir << kDirection_SerializationShift) | |
| 79 | (SerializationType::kRRect << kType_SerializationShift) | |
| 80 | kCurrent_Version; |
| 81 | |
| 82 | SkWBuffer buffer(storage); |
| 83 | buffer.write32(packed); |
| 84 | SkRRectPriv::WriteToBuffer(rrect, &buffer); |
| 85 | buffer.write32(SkToS32(start)); |
| 86 | buffer.padToAlign4(); |
| 87 | SkASSERT(sizeNeeded == buffer.pos()); |
| 88 | return buffer.pos(); |
| 89 | } |
| 90 | |
| 91 | size_t SkPath::writeToMemory(void* storage) const { |
| 92 | SkDEBUGCODE(this->validate();) |
| 93 | |
| 94 | if (size_t bytes = this->writeToMemoryAsRRect(storage)) { |
| 95 | return bytes; |
| 96 | } |
| 97 | |
| 98 | int32_t packed = (fFillType << kFillType_SerializationShift) | |
| 99 | (SerializationType::kGeneral << kType_SerializationShift) | |
| 100 | kCurrent_Version; |
| 101 | |
| 102 | int32_t pts = fPathRef->countPoints(); |
| 103 | int32_t cnx = fPathRef->countWeights(); |
| 104 | int32_t vbs = fPathRef->countVerbs(); |
| 105 | |
| 106 | SkSafeMath safe; |
| 107 | size_t size = 4 * sizeof(int32_t); |
| 108 | size = safe.add(size, safe.mul(pts, sizeof(SkPoint))); |
| 109 | size = safe.add(size, safe.mul(cnx, sizeof(SkScalar))); |
| 110 | size = safe.add(size, safe.mul(vbs, sizeof(uint8_t))); |
| 111 | size = safe.alignUp(size, 4); |
| 112 | if (!safe) { |
| 113 | return 0; |
| 114 | } |
| 115 | if (!storage) { |
| 116 | return size; |
| 117 | } |
| 118 | |
| 119 | SkWBuffer buffer(storage); |
| 120 | buffer.write32(packed); |
| 121 | buffer.write32(pts); |
| 122 | buffer.write32(cnx); |
| 123 | buffer.write32(vbs); |
| 124 | buffer.write(fPathRef->points(), pts * sizeof(SkPoint)); |
| 125 | buffer.write(fPathRef->conicWeights(), cnx * sizeof(SkScalar)); |
| 126 | buffer.write(fPathRef->verbsBegin(), vbs * sizeof(uint8_t)); |
| 127 | buffer.padToAlign4(); |
| 128 | |
| 129 | SkASSERT(buffer.pos() == size); |
| 130 | return size; |
| 131 | } |
| 132 | |
| 133 | sk_sp<SkData> SkPath::serialize() const { |
| 134 | size_t size = this->writeToMemory(nullptr); |
| 135 | sk_sp<SkData> data = SkData::MakeUninitialized(size); |
| 136 | this->writeToMemory(data->writable_data()); |
| 137 | return data; |
| 138 | } |
| 139 | |
| 140 | ////////////////////////////////////////////////////////////////////////////////////////////////// |
| 141 | // reading |
| 142 | |
| 143 | size_t SkPath::readFromMemory(const void* storage, size_t length) { |
| 144 | SkRBuffer buffer(storage, length); |
| 145 | uint32_t packed; |
| 146 | if (!buffer.readU32(&packed)) { |
| 147 | return 0; |
| 148 | } |
| 149 | unsigned version = extract_version(packed); |
| 150 | if (version < kMin_Version || version > kCurrent_Version) { |
| 151 | return 0; |
| 152 | } |
| 153 | |
| 154 | if (version == kJustPublicData_Version || version == kVerbsAreStoredForward_Version) { |
| 155 | return this->readFromMemory_EQ4Or5(storage, length); |
| 156 | } |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | size_t SkPath::readAsRRect(const void* storage, size_t length) { |
| 161 | SkRBuffer buffer(storage, length); |
| 162 | uint32_t packed; |
| 163 | if (!buffer.readU32(&packed)) { |
| 164 | return 0; |
| 165 | } |
| 166 | |
| 167 | SkASSERT(extract_serializationtype(packed) == SerializationType::kRRect); |
| 168 | |
| 169 | uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3; |
| 170 | SkPathFillType fillType = extract_filltype(packed); |
| 171 | |
| 172 | SkPathDirection rrectDir; |
| 173 | SkRRect rrect; |
| 174 | int32_t start; |
| 175 | switch (dir) { |
| 176 | case SkPathPriv::kCW_FirstDirection: |
| 177 | rrectDir = SkPathDirection::kCW; |
| 178 | break; |
| 179 | case SkPathPriv::kCCW_FirstDirection: |
| 180 | rrectDir = SkPathDirection::kCCW; |
| 181 | break; |
| 182 | default: |
| 183 | return 0; |
| 184 | } |
| 185 | if (!SkRRectPriv::ReadFromBuffer(&buffer, &rrect)) { |
| 186 | return 0; |
| 187 | } |
| 188 | if (!buffer.readS32(&start) || start != SkTPin(start, 0, 7)) { |
| 189 | return 0; |
| 190 | } |
| 191 | this->reset(); |
| 192 | this->addRRect(rrect, rrectDir, SkToUInt(start)); |
| 193 | this->setFillType(fillType); |
| 194 | buffer.skipToAlign4(); |
| 195 | return buffer.pos(); |
| 196 | } |
| 197 | |
| 198 | size_t SkPath::readFromMemory_EQ4Or5(const void* storage, size_t length) { |
| 199 | SkRBuffer buffer(storage, length); |
| 200 | uint32_t packed; |
| 201 | if (!buffer.readU32(&packed)) { |
| 202 | return 0; |
| 203 | } |
| 204 | |
| 205 | bool verbsAreReversed = true; |
| 206 | if (extract_version(packed) == kVerbsAreStoredForward_Version) { |
| 207 | verbsAreReversed = false; |
| 208 | } |
| 209 | |
| 210 | switch (extract_serializationtype(packed)) { |
| 211 | case SerializationType::kRRect: |
| 212 | return this->readAsRRect(storage, length); |
| 213 | case SerializationType::kGeneral: |
| 214 | break; // fall through |
| 215 | default: |
| 216 | return 0; |
| 217 | } |
| 218 | |
| 219 | int32_t pts, cnx, vbs; |
| 220 | if (!buffer.readS32(&pts) || !buffer.readS32(&cnx) || !buffer.readS32(&vbs)) { |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | const SkPoint* points = buffer.skipCount<SkPoint>(pts); |
| 225 | const SkScalar* conics = buffer.skipCount<SkScalar>(cnx); |
| 226 | const uint8_t* verbs = buffer.skipCount<uint8_t>(vbs); |
| 227 | buffer.skipToAlign4(); |
| 228 | if (!buffer.isValid()) { |
| 229 | return 0; |
| 230 | } |
| 231 | SkASSERT(buffer.pos() <= length); |
| 232 | |
| 233 | #define CHECK_POINTS_CONICS(p, c) \ |
| 234 | do { \ |
| 235 | if (p && ((pts -= p) < 0)) { \ |
| 236 | return 0; \ |
| 237 | } \ |
| 238 | if (c && ((cnx -= c) < 0)) { \ |
| 239 | return 0; \ |
| 240 | } \ |
| 241 | } while (0) |
| 242 | |
| 243 | int verbsStep = 1; |
| 244 | if (verbsAreReversed) { |
| 245 | verbs += vbs - 1; |
| 246 | verbsStep = -1; |
| 247 | } |
| 248 | |
| 249 | SkPath tmp; |
| 250 | tmp.setFillType(extract_filltype(packed)); |
| 251 | tmp.incReserve(pts); |
| 252 | for (int i = 0; i < vbs; ++i) { |
| 253 | switch (*verbs) { |
| 254 | case kMove_Verb: |
| 255 | CHECK_POINTS_CONICS(1, 0); |
| 256 | tmp.moveTo(*points++); |
| 257 | break; |
| 258 | case kLine_Verb: |
| 259 | CHECK_POINTS_CONICS(1, 0); |
| 260 | tmp.lineTo(*points++); |
| 261 | break; |
| 262 | case kQuad_Verb: |
| 263 | CHECK_POINTS_CONICS(2, 0); |
| 264 | tmp.quadTo(points[0], points[1]); |
| 265 | points += 2; |
| 266 | break; |
| 267 | case kConic_Verb: |
| 268 | CHECK_POINTS_CONICS(2, 1); |
| 269 | tmp.conicTo(points[0], points[1], *conics++); |
| 270 | points += 2; |
| 271 | break; |
| 272 | case kCubic_Verb: |
| 273 | CHECK_POINTS_CONICS(3, 0); |
| 274 | tmp.cubicTo(points[0], points[1], points[2]); |
| 275 | points += 3; |
| 276 | break; |
| 277 | case kClose_Verb: |
| 278 | tmp.close(); |
| 279 | break; |
| 280 | default: |
| 281 | return 0; // bad verb |
| 282 | } |
| 283 | verbs += verbsStep; |
| 284 | } |
| 285 | #undef CHECK_POINTS_CONICS |
| 286 | if (pts || cnx) { |
| 287 | return 0; // leftover points and/or conics |
| 288 | } |
| 289 | |
| 290 | *this = std::move(tmp); |
| 291 | return buffer.pos(); |
| 292 | } |
| 293 | |