1 /*
2 * Copyright (C) 2010 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6
7 #include "Wt/WBrush.h"
8 #include "Wt/WException.h"
9 #include "Wt/WFontMetrics.h"
10 #include "Wt/WLogger.h"
11 #include "Wt/WPainter.h"
12 #include "Wt/WPen.h"
13 #include "Wt/WRasterImage.h"
14 #include "Wt/WTransform.h"
15 #include "Wt/Http/Response.h"
16
17 #include "Wt/FontSupport.h"
18 #include "WebUtils.h"
19 #include "UriUtils.h"
20
21 #include <cstdio>
22 #include <cmath>
23 #include <boost/algorithm/string.hpp>
24
25 #include <SkBitmap.h>
26 #include <SkBitmapDevice.h>
27 #include <SkDashPathEffect.h>
28 #ifdef WT_SKIA_OLD
29 #include <SkImageDecoder.h>
30 #else
31 #include <SkCodec.h>
32 #endif
33 #include <SkImageEncoder.h>
34 #include <SkStream.h>
35 #include <SkTypeface.h>
36
37 //#include <SkForceLinking.h>
38
39 // Skia does not have official releases, which is annoying because
40 // this implies that there are no API version numberings. I'm not
41 // aware of any preprocessor define that can help us identifying
42 // skia version. Therefore, for binary builds of Wt, we use
43 // specific git versions of skia to build against wt.
44 // Wt can build against the following skia git versions:
45 // - 394c7bb04d89667d2164a554f310e8e6f819abc2
46 // Used for all binary builds up to wt 3.3.5. This version does
47 // not support MSVS 2015, so we needed to upgrade skia for this.
48 // Wt binary builds newer than 3.3.5 for compilers older than
49 // MSVS 2015 will still use this version, since newer skia
50 // version don't support old compilers.
51 // If you use this version, WT_SKIA_OLD must be defined while
52 // compiling this file.
53 // - 834d9e109298ae704043128005f8c1bc622350f4
54 // Used for MSVS 2015 builds, starting in Wt 3.3.5.
55 // Do not define WT_SKIA_OLD when you use this version.
56 // Other skia versions may work too.
57 //#define WT_SKIA_OLD
58
59 namespace {
fromWColor(const Wt::WColor & color)60 inline SkColor fromWColor(const Wt::WColor &color)
61 {
62 return SkColorSetARGB(color.alpha(),
63 color.red(), color.green(), color.blue());
64 }
65 }
66
67 namespace Wt {
68
69 LOGGER("WRasterImage");
70
71 class WRasterImage::Impl {
72 public:
Impl()73 Impl():
74 w_(0),
75 h_(0)
76 {}
77 unsigned w_, h_;
78 std::string type_;
79 std::unique_ptr<SkBitmap> bitmap_;
80 std::unique_ptr<SkBitmapDevice> device_;
81 std::unique_ptr<SkCanvas> canvas_;
82 SkPaint strokePaint_;
83 SkPaint fillPaint_;
84 SkPaint textPaint_;
85
86 void applyTransform(const WTransform& t);
87 void setTransform(const WTransform& t);
88 void drawPlainPath(SkPath &p, const WPainterPath& path);
89 };
90
WRasterImage(const std::string & type,const WLength & width,const WLength & height)91 WRasterImage::WRasterImage(const std::string& type,
92 const WLength& width, const WLength& height)
93 : width_(width),
94 height_(height),
95 painter_(0),
96 impl_(new Impl)
97 {
98 impl_->type_ = type;
99 impl_->w_ = static_cast<unsigned long>(width.toPixels());
100 impl_->h_ = static_cast<unsigned long>(height.toPixels());
101
102 if (!impl_->w_ || !impl_->h_) {
103 impl_->bitmap_ = 0;
104 return;
105 }
106 #ifdef WT_SKIA_OLD
107 impl_->bitmap_ = new SkBitmap();
108 impl_->bitmap_->setConfig(SkBitmap::kARGB_8888_Config, impl_->w_, impl_->h_);
109 impl_->bitmap_->allocPixels();
110 impl_->bitmap_->eraseARGB(0, 0, 0, 0);
111 impl_->device_ = new SkBitmapDevice(*impl_->bitmap_);
112 impl_->canvas_ = new SkCanvas(impl_->device_);
113 #else
114 impl_->bitmap_ = std::make_unique<SkBitmap>();
115 SkImageInfo ii = SkImageInfo::MakeN32Premul(impl_->w_, impl_->h_);
116 impl_->bitmap_->allocPixels(ii);
117 impl_->bitmap_->eraseARGB(0, 0, 0, 0);
118 impl_->device_ = std::make_unique<SkBitmapDevice>(*impl_->bitmap_);
119 impl_->canvas_ = std::make_unique<SkCanvas>(impl_->device_.get());
120 #endif
121
122 impl_->textPaint_.setStyle(SkPaint::kStrokeAndFill_Style);
123 impl_->textPaint_.setTextEncoding(SkPaint::kUTF8_TextEncoding);
124 impl_->strokePaint_.setStyle(SkPaint::kStroke_Style);
125 impl_->fillPaint_.setStyle(SkPaint::kFill_Style);
126
127 }
128
clear()129 void WRasterImage::clear()
130 {
131 impl_->canvas_->clear(0);
132 }
133
~WRasterImage()134 WRasterImage::~WRasterImage()
135 {
136 beingDeleted();
137 }
138
addFontCollection(const std::string & directory,bool recursive)139 void WRasterImage::addFontCollection(const std::string& directory,
140 bool recursive)
141 {
142 #if 0
143 fontSupport_->addFontCollection(directory, recursive);
144 #endif
145 }
146
features()147 WFlags<PaintDeviceFeatureFlag> WRasterImage::features() const
148 {
149 return HasFontMetrics;
150 }
151
init()152 void WRasterImage::init()
153 {
154 if (!impl_->w_ || !impl_->h_)
155 throw WException("Raster image should have non-0 width and height");
156
157 // save unit matrix & empty clipping
158 impl_->canvas_->save();
159
160 setChanged(Clipping | Transform | Pen | Brush | Font | Hints);
161 }
162
done()163 void WRasterImage::done()
164 {
165 impl_->canvas_->restore();
166 }
167
applyTransform(const WTransform & t)168 void WRasterImage::Impl::applyTransform(const WTransform& t)
169 {
170 SkMatrix sm;
171 sm.setAll(SkDoubleToScalar(t.m11()),
172 SkDoubleToScalar(t.m12()),
173 SkDoubleToScalar(t.dx()),
174 SkDoubleToScalar(t.m21()),
175 SkDoubleToScalar(t.m22()),
176 SkDoubleToScalar(t.dy()),
177 0,
178 0,
179 SkDoubleToScalar(1.0));
180 canvas_->concat(sm);
181 }
182
setTransform(const WTransform & t)183 void WRasterImage::Impl::setTransform(const WTransform& t)
184 {
185 /*
186 std::cout << "WRasterImage::setTransform "
187 << t.m11() << ", "
188 << t.m12() << ", "
189 << t.m21() << ", "
190 << t.m22() << ", "
191 << t.dx() << ", "
192 << t.dy() << ", "
193 << std::endl;
194 */
195 canvas_->resetMatrix();
196 applyTransform(t);
197 }
198
setChanged(WFlags<ChangeFlag> flags)199 void WRasterImage::setChanged(WFlags<ChangeFlag> flags)
200 {
201 if (flags & Clipping) {
202
203 if (painter()->hasClipping()) {
204 impl_->setTransform(painter()->clipPathTransform());
205 SkPath clipPath;
206 impl_->drawPlainPath(clipPath, painter()->clipPath());
207 impl_->canvas_->clipPath(clipPath, SkRegion::kReplace_Op);
208 } else {
209 impl_->canvas_->restore();
210 impl_->canvas_->save();
211 }
212 impl_->setTransform(painter()->combinedTransform());
213 }
214
215 if (flags & Transform) {
216 impl_->setTransform(painter()->combinedTransform());
217 flags = Pen | Brush | Font | Hints;
218 }
219
220 if (flags & Hints) {
221 if (!(painter()->renderHints() & RenderHint::Antialiasing)) {
222 impl_->strokePaint_.setAntiAlias(false);
223 impl_->fillPaint_.setAntiAlias(false);
224 impl_->textPaint_.setAntiAlias(false);
225 } else {
226 impl_->strokePaint_.setAntiAlias(true);
227 impl_->fillPaint_.setAntiAlias(true);
228 impl_->textPaint_.setAntiAlias(true);
229 }
230 }
231
232 if (flags & Pen) {
233 const WPen& pen = painter()->pen();
234
235 if (pen.style() != PenStyle::None) {
236 const WColor& color = pen.color();
237
238 impl_->strokePaint_.setColor(fromWColor(color));
239
240 WLength w = pen.width();
241 impl_->strokePaint_.setStrokeWidth(SkIntToScalar(w.toPixels()));
242
243 switch (pen.capStyle()) {
244 case PenCapStyle::Flat:
245 impl_->strokePaint_.setStrokeCap(SkPaint::kButt_Cap);
246 break;
247 case PenCapStyle::Square:
248 impl_->strokePaint_.setStrokeCap(SkPaint::kSquare_Cap);
249 break;
250 case PenCapStyle::Round:
251 impl_->strokePaint_.setStrokeCap(SkPaint::kRound_Cap);
252 break;
253 }
254
255 switch (pen.joinStyle()) {
256 case PenJoinStyle::Miter:
257 impl_->strokePaint_.setStrokeJoin(SkPaint::kMiter_Join);
258 break;
259 case PenJoinStyle::Bevel:
260 impl_->strokePaint_.setStrokeJoin(SkPaint::kBevel_Join);
261 break;
262 case PenJoinStyle::Round:
263 impl_->strokePaint_.setStrokeJoin(SkPaint::kRound_Join);
264 break;
265 }
266
267 #ifdef WT_SKIA_OLD
268 SkPathEffect *pe = impl_->strokePaint_.setPathEffect(0);
269 if (pe)
270 pe->unref();
271 #else
272 impl_->strokePaint_.setPathEffect(0);
273 #endif
274 switch (pen.style()) {
275 case PenStyle::None:
276 break;
277 case PenStyle::SolidLine:
278 break;
279 case PenStyle::DashLine: {
280 const SkScalar dasharray[] = { SkIntToScalar(4), SkIntToScalar(2) };
281 #ifdef WT_SKIA_OLD
282 impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 2,
283 false))->unref();
284 #else
285 impl_->strokePaint_.setPathEffect(
286 SkDashPathEffect::Make(dasharray, 2, 0));
287 #endif
288 break;
289 }
290 case PenStyle::DotLine: {
291 const SkScalar dasharray[] = { SkIntToScalar(1), SkIntToScalar(2) };
292 #ifdef WT_SKIA_OLD
293 impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 2,
294 false))->unref();
295 #else
296 impl_->strokePaint_.setPathEffect(
297 SkDashPathEffect::Make(dasharray, 2, 0));
298 #endif
299 break;
300 }
301 case PenStyle::DashDotLine: {
302 const SkScalar dasharray[] = {
303 SkIntToScalar(4),
304 SkIntToScalar(2),
305 SkIntToScalar(1),
306 SkIntToScalar(2)
307 };
308 #ifdef WT_SKIA_OLD
309 impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 4,
310 false))->unref();
311 #else
312 impl_->strokePaint_.setPathEffect(
313 SkDashPathEffect::Make(dasharray, 4, 0));
314 #endif
315 break;
316 }
317 case PenStyle::DashDotDotLine: {
318 const SkScalar dasharray[] = {
319 SkIntToScalar(4),
320 SkIntToScalar(2),
321 SkIntToScalar(1),
322 SkIntToScalar(2),
323 SkIntToScalar(1),
324 SkIntToScalar(2)
325 };
326 #ifdef WT_SKIA_OLD
327 impl_->strokePaint_.setPathEffect(new SkDashPathEffect(dasharray, 6,
328 false))->unref();
329 #else
330 impl_->strokePaint_.setPathEffect(
331 SkDashPathEffect::Make(dasharray, 6, 0));
332 #endif
333 break;
334 }
335 }
336
337 }
338 }
339
340 if (flags & Brush) {
341 const WBrush& brush = painter()->brush();
342 if (brush.style() != BrushStyle::None) {
343 const WColor& color = painter()->brush().color();
344 impl_->fillPaint_.setColor(fromWColor(color));
345 }
346 }
347
348 if (flags & Font) {
349 const WFont& font = painter()->font();
350
351 const char *base = 0;
352 switch (font.genericFamily()) {
353 case FontFamily::Default:
354 case FontFamily::Serif:
355 base = "Times";
356 break;
357 case FontFamily::SansSerif:
358 base = "Helvetica";
359 break;
360 case FontFamily::Monospace:
361 base = "Courier";
362 break;
363 case FontFamily::Fantasy: // Not really !
364 base = "Symbol";
365 break;
366 case FontFamily::Cursive: // Not really !
367 base = "ZapfDingbats";
368 }
369
370 int style = SkTypeface::kNormal;
371 if (font.style() != FontStyle::Normal)
372 style |= SkTypeface::kItalic;
373 if (font.weight() == FontWeight::Bold ||
374 font.weight() == FontWeight::Bolder)
375 style |= SkTypeface::kBold;
376
377 impl_->textPaint_.setTypeface(SkTypeface::CreateFromName(base,
378 (SkTypeface::Style)style))->unref();
379 impl_->textPaint_.setTextSize(SkIntToScalar(font.sizeLength(12).toPixels()));
380 }
381 }
382
drawArc(const WRectF & rect,double startAngle,double spanAngle)383 void WRasterImage::drawArc(const WRectF& rect,
384 double startAngle, double spanAngle)
385 {
386 SkRect r = SkRect::MakeLTRB(SkDoubleToScalar(rect.left()),
387 SkDoubleToScalar(rect.top()),
388 SkDoubleToScalar(rect.right()),
389 SkDoubleToScalar(rect.bottom()));
390 if (painter()->brush().style() != BrushStyle::None) {
391 impl_->canvas_->drawArc(r,
392 SkDoubleToScalar(-startAngle),
393 SkDoubleToScalar(-spanAngle),
394 false, impl_->fillPaint_);
395 }
396 if (painter()->pen().style() != PenStyle::None) {
397 impl_->canvas_->drawArc(r,
398 SkDoubleToScalar(-startAngle),
399 SkDoubleToScalar(-spanAngle),
400 false, impl_->strokePaint_);
401 }
402 }
403
drawImage(const WRectF & rect,const std::string & imgUri,int imgWidth,int imgHeight,const WRectF & srect)404 void WRasterImage::drawImage(const WRectF& rect, const std::string& imgUri,
405 int imgWidth, int imgHeight,
406 const WRectF& srect)
407 {
408 #ifdef WT_SKIA_OLD
409 SkBitmap bitmap;
410 bool success = false;
411 if (DataUri::isDataUri(imgUri)) {
412 DataUri uri(imgUri);
413 success =
414 SkImageDecoder::DecodeMemory(&uri.data[0], uri.data.size(),
415 &bitmap,
416 SkBitmap::kARGB_8888_Config,
417 SkImageDecoder::kDecodePixels_Mode, 0);
418 if (!success)
419 throw WException("WRasterImage: could not decode data URL (mime type "
420 + uri.mimeType);
421 } else {
422 success =
423 SkImageDecoder::DecodeFile(imgUri.c_str(),
424 &bitmap,
425 SkBitmap::kARGB_8888_Config,
426 SkImageDecoder::kDecodePixels_Mode, 0);
427 if (!success)
428 throw WException("WRasterImage: could not load file " + imgUri);
429 }
430 #else
431 SkBitmap bitmap;
432 SkAutoTDelete<SkCodec> codec;
433 if (DataUri::isDataUri(imgUri)) {
434 DataUri uri(imgUri);
435 sk_sp<SkData> data = SkData::MakeWithoutCopy(&uri.data[0], uri.data.size());
436 codec = SkCodec::NewFromData(data.get());
437 if (!codec) {
438 throw WException("WRasterImage: could not interprete data URL (mime type "
439 + uri.mimeType);
440 }
441 } else {
442 SkAutoTDelete<SkStream> stream = SkStream::NewFromFile(imgUri.c_str());
443 if (!stream)
444 throw WException("WRasterImage: could not open file " + imgUri);
445 SkAutoTDelete<SkCodec> codec = SkCodec::NewFromStream(stream);
446 if (!codec)
447 throw WException("WRasterImage: could not interprete file " + imgUri);
448 }
449
450 bitmap.allocPixels(codec->getInfo().makeColorType(kN32_SkColorType));
451 SkCodec::Result result = codec->getPixels(bitmap.info(),
452 bitmap.getPixels(), bitmap.rowBytes());
453 if (result != SkCodec::kSuccess)
454 throw WException("WRasterImage: could not decode image");
455
456 #endif
457
458 SkRect src = SkRect::MakeLTRB(SkDoubleToScalar(srect.left()),
459 SkDoubleToScalar(srect.top()),
460 SkDoubleToScalar(srect.right()),
461 SkDoubleToScalar(srect.bottom()));
462 SkRect dst = SkRect::MakeLTRB(SkDoubleToScalar(rect.left()),
463 SkDoubleToScalar(rect.top()),
464 SkDoubleToScalar(rect.right()),
465 SkDoubleToScalar(rect.bottom()));
466 #ifdef WT_SKIA_OLD
467 impl_->canvas_->drawBitmapRectToRect(bitmap, &src, dst);
468 #else
469 impl_->canvas_->drawBitmapRect(bitmap, src, dst, 0);
470 #endif
471 }
472
drawLine(double x1,double y1,double x2,double y2)473 void WRasterImage::drawLine(double x1, double y1, double x2, double y2)
474 {
475 impl_->canvas_->drawLine(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
476 SkDoubleToScalar(x2), SkDoubleToScalar(y2), impl_->strokePaint_);
477 }
478
drawPath(const WPainterPath & path)479 void WRasterImage::drawPath(const WPainterPath& path)
480 {
481 if (!path.isEmpty()) {
482 SkPath p;
483 impl_->drawPlainPath(p, path);
484
485 if (painter()->brush().style() != BrushStyle::None) {
486 impl_->canvas_->drawPath(p, impl_->fillPaint_);
487 }
488 if (painter()->pen().style() != PenStyle::None) {
489 impl_->canvas_->drawPath(p, impl_->strokePaint_);
490 }
491 }
492 }
493
setPixel(int x,int y,const WColor & c)494 void WRasterImage::setPixel(int x, int y, const WColor& c)
495 {
496 if (painter_)
497 throw WException("WRasterImage::setPixel(): cannot be used while a "
498 "painter is active");
499 uint8_t* addr = (uint8_t *)impl_->bitmap_->getAddr(x, y);
500 addr[0] = c.blue();
501 addr[1] = c.green();
502 addr[2] = c.red();
503 addr[3] = c.alpha();
504 }
505
getPixels(void * data)506 void WRasterImage::getPixels(void *data)
507 {
508 impl_->canvas_->flush();
509 uint32_t *pixel = impl_->bitmap_->getAddr32(0, 0);
510 unsigned char *d = (unsigned char *)data;
511 if (pixel) {
512 int i = 0;
513 for (unsigned p = 0; p < impl_->w_ * impl_->h_; ++p) {
514 d[i++] = SkColorGetR(*pixel);
515 d[i++] = SkColorGetG(*pixel);
516 d[i++] = SkColorGetB(*pixel);
517 d[i++] = SkColorGetA(*pixel);
518 pixel++;
519 }
520 } else {
521 throw WException("WRasterImage::getPixels(): error: no data");
522 }
523 }
524
getPixel(int x,int y)525 WColor WRasterImage::getPixel(int x, int y)
526 {
527 impl_->canvas_->flush();
528 SkColor c = impl_->bitmap_->getColor(x, y);
529 return WColor(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), SkColorGetA(c));
530 }
531
drawPlainPath(SkPath & p,const WPainterPath & path)532 void WRasterImage::Impl::drawPlainPath(SkPath &p, const WPainterPath& path)
533 {
534 const std::vector<WPainterPath::Segment>& segments = path.segments();
535
536 if (segments.size() > 0
537 && segments[0].type() != SegmentType::MoveTo)
538 p.moveTo(SkDoubleToScalar(0), SkDoubleToScalar(0));
539
540 for (unsigned i = 0; i < segments.size(); ++i) {
541 const WPainterPath::Segment s = segments[i];
542
543 switch (s.type()) {
544 case SegmentType::MoveTo:
545 p.moveTo(SkDoubleToScalar(s.x()), SkDoubleToScalar(s.y()));
546 break;
547 case SegmentType::LineTo:
548 p.lineTo(SkDoubleToScalar(s.x()), SkDoubleToScalar(s.y()));
549 break;
550 case SegmentType::CubicC1: {
551 const double x1 = s.x();
552 const double y1 = s.y();
553 const double x2 = segments[i+1].x();
554 const double y2 = segments[i+1].y();
555 const double x3 = segments[i+2].x();
556 const double y3 = segments[i+2].y();
557 p.cubicTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
558 SkDoubleToScalar(x2), SkDoubleToScalar(y2),
559 SkDoubleToScalar(x3), SkDoubleToScalar(y3));
560 i += 2;
561 break;
562 }
563 case SegmentType::CubicC2:
564 case SegmentType::CubicEnd:
565 assert(false);
566 case SegmentType::ArcC: {
567 const double x = s.x();
568 const double y = s.y();
569 const double width = segments[i+1].x();
570 const double height = segments[i+1].y();
571 const double startAngle = segments[i+2].x();
572 const double sweepAngle = segments[i+2].y();
573 SkRect rect = SkRect::MakeXYWH(SkDoubleToScalar(x - width),
574 SkDoubleToScalar(y - height),
575 SkDoubleToScalar(width * 2.0),
576 SkDoubleToScalar(height * 2.0));
577 if (sweepAngle != 360)
578 p.arcTo(rect,
579 SkDoubleToScalar(-startAngle), SkDoubleToScalar(-sweepAngle),
580 false);
581 else
582 p.addOval(rect, SkPath::kCCW_Direction);
583
584 i += 2;
585 break;
586 }
587 case SegmentType::ArcR:
588 case SegmentType::ArcAngleSweep:
589 assert(false);
590 case SegmentType::QuadC: {
591 const double x1 = s.x();
592 const double y1 = s.y();
593 const double x2 = segments[i+1].x();
594 const double y2 = segments[i+1].y();
595
596 p.quadTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1),
597 SkDoubleToScalar(x2), SkDoubleToScalar(y2));
598
599 i += 1;
600
601 break;
602 }
603 case SegmentType::QuadEnd:
604 assert(false);
605 }
606 }
607 }
608
drawText(const WRectF & rect,WFlags<AlignmentFlag> flags,TextFlag textFlag,const WString & text,const WPointF * clipPoint)609 void WRasterImage::drawText(const WRectF& rect,
610 WFlags<AlignmentFlag> flags,
611 TextFlag textFlag,
612 const WString& text,
613 const WPointF *clipPoint)
614 {
615 #if 0
616 SkRect r = SkRect::MakeLTRB(SkDoubleToScalar(rect.left()),
617 SkDoubleToScalar(rect.top()),
618 SkDoubleToScalar(rect.right()),
619 SkDoubleToScalar(rect.bottom()));
620 canvas_->drawRect(r, strokePaint_);
621 #endif
622 if (clipPoint && painter() && !painter()->clipPath().isEmpty()) {
623 if (!painter()->clipPathTransform().map(painter()->clipPath())
624 .isPointInPath(painter()->worldTransform().map(*clipPoint)))
625 return;
626 }
627
628 std::string txt = text.toUTF8();
629
630 AlignmentFlag horizontalAlign = flags & AlignHorizontalMask;
631 AlignmentFlag verticalAlign = flags & AlignVerticalMask;
632
633 const WTransform& t = painter()->combinedTransform();
634
635 WPointF p;
636
637 struct SkPaint::FontMetrics metrics;
638 impl_->textPaint_.getFontMetrics(&metrics);
639 double ascent = SkScalarToFloat(metrics.fAscent);
640 double descent = SkScalarToFloat(metrics.fDescent);
641
642 switch (verticalAlign) {
643 case AlignmentFlag::Top:
644 p = rect.topLeft();
645 p.setY(p.y() - ascent);
646 break;
647 case AlignmentFlag::Middle:
648 p = rect.center();
649 //p.setY(p.y() + SkScalarToFloat(textPaint_.getTextSize())/2);
650 p.setY(p.y() - ascent/2);
651 break;
652 case AlignmentFlag::Bottom:
653 p = rect.bottomLeft();
654 p.setY(p.y() - descent);
655 break;
656 default:
657 break;
658 }
659
660 switch (horizontalAlign) {
661 case AlignmentFlag::Left:
662 impl_->textPaint_.setTextAlign(SkPaint::kLeft_Align);
663 p.setX(rect.left());
664 break;
665 case AlignmentFlag::Center:
666 impl_->textPaint_.setTextAlign(SkPaint::kCenter_Align);
667 p.setX(rect.center().x());
668 break;
669 case AlignmentFlag::Right:
670 impl_->textPaint_.setTextAlign(SkPaint::kRight_Align);
671 p.setX(rect.right());
672 break;
673 default:
674 break;
675 }
676
677 impl_->canvas_->drawText(txt.c_str(), txt.size(),
678 SkDoubleToScalar(p.x()), SkDoubleToScalar(p.y()),
679 impl_->textPaint_);
680 }
681
measureText(const WString & text,double maxWidth,bool wordWrap)682 WTextItem WRasterImage::measureText(const WString& text, double maxWidth,
683 bool wordWrap)
684 {
685 SkScalar w =
686 impl_->textPaint_.measureText(text.toUTF8().c_str(), text.toUTF8().size());
687 return WTextItem(text, SkScalarToFloat(w), -1);
688
689 // TODO: what about wordwrap?
690 }
691
fontMetrics()692 WFontMetrics WRasterImage::fontMetrics()
693 {
694 struct SkPaint::FontMetrics metrics;
695 impl_->textPaint_.getFontMetrics(&metrics);
696 // assumed all are to be positive - ascent of skia is negative
697 double ascent = -SkScalarToFloat(metrics.fAscent);
698 double descent = SkScalarToFloat(metrics.fDescent);
699 double leading = SkScalarToFloat(metrics.fLeading);
700
701 return WFontMetrics(painter_->font(), leading, ascent, descent);
702 }
703
704 class RasterStream : public SkWStream {
705 public:
RasterStream(std::ostream & os)706 RasterStream(std::ostream &os):
707 os_(os)
708 {
709 }
710
write(const void * buffer,size_t size)711 virtual bool write(const void* buffer, size_t size) {
712 os_.write((const char *)buffer, size);
713 bytesWritten_ += size;
714 return os_.good();
715 }
716
bytesWritten()717 size_t bytesWritten() const {
718 return bytesWritten_;
719 }
720
721 private:
722 std::ostream &os_;
723 size_t bytesWritten_;
724 };
725
handleRequest(const Http::Request & request,Http::Response & response)726 void WRasterImage::handleRequest(const Http::Request& request,
727 Http::Response& response)
728 {
729 response.setMimeType("image/" + impl_->type_);
730
731 RasterStream s(response.out());
732
733 if (impl_->type_ == "png")
734 SkImageEncoder::EncodeStream(&s, *impl_->bitmap_, SkImageEncoder::kPNG_Type, 100);
735 else if (impl_->type_ == "jpg")
736 SkImageEncoder::EncodeStream(&s, *impl_->bitmap_, SkImageEncoder::kJPEG_Type, 100);
737
738 }
739
740 }
741