SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const { SkRefPtr<SkPDFInt> zero = new SkPDFInt(0); zero->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray(); mediaBox->unref(); // SkRefPtr and new both took a reference. mediaBox->reserve(4); mediaBox->append(zero.get()); mediaBox->append(zero.get()); mediaBox->append(new SkPDFInt(fWidth))->unref(); mediaBox->append(new SkPDFInt(fHeight))->unref(); return mediaBox; }
// static SkPDFObject* SkPDFGraphicState::GetInvertFunction() { // This assumes that canonicalPaintsMutex is held. static SkPDFStream* invertFunction = NULL; if (!invertFunction) { // Acrobat crashes if we use a type 0 function, kpdf crashes if we use // a type 2 function, so we use a type 4 function. SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray; domainAndRange->unref(); // SkRefPtr and new both took a reference. domainAndRange->reserve(2); domainAndRange->appendInt(0); domainAndRange->appendInt(1); static const char psInvert[] = "{1 exch sub}"; SkRefPtr<SkMemoryStream> psInvertStream = new SkMemoryStream(&psInvert, strlen(psInvert), true); psInvertStream->unref(); // SkRefPtr and new both took a reference. invertFunction = new SkPDFStream(psInvertStream.get()); invertFunction->insertInt("FunctionType", 4); invertFunction->insert("Domain", domainAndRange.get()); invertFunction->insert("Range", domainAndRange.get()); } return invertFunction; }
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()); }
// static void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages, SkPDFCatalog* catalog, SkTDArray<SkPDFDict*>* pageTree, SkPDFDict** rootNode) { // PDF wants a tree describing all the pages in the document. We arbitrary // choose 8 (kNodeSize) as the number of allowed children. The internal // nodes have type "Pages" with an array of children, a parent pointer, and // the number of leaves below the node as "Count." The leaves are passed // into the method, have type "Page" and need a parent pointer. This method // builds the tree bottom up, skipping internal nodes that would have only // one child. static const int kNodeSize = 8; SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids"); kidsName->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> countName = new SkPDFName("Count"); countName->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent"); parentName->unref(); // SkRefPtr and new both took a reference. // curNodes takes a reference to its items, which it passes to pageTree. SkTDArray<SkPDFDict*> curNodes; curNodes.setReserve(pages.count()); for (int i = 0; i < pages.count(); i++) { SkSafeRef(pages[i]); curNodes.push(pages[i]); } // nextRoundNodes passes its references to nodes on to curNodes. SkTDArray<SkPDFDict*> nextRoundNodes; nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize); int treeCapacity = kNodeSize; do { for (int i = 0; i < curNodes.count(); ) { if (i > 0 && i + 1 == curNodes.count()) { nextRoundNodes.push(curNodes[i]); break; } SkPDFDict* newNode = new SkPDFDict("Pages"); SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode); newNodeRef->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFArray> kids = new SkPDFArray; kids->unref(); // SkRefPtr and new both took a reference. kids->reserve(kNodeSize); int count = 0; for (; i < curNodes.count() && count < kNodeSize; i++, count++) { curNodes[i]->insert(parentName.get(), newNodeRef.get()); kids->append(new SkPDFObjRef(curNodes[i]))->unref(); // TODO(vandebo): put the objects in strict access order. // Probably doesn't matter because they are so small. if (curNodes[i] != pages[0]) { pageTree->push(curNodes[i]); // Transfer reference. catalog->addObject(curNodes[i], false); } else { SkSafeUnref(curNodes[i]); catalog->addObject(curNodes[i], true); } } newNode->insert(kidsName.get(), kids.get()); int pageCount = treeCapacity; if (count < kNodeSize) { pageCount = pages.count() % treeCapacity; } newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref(); nextRoundNodes.push(newNode); // Transfer reference. } curNodes = nextRoundNodes; nextRoundNodes.rewind(); treeCapacity *= kNodeSize; } while (curNodes.count() > 1); pageTree->push(curNodes[0]); // Transfer reference. catalog->addObject(curNodes[0], false); if (rootNode) { *rootNode = curNodes[0]; } }
const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() { if (fResourceDict.get() == NULL) { fResourceDict = new SkPDFDict; fResourceDict->unref(); // SkRefPtr and new both took a reference. if (fGraphicStateResources.count()) { SkRefPtr<SkPDFDict> extGState = new SkPDFDict(); extGState->unref(); // SkRefPtr and new both took a reference. for (int i = 0; i < fGraphicStateResources.count(); i++) { SkString nameString("G"); nameString.appendS32(i); extGState->insert( nameString.c_str(), new SkPDFObjRef(fGraphicStateResources[i]))->unref(); } fResourceDict->insert("ExtGState", extGState.get()); } if (fXObjectResources.count()) { SkRefPtr<SkPDFDict> xObjects = new SkPDFDict(); xObjects->unref(); // SkRefPtr and new both took a reference. for (int i = 0; i < fXObjectResources.count(); i++) { SkString nameString("X"); nameString.appendS32(i); xObjects->insert( nameString.c_str(), new SkPDFObjRef(fXObjectResources[i]))->unref(); } fResourceDict->insert("XObject", xObjects.get()); } if (fFontResources.count()) { SkRefPtr<SkPDFDict> fonts = new SkPDFDict(); fonts->unref(); // SkRefPtr and new both took a reference. for (int i = 0; i < fFontResources.count(); i++) { SkString nameString("F"); nameString.appendS32(i); fonts->insert(nameString.c_str(), new SkPDFObjRef(fFontResources[i]))->unref(); } fResourceDict->insert("Font", fonts.get()); } if (fShaderResources.count()) { SkRefPtr<SkPDFDict> patterns = new SkPDFDict(); patterns->unref(); // SkRefPtr and new both took a reference. for (int i = 0; i < fShaderResources.count(); i++) { SkString nameString("P"); nameString.appendS32(i); patterns->insert(nameString.c_str(), new SkPDFObjRef(fShaderResources[i]))->unref(); } fResourceDict->insert("Pattern", patterns.get()); } // For compatibility, add all proc sets (only used for output to PS // devices). const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"}; SkRefPtr<SkPDFArray> procSets = new SkPDFArray(); procSets->unref(); // SkRefPtr and new both took a reference. procSets->reserve(SK_ARRAY_COUNT(procs)); for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++) procSets->append(new SkPDFName(procs[i]))->unref(); fResourceDict->insert("ProcSet", procSets.get()); } return fResourceDict; }
SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) { SkBitmap::Config config = bitmap.getConfig(); // TODO(vandebo) Handle alpha and alpha only images correctly. SkASSERT(config == SkBitmap::kRGB_565_Config || config == SkBitmap::kARGB_4444_Config || config == SkBitmap::kARGB_8888_Config || config == SkBitmap::kIndex8_Config || config == SkBitmap::kRLE_Index8_Config); SkMemoryStream* image_data = extractImageData(bitmap); SkAutoUnref image_data_unref(image_data); fStream = new SkPDFStream(image_data); fStream->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject"); typeValue->unref(); // SkRefPtr and new both took a reference. insert("Type", typeValue.get()); SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image"); subTypeValue->unref(); // SkRefPtr and new both took a reference. insert("Subtype", subTypeValue.get()); SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width()); widthValue->unref(); // SkRefPtr and new both took a reference. insert("Width", widthValue.get()); SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height()); heightValue->unref(); // SkRefPtr and new both took a reference. insert("Height", heightValue.get()); // if (!image mask) { SkRefPtr<SkPDFObject> colorSpaceValue; if (config == SkBitmap::kIndex8_Config || config == SkBitmap::kRLE_Index8_Config) { colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable()); } else { colorSpaceValue = new SkPDFName("DeviceRGB"); } colorSpaceValue->unref(); // SkRefPtr and new both took a reference. insert("ColorSpace", colorSpaceValue.get()); // } int bitsPerComp = bitmap.bytesPerPixel() * 2; if (bitsPerComp == 0) { SkASSERT(config == SkBitmap::kA1_Config); bitsPerComp = 1; } else if (bitsPerComp == 2 || (bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) { bitsPerComp = 8; } SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp); bitsPerCompValue->unref(); // SkRefPtr and new both took a reference. insert("BitsPerComponent", bitsPerCompValue.get()); if (config == SkBitmap::kRGB_565_Config) { SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0); zeroVal->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1 scale5Val->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1 scale6Val->unref(); // SkRefPtr and new both took a reference. SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray(); decodeValue->unref(); // SkRefPtr and new both took a reference. decodeValue->reserve(6); decodeValue->append(zeroVal.get()); decodeValue->append(scale5Val.get()); decodeValue->append(zeroVal.get()); decodeValue->append(scale6Val.get()); decodeValue->append(zeroVal.get()); decodeValue->append(scale5Val.get()); insert("Decode", decodeValue.get()); } }