Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2012 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 "GrSWMaskHelper.h"
      9 
     10 #include "GrCaps.h"
     11 #include "GrDrawTarget.h"
     12 #include "GrGpu.h"
     13 #include "GrPipelineBuilder.h"
     14 
     15 #include "SkData.h"
     16 #include "SkDistanceFieldGen.h"
     17 #include "SkStrokeRec.h"
     18 
     19 #include "batches/GrRectBatchFactory.h"
     20 
     21 namespace {
     22 
     23 /*
     24  * Convert a boolean operation into a transfer mode code
     25  */
     26 SkXfermode::Mode op_to_mode(SkRegion::Op op) {
     27 
     28     static const SkXfermode::Mode modeMap[] = {
     29         SkXfermode::kDstOut_Mode,   // kDifference_Op
     30         SkXfermode::kModulate_Mode, // kIntersect_Op
     31         SkXfermode::kSrcOver_Mode,  // kUnion_Op
     32         SkXfermode::kXor_Mode,      // kXOR_Op
     33         SkXfermode::kClear_Mode,    // kReverseDifference_Op
     34         SkXfermode::kSrc_Mode,      // kReplace_Op
     35     };
     36 
     37     return modeMap[op];
     38 }
     39 
     40 static inline GrPixelConfig fmt_to_config(SkTextureCompressor::Format fmt) {
     41 
     42     GrPixelConfig config;
     43     switch (fmt) {
     44         case SkTextureCompressor::kLATC_Format:
     45             config = kLATC_GrPixelConfig;
     46             break;
     47 
     48         case SkTextureCompressor::kR11_EAC_Format:
     49             config = kR11_EAC_GrPixelConfig;
     50             break;
     51 
     52         case SkTextureCompressor::kASTC_12x12_Format:
     53             config = kASTC_12x12_GrPixelConfig;
     54             break;
     55 
     56         case SkTextureCompressor::kETC1_Format:
     57             config = kETC1_GrPixelConfig;
     58             break;
     59 
     60         default:
     61             SkDEBUGFAIL("No GrPixelConfig for compression format!");
     62             // Best guess
     63             config = kAlpha_8_GrPixelConfig;
     64             break;
     65     }
     66 
     67     return config;
     68 }
     69 
     70 static bool choose_compressed_fmt(const GrCaps* caps,
     71                                   SkTextureCompressor::Format *fmt) {
     72     if (nullptr == fmt) {
     73         return false;
     74     }
     75 
     76     // We can't use scratch textures without the ability to update
     77     // compressed textures...
     78     if (!(caps->compressedTexSubImageSupport())) {
     79         return false;
     80     }
     81 
     82     // Figure out what our preferred texture type is. If ASTC is available, that always
     83     // gives the biggest win. Otherwise, in terms of compression speed and accuracy,
     84     // LATC has a slight edge over R11 EAC.
     85     if (caps->isConfigTexturable(kASTC_12x12_GrPixelConfig)) {
     86         *fmt = SkTextureCompressor::kASTC_12x12_Format;
     87         return true;
     88     } else if (caps->isConfigTexturable(kLATC_GrPixelConfig)) {
     89         *fmt = SkTextureCompressor::kLATC_Format;
     90         return true;
     91     } else if (caps->isConfigTexturable(kR11_EAC_GrPixelConfig)) {
     92         *fmt = SkTextureCompressor::kR11_EAC_Format;
     93         return true;
     94     }
     95 
     96     return false;
     97 }
     98 
     99 }
    100 
    101 /**
    102  * Draw a single rect element of the clip stack into the accumulation bitmap
    103  */
    104 void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
    105                           bool antiAlias, uint8_t alpha) {
    106     SkPaint paint;
    107 
    108     SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
    109 
    110     SkASSERT(kNone_CompressionMode == fCompressionMode);
    111 
    112     paint.setXfermode(mode);
    113     paint.setAntiAlias(antiAlias);
    114     paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
    115 
    116     fDraw.drawRect(rect, paint);
    117 
    118     SkSafeUnref(mode);
    119 }
    120 
    121 /**
    122  * Draw a single path element of the clip stack into the accumulation bitmap
    123  */
    124 void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
    125                           bool antiAlias, uint8_t alpha) {
    126 
    127     SkPaint paint;
    128     if (stroke.isHairlineStyle()) {
    129         paint.setStyle(SkPaint::kStroke_Style);
    130         paint.setStrokeWidth(SK_Scalar1);
    131     } else {
    132         if (stroke.isFillStyle()) {
    133             paint.setStyle(SkPaint::kFill_Style);
    134         } else {
    135             paint.setStyle(SkPaint::kStroke_Style);
    136             paint.setStrokeJoin(stroke.getJoin());
    137             paint.setStrokeCap(stroke.getCap());
    138             paint.setStrokeWidth(stroke.getWidth());
    139         }
    140     }
    141     paint.setAntiAlias(antiAlias);
    142 
    143     SkTBlitterAllocator allocator;
    144     SkBlitter* blitter = nullptr;
    145     if (kBlitter_CompressionMode == fCompressionMode) {
    146         SkASSERT(fCompressedBuffer.get());
    147         blitter = SkTextureCompressor::CreateBlitterForFormat(
    148             fPixels.width(), fPixels.height(), fCompressedBuffer.get(), &allocator,
    149                                                               fCompressedFormat);
    150     }
    151 
    152     if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
    153         SkASSERT(0xFF == paint.getAlpha());
    154         fDraw.drawPathCoverage(path, paint, blitter);
    155     } else {
    156         paint.setXfermodeMode(op_to_mode(op));
    157         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
    158         fDraw.drawPath(path, paint, blitter);
    159     }
    160 }
    161 
    162 bool GrSWMaskHelper::init(const SkIRect& resultBounds,
    163                           const SkMatrix* matrix,
    164                           bool allowCompression) {
    165     if (matrix) {
    166         fMatrix = *matrix;
    167     } else {
    168         fMatrix.setIdentity();
    169     }
    170 
    171     // Now translate so the bound's UL corner is at the origin
    172     fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
    173                           -resultBounds.fTop * SK_Scalar1);
    174     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
    175                                      resultBounds.height());
    176 
    177     if (allowCompression &&
    178         fContext->caps()->drawPathMasksToCompressedTexturesSupport() &&
    179         choose_compressed_fmt(fContext->caps(), &fCompressedFormat)) {
    180         fCompressionMode = kCompress_CompressionMode;
    181     }
    182 
    183     // Make sure that the width is a multiple of the desired block dimensions
    184     // to allow for specialized SIMD instructions that compress multiple blocks at a time.
    185     int cmpWidth = bounds.fRight;
    186     int cmpHeight = bounds.fBottom;
    187     if (kCompress_CompressionMode == fCompressionMode) {
    188         int dimX, dimY;
    189         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
    190         cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
    191         cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
    192 
    193         // Can we create a blitter?
    194         if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
    195             int cmpSz = SkTextureCompressor::GetCompressedDataSize(
    196                 fCompressedFormat, cmpWidth, cmpHeight);
    197 
    198             SkASSERT(cmpSz > 0);
    199             SkASSERT(nullptr == fCompressedBuffer.get());
    200             fCompressedBuffer.reset(cmpSz);
    201             fCompressionMode = kBlitter_CompressionMode;
    202         }
    203     }
    204 
    205     sk_bzero(&fDraw, sizeof(fDraw));
    206 
    207     // If we don't have a custom blitter, then we either need a bitmap to compress
    208     // from or a bitmap that we're going to use as a texture. In any case, we should
    209     // allocate the pixels for a bitmap
    210     const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
    211     if (kBlitter_CompressionMode != fCompressionMode) {
    212         if (!fPixels.tryAlloc(bmImageInfo)) {
    213             return false;
    214         }
    215         fPixels.erase(0);
    216     } else {
    217         // Otherwise, we just need to remember how big the buffer is...
    218         fPixels.reset(bmImageInfo);
    219     }
    220     fDraw.fDst      = fPixels;
    221     fRasterClip.setRect(bounds);
    222     fDraw.fRC       = &fRasterClip;
    223     fDraw.fClip     = &fRasterClip.bwRgn();
    224     fDraw.fMatrix   = &fMatrix;
    225     return true;
    226 }
    227 
    228 /**
    229  * Get a texture (from the texture cache) of the correct size & format.
    230  */
    231 GrTexture* GrSWMaskHelper::createTexture() {
    232     GrSurfaceDesc desc;
    233     desc.fWidth = fPixels.width();
    234     desc.fHeight = fPixels.height();
    235     desc.fConfig = kAlpha_8_GrPixelConfig;
    236 
    237     if (kNone_CompressionMode != fCompressionMode) {
    238 
    239 #ifdef SK_DEBUG
    240         int dimX, dimY;
    241         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
    242         SkASSERT((desc.fWidth % dimX) == 0);
    243         SkASSERT((desc.fHeight % dimY) == 0);
    244 #endif
    245 
    246         desc.fConfig = fmt_to_config(fCompressedFormat);
    247         SkASSERT(fContext->caps()->isConfigTexturable(desc.fConfig));
    248     }
    249 
    250     return fContext->textureProvider()->createApproxTexture(desc);
    251 }
    252 
    253 void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrSurfaceDesc& desc,
    254                                      const void *data, size_t rowbytes) {
    255     // Since we're uploading to it, and it's compressed, 'texture' shouldn't
    256     // have a render target.
    257     SkASSERT(nullptr == texture->asRenderTarget());
    258 
    259     texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig, data, rowbytes);
    260 }
    261 
    262 void GrSWMaskHelper::compressTextureData(GrTexture *texture, const GrSurfaceDesc& desc) {
    263 
    264     SkASSERT(GrPixelConfigIsCompressed(desc.fConfig));
    265     SkASSERT(fmt_to_config(fCompressedFormat) == desc.fConfig);
    266 
    267     SkAutoDataUnref cmpData(SkTextureCompressor::CompressBitmapToFormat(fPixels,
    268                                                                         fCompressedFormat));
    269     SkASSERT(cmpData);
    270 
    271     this->sendTextureData(texture, desc, cmpData->data(), 0);
    272 }
    273 
    274 /**
    275  * Move the result of the software mask generation back to the gpu
    276  */
    277 void GrSWMaskHelper::toTexture(GrTexture *texture) {
    278     GrSurfaceDesc desc;
    279     desc.fWidth = fPixels.width();
    280     desc.fHeight = fPixels.height();
    281     desc.fConfig = texture->config();
    282 
    283     // First see if we should compress this texture before uploading.
    284     switch (fCompressionMode) {
    285         case kNone_CompressionMode:
    286             this->sendTextureData(texture, desc, fPixels.addr(), fPixels.rowBytes());
    287             break;
    288 
    289         case kCompress_CompressionMode:
    290             this->compressTextureData(texture, desc);
    291             break;
    292 
    293         case kBlitter_CompressionMode:
    294             SkASSERT(fCompressedBuffer.get());
    295             this->sendTextureData(texture, desc, fCompressedBuffer.get(), 0);
    296             break;
    297     }
    298 }
    299 
    300 /**
    301  * Convert mask generation results to a signed distance field
    302  */
    303 void GrSWMaskHelper::toSDF(unsigned char* sdf) {
    304     SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fPixels.addr(),
    305                                        fPixels.width(), fPixels.height(), fPixels.rowBytes());
    306 }
    307 
    308 ////////////////////////////////////////////////////////////////////////////////
    309 /**
    310  * Software rasterizes path to A8 mask (possibly using the context's matrix)
    311  * and uploads the result to a scratch texture. Returns the resulting
    312  * texture on success; nullptr on failure.
    313  */
    314 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
    315                                                  const SkPath& path,
    316                                                  const SkStrokeRec& stroke,
    317                                                  const SkIRect& resultBounds,
    318                                                  bool antiAlias,
    319                                                  const SkMatrix* matrix) {
    320     GrSWMaskHelper helper(context);
    321 
    322     if (!helper.init(resultBounds, matrix)) {
    323         return nullptr;
    324     }
    325 
    326     helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
    327 
    328     GrTexture* texture(helper.createTexture());
    329     if (!texture) {
    330         return nullptr;
    331     }
    332 
    333     helper.toTexture(texture);
    334 
    335     return texture;
    336 }
    337 
    338 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
    339                                               GrDrawTarget* target,
    340                                               GrPipelineBuilder* pipelineBuilder,
    341                                               GrColor color,
    342                                               const SkMatrix& viewMatrix,
    343                                               const SkIRect& rect) {
    344     SkMatrix invert;
    345     if (!viewMatrix.invert(&invert)) {
    346         return;
    347     }
    348     GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps(*pipelineBuilder);
    349 
    350     SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
    351                                       SK_Scalar1 * rect.fTop,
    352                                       SK_Scalar1 * rect.fRight,
    353                                       SK_Scalar1 * rect.fBottom);
    354 
    355     // We use device coords to compute the texture coordinates. We take the device coords and apply
    356     // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
    357     // matrix to normalized coords.
    358     SkMatrix maskMatrix;
    359     maskMatrix.setIDiv(texture->width(), texture->height());
    360     maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
    361 
    362     pipelineBuilder->addCoverageFragmentProcessor(
    363                          GrSimpleTextureEffect::Create(texture,
    364                                                        maskMatrix,
    365                                                        GrTextureParams::kNone_FilterMode,
    366                                                        kDevice_GrCoordSet))->unref();
    367 
    368     SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(),
    369                                                                         dstRect, nullptr, &invert));
    370     target->drawBatch(*pipelineBuilder, batch);
    371 }
    372