OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkPDFDevice.h" | 8 #include "SkPDFDevice.h" |
9 | 9 |
10 #include "SkAnnotation.h" | 10 #include "SkAnnotation.h" |
11 #include "SkColor.h" | 11 #include "SkColor.h" |
12 #include "SkColorFilter.h" | 12 #include "SkColorFilter.h" |
13 #include "SkClipStack.h" | 13 #include "SkClipStack.h" |
14 #include "SkData.h" | 14 #include "SkData.h" |
15 #include "SkDraw.h" | 15 #include "SkDraw.h" |
16 #include "SkGlyphCache.h" | 16 #include "SkGlyphCache.h" |
17 #include "SkPaint.h" | 17 #include "SkPaint.h" |
18 #include "SkPath.h" | 18 #include "SkPath.h" |
19 #include "SkPathOps.h" | 19 #include "SkPathOps.h" |
20 #include "SkPDFBitmap.h" | 20 #include "SkPDFBitmap.h" |
| 21 #include "SkPDFCanon.h" |
21 #include "SkPDFFont.h" | 22 #include "SkPDFFont.h" |
22 #include "SkPDFFormXObject.h" | 23 #include "SkPDFFormXObject.h" |
23 #include "SkPDFGraphicState.h" | 24 #include "SkPDFGraphicState.h" |
24 #include "SkPDFResourceDict.h" | 25 #include "SkPDFResourceDict.h" |
25 #include "SkPDFShader.h" | 26 #include "SkPDFShader.h" |
26 #include "SkPDFStream.h" | 27 #include "SkPDFStream.h" |
27 #include "SkPDFTypes.h" | 28 #include "SkPDFTypes.h" |
28 #include "SkPDFUtils.h" | 29 #include "SkPDFUtils.h" |
29 #include "SkRasterClip.h" | 30 #include "SkRasterClip.h" |
30 #include "SkRect.h" | 31 #include "SkRect.h" |
(...skipping 532 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
563 fContentStream->writeText(" Tr\n"); | 564 fContentStream->writeText(" Tr\n"); |
564 currentEntry()->fTextFill = state.fTextFill; | 565 currentEntry()->fTextFill = state.fTextFill; |
565 } | 566 } |
566 } | 567 } |
567 } | 568 } |
568 | 569 |
569 static bool not_supported_for_layers(const SkPaint& layerPaint) { | 570 static bool not_supported_for_layers(const SkPaint& layerPaint) { |
570 // PDF does not support image filters, so render them on CPU. | 571 // PDF does not support image filters, so render them on CPU. |
571 // Note that this rendering is done at "screen" resolution (100dpi), not | 572 // Note that this rendering is done at "screen" resolution (100dpi), not |
572 // printer resolution. | 573 // printer resolution. |
573 // FIXME: It may be possible to express some filters natively using PDF | 574 // TODO: It may be possible to express some filters natively using PDF |
574 // to improve quality and file size (http://skbug.com/3043) | 575 // to improve quality and file size (http://skbug.com/3043) |
575 | 576 |
576 // TODO: should we return true if there is a colorfilter? | 577 // TODO: should we return true if there is a colorfilter? |
577 return layerPaint.getImageFilter() != nullptr; | 578 return layerPaint.getImageFilter() != nullptr; |
578 } | 579 } |
579 | 580 |
580 SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint
* layerPaint) { | 581 SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint
* layerPaint) { |
581 if (cinfo.fForImageFilter || | 582 if (cinfo.fForImageFilter || |
582 (layerPaint && not_supported_for_layers(*layerPaint))) { | 583 (layerPaint && not_supported_for_layers(*layerPaint))) { |
583 return nullptr; | 584 return nullptr; |
(...skipping 449 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1033 ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint); | 1034 ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint); |
1034 if (!content.entry()) { | 1035 if (!content.entry()) { |
1035 return; | 1036 return; |
1036 } | 1037 } |
1037 SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), | 1038 SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), |
1038 &content.entry()->fContent); | 1039 &content.entry()->fContent); |
1039 SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), | 1040 SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), |
1040 &content.entry()->fContent); | 1041 &content.entry()->fContent); |
1041 } | 1042 } |
1042 | 1043 |
1043 void SkPDFDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, | 1044 void SkPDFDevice::drawBitmapRect(const SkDraw& draw, |
1044 const SkRect* src, const SkRect& dst, | 1045 const SkBitmap& bitmap, |
1045 const SkPaint& srcPaint, SkCanvas::SrcRectConst
raint constraint) { | 1046 const SkRect* src, |
| 1047 const SkRect& dst, |
| 1048 const SkPaint& srcPaint, |
| 1049 SkCanvas::SrcRectConstraint constraint) { |
| 1050 const SkImage* image = fCanon->bitmapToImage(bitmap); |
| 1051 if (!image) { |
| 1052 return; |
| 1053 } |
| 1054 // ownership of this image is retained by the canon. |
| 1055 this->drawImageRect(draw, image, src, dst, srcPaint, constraint); |
| 1056 } |
| 1057 |
| 1058 void SkPDFDevice::drawBitmap(const SkDraw& d, |
| 1059 const SkBitmap& bitmap, |
| 1060 const SkMatrix& matrix, |
| 1061 const SkPaint& srcPaint) { |
1046 SkPaint paint = srcPaint; | 1062 SkPaint paint = srcPaint; |
1047 if (bitmap.isOpaque()) { | 1063 if (bitmap.isOpaque()) { |
1048 replace_srcmode_on_opaque_paint(&paint); | 1064 replace_srcmode_on_opaque_paint(&paint); |
1049 } | 1065 } |
1050 | 1066 |
| 1067 if (d.fClip->isEmpty()) { |
| 1068 return; |
| 1069 } |
| 1070 |
| 1071 SkMatrix transform = matrix; |
| 1072 transform.postConcat(*d.fMatrix); |
| 1073 const SkImage* image = fCanon->bitmapToImage(bitmap); |
| 1074 if (!image) { |
| 1075 return; |
| 1076 } |
| 1077 this->internalDrawImage(transform, d.fClipStack, *d.fClip, image, nullptr, |
| 1078 paint); |
| 1079 } |
| 1080 |
| 1081 void SkPDFDevice::drawSprite(const SkDraw& d, |
| 1082 const SkBitmap& bitmap, |
| 1083 int x, |
| 1084 int y, |
| 1085 const SkPaint& srcPaint) { |
| 1086 SkPaint paint = srcPaint; |
| 1087 if (bitmap.isOpaque()) { |
| 1088 replace_srcmode_on_opaque_paint(&paint); |
| 1089 } |
| 1090 |
| 1091 if (d.fClip->isEmpty()) { |
| 1092 return; |
| 1093 } |
| 1094 |
| 1095 SkMatrix matrix; |
| 1096 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); |
| 1097 const SkImage* image = fCanon->bitmapToImage(bitmap); |
| 1098 if (!image) { |
| 1099 return; |
| 1100 } |
| 1101 this->internalDrawImage(matrix, d.fClipStack, *d.fClip, image, nullptr, |
| 1102 paint); |
| 1103 } |
| 1104 |
| 1105 void SkPDFDevice::drawImage(const SkDraw& draw, |
| 1106 const SkImage* image, |
| 1107 SkScalar x, |
| 1108 SkScalar y, |
| 1109 const SkPaint& srcPaint) { |
| 1110 SkPaint paint = srcPaint; |
| 1111 if (!image) { |
| 1112 return; |
| 1113 } |
| 1114 if (image->isOpaque()) { |
| 1115 replace_srcmode_on_opaque_paint(&paint); |
| 1116 } |
| 1117 if (draw.fClip->isEmpty()) { |
| 1118 return; |
| 1119 } |
| 1120 SkMatrix transform = SkMatrix::MakeTrans(x, y); |
| 1121 transform.postConcat(*draw.fMatrix); |
| 1122 this->internalDrawImage(transform, draw.fClipStack, *draw.fClip, image, |
| 1123 nullptr, paint); |
| 1124 } |
| 1125 |
| 1126 void SkPDFDevice::drawImageRect(const SkDraw& draw, |
| 1127 const SkImage* image, |
| 1128 const SkRect* src, |
| 1129 const SkRect& dst, |
| 1130 const SkPaint& srcPaint, |
| 1131 SkCanvas::SrcRectConstraint constraint) { |
| 1132 if (!image) { |
| 1133 return; |
| 1134 } |
| 1135 if (draw.fClip->isEmpty()) { |
| 1136 return; |
| 1137 } |
| 1138 SkPaint paint = srcPaint; |
| 1139 if (image->isOpaque()) { |
| 1140 replace_srcmode_on_opaque_paint(&paint); |
| 1141 } |
1051 // TODO: this code path must be updated to respect the flags parameter | 1142 // TODO: this code path must be updated to respect the flags parameter |
1052 SkMatrix matrix; | 1143 SkMatrix matrix; |
1053 SkRect bitmapBounds, tmpSrc, tmpDst; | 1144 SkRect tmpSrc, tmpDst; |
1054 SkBitmap tmpBitmap; | 1145 SkRect imageBounds = SkRect::Make(image->bounds()); |
1055 | |
1056 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); | |
1057 | 1146 |
1058 // Compute matrix from the two rectangles | 1147 // Compute matrix from the two rectangles |
1059 if (src) { | 1148 if (src) { |
1060 tmpSrc = *src; | 1149 tmpSrc = *src; |
1061 } else { | 1150 } else { |
1062 tmpSrc = bitmapBounds; | 1151 tmpSrc = imageBounds; |
1063 } | 1152 } |
1064 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); | 1153 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); |
1065 | 1154 |
1066 const SkBitmap* bitmapPtr = &bitmap; | |
1067 | |
1068 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if | 1155 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if |
1069 // needed (if the src was clipped). No check needed if src==null. | 1156 // needed (if the src was clipped). No check needed if src==null. |
| 1157 SkAutoTUnref<const SkImage> autoImageUnref; |
1070 if (src) { | 1158 if (src) { |
1071 if (!bitmapBounds.contains(*src)) { | 1159 if (!imageBounds.contains(*src)) { |
1072 if (!tmpSrc.intersect(bitmapBounds)) { | 1160 if (!tmpSrc.intersect(imageBounds)) { |
1073 return; // nothing to draw | 1161 return; // nothing to draw |
1074 } | 1162 } |
1075 // recompute dst, based on the smaller tmpSrc | 1163 // recompute dst, based on the smaller tmpSrc |
1076 matrix.mapRect(&tmpDst, tmpSrc); | 1164 matrix.mapRect(&tmpDst, tmpSrc); |
1077 } | 1165 } |
1078 | 1166 |
1079 // since we may need to clamp to the borders of the src rect within | 1167 // since we may need to clamp to the borders of the src rect within |
1080 // the bitmap, we extract a subset. | 1168 // the bitmap, we extract a subset. |
1081 // TODO: make sure this is handled in drawBitmap and remove from here. | |
1082 SkIRect srcIR; | 1169 SkIRect srcIR; |
1083 tmpSrc.roundOut(&srcIR); | 1170 tmpSrc.roundOut(&srcIR); |
1084 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { | 1171 |
| 1172 autoImageUnref.reset(image->newSubset(srcIR)); |
| 1173 if (!autoImageUnref) { |
1085 return; | 1174 return; |
1086 } | 1175 } |
1087 bitmapPtr = &tmpBitmap; | 1176 image = autoImageUnref; |
1088 | |
1089 // Since we did an extract, we need to adjust the matrix accordingly | 1177 // Since we did an extract, we need to adjust the matrix accordingly |
1090 SkScalar dx = 0, dy = 0; | 1178 SkScalar dx = 0, dy = 0; |
1091 if (srcIR.fLeft > 0) { | 1179 if (srcIR.fLeft > 0) { |
1092 dx = SkIntToScalar(srcIR.fLeft); | 1180 dx = SkIntToScalar(srcIR.fLeft); |
1093 } | 1181 } |
1094 if (srcIR.fTop > 0) { | 1182 if (srcIR.fTop > 0) { |
1095 dy = SkIntToScalar(srcIR.fTop); | 1183 dy = SkIntToScalar(srcIR.fTop); |
1096 } | 1184 } |
1097 if (dx || dy) { | 1185 if (dx || dy) { |
1098 matrix.preTranslate(dx, dy); | 1186 matrix.preTranslate(dx, dy); |
1099 } | 1187 } |
1100 } | 1188 } |
1101 this->drawBitmap(draw, *bitmapPtr, matrix, paint); | 1189 matrix.postConcat(*draw.fMatrix); |
1102 } | 1190 this->internalDrawImage(matrix, draw.fClipStack, *draw.fClip, image, |
1103 | 1191 nullptr, paint); |
1104 void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, | |
1105 const SkMatrix& matrix, const SkPaint& srcPaint) { | |
1106 SkPaint paint = srcPaint; | |
1107 if (bitmap.isOpaque()) { | |
1108 replace_srcmode_on_opaque_paint(&paint); | |
1109 } | |
1110 | |
1111 if (d.fClip->isEmpty()) { | |
1112 return; | |
1113 } | |
1114 | |
1115 SkMatrix transform = matrix; | |
1116 transform.postConcat(*d.fMatrix); | |
1117 this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, nullptr, | |
1118 paint); | |
1119 } | |
1120 | |
1121 void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap, | |
1122 int x, int y, const SkPaint& srcPaint) { | |
1123 SkPaint paint = srcPaint; | |
1124 if (bitmap.isOpaque()) { | |
1125 replace_srcmode_on_opaque_paint(&paint); | |
1126 } | |
1127 | |
1128 if (d.fClip->isEmpty()) { | |
1129 return; | |
1130 } | |
1131 | |
1132 SkMatrix matrix; | |
1133 matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y)); | |
1134 this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, nullptr, | |
1135 paint); | |
1136 } | 1192 } |
1137 | 1193 |
1138 // Create a PDF string. Maximum length (in bytes) is 65,535. | 1194 // Create a PDF string. Maximum length (in bytes) is 65,535. |
1139 // @param input A string value. | 1195 // @param input A string value. |
1140 // @param len The length of the input array. | 1196 // @param len The length of the input array. |
1141 // @param wideChars True iff the upper byte in each uint16_t is | 1197 // @param wideChars True iff the upper byte in each uint16_t is |
1142 // significant and should be encoded and not | 1198 // significant and should be encoded and not |
1143 // discarded. If true, the upper byte is encoded | 1199 // discarded. If true, the upper byte is encoded |
1144 // first. Otherwise, we assert the upper byte is | 1200 // first. Otherwise, we assert the upper byte is |
1145 // zero. | 1201 // zero. |
(...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1428 mediaBox->appendInt(0); | 1484 mediaBox->appendInt(0); |
1429 mediaBox->appendInt(0); | 1485 mediaBox->appendInt(0); |
1430 mediaBox->appendInt(fPageSize.fWidth); | 1486 mediaBox->appendInt(fPageSize.fWidth); |
1431 mediaBox->appendInt(fPageSize.fHeight); | 1487 mediaBox->appendInt(fPageSize.fHeight); |
1432 return mediaBox.detach(); | 1488 return mediaBox.detach(); |
1433 } | 1489 } |
1434 | 1490 |
1435 SkStreamAsset* SkPDFDevice::content() const { | 1491 SkStreamAsset* SkPDFDevice::content() const { |
1436 SkDynamicMemoryWStream buffer; | 1492 SkDynamicMemoryWStream buffer; |
1437 this->writeContent(&buffer); | 1493 this->writeContent(&buffer); |
1438 return buffer.detachAsStream(); | 1494 return buffer.bytesWritten() > 0 |
| 1495 ? buffer.detachAsStream() |
| 1496 : new SkMemoryStream; |
1439 } | 1497 } |
1440 | 1498 |
1441 void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry, | 1499 void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry, |
1442 SkWStream* data) const { | 1500 SkWStream* data) const { |
1443 // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the | 1501 // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the |
1444 // right thing to pass here. | 1502 // right thing to pass here. |
1445 GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data); | 1503 GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data); |
1446 while (entry != nullptr) { | 1504 while (entry != nullptr) { |
1447 SkPoint translation; | 1505 SkPoint translation; |
1448 translation.iset(this->getOrigin()); | 1506 translation.iset(this->getOrigin()); |
(...skipping 614 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2063 SkPDFFont::GetFontResource(fCanon, typeface, glyphID)); | 2121 SkPDFFont::GetFontResource(fCanon, typeface, glyphID)); |
2064 int resourceIndex = fFontResources.find(newFont.get()); | 2122 int resourceIndex = fFontResources.find(newFont.get()); |
2065 if (resourceIndex < 0) { | 2123 if (resourceIndex < 0) { |
2066 resourceIndex = fFontResources.count(); | 2124 resourceIndex = fFontResources.count(); |
2067 fFontResources.push(newFont.get()); | 2125 fFontResources.push(newFont.get()); |
2068 newFont.get()->ref(); | 2126 newFont.get()->ref(); |
2069 } | 2127 } |
2070 return resourceIndex; | 2128 return resourceIndex; |
2071 } | 2129 } |
2072 | 2130 |
2073 void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix, | 2131 static SkSize rect_to_size(const SkRect& r) { |
2074 const SkClipStack* clipStack, | 2132 return SkSize::Make(r.width(), r.height()); |
2075 const SkRegion& origClipRegion, | 2133 } |
2076 const SkBitmap& origBitmap, | 2134 |
2077 const SkIRect* srcRect, | 2135 static const SkImage* color_filter(const SkImage* image, |
2078 const SkPaint& paint) { | 2136 SkColorFilter* colorFilter) { |
| 2137 SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster( |
| 2138 SkImageInfo::MakeN32Premul(image->dimensions()))); |
| 2139 if (!surface) { |
| 2140 return image; |
| 2141 } |
| 2142 SkCanvas* canvas = surface->getCanvas(); |
| 2143 canvas->clear(SK_ColorTRANSPARENT); |
| 2144 SkPaint paint; |
| 2145 paint.setColorFilter(colorFilter); |
| 2146 canvas->drawImage(image, 0, 0, &paint); |
| 2147 canvas->flush(); |
| 2148 return surface->newImageSnapshot(); |
| 2149 } |
| 2150 |
| 2151 //////////////////////////////////////////////////////////////////////////////// |
| 2152 void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix, |
| 2153 const SkClipStack* clipStack, |
| 2154 const SkRegion& origClipRegion, |
| 2155 const SkImage* image, |
| 2156 const SkIRect* srcRect, |
| 2157 const SkPaint& paint) { |
| 2158 SkASSERT(image); |
| 2159 #ifdef SK_PDF_IMAGE_STATS |
| 2160 gDrawImageCalls.fetch_add(1); |
| 2161 #endif |
2079 SkMatrix matrix = origMatrix; | 2162 SkMatrix matrix = origMatrix; |
2080 SkRegion perspectiveBounds; | 2163 SkRegion perspectiveBounds; |
2081 const SkRegion* clipRegion = &origClipRegion; | 2164 const SkRegion* clipRegion = &origClipRegion; |
2082 SkBitmap perspectiveBitmap; | 2165 SkAutoTUnref<const SkImage> autoImageUnref; |
2083 const SkBitmap* bitmap = &origBitmap; | |
2084 SkBitmap tmpSubsetBitmap; | |
2085 | 2166 |
| 2167 if (srcRect) { |
| 2168 autoImageUnref.reset(image->newSubset(*srcRect)); |
| 2169 if (!autoImageUnref) { |
| 2170 return; |
| 2171 } |
| 2172 image = autoImageUnref; |
| 2173 } |
2086 // Rasterize the bitmap using perspective in a new bitmap. | 2174 // Rasterize the bitmap using perspective in a new bitmap. |
2087 if (origMatrix.hasPerspective()) { | 2175 if (origMatrix.hasPerspective()) { |
2088 if (fRasterDpi == 0) { | 2176 if (fRasterDpi == 0) { |
2089 return; | 2177 return; |
2090 } | 2178 } |
2091 SkBitmap* subsetBitmap; | |
2092 if (srcRect) { | |
2093 if (!origBitmap.extractSubset(&tmpSubsetBitmap, *srcRect)) { | |
2094 return; | |
2095 } | |
2096 subsetBitmap = &tmpSubsetBitmap; | |
2097 } else { | |
2098 subsetBitmap = &tmpSubsetBitmap; | |
2099 *subsetBitmap = origBitmap; | |
2100 } | |
2101 srcRect = nullptr; | |
2102 | |
2103 // Transform the bitmap in the new space, without taking into | 2179 // Transform the bitmap in the new space, without taking into |
2104 // account the initial transform. | 2180 // account the initial transform. |
2105 SkPath perspectiveOutline; | 2181 SkPath perspectiveOutline; |
2106 perspectiveOutline.addRect( | 2182 SkRect imageBounds = SkRect::Make(image->bounds()); |
2107 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()), | 2183 perspectiveOutline.addRect(imageBounds); |
2108 SkIntToScalar(subsetBitmap->height()))); | |
2109 perspectiveOutline.transform(origMatrix); | 2184 perspectiveOutline.transform(origMatrix); |
2110 | 2185 |
2111 // TODO(edisonn): perf - use current clip too. | 2186 // TODO(edisonn): perf - use current clip too. |
2112 // Retrieve the bounds of the new shape. | 2187 // Retrieve the bounds of the new shape. |
2113 SkRect bounds = perspectiveOutline.getBounds(); | 2188 SkRect bounds = perspectiveOutline.getBounds(); |
2114 | 2189 |
2115 // Transform the bitmap in the new space, taking into | 2190 // Transform the bitmap in the new space, taking into |
2116 // account the initial transform. | 2191 // account the initial transform. |
2117 SkMatrix total = origMatrix; | 2192 SkMatrix total = origMatrix; |
2118 total.postConcat(fInitialTransform); | 2193 total.postConcat(fInitialTransform); |
2119 total.postScale(SkIntToScalar(fRasterDpi) / | 2194 SkScalar dpiScale = SkIntToScalar(fRasterDpi) / |
2120 SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE), | 2195 SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE); |
2121 SkIntToScalar(fRasterDpi) / | 2196 total.postScale(dpiScale, dpiScale); |
2122 SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE)); | 2197 |
2123 SkPath physicalPerspectiveOutline; | 2198 SkPath physicalPerspectiveOutline; |
2124 physicalPerspectiveOutline.addRect( | 2199 physicalPerspectiveOutline.addRect(imageBounds); |
2125 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()), | |
2126 SkIntToScalar(subsetBitmap->height()))); | |
2127 physicalPerspectiveOutline.transform(total); | 2200 physicalPerspectiveOutline.transform(total); |
2128 | 2201 |
2129 SkScalar scaleX = physicalPerspectiveOutline.getBounds().width() / | 2202 SkRect physicalPerspectiveBounds = |
2130 bounds.width(); | 2203 physicalPerspectiveOutline.getBounds(); |
2131 SkScalar scaleY = physicalPerspectiveOutline.getBounds().height() / | 2204 SkScalar scaleX = physicalPerspectiveBounds.width() / bounds.width(); |
2132 bounds.height(); | 2205 SkScalar scaleY = physicalPerspectiveBounds.height() / bounds.height(); |
2133 | 2206 |
2134 // TODO(edisonn): A better approach would be to use a bitmap shader | 2207 // TODO(edisonn): A better approach would be to use a bitmap shader |
2135 // (in clamp mode) and draw a rect over the entire bounding box. Then | 2208 // (in clamp mode) and draw a rect over the entire bounding box. Then |
2136 // intersect perspectiveOutline to the clip. That will avoid introducing | 2209 // intersect perspectiveOutline to the clip. That will avoid introducing |
2137 // alpha to the image while still giving good behavior at the edge of | 2210 // alpha to the image while still giving good behavior at the edge of |
2138 // the image. Avoiding alpha will reduce the pdf size and generation | 2211 // the image. Avoiding alpha will reduce the pdf size and generation |
2139 // CPU time some. | 2212 // CPU time some. |
2140 | 2213 |
2141 const int w = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().w
idth()); | 2214 SkISize wh = rect_to_size(physicalPerspectiveBounds).toCeil(); |
2142 const int h = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().h
eight()); | 2215 |
2143 if (!perspectiveBitmap.tryAllocN32Pixels(w, h)) { | 2216 SkAutoTUnref<SkSurface> surface( |
| 2217 SkSurface::NewRaster(SkImageInfo::MakeN32Premul(wh))); |
| 2218 if (!surface) { |
2144 return; | 2219 return; |
2145 } | 2220 } |
2146 perspectiveBitmap.eraseColor(SK_ColorTRANSPARENT); | 2221 SkCanvas* canvas = surface->getCanvas(); |
2147 | 2222 canvas->clear(SK_ColorTRANSPARENT); |
2148 SkCanvas canvas(perspectiveBitmap); | |
2149 | 2223 |
2150 SkScalar deltaX = bounds.left(); | 2224 SkScalar deltaX = bounds.left(); |
2151 SkScalar deltaY = bounds.top(); | 2225 SkScalar deltaY = bounds.top(); |
2152 | 2226 |
2153 SkMatrix offsetMatrix = origMatrix; | 2227 SkMatrix offsetMatrix = origMatrix; |
2154 offsetMatrix.postTranslate(-deltaX, -deltaY); | 2228 offsetMatrix.postTranslate(-deltaX, -deltaY); |
2155 offsetMatrix.postScale(scaleX, scaleY); | 2229 offsetMatrix.postScale(scaleX, scaleY); |
2156 | 2230 |
2157 // Translate the draw in the new canvas, so we perfectly fit the | 2231 // Translate the draw in the new canvas, so we perfectly fit the |
2158 // shape in the bitmap. | 2232 // shape in the bitmap. |
2159 canvas.setMatrix(offsetMatrix); | 2233 canvas->setMatrix(offsetMatrix); |
2160 | 2234 canvas->drawImage(image, 0, 0, nullptr); |
2161 canvas.drawBitmap(*subsetBitmap, SkIntToScalar(0), SkIntToScalar(0)); | |
2162 | |
2163 // Make sure the final bits are in the bitmap. | 2235 // Make sure the final bits are in the bitmap. |
2164 canvas.flush(); | 2236 canvas->flush(); |
2165 | 2237 |
2166 // In the new space, we use the identity matrix translated | 2238 // In the new space, we use the identity matrix translated |
2167 // and scaled to reflect DPI. | 2239 // and scaled to reflect DPI. |
2168 matrix.setScale(1 / scaleX, 1 / scaleY); | 2240 matrix.setScale(1 / scaleX, 1 / scaleY); |
2169 matrix.postTranslate(deltaX, deltaY); | 2241 matrix.postTranslate(deltaX, deltaY); |
2170 | 2242 |
2171 perspectiveBounds.setRect( | 2243 perspectiveBounds.setRect(bounds.roundOut()); |
2172 SkIRect::MakeXYWH(SkScalarFloorToInt(bounds.x()), | |
2173 SkScalarFloorToInt(bounds.y()), | |
2174 SkScalarCeilToInt(bounds.width()), | |
2175 SkScalarCeilToInt(bounds.height()))); | |
2176 clipRegion = &perspectiveBounds; | 2244 clipRegion = &perspectiveBounds; |
2177 srcRect = nullptr; | 2245 srcRect = nullptr; |
2178 bitmap = &perspectiveBitmap; | 2246 |
| 2247 autoImageUnref.reset(surface->newImageSnapshot()); |
| 2248 image = autoImageUnref; |
2179 } | 2249 } |
2180 | 2250 |
2181 SkMatrix scaled; | 2251 SkMatrix scaled; |
2182 // Adjust for origin flip. | 2252 // Adjust for origin flip. |
2183 scaled.setScale(SK_Scalar1, -SK_Scalar1); | 2253 scaled.setScale(SK_Scalar1, -SK_Scalar1); |
2184 scaled.postTranslate(0, SK_Scalar1); | 2254 scaled.postTranslate(0, SK_Scalar1); |
2185 // Scale the image up from 1x1 to WxH. | 2255 // Scale the image up from 1x1 to WxH. |
2186 SkIRect subset = bitmap->bounds(); | 2256 SkIRect subset = image->bounds(); |
2187 scaled.postScale(SkIntToScalar(subset.width()), | 2257 scaled.postScale(SkIntToScalar(image->width()), |
2188 SkIntToScalar(subset.height())); | 2258 SkIntToScalar(image->height())); |
2189 scaled.postConcat(matrix); | 2259 scaled.postConcat(matrix); |
2190 ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint); | 2260 ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint); |
2191 if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) { | 2261 if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) { |
2192 return; | 2262 return; |
2193 } | 2263 } |
2194 if (content.needShape()) { | 2264 if (content.needShape()) { |
2195 SkPath shape; | 2265 SkPath shape; |
2196 shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()), | 2266 shape.addRect(SkRect::Make(subset)); |
2197 SkIntToScalar(subset.height()))); | |
2198 shape.transform(matrix); | 2267 shape.transform(matrix); |
2199 content.setShape(shape); | 2268 content.setShape(shape); |
2200 } | 2269 } |
2201 if (!content.needSource()) { | 2270 if (!content.needSource()) { |
2202 return; | 2271 return; |
2203 } | 2272 } |
2204 | 2273 |
2205 SkBitmap subsetBitmap; | |
2206 if (!bitmap->extractSubset(&subsetBitmap, subset)) { | |
2207 return; | |
2208 } | |
2209 if (SkColorFilter* colorFilter = paint.getColorFilter()) { | 2274 if (SkColorFilter* colorFilter = paint.getColorFilter()) { |
2210 // TODO(http://skbug.com/4378): implement colorfilter on other | 2275 // TODO(http://skbug.com/4378): implement colorfilter on other |
2211 // draw calls. This code here works for all drawBitmap*() | 2276 // draw calls. This code here works for all |
2212 // calls amd ImageFilters (which rasterize a layer on this | 2277 // drawBitmap*()/drawImage*() calls amd ImageFilters (which |
2213 // backend). Fortuanely, this seems to be how Chromium | 2278 // rasterize a layer on this backend). Fortuanely, this seems |
2214 // impements most color-filters. | 2279 // to be how Chromium impements most color-filters. |
2215 SkBitmap tmp; | 2280 autoImageUnref.reset(color_filter(image, colorFilter)); |
2216 if (subsetBitmap.copyTo(&tmp, kN32_SkColorType)) { | 2281 image = autoImageUnref; |
2217 SkAutoLockPixels autoLockPixelsTmp(tmp); | 2282 // TODO(halcanary): de-dupe this by caching filtered images. |
2218 for (int y = 0; y < tmp.height(); ++y) { | 2283 // (maybe in the resource cache?) |
2219 SkPMColor* pixels = tmp.getAddr32(0, y); | 2284 } |
2220 colorFilter->filterSpan(pixels, tmp.width(), pixels); | 2285 SkAutoTUnref<SkPDFObject> pdfimage(fCanon->findPDFBitmap(image)); |
2221 } | 2286 if (!pdfimage) { |
2222 tmp.setImmutable(); | 2287 pdfimage.reset(SkPDFCreateBitmapObject(image)); |
2223 subsetBitmap = tmp; | 2288 if (!pdfimage) { |
| 2289 return; |
2224 } | 2290 } |
| 2291 fCanon->addPDFBitmap(image->uniqueID(), pdfimage); |
2225 } | 2292 } |
2226 SkAutoTUnref<SkPDFObject> image(SkPDFBitmap::Create(fCanon, subsetBitmap)); | 2293 SkPDFUtils::DrawFormXObject(this->addXObjectResource(SkRef(pdfimage.get())), |
2227 if (!image) { | |
2228 return; | |
2229 } | |
2230 | |
2231 SkPDFUtils::DrawFormXObject(this->addXObjectResource(image.get()), | |
2232 &content.entry()->fContent); | 2294 &content.entry()->fContent); |
2233 } | 2295 } |
OLD | NEW |