Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2011 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 "SkColorFilter.h"
      9 #include "SkDevice.h"
     10 #include "SkDraw.h"
     11 #include "SkDrawFilter.h"
     12 #include "SkImage_Base.h"
     13 #include "SkMetaData.h"
     14 #include "SkNinePatchIter.h"
     15 #include "SkPatchUtils.h"
     16 #include "SkPathMeasure.h"
     17 #include "SkRasterClip.h"
     18 #include "SkRSXform.h"
     19 #include "SkShader.h"
     20 #include "SkTextBlobRunIterator.h"
     21 #include "SkTextToPathIter.h"
     22 
     23 SkBaseDevice::SkBaseDevice(const SkSurfaceProps& surfaceProps)
     24     : fSurfaceProps(surfaceProps)
     25 #ifdef SK_DEBUG
     26     , fAttachedToCanvas(false)
     27 #endif
     28 {
     29     fOrigin.setZero();
     30     fMetaData = nullptr;
     31 }
     32 
     33 SkBaseDevice::~SkBaseDevice() { delete fMetaData; }
     34 
     35 SkMetaData& SkBaseDevice::getMetaData() {
     36     // metadata users are rare, so we lazily allocate it. If that changes we
     37     // can decide to just make it a field in the device (rather than a ptr)
     38     if (nullptr == fMetaData) {
     39         fMetaData = new SkMetaData;
     40     }
     41     return *fMetaData;
     42 }
     43 
     44 SkImageInfo SkBaseDevice::imageInfo() const {
     45     return SkImageInfo::MakeUnknown();
     46 }
     47 
     48 const SkBitmap& SkBaseDevice::accessBitmap(bool changePixels) {
     49     const SkBitmap& bitmap = this->onAccessBitmap();
     50     if (changePixels) {
     51         bitmap.notifyPixelsChanged();
     52     }
     53     return bitmap;
     54 }
     55 
     56 SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info,
     57                                                          TileUsage tileUsage,
     58                                                          SkPixelGeometry geo,
     59                                                          bool preserveLCDText) {
     60     switch (tileUsage) {
     61         case kPossible_TileUsage:
     62             // (we think) for compatibility with old clients, we assume this layer can support LCD
     63             // even though they may not have marked it as opaque... seems like we should update
     64             // our callers (reed/robertphilips).
     65             break;
     66         case kNever_TileUsage:
     67             if (!preserveLCDText) {
     68                 geo = kUnknown_SkPixelGeometry;
     69             }
     70             break;
     71     }
     72     return geo;
     73 }
     74 
     75 void SkBaseDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
     76                               const SkRRect& inner, const SkPaint& paint) {
     77     SkPath path;
     78     path.addRRect(outer);
     79     path.addRRect(inner);
     80     path.setFillType(SkPath::kEvenOdd_FillType);
     81 
     82     const SkMatrix* preMatrix = nullptr;
     83     const bool pathIsMutable = true;
     84     this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
     85 }
     86 
     87 void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPoint cubics[12], const SkColor colors[4],
     88                              const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
     89     SkPatchUtils::VertexData data;
     90 
     91     SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, draw.fMatrix);
     92 
     93     // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
     94     // If it fails to generate the vertices, then we do not draw.
     95     if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
     96         this->drawVertices(draw, SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
     97                            data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
     98                            paint);
     99     }
    100 }
    101 
    102 void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y,
    103                                 const SkPaint &paint, SkDrawFilter* drawFilter) {
    104 
    105     SkPaint runPaint = paint;
    106 
    107     SkTextBlobRunIterator it(blob);
    108     for (;!it.done(); it.next()) {
    109         size_t textLen = it.glyphCount() * sizeof(uint16_t);
    110         const SkPoint& offset = it.offset();
    111         // applyFontToPaint() always overwrites the exact same attributes,
    112         // so it is safe to not re-seed the paint for this reason.
    113         it.applyFontToPaint(&runPaint);
    114 
    115         if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
    116             // A false return from filter() means we should abort the current draw.
    117             runPaint = paint;
    118             continue;
    119         }
    120 
    121         runPaint.setFlags(this->filterTextFlags(runPaint));
    122 
    123         switch (it.positioning()) {
    124         case SkTextBlob::kDefault_Positioning:
    125             this->drawText(draw, it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
    126             break;
    127         case SkTextBlob::kHorizontal_Positioning:
    128             this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 1,
    129                               SkPoint::Make(x, y + offset.y()), runPaint);
    130             break;
    131         case SkTextBlob::kFull_Positioning:
    132             this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 2,
    133                               SkPoint::Make(x, y), runPaint);
    134             break;
    135         default:
    136             SkFAIL("unhandled positioning mode");
    137         }
    138 
    139         if (drawFilter) {
    140             // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
    141             runPaint = paint;
    142         }
    143     }
    144 }
    145 
    146 void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
    147                              const SkPaint& paint) {
    148     // Default impl : turns everything into raster bitmap
    149     SkBitmap bm;
    150     if (as_IB(image)->getROPixels(&bm)) {
    151         this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
    152     }
    153 }
    154 
    155 void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
    156                                  const SkRect& dst, const SkPaint& paint,
    157                                  SkCanvas::SrcRectConstraint constraint) {
    158     // Default impl : turns everything into raster bitmap
    159     SkBitmap bm;
    160     if (as_IB(image)->getROPixels(&bm)) {
    161         this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
    162     }
    163 }
    164 
    165 void SkBaseDevice::drawImageNine(const SkDraw& draw, const SkImage* image, const SkIRect& center,
    166                                  const SkRect& dst, const SkPaint& paint) {
    167     SkNinePatchIter iter(image->width(), image->height(), center, dst);
    168 
    169     SkRect srcR, dstR;
    170     while (iter.next(&srcR, &dstR)) {
    171         this->drawImageRect(draw, image, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint);
    172     }
    173 }
    174 
    175 void SkBaseDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center,
    176                                   const SkRect& dst, const SkPaint& paint) {
    177     SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst);
    178 
    179     SkRect srcR, dstR;
    180     while (iter.next(&srcR, &dstR)) {
    181         this->drawBitmapRect(draw, bitmap, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint);
    182     }
    183 }
    184 
    185 void SkBaseDevice::drawAtlas(const SkDraw& draw, const SkImage* atlas, const SkRSXform xform[],
    186                              const SkRect tex[], const SkColor colors[], int count,
    187                              SkXfermode::Mode mode, const SkPaint& paint) {
    188     SkPath path;
    189     path.setIsVolatile(true);
    190 
    191     for (int i = 0; i < count; ++i) {
    192         SkPoint quad[4];
    193         xform[i].toQuad(tex[i].width(), tex[i].height(), quad);
    194 
    195         SkMatrix localM;
    196         localM.setRSXform(xform[i]);
    197         localM.preTranslate(-tex[i].left(), -tex[i].top());
    198 
    199         SkPaint pnt(paint);
    200         SkAutoTUnref<SkShader> shader(atlas->newShader(SkShader::kClamp_TileMode,
    201                                                        SkShader::kClamp_TileMode,
    202                                                        &localM));
    203         if (!shader) {
    204             break;
    205         }
    206         pnt.setShader(shader);
    207 
    208         if (colors) {
    209             SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(colors[i], mode));
    210             pnt.setColorFilter(cf);
    211         }
    212 
    213         path.rewind();
    214         path.addPoly(quad, 4, true);
    215         path.setConvexity(SkPath::kConvex_Convexity);
    216         this->drawPath(draw, path, pnt, nullptr, true);
    217     }
    218 }
    219 
    220 ///////////////////////////////////////////////////////////////////////////////////////////////////
    221 
    222 bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
    223 #ifdef SK_DEBUG
    224     SkASSERT(info.width() > 0 && info.height() > 0);
    225     SkASSERT(dstP);
    226     SkASSERT(rowBytes >= info.minRowBytes());
    227     SkASSERT(x >= 0 && y >= 0);
    228 
    229     const SkImageInfo& srcInfo = this->imageInfo();
    230     SkASSERT(x + info.width() <= srcInfo.width());
    231     SkASSERT(y + info.height() <= srcInfo.height());
    232 #endif
    233     return this->onReadPixels(info, dstP, rowBytes, x, y);
    234 }
    235 
    236 bool SkBaseDevice::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
    237                                int x, int y) {
    238 #ifdef SK_DEBUG
    239     SkASSERT(info.width() > 0 && info.height() > 0);
    240     SkASSERT(pixels);
    241     SkASSERT(rowBytes >= info.minRowBytes());
    242     SkASSERT(x >= 0 && y >= 0);
    243 
    244     const SkImageInfo& dstInfo = this->imageInfo();
    245     SkASSERT(x + info.width() <= dstInfo.width());
    246     SkASSERT(y + info.height() <= dstInfo.height());
    247 #endif
    248     return this->onWritePixels(info, pixels, rowBytes, x, y);
    249 }
    250 
    251 bool SkBaseDevice::onWritePixels(const SkImageInfo&, const void*, size_t, int, int) {
    252     return false;
    253 }
    254 
    255 bool SkBaseDevice::onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) {
    256     return false;
    257 }
    258 
    259 bool SkBaseDevice::EXPERIMENTAL_drawPicture(SkCanvas*, const SkPicture*, const SkMatrix*,
    260                                             const SkPaint*) {
    261     // The base class doesn't perform any accelerated picture rendering
    262     return false;
    263 }
    264 
    265 bool SkBaseDevice::accessPixels(SkPixmap* pmap) {
    266     SkPixmap tempStorage;
    267     if (nullptr == pmap) {
    268         pmap = &tempStorage;
    269     }
    270     return this->onAccessPixels(pmap);
    271 }
    272 
    273 bool SkBaseDevice::peekPixels(SkPixmap* pmap) {
    274     SkPixmap tempStorage;
    275     if (nullptr == pmap) {
    276         pmap = &tempStorage;
    277     }
    278     return this->onPeekPixels(pmap);
    279 }
    280 
    281 //////////////////////////////////////////////////////////////////////////////////////////
    282 
    283 static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
    284                         SkPathMeasure& meas, const SkMatrix& matrix) {
    285     SkMatrix::MapXYProc proc = matrix.getMapXYProc();
    286 
    287     for (int i = 0; i < count; i++) {
    288         SkPoint pos;
    289         SkVector tangent;
    290 
    291         proc(matrix, src[i].fX, src[i].fY, &pos);
    292         SkScalar sx = pos.fX;
    293         SkScalar sy = pos.fY;
    294 
    295         if (!meas.getPosTan(sx, &pos, &tangent)) {
    296             // set to 0 if the measure failed, so that we just set dst == pos
    297             tangent.set(0, 0);
    298         }
    299 
    300         /*  This is the old way (that explains our approach but is way too slow
    301          SkMatrix    matrix;
    302          SkPoint     pt;
    303 
    304          pt.set(sx, sy);
    305          matrix.setSinCos(tangent.fY, tangent.fX);
    306          matrix.preTranslate(-sx, 0);
    307          matrix.postTranslate(pos.fX, pos.fY);
    308          matrix.mapPoints(&dst[i], &pt, 1);
    309          */
    310         dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
    311                    pos.fY + SkScalarMul(tangent.fX, sy));
    312     }
    313 }
    314 
    315 /*  TODO
    316 
    317  Need differentially more subdivisions when the follow-path is curvy. Not sure how to
    318  determine that, but we need it. I guess a cheap answer is let the caller tell us,
    319  but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
    320  */
    321 static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
    322                       const SkMatrix& matrix) {
    323     SkPath::Iter    iter(src, false);
    324     SkPoint         srcP[4], dstP[3];
    325     SkPath::Verb    verb;
    326 
    327     while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
    328         switch (verb) {
    329             case SkPath::kMove_Verb:
    330                 morphpoints(dstP, srcP, 1, meas, matrix);
    331                 dst->moveTo(dstP[0]);
    332                 break;
    333             case SkPath::kLine_Verb:
    334                 // turn lines into quads to look bendy
    335                 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
    336                 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
    337                 morphpoints(dstP, srcP, 2, meas, matrix);
    338                 dst->quadTo(dstP[0], dstP[1]);
    339                 break;
    340             case SkPath::kQuad_Verb:
    341                 morphpoints(dstP, &srcP[1], 2, meas, matrix);
    342                 dst->quadTo(dstP[0], dstP[1]);
    343                 break;
    344             case SkPath::kCubic_Verb:
    345                 morphpoints(dstP, &srcP[1], 3, meas, matrix);
    346                 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
    347                 break;
    348             case SkPath::kClose_Verb:
    349                 dst->close();
    350                 break;
    351             default:
    352                 SkDEBUGFAIL("unknown verb");
    353                 break;
    354         }
    355     }
    356 }
    357 
    358 void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t byteLength,
    359                                   const SkPath& follow, const SkMatrix* matrix,
    360                                   const SkPaint& paint) {
    361     SkASSERT(byteLength == 0 || text != nullptr);
    362 
    363     // nothing to draw
    364     if (text == nullptr || byteLength == 0 || draw.fRC->isEmpty()) {
    365         return;
    366     }
    367 
    368     SkTextToPathIter    iter((const char*)text, byteLength, paint, true);
    369     SkPathMeasure       meas(follow, false);
    370     SkScalar            hOffset = 0;
    371 
    372     // need to measure first
    373     if (paint.getTextAlign() != SkPaint::kLeft_Align) {
    374         SkScalar pathLen = meas.getLength();
    375         if (paint.getTextAlign() == SkPaint::kCenter_Align) {
    376             pathLen = SkScalarHalf(pathLen);
    377         }
    378         hOffset += pathLen;
    379     }
    380 
    381     const SkPath*   iterPath;
    382     SkScalar        xpos;
    383     SkMatrix        scaledMatrix;
    384     SkScalar        scale = iter.getPathScale();
    385 
    386     scaledMatrix.setScale(scale, scale);
    387 
    388     while (iter.next(&iterPath, &xpos)) {
    389         if (iterPath) {
    390             SkPath      tmp;
    391             SkMatrix    m(scaledMatrix);
    392 
    393             tmp.setIsVolatile(true);
    394             m.postTranslate(xpos + hOffset, 0);
    395             if (matrix) {
    396                 m.postConcat(*matrix);
    397             }
    398             morphpath(&tmp, *iterPath, meas, m);
    399             this->drawPath(draw, tmp, iter.getPaint(), nullptr, true);
    400         }
    401     }
    402 }
    403 
    404 //////////////////////////////////////////////////////////////////////////////////////////
    405 
    406 void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y,
    407                                       const SkPaint& paint) {
    408     SkImageFilter* filter = paint.getImageFilter();
    409     if (filter && !this->canHandleImageFilter(filter)) {
    410         SkImageFilter::DeviceProxy proxy(this);
    411         SkBitmap dst;
    412         SkIPoint offset = SkIPoint::Make(0, 0);
    413         SkMatrix matrix = *draw.fMatrix;
    414         matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
    415         const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
    416         SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache());
    417         SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
    418         if (filter->filterImageDeprecated(&proxy, bitmap, ctx, &dst, &offset)) {
    419             SkPaint tmpUnfiltered(paint);
    420             tmpUnfiltered.setImageFilter(nullptr);
    421             this->drawSprite(draw, dst, x + offset.x(), y + offset.y(), tmpUnfiltered);
    422         }
    423     } else {
    424         this->drawSprite(draw, bitmap, x, y, paint);
    425     }
    426 }
    427 
    428 uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const {
    429     uint32_t flags = paint.getFlags();
    430 
    431     if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
    432         return flags;
    433     }
    434 
    435     if (kUnknown_SkPixelGeometry == fSurfaceProps.pixelGeometry()
    436         || this->onShouldDisableLCD(paint)) {
    437 
    438         flags &= ~SkPaint::kLCDRenderText_Flag;
    439         flags |= SkPaint::kGenA8FromLCD_Flag;
    440     }
    441 
    442     return flags;
    443 }
    444 
    445