void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) { SkPaint paint; fInverse.mapPoints(pts, 3); if (fGrid) { apply_grid(pts, 3); } SkPath path; path.moveTo(pts[0]); path.lineTo(pts[1]); path.lineTo(pts[2]); path.close(); erase(fMinSurface); this->setupPaint(&paint); paint.setColor(FAT_PIXEL_COLOR); fMinSurface->getCanvas()->drawPath(path, paint); this->copyMinToMax(); SkCanvas* max = fMaxSurface->getCanvas(); fMatrix.mapPoints(pts, 3); this->drawTriangleSkeleton(max, pts); fMaxSurface->draw(canvas, 0, 0, NULL); }
void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) { SkPaint paint; fInverse.mapPoints(pts, 2); if (fGrid) { apply_grid(pts, 2); } erase(fMinSurface); this->setupPaint(&paint); paint.setColor(FAT_PIXEL_COLOR); if (fUseClip) { fMinSurface->getCanvas()->save(); SkRect r = fClipRect; r.inset(SK_Scalar1/3, SK_Scalar1/3); fMinSurface->getCanvas()->clipRect(r, SkRegion::kIntersect_Op, true); } fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); if (fUseClip) { fMinSurface->getCanvas()->restore(); } this->copyMinToMax(); SkCanvas* max = fMaxSurface->getCanvas(); fMatrix.mapPoints(pts, 2); this->drawLineSkeleton(max, pts); fMaxSurface->draw(canvas, 0, 0, NULL); }
void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) { SkPaint paint; fInverse.mapPoints(pts, 2); if (fGrid) { apply_grid(pts, 2); } SkRect r; r.set(pts, 2); erase(fMinSurface); this->setupPaint(&paint); paint.setColor(FAT_PIXEL_COLOR); { SkCanvas* c = fMinSurface->getCanvas(); fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint); } this->copyMinToMax(); SkCanvas* max = fMaxSurface->getCanvas(); fMatrix.mapPoints(pts, 2); r.set(pts, 2); this->drawRectSkeleton(max, r); fMaxSurface->draw(canvas, 0, 0, NULL); }
virtual void onDraw(SkCanvas* canvas) { SkMatrix m; m.reset(); m.setRotate(33 * SK_Scalar1); m.postScale(3000 * SK_Scalar1, 3000 * SK_Scalar1); m.postTranslate(6000 * SK_Scalar1, -5000 * SK_Scalar1); canvas->concat(m); SkPaint paint; paint.setColor(SK_ColorRED); paint.setAntiAlias(true); bool success = m.invert(&m); SkASSERT(success); (void) success; // silence compiler :( SkPath path; SkPoint pt = {10 * SK_Scalar1, 10 * SK_Scalar1}; SkScalar small = 1 / (500 * SK_Scalar1); m.mapPoints(&pt, 1); path.addCircle(pt.fX, pt.fY, small); canvas->drawPath(path, paint); pt.set(30 * SK_Scalar1, 10 * SK_Scalar1); m.mapPoints(&pt, 1); SkRect rect = {pt.fX - small, pt.fY - small, pt.fX + small, pt.fY + small}; canvas->drawRect(rect, paint); SkBitmap bmp; bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); bmp.allocPixels(); bmp.lockPixels(); uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels()); pixels[0] = SkPackARGB32(0xFF, 0xFF, 0x00, 0x00); pixels[1] = SkPackARGB32(0xFF, 0x00, 0xFF, 0x00); pixels[2] = SkPackARGB32(0x80, 0x00, 0x00, 0x00); pixels[3] = SkPackARGB32(0xFF, 0x00, 0x00, 0xFF); bmp.unlockPixels(); pt.set(30 * SK_Scalar1, 30 * SK_Scalar1); m.mapPoints(&pt, 1); SkShader* shader = SkShader::CreateBitmapShader( bmp, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); SkMatrix s; s.reset(); s.setScale(SK_Scalar1 / 1000, SK_Scalar1 / 1000); shader->setLocalMatrix(s); paint.setShader(shader)->unref(); paint.setAntiAlias(false); paint.setFilterLevel(SkPaint::kLow_FilterLevel); rect.setLTRB(pt.fX - small, pt.fY - small, pt.fX + small, pt.fY + small); canvas->drawRect(rect, paint); }
void Matrix::NativeMapPoints( /* [in] */ Int64 matrixHandle, /* [out] */ ArrayOf<Float>* dst, /* [in] */ Int32 dstIndex, /* [in] */ ArrayOf<Float>* src, /* [in] */ Int32 srcIndex, /* [in] */ Int32 ptCount, /* [in] */ Boolean isPts) { SkASSERT(ptCount >= 0); SkASSERT(src->GetLength() >= srcIndex + (ptCount << 1)); SkASSERT(dst->GetLength() >= dstIndex + (ptCount << 1)); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); // AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), kRO_JNIAccess); // AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), kRW_JNIAccess); Float* srcArray = src->GetPayload() + srcIndex; Float* dstArray = dst->GetPayload() + dstIndex; #ifdef SK_SCALAR_IS_FLOAT if (isPts) matrix->mapPoints((SkPoint*)dstArray, (const SkPoint*)srcArray, ptCount); else matrix->mapVectors((SkVector*)dstArray, (const SkVector*)srcArray, ptCount); #else SkASSERT(FALSE); #endif }
GiantDashBench(LineType lt, SkScalar width) { fName.printf("giantdashline_%s_%g", LineTypeName(lt), width); fStrokeWidth = width; // deliberately pick intervals that won't be caught by asPoints(), so // we can test the filterPath code-path. const SkScalar intervals[] = { 20, 10, 10, 10 }; fPathEffect.reset(SkDashPathEffect::Create(intervals, SK_ARRAY_COUNT(intervals), 0)); SkScalar cx = 640 / 2; // center X SkScalar cy = 480 / 2; // center Y SkMatrix matrix; switch (lt) { case kHori_LineType: matrix.setIdentity(); break; case kVert_LineType: matrix.setRotate(90, cx, cy); break; case kDiag_LineType: matrix.setRotate(45, cx, cy); break; case kLineTypeCount: // Not a real enum value. break; } const SkScalar overshoot = 100*1000; const SkPoint pts[2] = { { -overshoot, cy }, { 640 + overshoot, cy } }; matrix.mapPoints(fPts, pts, 2); }
static void make_strip(Rec* rec, int texWidth, int texHeight) { const SkScalar tx = SkIntToScalar(texWidth); const SkScalar ty = SkIntToScalar(texHeight); const int n = 24; rec->fMode = SkCanvas::kTriangleStrip_VertexMode; rec->fCount = 2 * (n + 1); rec->fVerts = new SkPoint[rec->fCount]; rec->fTexs = new SkPoint[rec->fCount]; SkPoint* v = rec->fVerts; SkPoint* t = rec->fTexs; for (int i = 0; i < n; i++) { SkScalar cos; SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos); v[i*2 + 0].set(cos/2, sin/2); v[i*2 + 1].set(cos, sin); t[i*2 + 0].set(tx * i / n, ty); t[i*2 + 1].set(tx * i / n, 0); } v[2*n + 0] = v[0]; v[2*n + 1] = v[1]; t[2*n + 0].set(tx, ty); t[2*n + 1].set(tx, 0); SkMatrix m; m.setScale(SkIntToScalar(100), SkIntToScalar(100)); m.postTranslate(SkIntToScalar(110), SkIntToScalar(110)); m.mapPoints(v, rec->fCount); }
virtual bool onClick(Click* click) { SkPoint pts[2] = { click->fOrig, click->fCurr }; fInverse.mapPoints(pts, 2); this->warp(pts[0], pts[1]); this->inval(NULL); return true; }
/** * For the purposes of drawing bitmaps, if a matrix is "almost" translate * go ahead and treat it as if it were, so that subsequent code can go fast. */ static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) { SkASSERT(matrix_only_scale_translate(matrix)); if (matrix.getType() & SkMatrix::kScale_Mask) { SkRect src, dst; bitmap.getBounds(&src); // Can't call mapRect(), since that will fix up inverted rectangles, // e.g. when scale is negative, and we don't want to return true for // those. matrix.mapPoints(SkTCast<SkPoint*>(&dst), SkTCast<const SkPoint*>(&src), 2); // Now round all 4 edges to device space, and then compare the device // width/height to the original. Note: we must map all 4 and subtract // rather than map the "width" and compare, since we care about the // phase (in pixel space) that any translate in the matrix might impart. SkIRect idst; dst.round(&idst); return idst.width() == bitmap.width() && idst.height() == bitmap.height(); } // if we got here, we're either kTranslate_Mask or identity return true; }
bool check_bounds(const SkMatrix& viewMatrix, const SkRect& devBounds, void* vertices, int vCount) { SkRect tolDevBounds = devBounds; // The bounds ought to be tight, but in perspective the below code runs the verts // through the view matrix to get back to dev coords, which can introduce imprecision. if (viewMatrix.hasPerspective()) { tolDevBounds.outset(SK_Scalar1 / 1000, SK_Scalar1 / 1000); } else { // Non-persp matrices cause this path renderer to draw in device space. SkASSERT(viewMatrix.isIdentity()); } SkRect actualBounds; VertexType* verts = reinterpret_cast<VertexType*>(vertices); bool first = true; for (int i = 0; i < vCount; ++i) { SkPoint pos = verts[i].fPos; // This is a hack to workaround the fact that we move some degenerate segments offscreen. if (SK_ScalarMax == pos.fX) { continue; } viewMatrix.mapPoints(&pos, 1); if (first) { actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY); first = false; } else { actualBounds.growToInclude(pos.fX, pos.fY); } } if (!first) { return tolDevBounds.contains(actualBounds); } return true; }
void GrStencilAndCoverTextContext::flush() { if (fQueuedGlyphCount > 0) { SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(), fViewMatrix, fLocalMatrix)); // We should only be flushing about once every run. However, if this impacts performance // we could move the creation of the GrPipelineBuilder earlier. GrPipelineBuilder pipelineBuilder(fPaint, fRenderTarget, fClip); SkASSERT(fRenderTarget->isStencilBufferMultisampled() || !fPaint.isAntiAlias()); pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, fPaint.isAntiAlias()); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *pipelineBuilder.stencil() = kStencilPass; SkASSERT(0 == fQueuedGlyphCount); SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx); fDrawContext->drawPaths(&pipelineBuilder, pp, fGlyphs, fGlyphIndices, GrPathRange::kU16_PathIndexType, get_xy_scalar_array(fGlyphPositions), GrPathRendering::kTranslate_PathTransformType, fQueuedGlyphCount, GrPathRendering::kWinding_FillType); fQueuedGlyphCount = 0; } if (fFallbackGlyphsIdx < kGlyphBufferSize) { int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx; GrPaint paintFallback(fPaint); SkPaint skPaintFallback(fSkPaint); if (!fUsingDeviceSpaceGlyphs) { fStroke.applyToPaint(&skPaintFallback); } skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for. skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkMatrix inverse; if (this->mapToFallbackContext(&inverse)) { inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount); } fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback, fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx], 2 * fallbackGlyphCount, get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]), 2, SkPoint::Make(0, 0), fRegionClipBounds); fFallbackGlyphsIdx = kGlyphBufferSize; } }
void make_fan(Rec* rec, int texWidth, int texHeight) { const SkScalar tx = SkIntToScalar(texWidth); const SkScalar ty = SkIntToScalar(texHeight); const int n = 24; rec->fMode = SkCanvas::kTriangleFan_VertexMode; rec->fCount = n + 2; rec->fVerts = new SkPoint[rec->fCount]; rec->fTexs = new SkPoint[rec->fCount]; SkPoint* v = rec->fVerts; SkPoint* t = rec->fTexs; v[0].set(0, 0); t[0].set(0, 0); for (int i = 0; i < n; i++) { SkScalar cos; SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos); v[i+1].set(cos, sin); t[i+1].set(i*tx/n, ty); } v[n+1] = v[1]; t[n+1].set(tx, ty); SkMatrix m; m.setScale(SkIntToScalar(100), SkIntToScalar(100)); m.postTranslate(SkIntToScalar(110), SkIntToScalar(110)); m.mapPoints(v, rec->fCount); }
SkPoint View::convertToLocal(SkPoint point, View* reference) { SkMatrix m; if (currentTransformMatrix(reference).invert(&m)) { m.mapPoints(&point, 1); } return point; }
int hittest(SkScalar x, SkScalar y) { SkPoint target = { x, y }; SkPoint pts[2] = { fPts[1], fPts[2] }; fMatrix.mapPoints(pts, 2); for (int i = 0; i < 2; i++) { if (SkPoint::Distance(pts[i], target) < SkIntToScalar(4)) { return i + 1; } } return -1; }
static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt) { #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA // Now check for points that will overflow. We check the *transformed* // points since this is what will be rasterized. SkPoint xPt; transform.mapPoints(&xPt, &pt, 1); return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY); #else return true; #endif }
void drawShape(SkCanvas* canvas, SkPaint* paint, ShapeType type) { static const SkRect kRect = SkRect::MakeXYWH(SkIntToScalar(-50), SkIntToScalar(-50), SkIntToScalar(75), SkIntToScalar(105)); switch (type) { case kCircle_ShapeType: canvas->drawCircle(0, 0, 50, *paint); break; case kRoundRect_ShapeType: canvas->drawRoundRect(kRect, SkIntToScalar(10), SkIntToScalar(20), *paint); break; case kRect_ShapeType: canvas->drawRect(kRect, *paint); break; case kConvexPath_ShapeType: if (fConvexPath.isEmpty()) { SkPoint points[4]; kRect.toQuad(points); fConvexPath.moveTo(points[0]); fConvexPath.quadTo(points[1], points[2]); fConvexPath.quadTo(points[3], points[0]); SkASSERT(fConvexPath.isConvex()); } canvas->drawPath(fConvexPath, *paint); break; case kConcavePath_ShapeType: if (fConcavePath.isEmpty()) { SkPoint points[5] = {{0, SkIntToScalar(-50)} }; SkMatrix rot; rot.setRotate(SkIntToScalar(360) / 5); for (int i = 1; i < 5; ++i) { rot.mapPoints(points + i, points + i - 1, 1); } fConcavePath.moveTo(points[0]); for (int i = 0; i < 5; ++i) { fConcavePath.lineTo(points[(2 * i) % 5]); } fConcavePath.setFillType(SkPath::kEvenOdd_FillType); SkASSERT(!fConcavePath.isConvex()); } canvas->drawPath(fConcavePath, *paint); break; case kText_ShapeType: { const char* text = "Hello!"; paint->setTextSize(30); sk_tool_utils::set_portable_typeface(paint); canvas->drawText(text, strlen(text), 0, 0, *paint); } default: break; } }
void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) { if (fDraw) { SkASSERT(fDraw->count()); // We should only be flushing about once every run. However, if this impacts performance // we could move the creation of the GrPipelineBuilder earlier. GrPipelineBuilder pipelineBuilder(fPaint, fRenderTarget, fClip); SkASSERT(fRenderTarget->isStencilBufferMultisampled() || !fPaint.isAntiAlias()); pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, fPaint.isAntiAlias()); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kKeep_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *pipelineBuilder.stencil() = kStencilPass; dc->drawPathsFromRange(&pipelineBuilder, fViewMatrix, fLocalMatrix, fPaint.getColor(), fDraw, GrPathRendering::kWinding_FillType); fDraw->unref(); fDraw = nullptr; } if (fFallbackIndices.count()) { SkASSERT(fFallbackPositions.count() == fFallbackIndices.count()); GrPaint paintFallback(fPaint); SkPaint skPaintFallback(fSkPaint); if (!fUsingDeviceSpaceGlyphs) { fStroke.applyToPaint(&skPaintFallback); } skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for. skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkMatrix inverse; if (this->mapToFallbackContext(&inverse)) { inverse.mapPoints(fFallbackPositions.begin(), fFallbackPositions.count()); } fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, paintFallback, skPaintFallback, fViewMatrix, (char*)fFallbackIndices.begin(), sizeof(uint16_t) * fFallbackIndices.count(), get_xy_scalar_array(fFallbackPositions.begin()), 2, SkPoint::Make(0, 0), fRegionClipBounds); fFallbackIndices.reset(); fFallbackPositions.reset(); } }
// Transforms the given dimensions with the given matrix. Used to see how big // images will be once transformed. static void TransformDimensions(const SkMatrix& matrix, float srcWidth, float srcHeight, float* destWidth, float* destHeight) { // Transform 3 points to see how long each side of the bitmap will be. SkPoint src_points[3]; // (0, 0), (width, 0), (0, height). src_points[0].set(0, 0); src_points[1].set(SkFloatToScalar(srcWidth), 0); src_points[2].set(0, SkFloatToScalar(srcHeight)); // Now measure the length of the two transformed vectors relative to the // transformed origin to see how big the bitmap will be. Note: for skews, // this isn't the best thing, but we don't have skews. SkPoint dest_points[3]; matrix.mapPoints(dest_points, src_points, 3); *destWidth = SkScalarToFloat((dest_points[1] - dest_points[0]).length()); *destHeight = SkScalarToFloat((dest_points[2] - dest_points[0]).length()); }
SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { // holding down shift if (1 == modi) { return new PtClick(this, -1); } // holding down ctrl if (2 == modi) { return new PtClick(this, -2); } SkPoint clickPoint = {x, y}; fInvMatrix.mapPoints(&clickPoint, 1); for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) { if (hittest(fPts[i], clickPoint.fX, clickPoint.fY)) { return new PtClick(this, (int)i); } } return this->INHERITED::onFindClickHandler(x, y, modi); }
// Creates a star type shape using a SkPath static SkPath create_star() { static const int kNumPoints = 5; SkPath concavePath; SkPoint points[kNumPoints] = {{0, SkIntToScalar(-50)} }; SkMatrix rot; rot.setRotate(SkIntToScalar(360) / kNumPoints); for (int i = 1; i < kNumPoints; ++i) { rot.mapPoints(points + i, points + i - 1, 1); } concavePath.moveTo(points[0]); for (int i = 0; i < kNumPoints; ++i) { concavePath.lineTo(points[(2 * i) % kNumPoints]); } concavePath.setFillType(SkPath::kEvenOdd_FillType); SkASSERT(!concavePath.isConvex()); concavePath.close(); return concavePath; }
void GrStencilAndCoverTextContext::flush(GrDrawContext* drawContext) { if (fQueuedGlyphCount > 0) { SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(fPaint.getColor(), fViewMatrix, fLocalMatrix)); drawContext->drawPaths(&fPipelineBuilder, pp, fGlyphs, fGlyphIndices, GrPathRange::kU16_PathIndexType, get_xy_scalar_array(fGlyphPositions), GrPathRendering::kTranslate_PathTransformType, fQueuedGlyphCount, GrPathRendering::kWinding_FillType); fQueuedGlyphCount = 0; } if (fFallbackGlyphsIdx < kGlyphBufferSize) { int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx; GrPaint paintFallback(fPaint); SkPaint skPaintFallback(fSkPaint); if (!fUsingDeviceSpaceGlyphs) { fStroke.applyToPaint(&skPaintFallback); } skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for. skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkMatrix inverse; if (this->mapToFallbackContext(&inverse)) { inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount); } fFallbackTextContext->drawPosText(fRenderTarget, fClip, paintFallback, skPaintFallback, fViewMatrix, (char*)&fGlyphIndices[fFallbackGlyphsIdx], 2 * fallbackGlyphCount, get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]), 2, SkPoint::Make(0, 0), fRegionClipBounds); fFallbackGlyphsIdx = kGlyphBufferSize; } }
void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target, GrDrawState* drawState, GrColor color, const SkRect rects[2], const SkMatrix& combinedMatrix) { SkASSERT(combinedMatrix.rectStaysRect()); SkASSERT(!rects[1].isEmpty()); SkRect devOutside, devOutsideAssist, devInside; combinedMatrix.mapRect(&devOutside, rects[0]); // can't call mapRect for devInside since it calls sort combinedMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2); if (devInside.isEmpty()) { this->fillAARect(target, drawState, color, devOutside, SkMatrix::I(), devOutside); return; } this->geometryStrokeAARect(target, drawState, color, devOutside, devOutsideAssist, devInside, true); }
static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray dst, jint dstIndex, jfloatArray src, jint srcIndex, jint ptCount, jboolean isPts) { SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); SkASSERT(ptCount >= 0); AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), kRO_JNIAccess); AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), kRW_JNIAccess); float* srcArray = autoSrc.ptr() + srcIndex; float* dstArray = autoDst.ptr() + dstIndex; #ifdef SK_SCALAR_IS_FLOAT if (isPts) matrix->mapPoints((SkPoint*)dstArray, (const SkPoint*)srcArray, ptCount); else matrix->mapVectors((SkVector*)dstArray, (const SkVector*)srcArray, ptCount); #else SkASSERT(false); #endif }
static void morphpoints(SkPoint dst[], const SkPoint src[], int count, SkPathMeasure& meas, SkScalar dist) { for (int i = 0; i < count; i++) { SkPoint pos; SkVector tangent; SkScalar sx = src[i].fX; SkScalar sy = src[i].fY; meas.getPosTan(dist + sx, &pos, &tangent); SkMatrix matrix; SkPoint pt; pt.set(sx, sy); matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); matrix.preTranslate(-sx, 0); matrix.postTranslate(pos.fX, pos.fY); matrix.mapPoints(&dst[i], &pt, 1); } }
/** * Generates the lines and quads to be rendered. Lines are always recorded in * device space. We will do a device space bloat to account for the 1pixel * thickness. * Quads are recorded in device space unless m contains * perspective, then in they are in src space. We do this because we will * subdivide large quads to reduce over-fill. This subdivision has to be * performed before applying the perspective matrix. */ static int gather_lines_and_quads(const SkPath& path, const SkMatrix& m, const SkIRect& devClipBounds, GrAAHairLinePathRenderer::PtArray* lines, GrAAHairLinePathRenderer::PtArray* quads, GrAAHairLinePathRenderer::PtArray* conics, GrAAHairLinePathRenderer::IntArray* quadSubdivCnts, GrAAHairLinePathRenderer::FloatArray* conicWeights) { SkPath::Iter iter(path, false); int totalQuadCount = 0; SkRect bounds; SkIRect ibounds; bool persp = m.hasPerspective(); for (;;) { SkPoint pathPts[4]; SkPoint devPts[4]; SkPath::Verb verb = iter.next(pathPts); switch (verb) { case SkPath::kConic_Verb: { SkConic dst[4]; // We chop the conics to create tighter clipping to hide error // that appears near max curvature of very thin conics. Thin // hyperbolas with high weight still show error. int conicCnt = chop_conic(pathPts, dst, iter.conicWeight()); for (int i = 0; i < conicCnt; ++i) { SkPoint* chopPnts = dst[i].fPts; m.mapPoints(devPts, chopPnts, 3); bounds.setBounds(devPts, 3); bounds.outset(SK_Scalar1, SK_Scalar1); bounds.roundOut(&ibounds); if (SkIRect::Intersects(devClipBounds, ibounds)) { if (is_degen_quad_or_conic(devPts)) { SkPoint* pts = lines->push_back_n(4); pts[0] = devPts[0]; pts[1] = devPts[1]; pts[2] = devPts[1]; pts[3] = devPts[2]; } else { // when in perspective keep conics in src space SkPoint* cPts = persp ? chopPnts : devPts; SkPoint* pts = conics->push_back_n(3); pts[0] = cPts[0]; pts[1] = cPts[1]; pts[2] = cPts[2]; conicWeights->push_back() = dst[i].fW; } } } break; } case SkPath::kMove_Verb: break; case SkPath::kLine_Verb: m.mapPoints(devPts, pathPts, 2); bounds.setBounds(devPts, 2); bounds.outset(SK_Scalar1, SK_Scalar1); bounds.roundOut(&ibounds); if (SkIRect::Intersects(devClipBounds, ibounds)) { SkPoint* pts = lines->push_back_n(2); pts[0] = devPts[0]; pts[1] = devPts[1]; } break; case SkPath::kQuad_Verb: { SkPoint choppedPts[5]; // Chopping the quad helps when the quad is either degenerate or nearly degenerate. // When it is degenerate it allows the approximation with lines to work since the // chop point (if there is one) will be at the parabola's vertex. In the nearly // degenerate the QuadUVMatrix computed for the points is almost singular which // can cause rendering artifacts. int n = SkChopQuadAtMaxCurvature(pathPts, choppedPts); for (int i = 0; i < n; ++i) { SkPoint* quadPts = choppedPts + i * 2; m.mapPoints(devPts, quadPts, 3); bounds.setBounds(devPts, 3); bounds.outset(SK_Scalar1, SK_Scalar1); bounds.roundOut(&ibounds); if (SkIRect::Intersects(devClipBounds, ibounds)) { int subdiv = num_quad_subdivs(devPts); SkASSERT(subdiv >= -1); if (-1 == subdiv) { SkPoint* pts = lines->push_back_n(4); pts[0] = devPts[0]; pts[1] = devPts[1]; pts[2] = devPts[1]; pts[3] = devPts[2]; } else { // when in perspective keep quads in src space SkPoint* qPts = persp ? quadPts : devPts; SkPoint* pts = quads->push_back_n(3); pts[0] = qPts[0]; pts[1] = qPts[1]; pts[2] = qPts[2]; quadSubdivCnts->push_back() = subdiv; totalQuadCount += 1 << subdiv; } } } break; } case SkPath::kCubic_Verb: m.mapPoints(devPts, pathPts, 4); bounds.setBounds(devPts, 4); bounds.outset(SK_Scalar1, SK_Scalar1); bounds.roundOut(&ibounds); if (SkIRect::Intersects(devClipBounds, ibounds)) { PREALLOC_PTARRAY(32) q; // we don't need a direction if we aren't constraining the subdivision const SkPathPriv::FirstDirection kDummyDir = SkPathPriv::kCCW_FirstDirection; // We convert cubics to quadratics (for now). // In perspective have to do conversion in src space. if (persp) { SkScalar tolScale = GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m, path.getBounds()); GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q); } else { GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q); } for (int i = 0; i < q.count(); i += 3) { SkPoint* qInDevSpace; // bounds has to be calculated in device space, but q is // in src space when there is perspective. if (persp) { m.mapPoints(devPts, &q[i], 3); bounds.setBounds(devPts, 3); qInDevSpace = devPts; } else { bounds.setBounds(&q[i], 3); qInDevSpace = &q[i]; } bounds.outset(SK_Scalar1, SK_Scalar1); bounds.roundOut(&ibounds); if (SkIRect::Intersects(devClipBounds, ibounds)) { int subdiv = num_quad_subdivs(qInDevSpace); SkASSERT(subdiv >= -1); if (-1 == subdiv) { SkPoint* pts = lines->push_back_n(4); // lines should always be in device coords pts[0] = qInDevSpace[0]; pts[1] = qInDevSpace[1]; pts[2] = qInDevSpace[1]; pts[3] = qInDevSpace[2]; } else { SkPoint* pts = quads->push_back_n(3); // q is already in src space when there is no // perspective and dev coords otherwise. pts[0] = q[0 + i]; pts[1] = q[1 + i]; pts[2] = q[2 + i]; quadSubdivCnts->push_back() = subdiv; totalQuadCount += 1 << subdiv; } } } } break; case SkPath::kClose_Verb: break; case SkPath::kDone_Verb: return totalQuadCount; } } }
static void test_matrix_homogeneous(skiatest::Reporter* reporter) { SkMatrix mat; const float kRotation0 = 15.5f; const float kRotation1 = -50.f; const float kScale0 = 5000.f; const int kTripleCount = 1000; const int kMatrixCount = 1000; SkRandom rand; SkScalar randTriples[3*kTripleCount]; for (int i = 0; i < 3*kTripleCount; ++i) { randTriples[i] = rand.nextRangeF(-3000.f, 3000.f); } SkMatrix mats[kMatrixCount]; for (int i = 0; i < kMatrixCount; ++i) { for (int j = 0; j < 9; ++j) { mats[i].set(j, rand.nextRangeF(-3000.f, 3000.f)); } } // identity { mat.reset(); SkScalar dst[3*kTripleCount]; mat.mapHomogeneousPoints(dst, randTriples, kTripleCount); REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(randTriples, dst, kTripleCount*3)); } // zero matrix { mat.setAll(0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f); SkScalar dst[3*kTripleCount]; mat.mapHomogeneousPoints(dst, randTriples, kTripleCount); SkScalar zeros[3] = {0.f, 0.f, 0.f}; for (int i = 0; i < kTripleCount; ++i) { REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(&dst[i*3], zeros, 3)); } } // zero point { SkScalar zeros[3] = {0.f, 0.f, 0.f}; for (int i = 0; i < kMatrixCount; ++i) { SkScalar dst[3]; mats[i].mapHomogeneousPoints(dst, zeros, 1); REPORTER_ASSERT(reporter, scalar_array_nearly_equal_relative(dst, zeros, 3)); } } // doesn't crash with null dst, src, count == 0 { mats[0].mapHomogeneousPoints(NULL, NULL, 0); } // uniform scale of point { mat.setScale(kScale0, kScale0); SkScalar dst[3]; SkScalar src[3] = {randTriples[0], randTriples[1], 1.f}; SkPoint pnt; pnt.set(src[0], src[1]); mat.mapHomogeneousPoints(dst, src, 1); mat.mapPoints(&pnt, &pnt, 1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1)); } // rotation of point { mat.setRotate(kRotation0); SkScalar dst[3]; SkScalar src[3] = {randTriples[0], randTriples[1], 1.f}; SkPoint pnt; pnt.set(src[0], src[1]); mat.mapHomogeneousPoints(dst, src, 1); mat.mapPoints(&pnt, &pnt, 1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1)); } // rotation, scale, rotation of point { mat.setRotate(kRotation1); mat.postScale(kScale0, kScale0); mat.postRotate(kRotation0); SkScalar dst[3]; SkScalar src[3] = {randTriples[0], randTriples[1], 1.f}; SkPoint pnt; pnt.set(src[0], src[1]); mat.mapHomogeneousPoints(dst, src, 1); mat.mapPoints(&pnt, &pnt, 1); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[0], pnt.fX)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[1], pnt.fY)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(dst[2], SK_Scalar1)); } // compare with naive approach { for (int i = 0; i < kMatrixCount; ++i) { for (int j = 0; j < kTripleCount; ++j) { SkScalar dst[3]; mats[i].mapHomogeneousPoints(dst, &randTriples[j*3], 1); REPORTER_ASSERT(reporter, naive_homogeneous_mapping(mats[i], &randTriples[j*3], dst)); } } } }
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, const SkDescriptor* desc) : SkScalerContext(typeface, desc) , fTypeface(SkRef(typeface)) , fGlyphCount(-1) { // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC // except when bi-level rendering is requested or there are embedded // bi-level bitmaps (and the embedded bitmap flag is set and no rotation). // // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do // this. As a result, determine the actual size of the text and then see if // there are any embedded bi-level bitmaps of that size. If there are, then // force bitmaps by requesting bi-level rendering. // // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes // square pixels and only uses ppemY. Therefore the transform must track any // non-uniform x-scale. // // Also, rotated glyphs should have the same absolute advance widths as // horizontal glyphs and the subpixel flag should not affect glyph shapes. // A is the total matrix. SkMatrix A; fRec.getSingleMatrix(&A); // h is where A maps the horizontal baseline. SkPoint h = SkPoint::Make(SK_Scalar1, 0); A.mapPoints(&h, 1); // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0). SkMatrix G; SkComputeGivensRotation(h, &G); // GA is the matrix A with rotation removed. SkMatrix GA(G); GA.preConcat(A); // realTextSize is the actual device size we want (as opposed to the size the user requested). // gdiTextSize is the size we request when GDI compatible. // If the scale is negative, this means the matrix will do the flip anyway. SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY)); // Due to floating point math, the lower bits are suspect. Round carefully. SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; if (gdiTextSize == 0) { gdiTextSize = SK_Scalar1; } bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag); bool treatLikeBitmap = false; bool axisAlignedBitmap = false; if (bitmapRequested) { // When embedded bitmaps are requested, treat the entire range like // a bitmap strike if the range is gridfit only and contains a bitmap. int bitmapPPEM = SkScalarTruncToInt(gdiTextSize); PPEMRange range = { bitmapPPEM, bitmapPPEM }; expand_range_if_gridfit_only(typeface, bitmapPPEM, &range); treatLikeBitmap = has_bitmap_strike(typeface, range); axisAlignedBitmap = is_axis_aligned(fRec); } // If the user requested aliased, do so with aliased compatible metrics. if (SkMask::kBW_Format == fRec.fMaskFormat) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; fTextureType = DWRITE_TEXTURE_ALIASED_1x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // If we can use a bitmap, use gdi classic rendering and measurement. // This will not always provide a bitmap, but matches expected behavior. } else if (treatLikeBitmap && axisAlignedBitmap) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // If rotated but the horizontal text could have used a bitmap, // render high quality rotated glyphs but measure using bitmap metrics. } else if (treatLikeBitmap) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = gdiTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; // Fonts that have hints but no gasp table get non-symmetric rendering. // Usually such fonts have low quality hints which were never tested // with anything but GDI ClearType classic. Such fonts often rely on // drop out control in the y direction in order to be legible. } else if (is_hinted_without_gasp(typeface)) { fTextSizeRender = gdiTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; // The normal case is to use natural symmetric rendering and linear metrics. } else { fTextSizeRender = realTextSize; fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; } if (this->isSubpixel()) { fTextSizeMeasure = realTextSize; fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; } // Remove the realTextSize, as that is the text height scale currently in A. SkScalar scale = SkScalarInvert(realTextSize); // fSkXform is the total matrix A without the text height scale. fSkXform = A; fSkXform.preScale(scale, scale); //remove the text height scale. fXform.m11 = SkScalarToFloat(fSkXform.getScaleX()); fXform.m12 = SkScalarToFloat(fSkXform.getSkewY()); fXform.m21 = SkScalarToFloat(fSkXform.getSkewX()); fXform.m22 = SkScalarToFloat(fSkXform.getScaleY()); fXform.dx = 0; fXform.dy = 0; // GsA is the non-rotational part of A without the text height scale. SkMatrix GsA(GA); GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale. fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX)); fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0. fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX)); fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY)); fGsA.dx = 0; fGsA.dy = 0; // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational. fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX), -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY), G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2)); }
SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) : SkPDFDict("Pattern"), fState(state) { SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; SkPoint transformPoints[2]; // Depending on the type of the gradient, we want to transform the // coordinate space in different ways. const SkShader::GradientInfo* info = &fState.get()->fInfo; transformPoints[0] = info->fPoint[0]; transformPoints[1] = info->fPoint[1]; switch (fState.get()->fType) { case SkShader::kLinear_GradientType: codeFunction = &linearCode; break; case SkShader::kRadial_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += info->fRadius[0]; codeFunction = &radialCode; break; case SkShader::kRadial2_GradientType: { // Bail out if the radii are the same. Empty fResources signals // an error and isValid will return false. if (info->fRadius[0] == info->fRadius[1]) { return; } transformPoints[1] = transformPoints[0]; SkScalar dr = info->fRadius[1] - info->fRadius[0]; transformPoints[1].fX += dr; codeFunction = &twoPointRadialCode; break; } case SkShader::kSweep_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += 1; codeFunction = &sweepCode; break; case SkShader::kColor_GradientType: case SkShader::kNone_GradientType: default: return; } // Move any scaling (assuming a unit gradient) or translation // (and rotation for linear gradient), of the final gradient from // info->fPoints to the matrix (updating bbox appropriately). Now // the gradient can be drawn on on the unit segment. SkMatrix mapperMatrix; unitToPointsMatrix(transformPoints, &mapperMatrix); SkMatrix finalMatrix = fState.get()->fCanvasTransform; finalMatrix.preConcat(mapperMatrix); finalMatrix.preConcat(fState.get()->fShaderTransform); SkRect bbox; bbox.set(fState.get()->fBBox); transformBBox(finalMatrix, &bbox); SkRefPtr<SkPDFArray> domain = new SkPDFArray; domain->unref(); // SkRefPtr and new both took a reference. domain->reserve(4); domain->appendScalar(bbox.fLeft); domain->appendScalar(bbox.fRight); domain->appendScalar(bbox.fTop); domain->appendScalar(bbox.fBottom); SkString functionCode; // The two point radial gradient further references fState.get()->fInfo // in translating from x, y coordinates to the t parameter. So, we have // to transform the points and radii according to the calculated matrix. if (fState.get()->fType == SkShader::kRadial2_GradientType) { SkShader::GradientInfo twoPointRadialInfo = *info; SkMatrix inverseMapperMatrix; mapperMatrix.invert(&inverseMapperMatrix); inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); twoPointRadialInfo.fRadius[0] = inverseMapperMatrix.mapRadius(info->fRadius[0]); twoPointRadialInfo.fRadius[1] = inverseMapperMatrix.mapRadius(info->fRadius[1]); functionCode = codeFunction(twoPointRadialInfo); } else { functionCode = codeFunction(*info); } SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get()); // Pass one reference to fResources, SkRefPtr and new both took a reference. fResources.push(function.get()); SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict; pdfShader->unref(); // SkRefPtr and new both took a reference. pdfShader->insertInt("ShadingType", 1); pdfShader->insertName("ColorSpace", "DeviceRGB"); pdfShader->insert("Domain", domain.get()); pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref(); insertInt("PatternType", 2); insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); insert("Shading", pdfShader.get()); }
// TODO: This needs to be split into helper functions to better scope the // inputs/outputs, and reduce duplicate code. // This issue is tracked in https://bugs.webkit.org/show_bug.cgi?id=62989 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { COMPILE_ASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t), GlyphBufferGlyphSize_equals_uint16_t); bool shouldSmoothFonts = true; bool shouldAntialias = true; switch (fontDescription().fontSmoothing()) { case Antialiased: shouldSmoothFonts = false; break; case SubpixelAntialiased: break; case NoSmoothing: shouldAntialias = false; shouldSmoothFonts = false; break; case AutoSmoothing: // For the AutoSmooth case, don't do anything! Keep the default settings. break; } if (!shouldUseSmoothing() || PlatformSupport::layoutTestMode()) shouldSmoothFonts = false; const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); if (font->platformData().orientation() == Vertical) y += SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); // FIXME: text rendering speed: // Android has code in their WebCore fork to special case when the // GlyphBuffer has no advances other than the defaults. In that case the // text drawing can proceed faster. However, it's unclear when those // patches may be upstreamed to WebKit so we always use the slower path // here. const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); for (int i = 0; i < numGlyphs; i++) { pos[i].set(x, y); x += SkFloatToScalar(adv[i].width); y += SkFloatToScalar(adv[i].height); } SkCanvas* canvas = gc->platformContext()->canvas(); if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); SkMatrix rotator; rotator.reset(); rotator.setRotate(90); rotator.mapPoints(pos, numGlyphs); } TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); // We draw text up to two times (once for fill, once for stroke). if (textMode & TextModeFill) { SkPaint paint; gc->platformContext()->setupPaintForFilling(&paint); setupPaint(&paint, font, this, shouldAntialias, shouldSmoothFonts); gc->platformContext()->adjustTextRenderMode(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } if ((textMode & TextModeStroke) && gc->platformContext()->getStrokeStyle() != NoStroke && gc->platformContext()->getStrokeThickness() > 0) { SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); setupPaint(&paint, font, this, shouldAntialias, shouldSmoothFonts); gc->platformContext()->adjustTextRenderMode(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); if (textMode & TextModeFill) { // If we also filled, we don't want to draw shadows twice. // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. paint.setLooper(0); } canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } if (font->platformData().orientation() == Vertical) canvas->restore(); }
SkPDFFunctionShader* SkPDFFunctionShader::Create( SkPDFCanon* canon, SkAutoTDelete<SkPDFShader::State>* autoState) { const SkPDFShader::State& state = **autoState; SkString (*codeFunction)(const SkShader::GradientInfo& info, const SkMatrix& perspectiveRemover) = NULL; SkPoint transformPoints[2]; // Depending on the type of the gradient, we want to transform the // coordinate space in different ways. const SkShader::GradientInfo* info = &state.fInfo; transformPoints[0] = info->fPoint[0]; transformPoints[1] = info->fPoint[1]; switch (state.fType) { case SkShader::kLinear_GradientType: codeFunction = &linearCode; break; case SkShader::kRadial_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += info->fRadius[0]; codeFunction = &radialCode; break; case SkShader::kRadial2_GradientType: { // Bail out if the radii are the same. if (info->fRadius[0] == info->fRadius[1]) { return NULL; } transformPoints[1] = transformPoints[0]; SkScalar dr = info->fRadius[1] - info->fRadius[0]; transformPoints[1].fX += dr; codeFunction = &twoPointRadialCode; break; } case SkShader::kConical_GradientType: { transformPoints[1] = transformPoints[0]; transformPoints[1].fX += SK_Scalar1; codeFunction = &twoPointConicalCode; break; } case SkShader::kSweep_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += SK_Scalar1; codeFunction = &sweepCode; break; case SkShader::kColor_GradientType: case SkShader::kNone_GradientType: default: return NULL; } // Move any scaling (assuming a unit gradient) or translation // (and rotation for linear gradient), of the final gradient from // info->fPoints to the matrix (updating bbox appropriately). Now // the gradient can be drawn on on the unit segment. SkMatrix mapperMatrix; unitToPointsMatrix(transformPoints, &mapperMatrix); SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); finalMatrix.preConcat(mapperMatrix); // Preserves as much as posible in the final matrix, and only removes // the perspective. The inverse of the perspective is stored in // perspectiveInverseOnly matrix and has 3 useful numbers // (p0, p1, p2), while everything else is either 0 or 1. // In this way the shader will handle it eficiently, with minimal code. SkMatrix perspectiveInverseOnly = SkMatrix::I(); if (finalMatrix.hasPerspective()) { if (!split_perspective(finalMatrix, &finalMatrix, &perspectiveInverseOnly)) { return NULL; } } SkRect bbox; bbox.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &bbox)) { return NULL; } SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); domain->reserve(4); domain->appendScalar(bbox.fLeft); domain->appendScalar(bbox.fRight); domain->appendScalar(bbox.fTop); domain->appendScalar(bbox.fBottom); SkString functionCode; // The two point radial gradient further references // state.fInfo // in translating from x, y coordinates to the t parameter. So, we have // to transform the points and radii according to the calculated matrix. if (state.fType == SkShader::kRadial2_GradientType) { SkShader::GradientInfo twoPointRadialInfo = *info; SkMatrix inverseMapperMatrix; if (!mapperMatrix.invert(&inverseMapperMatrix)) { return NULL; } inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); twoPointRadialInfo.fRadius[0] = inverseMapperMatrix.mapRadius(info->fRadius[0]); twoPointRadialInfo.fRadius[1] = inverseMapperMatrix.mapRadius(info->fRadius[1]); functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly); } else { functionCode = codeFunction(*info, perspectiveInverseOnly); } SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); pdfShader->insertInt("ShadingType", 1); pdfShader->insertName("ColorSpace", "DeviceRGB"); pdfShader->insert("Domain", domain.get()); SkAutoTUnref<SkPDFStream> function( make_ps_function(functionCode, domain.get())); pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); SkAutoTUnref<SkPDFArray> matrixArray( SkPDFUtils::MatrixToArray(finalMatrix)); SkPDFFunctionShader* pdfFunctionShader = SkNEW_ARGS(SkPDFFunctionShader, (autoState->detach())); pdfFunctionShader->insertInt("PatternType", 2); pdfFunctionShader->insert("Matrix", matrixArray.get()); pdfFunctionShader->insert("Shading", pdfShader.get()); canon->addFunctionShader(pdfFunctionShader); return pdfFunctionShader; }