2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "GrSWMaskHelper.h"
10 #include "GrDrawState.h"
11 #include "GrDrawTargetCaps.h"
15 #include "SkStrokeRec.h"
17 // TODO: try to remove this #include
18 #include "GrContext.h"
23 * Convert a boolean operation into a transfer mode code
25 SkXfermode::Mode op_to_mode(SkRegion::Op op) {
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
39 static inline GrPixelConfig fmt_to_config(SkTextureCompressor::Format fmt) {
43 case SkTextureCompressor::kLATC_Format:
44 config = kLATC_GrPixelConfig;
47 case SkTextureCompressor::kR11_EAC_Format:
48 config = kR11_EAC_GrPixelConfig;
51 case SkTextureCompressor::kASTC_12x12_Format:
52 config = kASTC_12x12_GrPixelConfig;
55 case SkTextureCompressor::kETC1_Format:
56 config = kETC1_GrPixelConfig;
60 SkDEBUGFAIL("No GrPixelConfig for compression format!");
62 config = kAlpha_8_GrPixelConfig;
69 static bool choose_compressed_fmt(const GrDrawTargetCaps* caps,
70 SkTextureCompressor::Format *fmt) {
75 // We can't use scratch textures without the ability to update
76 // compressed textures...
77 if (!(caps->compressedTexSubImageSupport())) {
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;
87 } else if (caps->isConfigTexturable(kLATC_GrPixelConfig)) {
88 *fmt = SkTextureCompressor::kLATC_Format;
90 } else if (caps->isConfigTexturable(kR11_EAC_GrPixelConfig)) {
91 *fmt = SkTextureCompressor::kR11_EAC_Format;
101 * Draw a single rect element of the clip stack into the accumulation bitmap
103 void GrSWMaskHelper::draw(const SkRect& rect, SkRegion::Op op,
104 bool antiAlias, uint8_t alpha) {
107 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
109 SkASSERT(kNone_CompressionMode == fCompressionMode);
111 paint.setXfermode(mode);
112 paint.setAntiAlias(antiAlias);
113 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
115 fDraw.drawRect(rect, paint);
121 * Draw a single path element of the clip stack into the accumulation bitmap
123 void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
124 bool antiAlias, uint8_t alpha) {
127 if (stroke.isHairlineStyle()) {
128 paint.setStyle(SkPaint::kStroke_Style);
129 paint.setStrokeWidth(SK_Scalar1);
131 if (stroke.isFillStyle()) {
132 paint.setStyle(SkPaint::kFill_Style);
134 paint.setStyle(SkPaint::kStroke_Style);
135 paint.setStrokeJoin(stroke.getJoin());
136 paint.setStrokeCap(stroke.getCap());
137 paint.setStrokeWidth(stroke.getWidth());
140 paint.setAntiAlias(antiAlias);
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);
150 if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
151 SkASSERT(0xFF == paint.getAlpha());
152 fDraw.drawPathCoverage(path, paint, blitter);
154 paint.setXfermodeMode(op_to_mode(op));
155 paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
156 fDraw.drawPath(path, paint, blitter);
160 bool GrSWMaskHelper::init(const SkIRect& resultBounds,
161 const SkMatrix* matrix,
162 bool allowCompression) {
166 fMatrix.setIdentity();
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());
175 if (allowCompression &&
176 fContext->getOptions().fDrawPathToCompressedTexture &&
177 choose_compressed_fmt(fContext->getGpu()->caps(), &fCompressedFormat)) {
178 fCompressionMode = kCompress_CompressionMode;
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) {
187 SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
188 cmpWidth = dimX * ((cmpWidth + (dimX - 1)) / dimX);
189 cmpHeight = dimY * ((cmpHeight + (dimY - 1)) / dimY);
191 // Can we create a blitter?
192 if (SkTextureCompressor::ExistsBlitterForFormat(fCompressedFormat)) {
193 int cmpSz = SkTextureCompressor::GetCompressedDataSize(
194 fCompressedFormat, cmpWidth, cmpHeight);
197 SkASSERT(NULL == fCompressedBuffer.get());
198 fCompressedBuffer.reset(cmpSz);
199 fCompressionMode = kBlitter_CompressionMode;
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)) {
212 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
214 // Otherwise, we just need to remember how big the buffer is...
215 fBM.setInfo(bmImageInfo);
218 sk_bzero(&fDraw, sizeof(fDraw));
220 fRasterClip.setRect(bounds);
221 fDraw.fRC = &fRasterClip;
222 fDraw.fClip = &fRasterClip.bwRgn();
223 fDraw.fMatrix = &fMatrix;
224 fDraw.fBitmap = &fBM;
229 * Get a texture (from the texture cache) of the correct size & format.
230 * Return true on success; false on failure.
232 bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
234 desc.fWidth = fBM.width();
235 desc.fHeight = fBM.height();
236 desc.fConfig = kAlpha_8_GrPixelConfig;
238 if (kNone_CompressionMode != fCompressionMode) {
242 SkTextureCompressor::GetBlockDimensions(fCompressedFormat, &dimX, &dimY);
243 SkASSERT((desc.fWidth % dimX) == 0);
244 SkASSERT((desc.fHeight % dimY) == 0);
247 desc.fConfig = fmt_to_config(fCompressedFormat);
248 SkASSERT(fContext->getGpu()->caps()->isConfigTexturable(desc.fConfig));
251 texture->set(fContext, desc);
252 return SkToBool(texture->texture());
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();
261 // Since we're uploading to it, and it's compressed, 'texture' shouldn't
262 // have a render target.
263 SkASSERT(NULL == texture->asRenderTarget());
265 texture->writePixels(0, 0, desc.fWidth, desc.fHeight,
266 desc.fConfig, data, rowbytes,
267 reuseScratch ? 0 : GrContext::kDontFlush_PixelOpsFlag);
270 void GrSWMaskHelper::compressTextureData(GrTexture *texture, const GrTextureDesc& desc) {
272 SkASSERT(GrPixelConfigIsCompressed(desc.fConfig));
273 SkASSERT(fmt_to_config(fCompressedFormat) == desc.fConfig);
275 SkAutoDataUnref cmpData(SkTextureCompressor::CompressBitmapToFormat(fBM, fCompressedFormat));
278 this->sendTextureData(texture, desc, cmpData->data(), 0);
282 * Move the result of the software mask generation back to the gpu
284 void GrSWMaskHelper::toTexture(GrTexture *texture) {
285 SkAutoLockPixels alp(fBM);
288 desc.fWidth = fBM.width();
289 desc.fHeight = fBM.height();
290 desc.fConfig = texture->config();
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());
298 case kCompress_CompressionMode:
299 this->compressTextureData(texture, desc);
302 case kBlitter_CompressionMode:
303 SkASSERT(fCompressedBuffer.get());
304 this->sendTextureData(texture, desc, fCompressedBuffer.get(), 0);
309 ////////////////////////////////////////////////////////////////////////////////
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.
315 GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
317 const SkStrokeRec& stroke,
318 const SkIRect& resultBounds,
321 GrSWMaskHelper helper(context);
323 if (!helper.init(resultBounds, matrix)) {
327 helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
329 GrAutoScratchTexture ast;
330 if (!helper.getTexture(&ast)) {
334 helper.toTexture(ast.texture());
339 void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
340 GrDrawTarget* target,
341 const SkIRect& rect) {
342 GrDrawState* drawState = target->drawState();
344 GrDrawState::AutoViewMatrixRestore avmr;
345 if (!avmr.setIdentity(drawState)) {
348 GrDrawState::AutoRestoreEffects are(drawState);
350 SkRect dstRect = SkRect::MakeLTRB(SK_Scalar1 * rect.fLeft,
351 SK_Scalar1 * rect.fTop,
352 SK_Scalar1 * rect.fRight,
353 SK_Scalar1 * rect.fBottom);
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.
360 maskMatrix.setIDiv(texture->width(), texture->height());
361 maskMatrix.preTranslate(SkIntToScalar(-rect.fLeft), SkIntToScalar(-rect.fTop));
362 maskMatrix.preConcat(drawState->getViewMatrix());
364 drawState->addCoverageProcessor(
365 GrSimpleTextureEffect::Create(texture,
367 GrTextureParams::kNone_FilterMode,
368 kPosition_GrCoordSet))->unref();
370 target->drawSimpleRect(dstRect);