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 "GrPipelineBuilder.h"
     11 #include "GrDrawTargetCaps.h"
     12 #include "GrGpu.h"
     13 
     14 #include "SkData.h"
     15 #include "SkDistanceFieldGen.h"
     16 #include "SkStrokeRec.h"
     17 
     18 // TODO: try to remove this #include
     19 #include "GrContext.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 GrDrawTargetCaps* caps,
     71                                   SkTextureCompressor::Format *fmt) {
     72     if (NULL == 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 = NULL;
    145     if (kBlitter_CompressionMode == fCompressionMode) {
    146         SkASSERT(fCompressedBuffer.get());
    147         blitter = SkTextureCompressor::CreateBlitterForFormat(
    148             fBM.width(), fBM.height(), fCompressedBuffer.get(), &allocator, fCompressedFormat);
    149     }
    150 
    151     if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
    152         SkASSERT(0xFF == paint.getAlpha());
    153         fDraw.drawPathCoverage(path, paint, blitter);
    154     } else {
    155         paint.setXfermodeMode(op_to_mode(op));
    156         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
    157         fDraw.drawPath(path, paint, blitter);
    158     }
    159 }
    160 
    161 bool GrSWMaskHelper::init(const SkIRect& resultBounds,
    162                           const SkMatrix* matrix,
    163                           bool allowCompression) {
    164     if (matrix) {
    165         fMatrix = *matrix;
    166     } else {
    167         fMatrix.setIdentity();
    168     }
    169 
    170     // Now translate so the bound's UL corner is at the origin
    171     fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
    172                           -resultBounds.fTop * SK_Scalar1);
    173     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
    174                                      resultBounds.height());
    175 
    176     if (allowCompression &&
    177         fContext->getOptions().fDrawPathToCompressedTexture &&
    178         choose_compressed_fmt(fContext->getGpu()->caps(), &fCompressedFormat)) {
    179         fCompressionMode = kCompress_CompressionMode;
    180     }
    181 
    182     // Make sure that the width is a multiple of the desired block dimensions
    183     // to allow for specialized SIMD instructions that compress multiple blocks at a time.
    184     int cmpWidth = bounds.fRight;
    185     int cmpHeight = bounds.fBottom;
    186     if (kCompress_CompressionMode == fCompressionMode) {
    187         int dimX, dimY;
    188         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
    189         cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
    190         cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
    191 
    192         // Can we create a blitter?
    193         if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
    194             int cmpSz = SkTextureCompressor::GetCompressedDataSize(
    195                 fCompressedFormat, cmpWidth, cmpHeight);
    196 
    197             SkASSERT(cmpSz > 0);
    198             SkASSERT(NULL == fCompressedBuffer.get());
    199             fCompressedBuffer.reset(cmpSz);
    200             fCompressionMode = kBlitter_CompressionMode;
    201         }
    202     }
    203 
    204     // If we don't have a custom blitter, then we either need a bitmap to compress
    205     // from or a bitmap that we're going to use as a texture. In any case, we should
    206     // allocate the pixels for a bitmap
    207     const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
    208     if (kBlitter_CompressionMode != fCompressionMode) {
    209         if (!fBM.tryAllocPixels(bmImageInfo)) {
    210             return false;
    211         }
    212 
    213         sk_bzero(fBM.getPixels(), fBM.getSafeSize());
    214     } else {
    215         // Otherwise, we just need to remember how big the buffer is...
    216         fBM.setInfo(bmImageInfo);
    217     }
    218 
    219     sk_bzero(&fDraw, sizeof(fDraw));
    220 
    221     fRasterClip.setRect(bounds);
    222     fDraw.fRC    = &fRasterClip;
    223     fDraw.fClip  = &fRasterClip.bwRgn();
    224     fDraw.fMatrix = &fMatrix;
    225     fDraw.fBitmap = &fBM;
    226     return true;
    227 }
    228 
    229 /**
    230  * Get a texture (from the texture cache) of the correct size & format.
    231  */
    232 GrTexture* GrSWMaskHelper::createTexture() {
    233     GrSurfaceDesc desc;
    234     desc.fWidth = fBM.width();
    235     desc.fHeight = fBM.height();
    236     desc.fConfig = kAlpha_8_GrPixelConfig;
    237 
    238     if (kNone_CompressionMode != fCompressionMode) {
    239 
    240 #ifdef SK_DEBUG
    241         int dimX, dimY;
    242         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
    243         SkASSERT((desc.fWidth % dimX) == 0);
    244         SkASSERT((desc.fHeight % dimY) == 0);
    245 #endif
    246 
    247         desc.fConfig = fmt_to_config(fCompressedFormat);
    248         SkASSERT(fContext->getGpu()->caps()->isConfigTexturable(desc.fConfig));
    249     }
    250 
    251     return fContext->textureProvider()->refScratchTexture(
    252         desc, GrTextureProvider::kApprox_ScratchTexMatch);
    253 }
    254 
    255 void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrSurfaceDesc& desc,
    256                                      const void *data, size_t rowbytes) {
    257     // If we aren't reusing scratch textures we don't need to flush before
    258     // writing since no one else will be using 'texture'
    259     bool reuseScratch = fContext->getGpu()->caps()->reuseScratchTextures();
    260 
    261     // Since we're uploading to it, and it's compressed, 'texture' shouldn't
    262     // have a render target.
    263     SkASSERT(NULL == texture->asRenderTarget());
    264 
    265     texture->writePixels(0, 0, desc.fWidth, desc.fHeight,
    266                          desc.fConfig, data, rowbytes,
    267                          reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag);
    268 }
    269 
    270 void GrSWMaskHelper::compressTextureData(GrTexture *texture, const GrSurfaceDesc& desc) {
    271 
    272     SkASSERT(GrPixelConfigIsCompressed(desc.fConfig));
    273     SkASSERT(fmt_to_config(fCompressedFormat) == desc.fConfig);
    274 
    275     SkAutoDataUnref cmpData(SkTextureCompressor::CompressBitmapToFormat(fBM, fCompressedFormat));
    276     SkASSERT(cmpData);
    277 
    278     this->sendTextureData(texture, desc, cmpData->data(), 0);
    279 }
    280 
    281 /**
    282  * Move the result of the software mask generation back to the gpu
    283  */
    284 void GrSWMaskHelper::toTexture(GrTexture *texture) {
    285     SkAutoLockPixels alp(fBM);
    286 
    287     GrSurfaceDesc desc;
    288     desc.fWidth = fBM.width();
    289     desc.fHeight = fBM.height();
    290     desc.fConfig = texture->config();
    291 
    292     // First see if we should compress this texture before uploading.
    293     switch (fCompressionMode) {
    294         case kNone_CompressionMode:
    295             this->sendTextureData(texture, desc, fBM.getPixels(), fBM.rowBytes());
    296             break;
    297 
    298         case kCompress_CompressionMode:
    299             this->compressTextureData(texture, desc);
    300             break;
    301 
    302         case kBlitter_CompressionMode:
    303             SkASSERT(fCompressedBuffer.get());
    304             this->sendTextureData(texture, desc, fCompressedBuffer.get(), 0);
    305             break;
    306     }
    307 }
    308 
    309 /**
    310  * Convert mask generation results to a signed distance field
    311  */
    312 void GrSWMaskHelper::toSDF(unsigned char* sdf) {
    313     SkAutoLockPixels alp(fBM);
    314 
    315     SkGenerateDistanceFieldFromA8Image(sdf, (const unsigned char*)fBM.getPixels(),
    316                                        fBM.width(), fBM.height(), fBM.rowBytes());
    317 }
    318 
    319 ////////////////////////////////////////////////////////////////////////////////
    320 /**
    321  * Software rasterizes path to A8 mask (possibly using the context's matrix)
    322  * and uploads the result to a scratch texture. Returns the resulting
    323  * texture on success; NULL on failure.
    324  */
    325 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
    326                                                  const SkPath& path,
    327                                                  const SkStrokeRec& stroke,
    328                                                  const SkIRect& resultBounds,
    329                                                  bool antiAlias,
    330                                                  const SkMatrix* matrix) {
    331     GrSWMaskHelper helper(context);
    332 
    333     if (!helper.init(resultBounds, matrix)) {
    334         return NULL;
    335     }
    336 
    337     helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
    338 
    339     GrTexture* texture(helper.createTexture());
    340     if (!texture) {
    341         return NULL;
    342     }
    343 
    344     helper.toTexture(texture);
    345 
    346     return texture;
    347 }
    348 
    349 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
    350                                               GrDrawTarget* target,
    351                                               GrPipelineBuilder* pipelineBuilder,
    352                                               GrColor color,
    353                                               const SkMatrix& viewMatrix,
    354                                               const SkIRect& rect) {
    355     SkMatrix invert;
    356     if (!viewMatrix.invert(&invert)) {
    357         return;
    358     }
    359     GrPipelineBuilder::AutoRestoreFragmentProcessors arfp(pipelineBuilder);
    360 
    361     SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
    362                                       SK_Scalar1 * rect.fTop,
    363                                       SK_Scalar1 * rect.fRight,
    364                                       SK_Scalar1 * rect.fBottom);
    365 
    366     // We use device coords to compute the texture coordinates. We take the device coords and apply
    367     // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
    368     // matrix to normalized coords.
    369     SkMatrix maskMatrix;
    370     maskMatrix.setIDiv(texture->width(), texture->height());
    371     maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
    372 
    373     pipelineBuilder->addCoverageProcessor(
    374                          GrSimpleTextureEffect::Create(texture,
    375                                                        maskMatrix,
    376                                                        GrTextureParams::kNone_FilterMode,
    377                                                        kDevice_GrCoordSet))->unref();
    378 
    379     target->drawRect(pipelineBuilder, color, SkMatrix::I(), dstRect, NULL, &invert);
    380 }
    381