SkSVGDevice.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. /*
  2. * Copyright 2015 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "src/svg/SkSVGDevice.h"
  8. #include "include/core/SkBitmap.h"
  9. #include "include/core/SkBlendMode.h"
  10. #include "include/core/SkColorFilter.h"
  11. #include "include/core/SkData.h"
  12. #include "include/core/SkImage.h"
  13. #include "include/core/SkImageEncoder.h"
  14. #include "include/core/SkPaint.h"
  15. #include "include/core/SkShader.h"
  16. #include "include/core/SkStream.h"
  17. #include "include/core/SkTypeface.h"
  18. #include "include/private/SkChecksum.h"
  19. #include "include/private/SkTHash.h"
  20. #include "include/private/SkTo.h"
  21. #include "include/svg/SkSVGCanvas.h"
  22. #include "include/utils/SkBase64.h"
  23. #include "include/utils/SkParsePath.h"
  24. #include "src/codec/SkJpegCodec.h"
  25. #include "src/codec/SkPngCodec.h"
  26. #include "src/core/SkAnnotationKeys.h"
  27. #include "src/core/SkClipOpPriv.h"
  28. #include "src/core/SkClipStack.h"
  29. #include "src/core/SkDraw.h"
  30. #include "src/core/SkFontPriv.h"
  31. #include "src/core/SkUtils.h"
  32. #include "src/shaders/SkShaderBase.h"
  33. #include "src/xml/SkXMLWriter.h"
  34. namespace {
  35. static SkString svg_color(SkColor color) {
  36. return SkStringPrintf("rgb(%u,%u,%u)",
  37. SkColorGetR(color),
  38. SkColorGetG(color),
  39. SkColorGetB(color));
  40. }
  41. static SkScalar svg_opacity(SkColor color) {
  42. return SkIntToScalar(SkColorGetA(color)) / SK_AlphaOPAQUE;
  43. }
  44. // Keep in sync with SkPaint::Cap
  45. static const char* cap_map[] = {
  46. nullptr, // kButt_Cap (default)
  47. "round", // kRound_Cap
  48. "square" // kSquare_Cap
  49. };
  50. static_assert(SK_ARRAY_COUNT(cap_map) == SkPaint::kCapCount, "missing_cap_map_entry");
  51. static const char* svg_cap(SkPaint::Cap cap) {
  52. SkASSERT(cap < SK_ARRAY_COUNT(cap_map));
  53. return cap_map[cap];
  54. }
  55. // Keep in sync with SkPaint::Join
  56. static const char* join_map[] = {
  57. nullptr, // kMiter_Join (default)
  58. "round", // kRound_Join
  59. "bevel" // kBevel_Join
  60. };
  61. static_assert(SK_ARRAY_COUNT(join_map) == SkPaint::kJoinCount, "missing_join_map_entry");
  62. static const char* svg_join(SkPaint::Join join) {
  63. SkASSERT(join < SK_ARRAY_COUNT(join_map));
  64. return join_map[join];
  65. }
  66. static SkString svg_transform(const SkMatrix& t) {
  67. SkASSERT(!t.isIdentity());
  68. SkString tstr;
  69. switch (t.getType()) {
  70. case SkMatrix::kPerspective_Mask:
  71. // TODO: handle perspective matrices?
  72. break;
  73. case SkMatrix::kTranslate_Mask:
  74. tstr.printf("translate(%g %g)", t.getTranslateX(), t.getTranslateY());
  75. break;
  76. case SkMatrix::kScale_Mask:
  77. tstr.printf("scale(%g %g)", t.getScaleX(), t.getScaleY());
  78. break;
  79. default:
  80. // http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
  81. // | a c e |
  82. // | b d f |
  83. // | 0 0 1 |
  84. tstr.printf("matrix(%g %g %g %g %g %g)",
  85. t.getScaleX(), t.getSkewY(),
  86. t.getSkewX(), t.getScaleY(),
  87. t.getTranslateX(), t.getTranslateY());
  88. break;
  89. }
  90. return tstr;
  91. }
  92. struct Resources {
  93. Resources(const SkPaint& paint)
  94. : fPaintServer(svg_color(paint.getColor())) {}
  95. SkString fPaintServer;
  96. SkString fClip;
  97. SkString fColorFilter;
  98. };
  99. // Determine if the paint requires us to reset the viewport.
  100. // Currently, we do this whenever the paint shader calls
  101. // for a repeating image.
  102. bool RequiresViewportReset(const SkPaint& paint) {
  103. SkShader* shader = paint.getShader();
  104. if (!shader)
  105. return false;
  106. SkTileMode xy[2];
  107. SkImage* image = shader->isAImage(nullptr, xy);
  108. if (!image)
  109. return false;
  110. for (int i = 0; i < 2; i++) {
  111. if (xy[i] == SkTileMode::kRepeat)
  112. return true;
  113. }
  114. return false;
  115. }
  116. SkPath GetPath(const SkGlyphRun& glyphRun, const SkPoint& offset) {
  117. SkPath path;
  118. struct Rec {
  119. SkPath* fPath;
  120. const SkPoint fOffset;
  121. const SkPoint* fPos;
  122. } rec = { &path, offset, glyphRun.positions().data() };
  123. glyphRun.font().getPaths(glyphRun.glyphsIDs().data(), SkToInt(glyphRun.glyphsIDs().size()),
  124. [](const SkPath* path, const SkMatrix& mx, void* ctx) {
  125. Rec* rec = reinterpret_cast<Rec*>(ctx);
  126. if (path) {
  127. SkMatrix total = mx;
  128. total.postTranslate(rec->fPos->fX + rec->fOffset.fX,
  129. rec->fPos->fY + rec->fOffset.fY);
  130. rec->fPath->addPath(*path, total);
  131. } else {
  132. // TODO: this is going to drop color emojis.
  133. }
  134. rec->fPos += 1; // move to the next glyph's position
  135. }, &rec);
  136. return path;
  137. }
  138. } // namespace
  139. // For now all this does is serve unique serial IDs, but it will eventually evolve to track
  140. // and deduplicate resources.
  141. class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
  142. public:
  143. ResourceBucket()
  144. : fGradientCount(0)
  145. , fClipCount(0)
  146. , fPathCount(0)
  147. , fImageCount(0)
  148. , fPatternCount(0)
  149. , fColorFilterCount(0) {}
  150. SkString addLinearGradient() {
  151. return SkStringPrintf("gradient_%d", fGradientCount++);
  152. }
  153. SkString addClip() {
  154. return SkStringPrintf("clip_%d", fClipCount++);
  155. }
  156. SkString addPath() {
  157. return SkStringPrintf("path_%d", fPathCount++);
  158. }
  159. SkString addImage() {
  160. return SkStringPrintf("img_%d", fImageCount++);
  161. }
  162. SkString addColorFilter() { return SkStringPrintf("cfilter_%d", fColorFilterCount++); }
  163. SkString addPattern() {
  164. return SkStringPrintf("pattern_%d", fPatternCount++);
  165. }
  166. private:
  167. uint32_t fGradientCount;
  168. uint32_t fClipCount;
  169. uint32_t fPathCount;
  170. uint32_t fImageCount;
  171. uint32_t fPatternCount;
  172. uint32_t fColorFilterCount;
  173. };
  174. struct SkSVGDevice::MxCp {
  175. const SkMatrix* fMatrix;
  176. const SkClipStack* fClipStack;
  177. MxCp(const SkMatrix* mx, const SkClipStack* cs) : fMatrix(mx), fClipStack(cs) {}
  178. MxCp(SkSVGDevice* device) : fMatrix(&device->ctm()), fClipStack(&device->cs()) {}
  179. };
  180. class SkSVGDevice::AutoElement : ::SkNoncopyable {
  181. public:
  182. AutoElement(const char name[], SkXMLWriter* writer)
  183. : fWriter(writer)
  184. , fResourceBucket(nullptr) {
  185. fWriter->startElement(name);
  186. }
  187. AutoElement(const char name[], const std::unique_ptr<SkXMLWriter>& writer)
  188. : AutoElement(name, writer.get()) {}
  189. AutoElement(const char name[], const std::unique_ptr<SkXMLWriter>& writer,
  190. ResourceBucket* bucket, const MxCp& mc, const SkPaint& paint)
  191. : fWriter(writer.get())
  192. , fResourceBucket(bucket) {
  193. Resources res = this->addResources(mc, paint);
  194. if (!res.fClip.isEmpty()) {
  195. // The clip is in device space. Apply it via a <g> wrapper to avoid local transform
  196. // interference.
  197. fClipGroup.reset(new AutoElement("g", fWriter));
  198. fClipGroup->addAttribute("clip-path",res.fClip);
  199. }
  200. fWriter->startElement(name);
  201. this->addPaint(paint, res);
  202. if (!mc.fMatrix->isIdentity()) {
  203. this->addAttribute("transform", svg_transform(*mc.fMatrix));
  204. }
  205. }
  206. ~AutoElement() {
  207. fWriter->endElement();
  208. }
  209. void addAttribute(const char name[], const char val[]) {
  210. fWriter->addAttribute(name, val);
  211. }
  212. void addAttribute(const char name[], const SkString& val) {
  213. fWriter->addAttribute(name, val.c_str());
  214. }
  215. void addAttribute(const char name[], int32_t val) {
  216. fWriter->addS32Attribute(name, val);
  217. }
  218. void addAttribute(const char name[], SkScalar val) {
  219. fWriter->addScalarAttribute(name, val);
  220. }
  221. void addText(const SkString& text) {
  222. fWriter->addText(text.c_str(), text.size());
  223. }
  224. void addRectAttributes(const SkRect&);
  225. void addPathAttributes(const SkPath&);
  226. void addTextAttributes(const SkFont&);
  227. private:
  228. Resources addResources(const MxCp&, const SkPaint& paint);
  229. void addClipResources(const MxCp&, Resources* resources);
  230. void addShaderResources(const SkPaint& paint, Resources* resources);
  231. void addGradientShaderResources(const SkShader* shader, const SkPaint& paint,
  232. Resources* resources);
  233. void addColorFilterResources(const SkColorFilter& cf, Resources* resources);
  234. void addImageShaderResources(const SkShader* shader, const SkPaint& paint,
  235. Resources* resources);
  236. void addPatternDef(const SkBitmap& bm);
  237. void addPaint(const SkPaint& paint, const Resources& resources);
  238. SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkShader* shader);
  239. SkXMLWriter* fWriter;
  240. ResourceBucket* fResourceBucket;
  241. std::unique_ptr<AutoElement> fClipGroup;
  242. };
  243. void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& resources) {
  244. SkPaint::Style style = paint.getStyle();
  245. if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style) {
  246. this->addAttribute("fill", resources.fPaintServer);
  247. if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
  248. this->addAttribute("fill-opacity", svg_opacity(paint.getColor()));
  249. }
  250. } else {
  251. SkASSERT(style == SkPaint::kStroke_Style);
  252. this->addAttribute("fill", "none");
  253. }
  254. if (!resources.fColorFilter.isEmpty()) {
  255. this->addAttribute("filter", resources.fColorFilter.c_str());
  256. }
  257. if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) {
  258. this->addAttribute("stroke", resources.fPaintServer);
  259. SkScalar strokeWidth = paint.getStrokeWidth();
  260. if (strokeWidth == 0) {
  261. // Hairline stroke
  262. strokeWidth = 1;
  263. this->addAttribute("vector-effect", "non-scaling-stroke");
  264. }
  265. this->addAttribute("stroke-width", strokeWidth);
  266. if (const char* cap = svg_cap(paint.getStrokeCap())) {
  267. this->addAttribute("stroke-linecap", cap);
  268. }
  269. if (const char* join = svg_join(paint.getStrokeJoin())) {
  270. this->addAttribute("stroke-linejoin", join);
  271. }
  272. if (paint.getStrokeJoin() == SkPaint::kMiter_Join) {
  273. this->addAttribute("stroke-miterlimit", paint.getStrokeMiter());
  274. }
  275. if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
  276. this->addAttribute("stroke-opacity", svg_opacity(paint.getColor()));
  277. }
  278. } else {
  279. SkASSERT(style == SkPaint::kFill_Style);
  280. this->addAttribute("stroke", "none");
  281. }
  282. }
  283. Resources SkSVGDevice::AutoElement::addResources(const MxCp& mc, const SkPaint& paint) {
  284. Resources resources(paint);
  285. // FIXME: this is a weak heuristic and we end up with LOTS of redundant clips.
  286. bool hasClip = !mc.fClipStack->isWideOpen();
  287. bool hasShader = SkToBool(paint.getShader());
  288. if (hasClip || hasShader) {
  289. AutoElement defs("defs", fWriter);
  290. if (hasClip) {
  291. this->addClipResources(mc, &resources);
  292. }
  293. if (hasShader) {
  294. this->addShaderResources(paint, &resources);
  295. }
  296. }
  297. if (const SkColorFilter* cf = paint.getColorFilter()) {
  298. // TODO: Implement skia color filters for blend modes other than SrcIn
  299. SkBlendMode mode;
  300. if (cf->asAColorMode(nullptr, &mode) && mode == SkBlendMode::kSrcIn) {
  301. this->addColorFilterResources(*cf, &resources);
  302. }
  303. }
  304. return resources;
  305. }
  306. void SkSVGDevice::AutoElement::addGradientShaderResources(const SkShader* shader,
  307. const SkPaint& paint,
  308. Resources* resources) {
  309. SkShader::GradientInfo grInfo;
  310. grInfo.fColorCount = 0;
  311. if (SkShader::kLinear_GradientType != shader->asAGradient(&grInfo)) {
  312. // TODO: non-linear gradient support
  313. return;
  314. }
  315. SkAutoSTArray<16, SkColor> grColors(grInfo.fColorCount);
  316. SkAutoSTArray<16, SkScalar> grOffsets(grInfo.fColorCount);
  317. grInfo.fColors = grColors.get();
  318. grInfo.fColorOffsets = grOffsets.get();
  319. // One more call to get the actual colors/offsets.
  320. shader->asAGradient(&grInfo);
  321. SkASSERT(grInfo.fColorCount <= grColors.count());
  322. SkASSERT(grInfo.fColorCount <= grOffsets.count());
  323. resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shader).c_str());
  324. }
  325. void SkSVGDevice::AutoElement::addColorFilterResources(const SkColorFilter& cf,
  326. Resources* resources) {
  327. SkString colorfilterID = fResourceBucket->addColorFilter();
  328. {
  329. AutoElement filterElement("filter", fWriter);
  330. filterElement.addAttribute("id", colorfilterID);
  331. filterElement.addAttribute("x", "0%");
  332. filterElement.addAttribute("y", "0%");
  333. filterElement.addAttribute("width", "100%");
  334. filterElement.addAttribute("height", "100%");
  335. SkColor filterColor;
  336. SkBlendMode mode;
  337. bool asAColorMode = cf.asAColorMode(&filterColor, &mode);
  338. SkAssertResult(asAColorMode);
  339. SkASSERT(mode == SkBlendMode::kSrcIn);
  340. {
  341. // first flood with filter color
  342. AutoElement floodElement("feFlood", fWriter);
  343. floodElement.addAttribute("flood-color", svg_color(filterColor));
  344. floodElement.addAttribute("flood-opacity", svg_opacity(filterColor));
  345. floodElement.addAttribute("result", "flood");
  346. }
  347. {
  348. // apply the transform to filter color
  349. AutoElement compositeElement("feComposite", fWriter);
  350. compositeElement.addAttribute("in", "flood");
  351. compositeElement.addAttribute("operator", "in");
  352. }
  353. }
  354. resources->fColorFilter.printf("url(#%s)", colorfilterID.c_str());
  355. }
  356. // Returns data uri from bytes.
  357. // it will use any cached data if available, otherwise will
  358. // encode as png.
  359. sk_sp<SkData> AsDataUri(SkImage* image) {
  360. sk_sp<SkData> imageData = image->encodeToData();
  361. if (!imageData) {
  362. return nullptr;
  363. }
  364. const char* src = (char*)imageData->data();
  365. const char* selectedPrefix = nullptr;
  366. size_t selectedPrefixLength = 0;
  367. const static char pngDataPrefix[] = "data:image/png;base64,";
  368. const static char jpgDataPrefix[] = "data:image/jpeg;base64,";
  369. if (SkJpegCodec::IsJpeg(src, imageData->size())) {
  370. selectedPrefix = jpgDataPrefix;
  371. selectedPrefixLength = sizeof(jpgDataPrefix);
  372. } else {
  373. if (!SkPngCodec::IsPng(src, imageData->size())) {
  374. imageData = image->encodeToData(SkEncodedImageFormat::kPNG, 100);
  375. }
  376. selectedPrefix = pngDataPrefix;
  377. selectedPrefixLength = sizeof(pngDataPrefix);
  378. }
  379. size_t b64Size = SkBase64::Encode(imageData->data(), imageData->size(), nullptr);
  380. sk_sp<SkData> dataUri = SkData::MakeUninitialized(selectedPrefixLength + b64Size);
  381. char* dest = (char*)dataUri->writable_data();
  382. memcpy(dest, selectedPrefix, selectedPrefixLength);
  383. SkBase64::Encode(imageData->data(), imageData->size(), dest + selectedPrefixLength - 1);
  384. dest[dataUri->size() - 1] = 0;
  385. return dataUri;
  386. }
  387. void SkSVGDevice::AutoElement::addImageShaderResources(const SkShader* shader, const SkPaint& paint,
  388. Resources* resources) {
  389. SkMatrix outMatrix;
  390. SkTileMode xy[2];
  391. SkImage* image = shader->isAImage(&outMatrix, xy);
  392. SkASSERT(image);
  393. SkString patternDims[2]; // width, height
  394. sk_sp<SkData> dataUri = AsDataUri(image);
  395. if (!dataUri) {
  396. return;
  397. }
  398. SkIRect imageSize = image->bounds();
  399. for (int i = 0; i < 2; i++) {
  400. int imageDimension = i == 0 ? imageSize.width() : imageSize.height();
  401. switch (xy[i]) {
  402. case SkTileMode::kRepeat:
  403. patternDims[i].appendScalar(imageDimension);
  404. break;
  405. default:
  406. // TODO: other tile modes?
  407. patternDims[i] = "100%";
  408. }
  409. }
  410. SkString patternID = fResourceBucket->addPattern();
  411. {
  412. AutoElement pattern("pattern", fWriter);
  413. pattern.addAttribute("id", patternID);
  414. pattern.addAttribute("patternUnits", "userSpaceOnUse");
  415. pattern.addAttribute("patternContentUnits", "userSpaceOnUse");
  416. pattern.addAttribute("width", patternDims[0]);
  417. pattern.addAttribute("height", patternDims[1]);
  418. pattern.addAttribute("x", 0);
  419. pattern.addAttribute("y", 0);
  420. {
  421. SkString imageID = fResourceBucket->addImage();
  422. AutoElement imageTag("image", fWriter);
  423. imageTag.addAttribute("id", imageID);
  424. imageTag.addAttribute("x", 0);
  425. imageTag.addAttribute("y", 0);
  426. imageTag.addAttribute("width", image->width());
  427. imageTag.addAttribute("height", image->height());
  428. imageTag.addAttribute("xlink:href", static_cast<const char*>(dataUri->data()));
  429. }
  430. }
  431. resources->fPaintServer.printf("url(#%s)", patternID.c_str());
  432. }
  433. void SkSVGDevice::AutoElement::addShaderResources(const SkPaint& paint, Resources* resources) {
  434. const SkShader* shader = paint.getShader();
  435. SkASSERT(shader);
  436. if (shader->asAGradient(nullptr) != SkShader::kNone_GradientType) {
  437. this->addGradientShaderResources(shader, paint, resources);
  438. } else if (shader->isAImage()) {
  439. this->addImageShaderResources(shader, paint, resources);
  440. }
  441. // TODO: other shader types?
  442. }
  443. void SkSVGDevice::AutoElement::addClipResources(const MxCp& mc, Resources* resources) {
  444. SkASSERT(!mc.fClipStack->isWideOpen());
  445. SkPath clipPath;
  446. (void) mc.fClipStack->asPath(&clipPath);
  447. SkString clipID = fResourceBucket->addClip();
  448. const char* clipRule = clipPath.getFillType() == SkPath::kEvenOdd_FillType ?
  449. "evenodd" : "nonzero";
  450. {
  451. // clipPath is in device space, but since we're only pushing transform attributes
  452. // to the leaf nodes, so are all our elements => SVG userSpaceOnUse == device space.
  453. AutoElement clipPathElement("clipPath", fWriter);
  454. clipPathElement.addAttribute("id", clipID);
  455. SkRect clipRect = SkRect::MakeEmpty();
  456. if (clipPath.isEmpty() || clipPath.isRect(&clipRect)) {
  457. AutoElement rectElement("rect", fWriter);
  458. rectElement.addRectAttributes(clipRect);
  459. rectElement.addAttribute("clip-rule", clipRule);
  460. } else {
  461. AutoElement pathElement("path", fWriter);
  462. pathElement.addPathAttributes(clipPath);
  463. pathElement.addAttribute("clip-rule", clipRule);
  464. }
  465. }
  466. resources->fClip.printf("url(#%s)", clipID.c_str());
  467. }
  468. SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::GradientInfo& info,
  469. const SkShader* shader) {
  470. SkASSERT(fResourceBucket);
  471. SkString id = fResourceBucket->addLinearGradient();
  472. {
  473. AutoElement gradient("linearGradient", fWriter);
  474. gradient.addAttribute("id", id);
  475. gradient.addAttribute("gradientUnits", "userSpaceOnUse");
  476. gradient.addAttribute("x1", info.fPoint[0].x());
  477. gradient.addAttribute("y1", info.fPoint[0].y());
  478. gradient.addAttribute("x2", info.fPoint[1].x());
  479. gradient.addAttribute("y2", info.fPoint[1].y());
  480. if (!as_SB(shader)->getLocalMatrix().isIdentity()) {
  481. this->addAttribute("gradientTransform", svg_transform(as_SB(shader)->getLocalMatrix()));
  482. }
  483. SkASSERT(info.fColorCount >= 2);
  484. for (int i = 0; i < info.fColorCount; ++i) {
  485. SkColor color = info.fColors[i];
  486. SkString colorStr(svg_color(color));
  487. {
  488. AutoElement stop("stop", fWriter);
  489. stop.addAttribute("offset", info.fColorOffsets[i]);
  490. stop.addAttribute("stop-color", colorStr.c_str());
  491. if (SK_AlphaOPAQUE != SkColorGetA(color)) {
  492. stop.addAttribute("stop-opacity", svg_opacity(color));
  493. }
  494. }
  495. }
  496. }
  497. return id;
  498. }
  499. void SkSVGDevice::AutoElement::addRectAttributes(const SkRect& rect) {
  500. // x, y default to 0
  501. if (rect.x() != 0) {
  502. this->addAttribute("x", rect.x());
  503. }
  504. if (rect.y() != 0) {
  505. this->addAttribute("y", rect.y());
  506. }
  507. this->addAttribute("width", rect.width());
  508. this->addAttribute("height", rect.height());
  509. }
  510. void SkSVGDevice::AutoElement::addPathAttributes(const SkPath& path) {
  511. SkString pathData;
  512. SkParsePath::ToSVGString(path, &pathData);
  513. this->addAttribute("d", pathData);
  514. }
  515. void SkSVGDevice::AutoElement::addTextAttributes(const SkFont& font) {
  516. this->addAttribute("font-size", font.getSize());
  517. SkString familyName;
  518. SkTHashSet<SkString> familySet;
  519. sk_sp<SkTypeface> tface = font.refTypefaceOrDefault();
  520. SkASSERT(tface);
  521. SkFontStyle style = tface->fontStyle();
  522. if (style.slant() == SkFontStyle::kItalic_Slant) {
  523. this->addAttribute("font-style", "italic");
  524. } else if (style.slant() == SkFontStyle::kOblique_Slant) {
  525. this->addAttribute("font-style", "oblique");
  526. }
  527. int weightIndex = (SkTPin(style.weight(), 100, 900) - 50) / 100;
  528. if (weightIndex != 3) {
  529. static constexpr const char* weights[] = {
  530. "100", "200", "300", "normal", "400", "500", "600", "bold", "800", "900"
  531. };
  532. this->addAttribute("font-weight", weights[weightIndex]);
  533. }
  534. int stretchIndex = style.width() - 1;
  535. if (stretchIndex != 4) {
  536. static constexpr const char* stretches[] = {
  537. "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
  538. "normal",
  539. "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
  540. };
  541. this->addAttribute("font-stretch", stretches[stretchIndex]);
  542. }
  543. sk_sp<SkTypeface::LocalizedStrings> familyNameIter(tface->createFamilyNameIterator());
  544. SkTypeface::LocalizedString familyString;
  545. if (familyNameIter) {
  546. while (familyNameIter->next(&familyString)) {
  547. if (familySet.contains(familyString.fString)) {
  548. continue;
  549. }
  550. familySet.add(familyString.fString);
  551. familyName.appendf((familyName.isEmpty() ? "%s" : ", %s"), familyString.fString.c_str());
  552. }
  553. }
  554. if (!familyName.isEmpty()) {
  555. this->addAttribute("font-family", familyName);
  556. }
  557. }
  558. sk_sp<SkBaseDevice> SkSVGDevice::Make(const SkISize& size, std::unique_ptr<SkXMLWriter> writer,
  559. uint32_t flags) {
  560. return writer ? sk_sp<SkBaseDevice>(new SkSVGDevice(size, std::move(writer), flags))
  561. : nullptr;
  562. }
  563. SkSVGDevice::SkSVGDevice(const SkISize& size, std::unique_ptr<SkXMLWriter> writer, uint32_t flags)
  564. : INHERITED(SkImageInfo::MakeUnknown(size.fWidth, size.fHeight),
  565. SkSurfaceProps(0, kUnknown_SkPixelGeometry))
  566. , fWriter(std::move(writer))
  567. , fResourceBucket(new ResourceBucket)
  568. , fFlags(flags)
  569. {
  570. SkASSERT(fWriter);
  571. fWriter->writeHeader();
  572. // The root <svg> tag gets closed by the destructor.
  573. fRootElement.reset(new AutoElement("svg", fWriter));
  574. fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
  575. fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
  576. fRootElement->addAttribute("width", size.width());
  577. fRootElement->addAttribute("height", size.height());
  578. }
  579. SkSVGDevice::~SkSVGDevice() = default;
  580. void SkSVGDevice::drawPaint(const SkPaint& paint) {
  581. AutoElement rect("rect", fWriter, fResourceBucket.get(), MxCp(this), paint);
  582. rect.addRectAttributes(SkRect::MakeWH(SkIntToScalar(this->width()),
  583. SkIntToScalar(this->height())));
  584. }
  585. void SkSVGDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
  586. if (!value) {
  587. return;
  588. }
  589. if (!strcmp(SkAnnotationKeys::URL_Key(), key) ||
  590. !strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
  591. this->cs().save();
  592. this->cs().clipRect(rect, this->ctm(), kIntersect_SkClipOp, true);
  593. SkRect transformedRect = this->cs().bounds(this->getGlobalBounds());
  594. this->cs().restore();
  595. if (transformedRect.isEmpty()) {
  596. return;
  597. }
  598. SkString url(static_cast<const char*>(value->data()), value->size() - 1);
  599. AutoElement a("a", fWriter);
  600. a.addAttribute("xlink:href", url.c_str());
  601. {
  602. AutoElement r("rect", fWriter);
  603. r.addAttribute("fill-opacity", "0.0");
  604. r.addRectAttributes(transformedRect);
  605. }
  606. }
  607. }
  608. void SkSVGDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
  609. const SkPoint pts[], const SkPaint& paint) {
  610. SkPath path;
  611. switch (mode) {
  612. // todo
  613. case SkCanvas::kPoints_PointMode:
  614. // TODO?
  615. break;
  616. case SkCanvas::kLines_PointMode:
  617. count -= 1;
  618. for (size_t i = 0; i < count; i += 2) {
  619. path.rewind();
  620. path.moveTo(pts[i]);
  621. path.lineTo(pts[i+1]);
  622. AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this), paint);
  623. elem.addPathAttributes(path);
  624. }
  625. break;
  626. case SkCanvas::kPolygon_PointMode:
  627. if (count > 1) {
  628. path.addPoly(pts, SkToInt(count), false);
  629. path.moveTo(pts[0]);
  630. AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this), paint);
  631. elem.addPathAttributes(path);
  632. }
  633. break;
  634. }
  635. }
  636. void SkSVGDevice::drawRect(const SkRect& r, const SkPaint& paint) {
  637. std::unique_ptr<AutoElement> svg;
  638. if (RequiresViewportReset(paint)) {
  639. svg.reset(new AutoElement("svg", fWriter, fResourceBucket.get(), MxCp(this), paint));
  640. svg->addRectAttributes(r);
  641. }
  642. AutoElement rect("rect", fWriter, fResourceBucket.get(), MxCp(this), paint);
  643. if (svg) {
  644. rect.addAttribute("x", 0);
  645. rect.addAttribute("y", 0);
  646. rect.addAttribute("width", "100%");
  647. rect.addAttribute("height", "100%");
  648. } else {
  649. rect.addRectAttributes(r);
  650. }
  651. }
  652. void SkSVGDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
  653. AutoElement ellipse("ellipse", fWriter, fResourceBucket.get(), MxCp(this), paint);
  654. ellipse.addAttribute("cx", oval.centerX());
  655. ellipse.addAttribute("cy", oval.centerY());
  656. ellipse.addAttribute("rx", oval.width() / 2);
  657. ellipse.addAttribute("ry", oval.height() / 2);
  658. }
  659. void SkSVGDevice::drawRRect(const SkRRect& rr, const SkPaint& paint) {
  660. SkPath path;
  661. path.addRRect(rr);
  662. AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this), paint);
  663. elem.addPathAttributes(path);
  664. }
  665. void SkSVGDevice::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
  666. AutoElement elem("path", fWriter, fResourceBucket.get(), MxCp(this), paint);
  667. elem.addPathAttributes(path);
  668. // TODO: inverse fill types?
  669. if (path.getFillType() == SkPath::kEvenOdd_FillType) {
  670. elem.addAttribute("fill-rule", "evenodd");
  671. }
  672. }
  673. static sk_sp<SkData> encode(const SkBitmap& src) {
  674. SkDynamicMemoryWStream buf;
  675. return SkEncodeImage(&buf, src, SkEncodedImageFormat::kPNG, 80) ? buf.detachAsData() : nullptr;
  676. }
  677. void SkSVGDevice::drawBitmapCommon(const MxCp& mc, const SkBitmap& bm, const SkPaint& paint) {
  678. sk_sp<SkData> pngData = encode(bm);
  679. if (!pngData) {
  680. return;
  681. }
  682. size_t b64Size = SkBase64::Encode(pngData->data(), pngData->size(), nullptr);
  683. SkAutoTMalloc<char> b64Data(b64Size);
  684. SkBase64::Encode(pngData->data(), pngData->size(), b64Data.get());
  685. SkString svgImageData("data:image/png;base64,");
  686. svgImageData.append(b64Data.get(), b64Size);
  687. SkString imageID = fResourceBucket->addImage();
  688. {
  689. AutoElement defs("defs", fWriter);
  690. {
  691. AutoElement image("image", fWriter);
  692. image.addAttribute("id", imageID);
  693. image.addAttribute("width", bm.width());
  694. image.addAttribute("height", bm.height());
  695. image.addAttribute("xlink:href", svgImageData);
  696. }
  697. }
  698. {
  699. AutoElement imageUse("use", fWriter, fResourceBucket.get(), mc, paint);
  700. imageUse.addAttribute("xlink:href", SkStringPrintf("#%s", imageID.c_str()));
  701. }
  702. }
  703. void SkSVGDevice::drawSprite(const SkBitmap& bitmap,
  704. int x, int y, const SkPaint& paint) {
  705. MxCp mc(this);
  706. SkMatrix adjustedMatrix = *mc.fMatrix;
  707. adjustedMatrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y));
  708. mc.fMatrix = &adjustedMatrix;
  709. drawBitmapCommon(mc, bitmap, paint);
  710. }
  711. void SkSVGDevice::drawBitmapRect(const SkBitmap& bm, const SkRect* srcOrNull,
  712. const SkRect& dst, const SkPaint& paint,
  713. SkCanvas::SrcRectConstraint) {
  714. SkClipStack* cs = &this->cs();
  715. SkClipStack::AutoRestore ar(cs, false);
  716. if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) {
  717. cs->save();
  718. cs->clipRect(dst, this->ctm(), kIntersect_SkClipOp, paint.isAntiAlias());
  719. }
  720. SkMatrix adjustedMatrix;
  721. adjustedMatrix.setRectToRect(srcOrNull ? *srcOrNull : SkRect::Make(bm.bounds()),
  722. dst,
  723. SkMatrix::kFill_ScaleToFit);
  724. adjustedMatrix.postConcat(this->ctm());
  725. drawBitmapCommon(MxCp(&adjustedMatrix, cs), bm, paint);
  726. }
  727. class SVGTextBuilder : SkNoncopyable {
  728. public:
  729. SVGTextBuilder(SkPoint origin, const SkGlyphRun& glyphRun)
  730. : fOrigin(origin) {
  731. auto runSize = glyphRun.runSize();
  732. SkAutoSTArray<64, SkUnichar> unichars(runSize);
  733. SkFontPriv::GlyphsToUnichars(glyphRun.font(), glyphRun.glyphsIDs().data(),
  734. runSize, unichars.get());
  735. auto positions = glyphRun.positions();
  736. for (size_t i = 0; i < runSize; ++i) {
  737. this->appendUnichar(unichars[i], positions[i]);
  738. }
  739. }
  740. const SkString& text() const { return fText; }
  741. const SkString& posX() const { return fPosXStr; }
  742. const SkString& posY() const { return fHasConstY ? fConstYStr : fPosYStr; }
  743. private:
  744. void appendUnichar(SkUnichar c, SkPoint position) {
  745. bool discardPos = false;
  746. bool isWhitespace = false;
  747. switch(c) {
  748. case ' ':
  749. case '\t':
  750. // consolidate whitespace to match SVG's xml:space=default munging
  751. // (http://www.w3.org/TR/SVG/text.html#WhiteSpace)
  752. if (fLastCharWasWhitespace) {
  753. discardPos = true;
  754. } else {
  755. fText.appendUnichar(c);
  756. }
  757. isWhitespace = true;
  758. break;
  759. case '\0':
  760. // SkPaint::glyphsToUnichars() returns \0 for inconvertible glyphs, but these
  761. // are not legal XML characters (http://www.w3.org/TR/REC-xml/#charsets)
  762. discardPos = true;
  763. isWhitespace = fLastCharWasWhitespace; // preserve whitespace consolidation
  764. break;
  765. case '&':
  766. fText.append("&amp;");
  767. break;
  768. case '"':
  769. fText.append("&quot;");
  770. break;
  771. case '\'':
  772. fText.append("&apos;");
  773. break;
  774. case '<':
  775. fText.append("&lt;");
  776. break;
  777. case '>':
  778. fText.append("&gt;");
  779. break;
  780. default:
  781. fText.appendUnichar(c);
  782. break;
  783. }
  784. fLastCharWasWhitespace = isWhitespace;
  785. if (discardPos) {
  786. return;
  787. }
  788. position += fOrigin;
  789. fPosXStr.appendf("%.8g, ", position.fX);
  790. fPosYStr.appendf("%.8g, ", position.fY);
  791. if (fConstYStr.isEmpty()) {
  792. fConstYStr = fPosYStr;
  793. fConstY = position.fY;
  794. } else {
  795. fHasConstY &= SkScalarNearlyEqual(fConstY, position.fY);
  796. }
  797. }
  798. const SkPoint fOrigin;
  799. SkString fText,
  800. fPosXStr, fPosYStr,
  801. fConstYStr;
  802. SkScalar fConstY;
  803. bool fLastCharWasWhitespace = true, // start off in whitespace mode to strip leading space
  804. fHasConstY = true;
  805. };
  806. void SkSVGDevice::drawGlyphRunAsPath(const SkGlyphRun& glyphRun, const SkPoint& origin,
  807. const SkPaint& runPaint) {
  808. this->drawPath(GetPath(glyphRun, origin), runPaint);
  809. }
  810. void SkSVGDevice::drawGlyphRunAsText(const SkGlyphRun& glyphRun, const SkPoint& origin,
  811. const SkPaint& runPaint) {
  812. AutoElement elem("text", fWriter, fResourceBucket.get(), MxCp(this), runPaint);
  813. elem.addTextAttributes(glyphRun.font());
  814. SVGTextBuilder builder(origin, glyphRun);
  815. elem.addAttribute("x", builder.posX());
  816. elem.addAttribute("y", builder.posY());
  817. elem.addText(builder.text());
  818. }
  819. void SkSVGDevice::drawGlyphRunList(const SkGlyphRunList& glyphRunList) {
  820. const auto processGlyphRun = (fFlags & SkSVGCanvas::kConvertTextToPaths_Flag)
  821. ? &SkSVGDevice::drawGlyphRunAsPath
  822. : &SkSVGDevice::drawGlyphRunAsText;
  823. for (auto& glyphRun : glyphRunList) {
  824. (this->*processGlyphRun)(glyphRun, glyphRunList.origin(), glyphRunList.paint());
  825. }
  826. }
  827. void SkSVGDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
  828. const SkPaint&) {
  829. // todo
  830. }
  831. void SkSVGDevice::drawDevice(SkBaseDevice*, int x, int y,
  832. const SkPaint&) {
  833. // todo
  834. }