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