Home | History | Annotate | Download | only in core
      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 
     11 void SkBBoxRecord::drawOval(const SkRect& rect, const SkPaint& paint) {
     12     if (this->transformBounds(rect, &paint)) {
     13         INHERITED::drawOval(rect, paint);
     14     }
     15 }
     16 
     17 void SkBBoxRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
     18     if (this->transformBounds(rrect.rect(), &paint)) {
     19         INHERITED::drawRRect(rrect, paint);
     20     }
     21 }
     22 
     23 void SkBBoxRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
     24     if (this->transformBounds(rect, &paint)) {
     25         INHERITED::drawRect(rect, paint);
     26     }
     27 }
     28 
     29 void SkBBoxRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
     30                                 const SkPaint& paint) {
     31     if (this->transformBounds(outer.rect(), &paint)) {
     32         this->INHERITED::onDrawDRRect(outer, inner, paint);
     33     }
     34 }
     35 
     36 void SkBBoxRecord::drawPath(const SkPath& path, const SkPaint& paint) {
     37     if (path.isInverseFillType()) {
     38         // If path is inverse filled, use the current clip bounds as the
     39         // path's device-space bounding box.
     40         SkIRect clipBounds;
     41         if (this->getClipDeviceBounds(&clipBounds)) {
     42             this->handleBBox(SkRect::Make(clipBounds));
     43             INHERITED::drawPath(path, paint);
     44         }
     45     } else if (this->transformBounds(path.getBounds(), &paint)) {
     46         INHERITED::drawPath(path, paint);
     47     }
     48 }
     49 
     50 void SkBBoxRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
     51                               const SkPaint& paint) {
     52     SkRect bbox;
     53     bbox.set(pts, SkToInt(count));
     54     // Small min width value, just to ensure hairline point bounding boxes aren't empty.
     55     // Even though we know hairline primitives are drawn one pixel wide, we do not use a
     56     // minimum of 1 because the playback scale factor is unknown at record time. Later
     57     // outsets will take care of adding additional padding for antialiasing and rounding out
     58     // to integer device coordinates, guaranteeing that the rasterized pixels will be included
     59     // in the computed bounds.
     60     // Note: The device coordinate outset in SkBBoxHierarchyRecord::handleBBox is currently
     61     // done in the recording coordinate space, which is wrong.
     62     // http://code.google.com/p/skia/issues/detail?id=1021
     63     static const SkScalar kMinWidth = 0.01f;
     64     SkScalar halfStrokeWidth = SkMaxScalar(paint.getStrokeWidth(), kMinWidth) / 2;
     65     bbox.outset(halfStrokeWidth, halfStrokeWidth);
     66     if (this->transformBounds(bbox, &paint)) {
     67         INHERITED::drawPoints(mode, count, pts, paint);
     68     }
     69 }
     70 
     71 void SkBBoxRecord::drawPaint(const SkPaint& paint) {
     72     SkRect bbox;
     73     if (this->getClipBounds(&bbox)) {
     74         if (this->transformBounds(bbox, &paint)) {
     75             INHERITED::drawPaint(paint);
     76         }
     77     }
     78 }
     79 
     80 void SkBBoxRecord::clear(SkColor color) {
     81     SkISize size = this->getDeviceSize();
     82     SkRect bbox = {0, 0, SkIntToScalar(size.width()), SkIntToScalar(size.height())};
     83     this->handleBBox(bbox);
     84     INHERITED::clear(color);
     85 }
     86 
     87 void SkBBoxRecord::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
     88                               const SkPaint& paint) {
     89     SkRect bbox;
     90     paint.measureText(text, byteLength, &bbox);
     91     SkPaint::FontMetrics metrics;
     92     paint.getFontMetrics(&metrics);
     93 
     94     // Vertical and aligned text need to be offset
     95     if (paint.isVerticalText()) {
     96         SkScalar h = bbox.fBottom - bbox.fTop;
     97         if (paint.getTextAlign() == SkPaint::kCenter_Align) {
     98             bbox.fTop    -= h / 2;
     99             bbox.fBottom -= h / 2;
    100         }
    101         // Pad top and bottom with max extents from FontMetrics
    102         bbox.fBottom += metrics.fBottom;
    103         bbox.fTop += metrics.fTop;
    104     } else {
    105         SkScalar w = bbox.fRight - bbox.fLeft;
    106         if (paint.getTextAlign() == SkPaint::kCenter_Align) {
    107             bbox.fLeft  -= w / 2;
    108             bbox.fRight -= w / 2;
    109         } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
    110             bbox.fLeft  -= w;
    111             bbox.fRight -= w;
    112         }
    113         // Set vertical bounds to max extents from font metrics
    114         bbox.fTop = metrics.fTop;
    115         bbox.fBottom = metrics.fBottom;
    116     }
    117 
    118     // Pad horizontal bounds on each side by half of max vertical extents (this is sort of
    119     // arbitrary, but seems to produce reasonable results, if there were a way of getting max
    120     // glyph X-extents to pad by, that may be better here, but FontMetrics fXMin and fXMax seem
    121     // incorrect on most platforms (too small in Linux, never even set in Windows).
    122     SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
    123     bbox.fLeft  -= pad;
    124     bbox.fRight += pad;
    125 
    126     bbox.fLeft += x;
    127     bbox.fRight += x;
    128     bbox.fTop += y;
    129     bbox.fBottom += y;
    130     if (this->transformBounds(bbox, &paint)) {
    131         INHERITED::onDrawText(text, byteLength, x, y, paint);
    132     }
    133 }
    134 
    135 void SkBBoxRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
    136                               const SkPaint* paint) {
    137     SkRect bbox = {left, top, left + bitmap.width(), top + bitmap.height()};
    138     if (this->transformBounds(bbox, paint)) {
    139         INHERITED::drawBitmap(bitmap, left, top, paint);
    140     }
    141 }
    142 
    143 void SkBBoxRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
    144                                         const SkRect& dst, const SkPaint* paint,
    145                                         DrawBitmapRectFlags flags) {
    146     if (this->transformBounds(dst, paint)) {
    147         INHERITED::drawBitmapRectToRect(bitmap, src, dst, paint, flags);
    148     }
    149 }
    150 
    151 void SkBBoxRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& mat,
    152                                     const SkPaint* paint) {
    153     SkMatrix m = mat;
    154     SkRect bbox = {0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())};
    155     m.mapRect(&bbox);
    156     if (this->transformBounds(bbox, paint)) {
    157         INHERITED::drawBitmapMatrix(bitmap, mat, paint);
    158     }
    159 }
    160 
    161 void SkBBoxRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
    162                                   const SkRect& dst, const SkPaint* paint) {
    163     if (this->transformBounds(dst, paint)) {
    164         INHERITED::drawBitmapNine(bitmap, center, dst, paint);
    165     }
    166 }
    167 
    168 // Hack to work-around https://code.google.com/p/chromium/issues/detail?id=373785
    169 // This logic assums that 'pad' is enough to add to the left and right to account for
    170 // big glyphs. For the font in question (a logo font) the glyphs is much wider than just
    171 // the pointsize (approx 3x wider).
    172 // As a temp work-around, we scale-up pad.
    173 // A more correct fix might be to add fontmetrics.fMaxX, but we don't have that value in hand
    174 // at the moment, and (possibly) the value in the font may not be accurate (but who knows).
    175 //
    176 static SkScalar hack_373785_amend_pad(SkScalar pad) {
    177     return pad * 4;
    178 }
    179 
    180 void SkBBoxRecord::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
    181                                  const SkPaint& paint) {
    182     SkRect bbox;
    183     bbox.set(pos, paint.countText(text, byteLength));
    184     SkPaint::FontMetrics metrics;
    185     paint.getFontMetrics(&metrics);
    186     bbox.fTop += metrics.fTop;
    187     bbox.fBottom += metrics.fBottom;
    188 
    189     // pad on left and right by half of max vertical glyph extents
    190     SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
    191     pad = hack_373785_amend_pad(pad);
    192     bbox.fLeft += pad;
    193     bbox.fRight -= pad;
    194 
    195     if (this->transformBounds(bbox, &paint)) {
    196         INHERITED::onDrawPosText(text, byteLength, pos, paint);
    197     }
    198 }
    199 
    200 void SkBBoxRecord::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
    201                                   SkScalar constY, const SkPaint& paint) {
    202     size_t numChars = paint.countText(text, byteLength);
    203     if (numChars == 0) {
    204         return;
    205     }
    206 
    207     const SkFlatData* flatPaintData = this->getFlatPaintData(paint);
    208     WriteTopBot(paint, *flatPaintData);
    209 
    210     SkScalar top = flatPaintData->topBot()[0];
    211     SkScalar bottom = flatPaintData->topBot()[1];
    212     SkScalar pad = top - bottom;
    213 
    214     SkRect bbox;
    215     bbox.fLeft = SK_ScalarMax;
    216     bbox.fRight = SK_ScalarMin;
    217 
    218     for (size_t i = 0; i < numChars; ++i) {
    219         if (xpos[i] < bbox.fLeft) {
    220             bbox.fLeft = xpos[i];
    221         }
    222         if (xpos[i] > bbox.fRight) {
    223             bbox.fRight = xpos[i];
    224         }
    225     }
    226 
    227     // pad horizontally by max glyph height
    228     pad = hack_373785_amend_pad(pad);
    229     bbox.fLeft  += pad;
    230     bbox.fRight -= pad;
    231 
    232     bbox.fTop    = top + constY;
    233     bbox.fBottom = bottom + constY;
    234 
    235     if (!this->transformBounds(bbox, &paint)) {
    236         return;
    237     }
    238     // This is the equivalent of calling:
    239     //  INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
    240     // but we filled our flat paint beforehand so that we could get font metrics.
    241     drawPosTextHImpl(text, byteLength, xpos, constY, paint, flatPaintData);
    242 }
    243 
    244 void SkBBoxRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
    245                               const SkPaint* paint) {
    246     SkRect bbox;
    247     bbox.set(SkIRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()));
    248     this->handleBBox(bbox); // directly call handleBBox, matrix is ignored
    249     INHERITED::drawSprite(bitmap, left, top, paint);
    250 }
    251 
    252 void SkBBoxRecord::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
    253                                     const SkMatrix* matrix, const SkPaint& paint) {
    254     SkRect bbox = path.getBounds();
    255     SkPaint::FontMetrics metrics;
    256     paint.getFontMetrics(&metrics);
    257 
    258     // pad out all sides by the max glyph height above baseline
    259     SkScalar pad = metrics.fTop;
    260     bbox.fLeft += pad;
    261     bbox.fRight -= pad;
    262     bbox.fTop += pad;
    263     bbox.fBottom -= pad;
    264 
    265     if (this->transformBounds(bbox, &paint)) {
    266         INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, paint);
    267     }
    268 }
    269 
    270 void SkBBoxRecord::drawVertices(VertexMode mode, int vertexCount,
    271                                 const SkPoint vertices[], const SkPoint texs[],
    272                                 const SkColor colors[], SkXfermode* xfer,
    273                                 const uint16_t indices[], int indexCount,
    274                                 const SkPaint& paint) {
    275     SkRect bbox;
    276     bbox.set(vertices, vertexCount);
    277     if (this->transformBounds(bbox, &paint)) {
    278         INHERITED::drawVertices(mode, vertexCount, vertices, texs,
    279                                 colors, xfer, indices, indexCount, paint);
    280     }
    281 }
    282 
    283 void SkBBoxRecord::onDrawPicture(const SkPicture* picture) {
    284     if (picture->width() > 0 && picture->height() > 0 &&
    285         this->transformBounds(SkRect::MakeWH(picture->width(), picture->height()), NULL)) {
    286         this->INHERITED::onDrawPicture(picture);
    287     }
    288 }
    289 
    290 bool SkBBoxRecord::transformBounds(const SkRect& bounds, const SkPaint* paint) {
    291     SkRect outBounds = bounds;
    292     outBounds.sort();
    293 
    294     if (paint) {
    295         // account for stroking, path effects, shadows, etc
    296         if (paint->canComputeFastBounds()) {
    297             SkRect temp;
    298             outBounds = paint->computeFastBounds(outBounds, &temp);
    299         } else {
    300             // set bounds to current clip
    301             if (!this->getClipBounds(&outBounds)) {
    302                 // current clip is empty
    303                 return false;
    304             }
    305         }
    306     }
    307 
    308     if (!outBounds.isEmpty() && !this->quickReject(outBounds)) {
    309         this->getTotalMatrix().mapRect(&outBounds);
    310         this->handleBBox(outBounds);
    311         return true;
    312     }
    313 
    314     return false;
    315 }
    316