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 "GrDrawState.h"
     11 #include "GrDrawTargetCaps.h"
     12 #include "GrGpu.h"
     13 
     14 #include "SkData.h"
     15 #include "SkStrokeRec.h"
     16 
     17 // TODO: try to remove this #include
     18 #include "GrContext.h"
     19 
     20 namespace {
     21 
     22 /*
     23  * Convert a boolean operation into a transfer mode code
     24  */
     25 SkXfermode::Mode op_to_mode(SkRegion::Op op) {
     26 
     27     static const SkXfermode::Mode modeMap[] = {
     28         SkXfermode::kDstOut_Mode,   // kDifference_Op
     29         SkXfermode::kModulate_Mode, // kIntersect_Op
     30         SkXfermode::kSrcOver_Mode,  // kUnion_Op
     31         SkXfermode::kXor_Mode,      // kXOR_Op
     32         SkXfermode::kClear_Mode,    // kReverseDifference_Op
     33         SkXfermode::kSrc_Mode,      // kReplace_Op
     34     };
     35 
     36     return modeMap[op];
     37 }
     38 
     39 static inline GrPixelConfig fmt_to_config(SkTextureCompressor::Format fmt) {
     40 
     41     GrPixelConfig config;
     42     switch (fmt) {
     43         case SkTextureCompressor::kLATC_Format:
     44             config = kLATC_GrPixelConfig;
     45             break;
     46 
     47         case SkTextureCompressor::kR11_EAC_Format:
     48             config = kR11_EAC_GrPixelConfig;
     49             break;
     50 
     51         case SkTextureCompressor::kASTC_12x12_Format:
     52             config = kASTC_12x12_GrPixelConfig;
     53             break;
     54 
     55         case SkTextureCompressor::kETC1_Format:
     56             config = kETC1_GrPixelConfig;
     57             break;
     58 
     59         default:
     60             SkDEBUGFAIL("No GrPixelConfig for compression format!");
     61             // Best guess
     62             config = kAlpha_8_GrPixelConfig;
     63             break;
     64     }
     65 
     66     return config;
     67 }
     68 
     69 static bool choose_compressed_fmt(const GrDrawTargetCaps* caps,
     70                                   SkTextureCompressor::Format *fmt) {
     71     if (NULL == fmt) {
     72         return false;
     73     }
     74 
     75     // We can't use scratch textures without the ability to update
     76     // compressed textures...
     77     if (!(caps->compressedTexSubImageSupport())) {
     78         return false;
     79     }
     80 
     81     // Figure out what our preferred texture type is. If ASTC is available, that always
     82     // gives the biggest win. Otherwise, in terms of compression speed and accuracy,
     83     // LATC has a slight edge over R11 EAC.
     84     if (caps->isConfigTexturable(kASTC_12x12_GrPixelConfig)) {
     85         *fmt = SkTextureCompressor::kASTC_12x12_Format;
     86         return true;
     87     } else if (caps->isConfigTexturable(kLATC_GrPixelConfig)) {
     88         *fmt = SkTextureCompressor::kLATC_Format;
     89         return true;
     90     } else if (caps->isConfigTexturable(kR11_EAC_GrPixelConfig)) {
     91         *fmt = SkTextureCompressor::kR11_EAC_Format;
     92         return true;
     93     }
     94 
     95     return false;
     96 }
     97 
     98 }
     99 
    100 /**
    101  * Draw a single rect element of the clip stack into the accumulation bitmap
    102  */
    103 void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
    104                           bool antiAlias, uint8_t alpha) {
    105     SkPaint paint;
    106 
    107     SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
    108 
    109     SkASSERT(kNone_CompressionMode == fCompressionMode);
    110 
    111     paint.setXfermode(mode);
    112     paint.setAntiAlias(antiAlias);
    113     paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
    114 
    115     fDraw.drawRect(rect, paint);
    116 
    117     SkSafeUnref(mode);
    118 }
    119 
    120 /**
    121  * Draw a single path element of the clip stack into the accumulation bitmap
    122  */
    123 void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
    124                           bool antiAlias, uint8_t alpha) {
    125 
    126     SkPaint paint;
    127     if (stroke.isHairlineStyle()) {
    128         paint.setStyle(SkPaint::kStroke_Style);
    129         paint.setStrokeWidth(SK_Scalar1);
    130     } else {
    131         if (stroke.isFillStyle()) {
    132             paint.setStyle(SkPaint::kFill_Style);
    133         } else {
    134             paint.setStyle(SkPaint::kStroke_Style);
    135             paint.setStrokeJoin(stroke.getJoin());
    136             paint.setStrokeCap(stroke.getCap());
    137             paint.setStrokeWidth(stroke.getWidth());
    138         }
    139     }
    140     paint.setAntiAlias(antiAlias);
    141 
    142     SkTBlitterAllocator allocator;
    143     SkBlitter* blitter = NULL;
    144     if (kBlitter_CompressionMode == fCompressionMode) {
    145         SkASSERT(fCompressedBuffer.get());
    146         blitter = SkTextureCompressor::CreateBlitterForFormat(
    147             fBM.width(), fBM.height(), fCompressedBuffer.get(), &allocator, fCompressedFormat);
    148     }
    149 
    150     if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
    151         SkASSERT(0xFF == paint.getAlpha());
    152         fDraw.drawPathCoverage(path, paint, blitter);
    153     } else {
    154         paint.setXfermodeMode(op_to_mode(op));
    155         paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
    156         fDraw.drawPath(path, paint, blitter);
    157     }
    158 }
    159 
    160 bool GrSWMaskHelper::init(const SkIRect& resultBounds,
    161                           const SkMatrix* matrix,
    162                           bool allowCompression) {
    163     if (matrix) {
    164         fMatrix = *matrix;
    165     } else {
    166         fMatrix.setIdentity();
    167     }
    168 
    169     // Now translate so the bound's UL corner is at the origin
    170     fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
    171                           -resultBounds.fTop * SK_Scalar1);
    172     SkIRect bounds = SkIRect::MakeWH(resultBounds.width(),
    173                                      resultBounds.height());
    174 
    175     if (allowCompression &&
    176         fContext->getOptions().fDrawPathToCompressedTexture &&
    177         choose_compressed_fmt(fContext->getGpu()->caps(), &fCompressedFormat)) {
    178         fCompressionMode = kCompress_CompressionMode;
    179     }
    180 
    181     // Make sure that the width is a multiple of the desired block dimensions
    182     // to allow for specialized SIMD instructions that compress multiple blocks at a time.
    183     int cmpWidth = bounds.fRight;
    184     int cmpHeight = bounds.fBottom;
    185     if (kCompress_CompressionMode == fCompressionMode) {
    186         int dimX, dimY;
    187         SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
    188         cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
    189         cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
    190 
    191         // Can we create a blitter?
    192         if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
    193             int cmpSz = SkTextureCompressor::GetCompressedDataSize(
    194                 fCompressedFormat, cmpWidth, cmpHeight);
    195 
    196             SkASSERT(cmpSz > 0);
    197             SkASSERT(NULL == fCompressedBuffer.get());
    198             fCompressedBuffer.reset(cmpSz);
    199             fCompressionMode = kBlitter_CompressionMode;
    200         }
    201     }
    202 
    203     // If we don't have a custom blitter, then we either need a bitmap to compress
    204     // from or a bitmap that we're going to use as a texture. In any case, we should
    205     // allocate the pixels for a bitmap
    206     const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(cmpWidth, cmpHeight);
    207     if (kBlitter_CompressionMode != fCompressionMode) {
    208         if (!fBM.tryAllocPixels(bmImageInfo)) {
    209             return false;
    210         }
    211 
    212         sk_bzero(fBM.getPixels(), fBM.getSafeSize());
    213     } else {
    214         // Otherwise, we just need to remember how big the buffer is...
    215         fBM.setInfo(bmImageInfo);
    216     }
    217 
    218     sk_bzero(&fDraw, sizeof(fDraw));
    219 
    220     fRasterClip.setRect(bounds);
    221     fDraw.fRC    = &fRasterClip;
    222     fDraw.fClip  = &fRasterClip.bwRgn();
    223     fDraw.fMatrix = &fMatrix;
    224     fDraw.fBitmap = &fBM;
    225     return true;
    226 }
    227 
    228 /**
    229  * Get a texture (from the texture cache) of the correct size & format.
    230  * Return true on success; false on failure.
    231  */
    232 bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
    233     GrTextureDesc 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     texture->set(fContext, desc);
    252     return SkToBool(texture->texture());
    253 }
    254 
    255 void GrSWMaskHelper::sendTextureData(GrTexture *texture, const GrTextureDesc& desc,
    256                                      const void *data, int 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 GrTextureDesc& 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     GrTextureDesc 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 /**
    311  * Software rasterizes path to A8 mask (possibly using the context's matrix)
    312  * and uploads the result to a scratch texture. Returns the resulting
    313  * texture on success; NULL on failure.
    314  */
    315 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
    316                                                  const SkPath& path,
    317                                                  const SkStrokeRec& stroke,
    318                                                  const SkIRect& resultBounds,
    319                                                  bool antiAlias,
    320                                                  SkMatrix* matrix) {
    321     GrSWMaskHelper helper(context);
    322 
    323     if (!helper.init(resultBounds, matrix)) {
    324         return NULL;
    325     }
    326 
    327     helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
    328 
    329     GrAutoScratchTexture ast;
    330     if (!helper.getTexture(&ast)) {
    331         return NULL;
    332     }
    333 
    334     helper.toTexture(ast.texture());
    335 
    336     return ast.detach();
    337 }
    338 
    339 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
    340                                               GrDrawTarget* target,
    341                                               const SkIRect& rect) {
    342     GrDrawState* drawState = target->drawState();
    343 
    344     GrDrawState::AutoViewMatrixRestore avmr;
    345     if (!avmr.setIdentity(drawState)) {
    346         return;
    347     }
    348     GrDrawState::AutoRestoreEffects are(drawState);
    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 want to use device coords to compute the texture coordinates. We set our matrix to be
    356     // equal to the view matrix followed by a translation so that the top-left of the device bounds
    357     // maps to 0,0, and then a scaling matrix to normalized coords. We apply this matrix to the
    358     // vertex positions rather than local coords.
    359     SkMatrix maskMatrix;
    360     maskMatrix.setIDiv(texture->width(), texture->height());
    361     maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
    362     maskMatrix.preConcat(drawState->getViewMatrix());
    363 
    364     drawState->addCoverageProcessor(
    365                          GrSimpleTextureEffect::Create(texture,
    366                                                        maskMatrix,
    367                                                        GrTextureParams::kNone_FilterMode,
    368                                                        kPosition_GrCoordSet))->unref();
    369 
    370     target->drawSimpleRect(dstRect);
    371 }
    372