SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { fState.get()->fImage.lockPixels(); SkMatrix finalMatrix = fState.get()->fCanvasTransform; finalMatrix.preConcat(fState.get()->fShaderTransform); SkRect surfaceBBox; surfaceBBox.set(fState.get()->fBBox); transformBBox(finalMatrix, &surfaceBBox); SkMatrix unflip; unflip.setTranslate(0, SkScalarRound(surfaceBBox.height())); unflip.preScale(SK_Scalar1, -SK_Scalar1); SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), SkScalarRound(surfaceBBox.height())); SkPDFDevice pattern(size, size, unflip); SkCanvas canvas(&pattern); canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); const SkBitmap* image = &fState.get()->fImage; int width = image->width(); int height = image->height(); SkShader::TileMode tileModes[2]; tileModes[0] = fState.get()->fImageTileModes[0]; tileModes[1] = fState.get()->fImageTileModes[1]; canvas.drawBitmap(*image, 0, 0); SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, width, 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); canvas.drawBitmapMatrix(*image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(*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); canvas.drawBitmapMatrix(*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(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, height - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 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, height); if (surfaceBBox.fLeft < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-surfaceBBox.fLeft, 1); leftMatrix.postTranslate(surfaceBBox.fLeft, 0); canvas.drawBitmapMatrix(left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(left, leftMatrix); } patternBBox.fLeft = 0; } if (surfaceBBox.fRight > width) { SkBitmap right; subset.offset(width - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(surfaceBBox.fRight - width, 1); rightMatrix.postTranslate(width, 0); canvas.drawBitmapMatrix(right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(right, rightMatrix); } patternBBox.fRight = surfaceBBox.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1); if (surfaceBBox.fTop < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); topMatrix.postTranslate(0, surfaceBBox.fTop); canvas.drawBitmapMatrix(top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(top, topMatrix); } patternBBox.fTop = 0; } if (surfaceBBox.fBottom > height) { SkBitmap bottom; subset.offset(0, height - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); bottomMatrix.postTranslate(0, height); canvas.drawBitmapMatrix(bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(bottom, bottomMatrix); } patternBBox.fBottom = surfaceBBox.height(); } } SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray; patternBBoxArray->unref(); // SkRefPtr and new both took a reference. patternBBoxArray->reserve(4); patternBBoxArray->appendScalar(patternBBox.fLeft); patternBBoxArray->appendScalar(patternBBox.fTop); patternBBoxArray->appendScalar(patternBBox.fRight); patternBBoxArray->appendScalar(patternBBox.fBottom); // Put the canvas into the pattern stream (fContent). SkRefPtr<SkStream> content = pattern.content(); content->unref(); // SkRefPtr and content() both took a reference. pattern.getResources(&fResources); setData(content.get()); insertName("Type", "Pattern"); insertInt("PatternType", 1); insertInt("PaintType", 1); insertInt("TilingType", 1); insert("BBox", patternBBoxArray.get()); insertScalar("XStep", patternBBox.width()); insertScalar("YStep", patternBBox.height()); insert("Resources", pattern.getResourceDict()); insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); fState.get()->fImage.unlockPixels(); }
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()); }