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 "src/core/SkGlyph.h" 9 10#include "src/core/SkArenaAlloc.h" 11#include "src/core/SkScalerContext.h" 12#include "src/pathops/SkPathOpsCubic.h" 13#include "src/pathops/SkPathOpsQuad.h" 14 15SkMask SkGlyph::mask() const { 16 SkMask mask; 17 mask.fImage = (uint8_t*)fImage; 18 mask.fBounds.setXYWH(fLeft, fTop, fWidth, fHeight); 19 mask.fRowBytes = this->rowBytes(); 20 mask.fFormat = fMaskFormat; 21 return mask; 22} 23 24SkMask SkGlyph::mask(SkPoint position) const { 25 SkMask answer = this->mask(); 26 answer.fBounds.offset(SkScalarFloorToInt(position.x()), SkScalarFloorToInt(position.y())); 27 return answer; 28} 29 30void SkGlyph::zeroMetrics() { 31 fAdvanceX = 0; 32 fAdvanceY = 0; 33 fWidth = 0; 34 fHeight = 0; 35 fTop = 0; 36 fLeft = 0; 37} 38 39static size_t bits_to_bytes(size_t bits) { 40 return (bits + 7) >> 3; 41} 42 43static size_t format_alignment(SkMask::Format format) { 44 switch (format) { 45 case SkMask::kBW_Format: 46 case SkMask::kA8_Format: 47 case SkMask::k3D_Format: 48 case SkMask::kSDF_Format: 49 return alignof(uint8_t); 50 case SkMask::kARGB32_Format: 51 return alignof(uint32_t); 52 case SkMask::kLCD16_Format: 53 return alignof(uint16_t); 54 default: 55 SK_ABORT("Unknown mask format."); 56 break; 57 } 58 return 0; 59} 60 61static size_t format_rowbytes(int width, SkMask::Format format) { 62 return format == SkMask::kBW_Format ? bits_to_bytes(width) 63 : width * format_alignment(format); 64} 65 66size_t SkGlyph::formatAlignment() const { 67 return format_alignment(this->maskFormat()); 68} 69 70size_t SkGlyph::allocImage(SkArenaAlloc* alloc) { 71 SkASSERT(!this->isEmpty()); 72 auto size = this->imageSize(); 73 fImage = alloc->makeBytesAlignedTo(size, this->formatAlignment()); 74 75 return size; 76} 77 78bool SkGlyph::setImage(SkArenaAlloc* alloc, SkScalerContext* scalerContext) { 79 if (!this->setImageHasBeenCalled()) { 80 // It used to be that getImage() could change the fMaskFormat. Extra checking to make 81 // sure there are no regressions. 82 SkDEBUGCODE(SkMask::Format oldFormat = this->maskFormat()); 83 this->allocImage(alloc); 84 scalerContext->getImage(*this); 85 SkASSERT(oldFormat == this->maskFormat()); 86 return true; 87 } 88 return false; 89} 90 91bool SkGlyph::setImage(SkArenaAlloc* alloc, const void* image) { 92 if (!this->setImageHasBeenCalled()) { 93 this->allocImage(alloc); 94 memcpy(fImage, image, this->imageSize()); 95 return true; 96 } 97 return false; 98} 99 100size_t SkGlyph::setMetricsAndImage(SkArenaAlloc* alloc, const SkGlyph& from) { 101 // Since the code no longer tries to find replacement glyphs, the image should always be 102 // nullptr. 103 SkASSERT(fImage == nullptr); 104 105 // TODO(herb): remove "if" when we are sure there are no colliding glyphs. 106 if (fImage == nullptr) { 107 fAdvanceX = from.fAdvanceX; 108 fAdvanceY = from.fAdvanceY; 109 fWidth = from.fWidth; 110 fHeight = from.fHeight; 111 fTop = from.fTop; 112 fLeft = from.fLeft; 113 fForceBW = from.fForceBW; 114 fMaskFormat = from.fMaskFormat; 115 116 // From glyph may not have an image because the glyph is too large. 117 if (from.fImage != nullptr && this->setImage(alloc, from.image())) { 118 return this->imageSize(); 119 } 120 } 121 return 0; 122} 123 124size_t SkGlyph::rowBytes() const { 125 return format_rowbytes(fWidth, fMaskFormat); 126} 127 128size_t SkGlyph::rowBytesUsingFormat(SkMask::Format format) const { 129 return format_rowbytes(fWidth, format); 130} 131 132size_t SkGlyph::imageSize() const { 133 if (this->isEmpty() || this->imageTooLarge()) { return 0; } 134 135 size_t size = this->rowBytes() * fHeight; 136 137 if (fMaskFormat == SkMask::k3D_Format) { 138 size *= 3; 139 } 140 141 return size; 142} 143 144void SkGlyph::installPath(SkArenaAlloc* alloc, const SkPath* path) { 145 SkASSERT(fPathData == nullptr); 146 SkASSERT(!this->setPathHasBeenCalled()); 147 fPathData = alloc->make<SkGlyph::PathData>(); 148 if (path != nullptr) { 149 fPathData->fPath = *path; 150 fPathData->fPath.updateBoundsCache(); 151 fPathData->fPath.getGenerationID(); 152 fPathData->fHasPath = true; 153 } 154} 155 156bool SkGlyph::setPath(SkArenaAlloc* alloc, SkScalerContext* scalerContext) { 157 if (!this->setPathHasBeenCalled()) { 158 SkPath path; 159 if (scalerContext->getPath(this->getPackedID(), &path)) { 160 this->installPath(alloc, &path); 161 } else { 162 this->installPath(alloc, nullptr); 163 } 164 return this->path() != nullptr; 165 } 166 167 return false; 168} 169 170bool SkGlyph::setPath(SkArenaAlloc* alloc, const SkPath* path) { 171 if (!this->setPathHasBeenCalled()) { 172 this->installPath(alloc, path); 173 return this->path() != nullptr; 174 } 175 return false; 176} 177 178const SkPath* SkGlyph::path() const { 179 // setPath must have been called previously. 180 SkASSERT(this->setPathHasBeenCalled()); 181 if (fPathData->fHasPath) { 182 return &fPathData->fPath; 183 } 184 return nullptr; 185} 186 187static std::tuple<SkScalar, SkScalar> calculate_path_gap( 188 SkScalar topOffset, SkScalar bottomOffset, const SkPath& path) { 189 190 // Left and Right of an ever expanding gap around the path. 191 SkScalar left = SK_ScalarMax, 192 right = SK_ScalarMin; 193 auto expandGap = [&left, &right](SkScalar v) { 194 left = std::min(left, v); 195 right = std::max(right, v); 196 }; 197 198 // Handle all the different verbs for the path. 199 SkPoint pts[4]; 200 auto addLine = [&expandGap, &pts](SkScalar offset) { 201 SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY); 202 if (0 <= t && t < 1) { // this handles divide by zero above 203 expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX)); 204 } 205 }; 206 207 auto addQuad = [&expandGap, &pts](SkScalar offset) { 208 SkDQuad quad; 209 quad.set(pts); 210 double roots[2]; 211 int count = quad.horizontalIntersect(offset, roots); 212 while (--count >= 0) { 213 expandGap(quad.ptAtT(roots[count]).asSkPoint().fX); 214 } 215 }; 216 217 auto addCubic = [&expandGap, &pts](SkScalar offset) { 218 SkDCubic cubic; 219 cubic.set(pts); 220 double roots[3]; 221 int count = cubic.horizontalIntersect(offset, roots); 222 while (--count >= 0) { 223 expandGap(cubic.ptAtT(roots[count]).asSkPoint().fX); 224 } 225 }; 226 227 // Handle when a verb's points are in the gap between top and bottom. 228 auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) { 229 for (int i = 0; i < ptCount; ++i) { 230 if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) { 231 expandGap(pts[i].fX); 232 } 233 } 234 }; 235 236 SkPath::Iter iter(path, false); 237 SkPath::Verb verb; 238 while (SkPath::kDone_Verb != (verb = iter.next(pts))) { 239 switch (verb) { 240 case SkPath::kMove_Verb: { 241 break; 242 } 243 case SkPath::kLine_Verb: { 244 addLine(topOffset); 245 addLine(bottomOffset); 246 addPts(2); 247 break; 248 } 249 case SkPath::kQuad_Verb: { 250 SkScalar quadTop = std::min(std::min(pts[0].fY, pts[1].fY), pts[2].fY); 251 if (bottomOffset < quadTop) { break; } 252 SkScalar quadBottom = std::max(std::max(pts[0].fY, pts[1].fY), pts[2].fY); 253 if (topOffset > quadBottom) { break; } 254 addQuad(topOffset); 255 addQuad(bottomOffset); 256 addPts(3); 257 break; 258 } 259 case SkPath::kConic_Verb: { 260 SkASSERT(0); // no support for text composed of conics 261 break; 262 } 263 case SkPath::kCubic_Verb: { 264 SkScalar quadTop = 265 std::min(std::min(std::min(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY); 266 if (bottomOffset < quadTop) { break; } 267 SkScalar quadBottom = 268 std::max(std::max(std::max(pts[0].fY, pts[1].fY), pts[2].fY), pts[3].fY); 269 if (topOffset > quadBottom) { break; } 270 addCubic(topOffset); 271 addCubic(bottomOffset); 272 addPts(4); 273 break; 274 } 275 case SkPath::kClose_Verb: { 276 break; 277 } 278 default: { 279 SkASSERT(0); 280 break; 281 } 282 } 283 } 284 285 return std::tie(left, right); 286} 287 288void SkGlyph::ensureIntercepts(const SkScalar* bounds, SkScalar scale, SkScalar xPos, 289 SkScalar* array, int* count, SkArenaAlloc* alloc) { 290 291 auto offsetResults = [scale, xPos]( 292 const SkGlyph::Intercept* intercept,SkScalar* array, int* count) { 293 if (array) { 294 array += *count; 295 for (int index = 0; index < 2; index++) { 296 *array++ = intercept->fInterval[index] * scale + xPos; 297 } 298 } 299 *count += 2; 300 }; 301 302 const SkGlyph::Intercept* match = 303 [this](const SkScalar bounds[2]) -> const SkGlyph::Intercept* { 304 if (!fPathData) { 305 return nullptr; 306 } 307 const SkGlyph::Intercept* intercept = fPathData->fIntercept; 308 while (intercept) { 309 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) { 310 return intercept; 311 } 312 intercept = intercept->fNext; 313 } 314 return nullptr; 315 }(bounds); 316 317 if (match) { 318 if (match->fInterval[0] < match->fInterval[1]) { 319 offsetResults(match, array, count); 320 } 321 return; 322 } 323 324 SkGlyph::Intercept* intercept = alloc->make<SkGlyph::Intercept>(); 325 intercept->fNext = fPathData->fIntercept; 326 intercept->fBounds[0] = bounds[0]; 327 intercept->fBounds[1] = bounds[1]; 328 intercept->fInterval[0] = SK_ScalarMax; 329 intercept->fInterval[1] = SK_ScalarMin; 330 fPathData->fIntercept = intercept; 331 const SkPath* path = &(fPathData->fPath); 332 const SkRect& pathBounds = path->getBounds(); 333 if (pathBounds.fBottom < bounds[0] || bounds[1] < pathBounds.fTop) { 334 return; 335 } 336 337 std::tie(intercept->fInterval[0], intercept->fInterval[1]) 338 = calculate_path_gap(bounds[0], bounds[1], *path); 339 340 if (intercept->fInterval[0] >= intercept->fInterval[1]) { 341 intercept->fInterval[0] = SK_ScalarMax; 342 intercept->fInterval[1] = SK_ScalarMin; 343 return; 344 } 345 offsetResults(intercept, array, count); 346} 347