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