| OLD | NEW |
| (Empty) |
| 1 | |
| 2 /* | |
| 3 * Copyright 2012 Google Inc. | |
| 4 * | |
| 5 * Use of this source code is governed by a BSD-style license that can be | |
| 6 * found in the LICENSE file. | |
| 7 */ | |
| 8 | |
| 9 #include "SkBBoxRecord.h" | |
| 10 #include "SkPatchUtils.h" | |
| 11 | |
| 12 #include "SkTextBlob.h" | |
| 13 | |
| 14 SkBBoxRecord::~SkBBoxRecord() { | |
| 15 fSaveStack.deleteAll(); | |
| 16 } | |
| 17 | |
| 18 void SkBBoxRecord::drawOval(const SkRect& rect, const SkPaint& paint) { | |
| 19 if (this->transformBounds(rect, &paint)) { | |
| 20 INHERITED::drawOval(rect, paint); | |
| 21 } | |
| 22 } | |
| 23 | |
| 24 void SkBBoxRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) { | |
| 25 if (this->transformBounds(rrect.rect(), &paint)) { | |
| 26 INHERITED::drawRRect(rrect, paint); | |
| 27 } | |
| 28 } | |
| 29 | |
| 30 void SkBBoxRecord::drawRect(const SkRect& rect, const SkPaint& paint) { | |
| 31 if (this->transformBounds(rect, &paint)) { | |
| 32 INHERITED::drawRect(rect, paint); | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 void SkBBoxRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, | |
| 37 const SkPaint& paint) { | |
| 38 if (this->transformBounds(outer.rect(), &paint)) { | |
| 39 this->INHERITED::onDrawDRRect(outer, inner, paint); | |
| 40 } | |
| 41 } | |
| 42 | |
| 43 void SkBBoxRecord::drawPath(const SkPath& path, const SkPaint& paint) { | |
| 44 if (path.isInverseFillType()) { | |
| 45 // If path is inverse filled, use the current clip bounds as the | |
| 46 // path's device-space bounding box. | |
| 47 SkIRect clipBounds; | |
| 48 if (this->getClipDeviceBounds(&clipBounds)) { | |
| 49 this->handleBBox(SkRect::Make(clipBounds)); | |
| 50 INHERITED::drawPath(path, paint); | |
| 51 } | |
| 52 } else if (this->transformBounds(path.getBounds(), &paint)) { | |
| 53 INHERITED::drawPath(path, paint); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 void SkBBoxRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[], | |
| 58 const SkPaint& paint) { | |
| 59 SkRect bbox; | |
| 60 bbox.set(pts, SkToInt(count)); | |
| 61 // Small min width value, just to ensure hairline point bounding boxes aren'
t empty. | |
| 62 // Even though we know hairline primitives are drawn one pixel wide, we do n
ot use a | |
| 63 // minimum of 1 because the playback scale factor is unknown at record time.
Later | |
| 64 // outsets will take care of adding additional padding for antialiasing and
rounding out | |
| 65 // to integer device coordinates, guaranteeing that the rasterized pixels wi
ll be included | |
| 66 // in the computed bounds. | |
| 67 // Note: The device coordinate outset in SkBBoxHierarchyRecord::handleBBox i
s currently | |
| 68 // done in the recording coordinate space, which is wrong. | |
| 69 // http://code.google.com/p/skia/issues/detail?id=1021 | |
| 70 static const SkScalar kMinWidth = 0.01f; | |
| 71 SkScalar halfStrokeWidth = SkMaxScalar(paint.getStrokeWidth(), kMinWidth) /
2; | |
| 72 bbox.outset(halfStrokeWidth, halfStrokeWidth); | |
| 73 if (this->transformBounds(bbox, &paint)) { | |
| 74 INHERITED::drawPoints(mode, count, pts, paint); | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 void SkBBoxRecord::drawPaint(const SkPaint& paint) { | |
| 79 SkRect bbox; | |
| 80 if (this->getClipBounds(&bbox)) { | |
| 81 if (this->transformBounds(bbox, &paint)) { | |
| 82 INHERITED::drawPaint(paint); | |
| 83 } | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 void SkBBoxRecord::clear(SkColor color) { | |
| 88 SkISize size = this->getDeviceSize(); | |
| 89 SkRect bbox = {0, 0, SkIntToScalar(size.width()), SkIntToScalar(size.height(
))}; | |
| 90 this->handleBBox(bbox); | |
| 91 INHERITED::clear(color); | |
| 92 } | |
| 93 | |
| 94 void SkBBoxRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, S
kScalar y, | |
| 95 const SkPaint& paint) { | |
| 96 SkRect bbox; | |
| 97 paint.measureText(text, byteLength, &bbox); | |
| 98 SkPaint::FontMetrics metrics; | |
| 99 paint.getFontMetrics(&metrics); | |
| 100 | |
| 101 // Vertical and aligned text need to be offset | |
| 102 if (paint.isVerticalText()) { | |
| 103 SkScalar h = bbox.fBottom - bbox.fTop; | |
| 104 if (paint.getTextAlign() == SkPaint::kCenter_Align) { | |
| 105 bbox.fTop -= h / 2; | |
| 106 bbox.fBottom -= h / 2; | |
| 107 } | |
| 108 // Pad top and bottom with max extents from FontMetrics | |
| 109 bbox.fBottom += metrics.fBottom; | |
| 110 bbox.fTop += metrics.fTop; | |
| 111 } else { | |
| 112 SkScalar w = bbox.fRight - bbox.fLeft; | |
| 113 if (paint.getTextAlign() == SkPaint::kCenter_Align) { | |
| 114 bbox.fLeft -= w / 2; | |
| 115 bbox.fRight -= w / 2; | |
| 116 } else if (paint.getTextAlign() == SkPaint::kRight_Align) { | |
| 117 bbox.fLeft -= w; | |
| 118 bbox.fRight -= w; | |
| 119 } | |
| 120 // Set vertical bounds to max extents from font metrics | |
| 121 bbox.fTop = metrics.fTop; | |
| 122 bbox.fBottom = metrics.fBottom; | |
| 123 } | |
| 124 | |
| 125 // Pad horizontal bounds on each side by half of max vertical extents (this
is sort of | |
| 126 // arbitrary, but seems to produce reasonable results, if there were a way o
f getting max | |
| 127 // glyph X-extents to pad by, that may be better here, but FontMetrics fXMin
and fXMax seem | |
| 128 // incorrect on most platforms (too small in Linux, never even set in Window
s). | |
| 129 SkScalar pad = (metrics.fBottom - metrics.fTop) / 2; | |
| 130 bbox.fLeft -= pad; | |
| 131 bbox.fRight += pad; | |
| 132 | |
| 133 bbox.fLeft += x; | |
| 134 bbox.fRight += x; | |
| 135 bbox.fTop += y; | |
| 136 bbox.fBottom += y; | |
| 137 if (this->transformBounds(bbox, &paint)) { | |
| 138 INHERITED::onDrawText(text, byteLength, x, y, paint); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 void SkBBoxRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar to
p, | |
| 143 const SkPaint* paint) { | |
| 144 SkRect bbox = {left, top, left + bitmap.width(), top + bitmap.height()}; | |
| 145 if (this->transformBounds(bbox, paint)) { | |
| 146 INHERITED::drawBitmap(bitmap, left, top, paint); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void SkBBoxRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* sr
c, | |
| 151 const SkRect& dst, const SkPaint* paint, | |
| 152 DrawBitmapRectFlags flags) { | |
| 153 if (this->transformBounds(dst, paint)) { | |
| 154 INHERITED::drawBitmapRectToRect(bitmap, src, dst, paint, flags); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 void SkBBoxRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& mat, | |
| 159 const SkPaint* paint) { | |
| 160 SkMatrix m = mat; | |
| 161 SkRect bbox = {0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.hei
ght())}; | |
| 162 m.mapRect(&bbox); | |
| 163 if (this->transformBounds(bbox, paint)) { | |
| 164 INHERITED::drawBitmapMatrix(bitmap, mat, paint); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void SkBBoxRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, | |
| 169 const SkRect& dst, const SkPaint* paint) { | |
| 170 if (this->transformBounds(dst, paint)) { | |
| 171 INHERITED::drawBitmapNine(bitmap, center, dst, paint); | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 // Hack to work-around https://code.google.com/p/chromium/issues/detail?id=37378
5 | |
| 176 // This logic assums that 'pad' is enough to add to the left and right to accoun
t for | |
| 177 // big glyphs. For the font in question (a logo font) the glyphs is much wider t
han just | |
| 178 // the pointsize (approx 3x wider). | |
| 179 // As a temp work-around, we scale-up pad. | |
| 180 // A more correct fix might be to add fontmetrics.fMaxX, but we don't have that
value in hand | |
| 181 // at the moment, and (possibly) the value in the font may not be accurate (but
who knows). | |
| 182 // | |
| 183 static SkScalar hack_373785_amend_pad(SkScalar pad) { | |
| 184 return pad * 4; | |
| 185 } | |
| 186 | |
| 187 void SkBBoxRecord::onDrawPosText(const void* text, size_t byteLength, const SkPo
int pos[], | |
| 188 const SkPaint& paint) { | |
| 189 SkRect bbox; | |
| 190 bbox.set(pos, paint.countText(text, byteLength)); | |
| 191 SkPaint::FontMetrics metrics; | |
| 192 paint.getFontMetrics(&metrics); | |
| 193 bbox.fTop += metrics.fTop; | |
| 194 bbox.fBottom += metrics.fBottom; | |
| 195 | |
| 196 // pad on left and right by half of max vertical glyph extents | |
| 197 SkScalar pad = (metrics.fTop - metrics.fBottom) / 2; | |
| 198 pad = hack_373785_amend_pad(pad); | |
| 199 bbox.fLeft += pad; | |
| 200 bbox.fRight -= pad; | |
| 201 | |
| 202 if (this->transformBounds(bbox, &paint)) { | |
| 203 INHERITED::onDrawPosText(text, byteLength, pos, paint); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void SkBBoxRecord::onDrawPosTextH(const void* text, size_t byteLength, const SkS
calar xpos[], | |
| 208 SkScalar constY, const SkPaint& paint) { | |
| 209 size_t numChars = paint.countText(text, byteLength); | |
| 210 if (numChars == 0) { | |
| 211 return; | |
| 212 } | |
| 213 | |
| 214 const SkFlatData* flatPaintData = this->getFlatPaintData(paint); | |
| 215 WriteTopBot(paint, *flatPaintData); | |
| 216 | |
| 217 SkScalar top = flatPaintData->topBot()[0]; | |
| 218 SkScalar bottom = flatPaintData->topBot()[1]; | |
| 219 SkScalar pad = top - bottom; | |
| 220 | |
| 221 SkRect bbox; | |
| 222 bbox.fLeft = SK_ScalarMax; | |
| 223 bbox.fRight = SK_ScalarMin; | |
| 224 | |
| 225 for (size_t i = 0; i < numChars; ++i) { | |
| 226 if (xpos[i] < bbox.fLeft) { | |
| 227 bbox.fLeft = xpos[i]; | |
| 228 } | |
| 229 if (xpos[i] > bbox.fRight) { | |
| 230 bbox.fRight = xpos[i]; | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 // pad horizontally by max glyph height | |
| 235 pad = hack_373785_amend_pad(pad); | |
| 236 bbox.fLeft += pad; | |
| 237 bbox.fRight -= pad; | |
| 238 | |
| 239 bbox.fTop = top + constY; | |
| 240 bbox.fBottom = bottom + constY; | |
| 241 | |
| 242 if (!this->transformBounds(bbox, &paint)) { | |
| 243 return; | |
| 244 } | |
| 245 // This is the equivalent of calling: | |
| 246 // INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint); | |
| 247 // but we filled our flat paint beforehand so that we could get font metrics
. | |
| 248 drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData); | |
| 249 } | |
| 250 | |
| 251 void SkBBoxRecord::drawSprite(const SkBitmap& bitmap, int left, int top, | |
| 252 const SkPaint* paint) { | |
| 253 SkRect bbox; | |
| 254 bbox.set(SkIRect::MakeXYWH(left, top, bitmap.width(), bitmap.height())); | |
| 255 this->handleBBox(bbox); // directly call handleBBox, matrix is ignored | |
| 256 INHERITED::drawSprite(bitmap, left, top, paint); | |
| 257 } | |
| 258 | |
| 259 void SkBBoxRecord::onDrawTextOnPath(const void* text, size_t byteLength, const S
kPath& path, | |
| 260 const SkMatrix* matrix, const SkPaint& paint
) { | |
| 261 SkRect bbox = path.getBounds(); | |
| 262 SkPaint::FontMetrics metrics; | |
| 263 paint.getFontMetrics(&metrics); | |
| 264 | |
| 265 // pad out all sides by the max glyph height above baseline | |
| 266 SkScalar pad = metrics.fTop; | |
| 267 bbox.fLeft += pad; | |
| 268 bbox.fRight -= pad; | |
| 269 bbox.fTop += pad; | |
| 270 bbox.fBottom -= pad; | |
| 271 | |
| 272 if (this->transformBounds(bbox, &paint)) { | |
| 273 INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, paint); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 void SkBBoxRecord::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y
, | |
| 278 const SkPaint& paint) { | |
| 279 SkRect bbox = blob->bounds(); | |
| 280 bbox.offset(x, y); | |
| 281 // FIXME: implement implicit blob bounds! | |
| 282 if (bbox.isEmpty()) { | |
| 283 this->getClipBounds(&bbox); | |
| 284 } | |
| 285 | |
| 286 if (this->transformBounds(bbox, &paint)) { | |
| 287 INHERITED::onDrawTextBlob(blob, x, y, paint); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void SkBBoxRecord::drawVertices(VertexMode mode, int vertexCount, | |
| 292 const SkPoint vertices[], const SkPoint texs[], | |
| 293 const SkColor colors[], SkXfermode* xfer, | |
| 294 const uint16_t indices[], int indexCount, | |
| 295 const SkPaint& paint) { | |
| 296 SkRect bbox; | |
| 297 bbox.set(vertices, vertexCount); | |
| 298 if (this->transformBounds(bbox, &paint)) { | |
| 299 INHERITED::drawVertices(mode, vertexCount, vertices, texs, | |
| 300 colors, xfer, indices, indexCount, paint); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 void SkBBoxRecord::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4]
, | |
| 305 const SkPoint texCoords[4], SkXfermode* xmode, | |
| 306 const SkPaint& paint) { | |
| 307 SkRect bbox; | |
| 308 bbox.set(cubics, SkPatchUtils::kNumCtrlPts); | |
| 309 if (this->transformBounds(bbox, &paint)) { | |
| 310 INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, paint); | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 void SkBBoxRecord::onDrawPicture(const SkPicture* picture, const SkMatrix* matri
x, | |
| 315 const SkPaint* paint) { | |
| 316 SkRect bounds = picture->cullRect(); | |
| 317 // todo: wonder if we should allow passing an optional matrix to transformBo
unds so we don't | |
| 318 // end up transforming the rect twice. | |
| 319 if (matrix) { | |
| 320 matrix->mapRect(&bounds); | |
| 321 } | |
| 322 if (this->transformBounds(bounds, paint)) { | |
| 323 this->INHERITED::onDrawPicture(picture, matrix, paint); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 void SkBBoxRecord::willSave() { | |
| 328 fSaveStack.push(NULL); | |
| 329 this->INHERITED::willSave(); | |
| 330 } | |
| 331 | |
| 332 SkCanvas::SaveLayerStrategy SkBBoxRecord::willSaveLayer(const SkRect* bounds, | |
| 333 const SkPaint* paint, | |
| 334 SaveFlags flags) { | |
| 335 // Image filters can affect the effective bounds of primitives drawn inside
saveLayer(). | |
| 336 // Copy the paint so we can compute the modified bounds in transformBounds()
. | |
| 337 fSaveStack.push(paint && paint->getImageFilter() ? new SkPaint(*paint) : NUL
L); | |
| 338 return this->INHERITED::willSaveLayer(bounds, paint, flags); | |
| 339 } | |
| 340 | |
| 341 void SkBBoxRecord::willRestore() { | |
| 342 delete fSaveStack.top(); | |
| 343 fSaveStack.pop(); | |
| 344 this->INHERITED::willRestore(); | |
| 345 } | |
| 346 | |
| 347 bool SkBBoxRecord::transformBounds(const SkRect& bounds, const SkPaint* paint) { | |
| 348 SkRect outBounds = bounds; | |
| 349 outBounds.sort(); | |
| 350 | |
| 351 if (paint) { | |
| 352 // account for stroking, path effects, shadows, etc | |
| 353 if (paint->canComputeFastBounds()) { | |
| 354 SkRect temp; | |
| 355 outBounds = paint->computeFastBounds(outBounds, &temp); | |
| 356 } else { | |
| 357 // set bounds to current clip | |
| 358 if (!this->getClipBounds(&outBounds)) { | |
| 359 // current clip is empty | |
| 360 return false; | |
| 361 } | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 for (int i = fSaveStack.count() - 1; i >= 0; --i) { | |
| 366 const SkPaint* paint = fSaveStack.getAt(i); | |
| 367 if (paint && paint->canComputeFastBounds()) { | |
| 368 SkRect temp; | |
| 369 outBounds = paint->computeFastBounds(outBounds, &temp); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 if (!outBounds.isEmpty() && !this->quickReject(outBounds)) { | |
| 374 this->getTotalMatrix().mapRect(&outBounds); | |
| 375 this->handleBBox(outBounds); | |
| 376 return true; | |
| 377 } | |
| 378 | |
| 379 return false; | |
| 380 } | |
| OLD | NEW |