SkRect SkImageFilter::computeFastBounds(const SkRect& src) const { if (0 == this->countInputs()) { return src; } SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; for (int i = 1; i < this->countInputs(); i++) { SkImageFilter* input = this->getInput(i); if (input) { combinedBounds.join(input->computeFastBounds(src)); } else { combinedBounds.join(src); } } return combinedBounds; }
// doesn't work yet void comparePathsTiny(const SkPath& one, const SkPath& two) { const SkRect& bounds1 = one.getBounds(); const SkRect& bounds2 = two.getBounds(); SkRect larger = bounds1; larger.join(bounds2); SkBitmap bits; int bitWidth = SkScalarCeil(larger.width()) + 2; int bitHeight = SkScalarCeil(larger.height()) + 2; bits.setConfig(SkBitmap::kA1_Config, bitWidth * 2, bitHeight); bits.allocPixels(); SkCanvas canvas(bits); canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(one, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds2.fLeft + 1, -bounds2.fTop + 1); canvas.drawPath(two, paint); canvas.restore(); for (int y = 0; y < bitHeight; ++y) { uint8_t* addr1 = bits.getAddr1(0, y); uint8_t* addr2 = bits.getAddr1(bitWidth, y); for (int x = 0; x < bits.rowBytes(); ++x) { SkASSERT(addr1[x] == addr2[x]); } } }
void GrDrawTarget::drawPaths(int pathCount, const GrPath** paths, const SkMatrix* transforms, SkPath::FillType fill, SkStrokeRec::Style stroke) { SkASSERT(pathCount > 0); SkASSERT(NULL != paths); SkASSERT(NULL != paths[0]); SkASSERT(this->caps()->pathRenderingSupport()); SkASSERT(!SkPath::IsInverseFillType(fill)); const GrDrawState* drawState = &getDrawState(); SkRect devBounds; for (int i = 0; i < pathCount; ++i) { SkRect mappedPathBounds; transforms[i].mapRect(&mappedPathBounds, paths[i]->getBounds()); devBounds.join(mappedPathBounds); } SkMatrix viewM = drawState->getViewMatrix(); viewM.mapRect(&devBounds); GrDeviceCoordTexture dstCopy; if (!this->setupDstReadIfNecessary(&dstCopy, &devBounds)) { return; } this->onDrawPaths(pathCount, paths, transforms, fill, stroke, dstCopy.texture() ? &dstCopy : NULL); }
static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); larger.fLeft *= hScale; larger.fRight *= hScale; larger.fTop *= vScale; larger.fBottom *= vScale; SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft : 16000 < larger.fRight ? 16000 - larger.fRight : 0; SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; scale.postTranslate(dx, dy); }
SkRect FindCanvas::addMatchPos(int index, const SkPaint& paint, int count, const uint16_t* glyphs, const SkScalar xPos[], SkScalar /* y */) { SkRect r; r.setEmpty(); const SkPoint* temp = reinterpret_cast<const SkPoint*> (xPos); const SkPoint* points = &temp[index]; int countInBytes = count * sizeof(uint16_t); SkPaint::FontMetrics fontMetrics; paint.getFontMetrics(&fontMetrics); // Need to check each character individually, since the heights may be // different. for (int j = 0; j < count; j++) { SkRect bounds; bounds.fLeft = points[j].fX; bounds.fRight = bounds.fLeft + paint.measureText(&glyphs[j], sizeof(uint16_t), 0); SkScalar baseline = points[j].fY; bounds.fTop = baseline + fontMetrics.fAscent; bounds.fBottom = baseline + fontMetrics.fDescent; /* Accumulate and then add the resulting rect to mMatches */ r.join(bounds); } SkMatrix matrix = getTotalMatrix(); matrix.mapRect(&r); SkCanvas* canvas = getWorkingCanvas(); int saveCount = canvas->save(); canvas->concat(matrix); canvas->drawPosText(glyphs, countInBytes, points, paint); canvas->restoreToCount(saveCount); return r; }
SkRect SkDropShadowImageFilter::computeFastBounds(const SkRect& src) const { SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; SkRect shadowBounds = bounds; shadowBounds.offset(fDx, fDy); shadowBounds.outset(fSigmaX * 3, fSigmaY * 3); if (fShadowMode == kDrawShadowAndForeground_ShadowMode) { bounds.join(shadowBounds); } else { bounds = shadowBounds; } return bounds; }
bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) { if (!drawPaths) { return true; } if (gShowAsciiPaths) { showPath(one, "one:"); showPath(two, "two:"); } const SkRect& bounds1 = one.getBounds(); const SkRect& bounds2 = two.getBounds(); SkRect larger = bounds1; larger.join(bounds2); SkBitmap bits; char out[256]; int bitWidth = SkScalarCeil(larger.width()) + 2; if (bitWidth * 2 + 1 >= (int) sizeof(out)) { return false; } int bitHeight = SkScalarCeil(larger.height()) + 2; if (bitHeight >= (int) sizeof(out)) { return false; } bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight); bits.allocPixels(); SkCanvas canvas(bits); canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(one, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1); canvas.drawPath(two, paint); canvas.restore(); for (int y = 0; y < bitHeight; ++y) { uint32_t* addr1 = bits.getAddr32(0, y); int x; char* outPtr = out; for (x = 0; x < bitWidth; ++x) { *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; } *outPtr++ = '|'; for (x = bitWidth; x < bitWidth * 2; ++x) { *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; } *outPtr++ = '\0'; SkDebugf("%s\n", out); } return true; }
static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); }
static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkCanvas* c) { SkCanvas* canvasPtr = c; if (!c) { canvasPtr = new SkCanvas(bits); } const SkRect& bounds1 = one.getBounds(); const SkRect& bounds2 = two.getBounds(); SkRect larger = bounds1; larger.join(bounds2); int bitWidth = SkScalarCeil(larger.width()) + 2; int bitHeight = SkScalarCeil(larger.height()) + 2; if (bits.width() < bitWidth * 2 || bits.height() < bitHeight) { if (bits.width() >= 200) { SkDebugf("%s bitWidth=%d bitHeight=%d\n", __FUNCTION__, bitWidth, bitHeight); } bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight); bits.allocPixels(); canvasPtr->setBitmapDevice(bits); } SkCanvas& canvas = *canvasPtr; canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(one, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); canvas.drawPath(two, paint); canvas.restore(); int errors = 0; for (int y = 0; y < bitHeight; ++y) { uint32_t* addr1 = bits.getAddr32(0, y); uint32_t* addr2 = bits.getAddr32(bitWidth, y); for (int x = 0; x < bitWidth; ++x) { errors += addr1[x] != addr2[x]; } } if (!c) { delete canvasPtr; } return errors; }
SkPDFImageShader* SkPDFImageShader::Create( SkPDFCanon* canon, SkScalar dpi, SkAutoTDelete<SkPDFShader::State>* autoState) { const SkPDFShader::State& state = **autoState; state.fImage.lockPixels(); // The image shader pattern cell will be drawn into a separate device // in pattern cell space (no scaling on the bitmap, though there may be // translations so that all content is in the device, coordinates > 0). // Map clip bounds to shader space to ensure the device is large enough // to handle fake clamping. SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); SkRect deviceBounds; deviceBounds.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) { return NULL; } const SkBitmap* image = &state.fImage; SkRect bitmapBounds; image->getBounds(&bitmapBounds); // For tiling modes, the bounds should be extended to include the bitmap, // otherwise the bitmap gets clipped out and the shader is empty and awful. // For clamp modes, we're only interested in the clip region, whether // or not the main bitmap is in it. SkShader::TileMode tileModes[2]; tileModes[0] = state.fImageTileModes[0]; tileModes[1] = state.fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); } SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), SkScalarRoundToInt(deviceBounds.height())); SkAutoTUnref<SkPDFDevice> patternDevice( SkPDFDevice::CreateUnflipped(size, dpi, canon)); SkCanvas canvas(patternDevice.get()); SkRect patternBBox; image->getBounds(&patternBBox); // Translate the canvas so that the bitmap origin is at (0, 0). canvas.translate(-deviceBounds.left(), -deviceBounds.top()); patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); // Undo the translation in the final matrix finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); // If the bitmap is out of bounds (i.e. clamp mode where we only see the // stretched sides), canvas will clip this out and the extraneous data // won't be saved to the PDF. canvas.drawBitmap(*image, 0, 0); SkScalar width = SkIntToScalar(image->width()); SkScalar height = SkIntToScalar(image->height()); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, *image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, *image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); drawBitmapMatrix(&canvas, *image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, deviceBounds.top(), deviceBounds.right(), 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, deviceBounds.right(), deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, image->height() - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(deviceBounds.left(), height, 0, deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, image->height() - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); if (deviceBounds.left() < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-deviceBounds.left(), 1); leftMatrix.postTranslate(deviceBounds.left(), 0); drawBitmapMatrix(&canvas, left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, left, leftMatrix); } patternBBox.fLeft = 0; } if (deviceBounds.right() > width) { SkBitmap right; subset.offset(image->width() - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(deviceBounds.right() - width, 1); rightMatrix.postTranslate(width, 0); drawBitmapMatrix(&canvas, right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, right, rightMatrix); } patternBBox.fRight = deviceBounds.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); if (deviceBounds.top() < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); topMatrix.postTranslate(0, deviceBounds.top()); drawBitmapMatrix(&canvas, top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, top, topMatrix); } patternBBox.fTop = 0; } if (deviceBounds.bottom() > height) { SkBitmap bottom; subset.offset(0, image->height() - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); bottomMatrix.postTranslate(0, height); drawBitmapMatrix(&canvas, bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, bottom, bottomMatrix); } patternBBox.fBottom = deviceBounds.height(); } } // Put the canvas into the pattern stream (fContent). SkAutoTDelete<SkStreamAsset> content(patternDevice->content()); SkPDFImageShader* imageShader = SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); imageShader->setData(content.get()); SkAutoTUnref<SkPDFDict> resourceDict( patternDevice->createResourceDict()); populate_tiling_pattern_dict(imageShader, patternBBox, resourceDict.get(), finalMatrix); imageShader->fShaderState->fImage.unlockPixels(); canon->addImageShader(imageShader); return imageShader; }
void GrDrawTarget::drawBatches(GrBatchFlushState* flushState) { // Draw all the generated geometry. SkRandom random; GrRenderTarget* currentRT = nullptr; SkAutoTDelete<GrGpuCommandBuffer> commandBuffer; SkRect bounds = SkRect::MakeEmpty(); for (int i = 0; i < fBatches.count(); ++i) { if (!fBatches[i]) { continue; } if (fBatches[i]->renderTarget() != currentRT) { if (commandBuffer) { commandBuffer->end(); if (bounds.intersect(0, 0, SkIntToScalar(currentRT->width()), SkIntToScalar(currentRT->height()))) { SkIRect iBounds; bounds.roundOut(&iBounds); commandBuffer->submit(iBounds); } commandBuffer.reset(); } bounds.setEmpty(); currentRT = fBatches[i]->renderTarget(); if (currentRT) { static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo { GrGpuCommandBuffer::LoadOp::kLoad,GrGpuCommandBuffer::StoreOp::kStore, GrColor_ILLEGAL }; commandBuffer.reset(fGpu->createCommandBuffer(currentRT, kBasicLoadStoreInfo, // Color kBasicLoadStoreInfo)); // Stencil } flushState->setCommandBuffer(commandBuffer); } if (commandBuffer) { bounds.join(fBatches[i]->bounds()); } if (fDrawBatchBounds) { const SkRect& batchBounds = fBatches[i]->bounds(); SkIRect iBatchBounds; batchBounds.roundOut(&iBatchBounds); // In multi-draw buffer all the batches use the same render target and we won't need to // get the batchs bounds. if (GrRenderTarget* rt = fBatches[i]->renderTarget()) { fGpu->drawDebugWireRect(rt, iBatchBounds, 0xFF000000 | random.nextU()); } } fBatches[i]->draw(flushState); } if (commandBuffer) { commandBuffer->end(); if (bounds.intersect(0, 0, SkIntToScalar(currentRT->width()), SkIntToScalar(currentRT->height()))) { SkIRect iBounds; bounds.roundOut(&iBounds); commandBuffer->submit(iBounds); } flushState->setCommandBuffer(nullptr); } fGpu->finishDrawTarget(); }
GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor color, sk_sp<SkVertices> vertices, const SkVertices::Bone bones[], int boneCount, GrPrimitiveType primitiveType, GrAAType aaType, sk_sp<GrColorSpaceXform> colorSpaceXform, const SkMatrix& viewMatrix) : INHERITED(ClassID()) , fHelper(helperArgs, aaType) , fPrimitiveType(primitiveType) , fColorSpaceXform(std::move(colorSpaceXform)) { SkASSERT(vertices); fVertexCount = vertices->vertexCount(); fIndexCount = vertices->indexCount(); fColorArrayType = vertices->hasColors() ? ColorArrayType::kSkColor : ColorArrayType::kPremulGrColor; Mesh& mesh = fMeshes.push_back(); mesh.fColor = color; mesh.fViewMatrix = viewMatrix; mesh.fVertices = std::move(vertices); mesh.fIgnoreTexCoords = false; mesh.fIgnoreColors = false; mesh.fIgnoreBones = false; if (mesh.fVertices->hasBones() && bones) { // Perform the transformations on the CPU instead of the GPU. mesh.fVertices = mesh.fVertices->applyBones(bones, boneCount); } else { if (bones && boneCount > 1) { // NOTE: This should never be used. All bone transforms are being done on the CPU // instead of the GPU. // Copy the bone data. fBones.assign(bones, bones + boneCount); } } fFlags = 0; if (mesh.hasPerVertexColors()) { fFlags |= kRequiresPerVertexColors_Flag; } if (mesh.hasExplicitLocalCoords()) { fFlags |= kAnyMeshHasExplicitLocalCoords_Flag; } if (mesh.hasBones()) { fFlags |= kHasBones_Flag; } // Special case for meshes with a world transform but no bone weights. // These will be considered normal vertices draws without bones. if (!mesh.fVertices->hasBones() && boneCount == 1) { SkMatrix worldTransform; worldTransform.setAffine(bones[0].values); mesh.fViewMatrix.preConcat(worldTransform); } IsZeroArea zeroArea; if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) { zeroArea = IsZeroArea::kYes; } else { zeroArea = IsZeroArea::kNo; } if (this->hasBones()) { // We don't know the bounds if there are deformations involved, so attempt to calculate // the maximum possible. SkRect bounds = SkRect::MakeEmpty(); const SkRect originalBounds = bones[0].mapRect(mesh.fVertices->bounds()); for (int i = 1; i < boneCount; i++) { const SkVertices::Bone& matrix = bones[i]; bounds.join(matrix.mapRect(originalBounds)); } this->setTransformedBounds(bounds, mesh.fViewMatrix, HasAABloat::kNo, zeroArea); } else { this->setTransformedBounds(mesh.fVertices->bounds(), mesh.fViewMatrix, HasAABloat::kNo, zeroArea); } }
virtual void onDraw(SkCanvas* canvas) { SkPath path; path.moveTo(SkIntToScalar(0), SkIntToScalar(50)); path.quadTo(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(50), SkIntToScalar(0)); path.lineTo(SkIntToScalar(175), SkIntToScalar(0)); path.quadTo(SkIntToScalar(200), SkIntToScalar(0), SkIntToScalar(200), SkIntToScalar(25)); path.lineTo(SkIntToScalar(200), SkIntToScalar(150)); path.quadTo(SkIntToScalar(200), SkIntToScalar(200), SkIntToScalar(150), SkIntToScalar(200)); path.lineTo(SkIntToScalar(0), SkIntToScalar(200)); path.close(); path.moveTo(SkIntToScalar(50), SkIntToScalar(50)); path.lineTo(SkIntToScalar(150), SkIntToScalar(50)); path.lineTo(SkIntToScalar(150), SkIntToScalar(125)); path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150)); path.lineTo(SkIntToScalar(50), SkIntToScalar(150)); path.close(); if (fInvertDraw) { path.setFillType(SkPath::kInverseEvenOdd_FillType); } else { path.setFillType(SkPath::kEvenOdd_FillType); } SkPaint pathPaint; pathPaint.setAntiAlias(true); pathPaint.setColor(gPathColor); SkPath clipA; clipA.moveTo(SkIntToScalar(10), SkIntToScalar(20)); clipA.lineTo(SkIntToScalar(165), SkIntToScalar(22)); clipA.lineTo(SkIntToScalar(70), SkIntToScalar(105)); clipA.lineTo(SkIntToScalar(165), SkIntToScalar(177)); clipA.lineTo(SkIntToScalar(-5), SkIntToScalar(180)); clipA.close(); SkPath clipB; clipB.moveTo(SkIntToScalar(40), SkIntToScalar(10)); clipB.lineTo(SkIntToScalar(190), SkIntToScalar(15)); clipB.lineTo(SkIntToScalar(195), SkIntToScalar(190)); clipB.lineTo(SkIntToScalar(40), SkIntToScalar(185)); clipB.lineTo(SkIntToScalar(155), SkIntToScalar(100)); clipB.close(); SkPaint paint; paint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&paint); paint.setTextSize(SkIntToScalar(20)); static const struct { SkRegion::Op fOp; const char* fName; } gOps[] = { //extra spaces in names for measureText {SkRegion::kIntersect_Op, "Isect "}, {SkRegion::kDifference_Op, "Diff " }, {SkRegion::kUnion_Op, "Union "}, {SkRegion::kXOR_Op, "Xor " }, {SkRegion::kReverseDifference_Op, "RDiff "} }; canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4); if (fDoSaveLayer) { // We want the layer to appear symmetric relative to actual // device boundaries so we need to "undo" the effect of the // scale and translate SkRect bounds = SkRect::MakeLTRB( 4.0f/3.0f * -20, 4.0f/3.0f * -20, 4.0f/3.0f * (this->getISize().fWidth - 20), 4.0f/3.0f * (this->getISize().fHeight - 20)); bounds.inset(SkIntToScalar(100), SkIntToScalar(100)); SkPaint boundPaint; boundPaint.setColor(SK_ColorRED); boundPaint.setStyle(SkPaint::kStroke_Style); canvas->drawRect(bounds, boundPaint); canvas->saveLayer(&bounds, NULL); } for (int invBits = 0; invBits < 4; ++invBits) { canvas->save(); for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { this->drawHairlines(canvas, path, clipA, clipB); bool doInvA = SkToBool(invBits & 1); bool doInvB = SkToBool(invBits & 2); canvas->save(); // set clip clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType); clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType); canvas->clipPath(clipA, SkRegion::kIntersect_Op, fDoAAClip); canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip); // In the inverse case we need to prevent the draw from covering the whole // canvas. if (fInvertDraw) { SkRect rectClip = clipA.getBounds(); rectClip.join(path.getBounds()); rectClip.join(path.getBounds()); rectClip.outset(5, 5); canvas->clipRect(rectClip); } // draw path clipped canvas->drawPath(path, pathPaint); canvas->restore(); SkScalar txtX = SkIntToScalar(45); paint.setColor(gClipAColor); const char* aTxt = doInvA ? "InvA " : "A "; canvas->drawText(aTxt, strlen(aTxt), txtX, SkIntToScalar(220), paint); txtX += paint.measureText(aTxt, strlen(aTxt)); paint.setColor(SK_ColorBLACK); canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), txtX, SkIntToScalar(220), paint); txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName)); paint.setColor(gClipBColor); const char* bTxt = doInvB ? "InvB " : "B "; canvas->drawText(bTxt, strlen(bTxt), txtX, SkIntToScalar(220), paint); canvas->translate(SkIntToScalar(250),0); } canvas->restore(); canvas->translate(0, SkIntToScalar(250)); } if (fDoSaveLayer) { canvas->restore(); } }
static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne, SkPath& scaledTwo, int& error2x2) { const int bitWidth = 64; const int bitHeight = 64; if (bits.width() == 0) { bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight); bits.allocPixels(); } SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; SkMatrix scale; scale.reset(); scale.preScale(hScale, vScale); one.transform(scale, &scaledOne); two.transform(scale, &scaledTwo); const SkRect& bounds1 = scaledOne.getBounds(); SkCanvas canvas(bits); canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(scaledOne, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); canvas.drawPath(scaledTwo, paint); canvas.restore(); int errors2 = 0; int errors = 0; for (int y = 0; y < bitHeight - 1; ++y) { uint32_t* addr1 = bits.getAddr32(0, y); uint32_t* addr2 = bits.getAddr32(0, y + 1); uint32_t* addr3 = bits.getAddr32(bitWidth, y); uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1); for (int x = 0; x < bitWidth - 1; ++x) { // count 2x2 blocks bool err = addr1[x] != addr3[x]; if (err) { errors2 += addr1[x + 1] != addr3[x + 1] && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1]; errors++; } } } if (errors2 >= 6 || errors > 160) { SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors); } error2x2 = errors2; return errors; }