• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "skia/ext/vector_platform_device_emf_win.h"
6 
7 #include <windows.h>
8 
9 #include "base/logging.h"
10 #include "base/strings/string16.h"
11 #include "skia/ext/bitmap_platform_device.h"
12 #include "skia/ext/skia_utils_win.h"
13 #include "third_party/skia/include/core/SkFontHost.h"
14 #include "third_party/skia/include/core/SkPathEffect.h"
15 #include "third_party/skia/include/core/SkTemplates.h"
16 #include "third_party/skia/include/core/SkUtils.h"
17 #include "third_party/skia/include/ports/SkTypeface_win.h"
18 
19 namespace skia {
20 
21 #define CHECK_FOR_NODRAW_ANNOTATION(paint) \
22     do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
23 
24 // static
CreateDevice(int width,int height,bool is_opaque,HANDLE shared_section)25 SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice(
26     int width, int height, bool is_opaque, HANDLE shared_section) {
27   if (!is_opaque) {
28     // TODO(maruel):  http://crbug.com/18382 When restoring a semi-transparent
29     // layer, i.e. merging it, we need to rasterize it because GDI doesn't
30     // support transparency except for AlphaBlend(). Right now, a
31     // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
32     // call is being done. The way to save a layer would be to create an
33     // EMF-based VectorDevice and have this device registers the drawing. When
34     // playing back the device into a bitmap, do it at the printer's dpi instead
35     // of the layout's dpi (which is much lower).
36     return BitmapPlatformDevice::Create(width, height, is_opaque,
37                                         shared_section);
38   }
39 
40   // TODO(maruel):  http://crbug.com/18383 Look if it would be worth to
41   // increase the resolution by ~10x (any worthy factor) to increase the
42   // rendering precision (think about printing) while using a relatively
43   // low dpi. This happens because we receive float as input but the GDI
44   // functions works with integers. The idea is to premultiply the matrix
45   // with this factor and multiply each SkScalar that are passed to
46   // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
47   // doing the same for text rendering.
48   SkASSERT(shared_section);
49   SkBaseDevice* device = VectorPlatformDeviceEmf::create(
50       reinterpret_cast<HDC>(shared_section), width, height);
51   return device;
52 }
53 
FillBitmapInfoHeader(int width,int height,BITMAPINFOHEADER * hdr)54 static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) {
55   hdr->biSize = sizeof(BITMAPINFOHEADER);
56   hdr->biWidth = width;
57   hdr->biHeight = -height;  // Minus means top-down bitmap.
58   hdr->biPlanes = 1;
59   hdr->biBitCount = 32;
60   hdr->biCompression = BI_RGB;  // no compression
61   hdr->biSizeImage = 0;
62   hdr->biXPelsPerMeter = 1;
63   hdr->biYPelsPerMeter = 1;
64   hdr->biClrUsed = 0;
65   hdr->biClrImportant = 0;
66 }
67 
create(HDC dc,int width,int height)68 SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
69   InitializeDC(dc);
70 
71   // Link the SkBitmap to the current selected bitmap in the device context.
72   SkBitmap bitmap;
73   HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
74   bool succeeded = false;
75   if (selected_bitmap != NULL) {
76     BITMAP bitmap_data = {0};
77     if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
78         sizeof(BITMAP)) {
79       // The context has a bitmap attached. Attach our SkBitmap to it.
80       // Warning: If the bitmap gets unselected from the HDC,
81       // VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP
82       // could be released while SkBitmap still has a reference to it. Be
83       // cautious.
84       if (width == bitmap_data.bmWidth && height == bitmap_data.bmHeight) {
85         SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
86         succeeded = bitmap.installPixels(info, bitmap_data.bmBits,
87                                          bitmap_data.bmWidthBytes);
88       }
89     }
90   }
91 
92   if (!succeeded)
93     bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
94 
95   return new VectorPlatformDeviceEmf(dc, bitmap);
96 }
97 
VectorPlatformDeviceEmf(HDC dc,const SkBitmap & bitmap)98 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
99     : SkBitmapDevice(bitmap),
100       hdc_(dc),
101       previous_brush_(NULL),
102       previous_pen_(NULL) {
103   transform_.reset();
104   SetPlatformDevice(this, this);
105 }
106 
~VectorPlatformDeviceEmf()107 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
108   SkASSERT(previous_brush_ == NULL);
109   SkASSERT(previous_pen_ == NULL);
110 }
111 
BeginPlatformPaint()112 HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
113   return hdc_;
114 }
115 
drawPaint(const SkDraw & draw,const SkPaint & paint)116 void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
117                                         const SkPaint& paint) {
118   // TODO(maruel):  Bypass the current transformation matrix.
119   SkRect rect;
120   rect.fLeft = 0;
121   rect.fTop = 0;
122   rect.fRight = SkIntToScalar(width() + 1);
123   rect.fBottom = SkIntToScalar(height() + 1);
124   drawRect(draw, rect, paint);
125 }
126 
drawPoints(const SkDraw & draw,SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)127 void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
128                                          SkCanvas::PointMode mode,
129                                          size_t count,
130                                          const SkPoint pts[],
131                                          const SkPaint& paint) {
132   if (!count)
133     return;
134 
135   if (mode == SkCanvas::kPoints_PointMode) {
136     SkASSERT(false);
137     return;
138   }
139 
140   SkPaint tmp_paint(paint);
141   tmp_paint.setStyle(SkPaint::kStroke_Style);
142 
143   // Draw a path instead.
144   SkPath path;
145   switch (mode) {
146     case SkCanvas::kLines_PointMode:
147       if (count % 2) {
148         SkASSERT(false);
149         return;
150       }
151       for (size_t i = 0; i < count / 2; ++i) {
152         path.moveTo(pts[2 * i]);
153         path.lineTo(pts[2 * i + 1]);
154       }
155       break;
156     case SkCanvas::kPolygon_PointMode:
157       path.moveTo(pts[0]);
158       for (size_t i = 1; i < count; ++i) {
159         path.lineTo(pts[i]);
160       }
161       break;
162     default:
163       SkASSERT(false);
164       return;
165   }
166   // Draw the calculated path.
167   drawPath(draw, path, tmp_paint);
168 }
169 
drawRect(const SkDraw & draw,const SkRect & rect,const SkPaint & paint)170 void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
171                                        const SkRect& rect,
172                                        const SkPaint& paint) {
173   CHECK_FOR_NODRAW_ANNOTATION(paint);
174   if (paint.getPathEffect()) {
175     // Draw a path instead.
176     SkPath path_orginal;
177     path_orginal.addRect(rect);
178 
179     // Apply the path effect to the rect.
180     SkPath path_modified;
181     paint.getFillPath(path_orginal, &path_modified);
182 
183     // Removes the path effect from the temporary SkPaint object.
184     SkPaint paint_no_effet(paint);
185     paint_no_effet.setPathEffect(NULL);
186 
187     // Draw the calculated path.
188     drawPath(draw, path_modified, paint_no_effet);
189     return;
190   }
191 
192   if (!ApplyPaint(paint)) {
193     return;
194   }
195   HDC dc = BeginPlatformPaint();
196   if (!Rectangle(dc, SkScalarRoundToInt(rect.fLeft),
197                  SkScalarRoundToInt(rect.fTop),
198                  SkScalarRoundToInt(rect.fRight),
199                  SkScalarRoundToInt(rect.fBottom))) {
200     SkASSERT(false);
201   }
202   EndPlatformPaint();
203   Cleanup();
204 }
205 
drawRRect(const SkDraw & draw,const SkRRect & rr,const SkPaint & paint)206 void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr,
207                                         const SkPaint& paint) {
208   SkPath path;
209   path.addRRect(rr);
210   this->drawPath(draw, path, paint, NULL, true);
211 }
212 
drawPath(const SkDraw & draw,const SkPath & path,const SkPaint & paint,const SkMatrix * prePathMatrix,bool pathIsMutable)213 void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
214                                        const SkPath& path,
215                                        const SkPaint& paint,
216                                        const SkMatrix* prePathMatrix,
217                                        bool pathIsMutable) {
218   CHECK_FOR_NODRAW_ANNOTATION(paint);
219   if (paint.getPathEffect()) {
220     // Apply the path effect forehand.
221     SkPath path_modified;
222     paint.getFillPath(path, &path_modified);
223 
224     // Removes the path effect from the temporary SkPaint object.
225     SkPaint paint_no_effet(paint);
226     paint_no_effet.setPathEffect(NULL);
227 
228     // Draw the calculated path.
229     drawPath(draw, path_modified, paint_no_effet);
230     return;
231   }
232 
233   if (!ApplyPaint(paint)) {
234     return;
235   }
236   HDC dc = BeginPlatformPaint();
237   if (PlatformDevice::LoadPathToDC(dc, path)) {
238     switch (paint.getStyle()) {
239       case SkPaint::kFill_Style: {
240         BOOL res = StrokeAndFillPath(dc);
241         SkASSERT(res != 0);
242         break;
243       }
244       case SkPaint::kStroke_Style: {
245         BOOL res = StrokePath(dc);
246         SkASSERT(res != 0);
247         break;
248       }
249       case SkPaint::kStrokeAndFill_Style: {
250         BOOL res = StrokeAndFillPath(dc);
251         SkASSERT(res != 0);
252         break;
253       }
254       default:
255         SkASSERT(false);
256         break;
257     }
258   }
259   EndPlatformPaint();
260   Cleanup();
261 }
262 
drawBitmapRect(const SkDraw & draw,const SkBitmap & bitmap,const SkRect * src,const SkRect & dst,const SkPaint & paint,SkCanvas::DrawBitmapRectFlags flags)263 void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
264                                              const SkBitmap& bitmap,
265                                              const SkRect* src,
266                                              const SkRect& dst,
267                                              const SkPaint& paint,
268                                              SkCanvas::DrawBitmapRectFlags flags) {
269     SkMatrix    matrix;
270     SkRect      bitmapBounds, tmpSrc, tmpDst;
271     SkBitmap    tmpBitmap;
272 
273     bitmapBounds.isetWH(bitmap.width(), bitmap.height());
274 
275     // Compute matrix from the two rectangles
276     if (src) {
277         tmpSrc = *src;
278     } else {
279         tmpSrc = bitmapBounds;
280     }
281     matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
282 
283     const SkBitmap* bitmapPtr = &bitmap;
284 
285     // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
286     // needed (if the src was clipped). No check needed if src==null.
287     if (src) {
288         if (!bitmapBounds.contains(*src)) {
289             if (!tmpSrc.intersect(bitmapBounds)) {
290                 return; // nothing to draw
291             }
292             // recompute dst, based on the smaller tmpSrc
293             matrix.mapRect(&tmpDst, tmpSrc);
294         }
295 
296         // since we may need to clamp to the borders of the src rect within
297         // the bitmap, we extract a subset.
298         // TODO: make sure this is handled in drawrect and remove it from here.
299         SkIRect srcIR;
300         tmpSrc.roundOut(&srcIR);
301         if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
302             return;
303         }
304         bitmapPtr = &tmpBitmap;
305 
306         // Since we did an extract, we need to adjust the matrix accordingly
307         SkScalar dx = 0, dy = 0;
308         if (srcIR.fLeft > 0) {
309             dx = SkIntToScalar(srcIR.fLeft);
310         }
311         if (srcIR.fTop > 0) {
312             dy = SkIntToScalar(srcIR.fTop);
313         }
314         if (dx || dy) {
315             matrix.preTranslate(dx, dy);
316         }
317     }
318     this->drawBitmap(draw, *bitmapPtr, matrix, paint);
319 }
320 
drawBitmap(const SkDraw & draw,const SkBitmap & bitmap,const SkMatrix & matrix,const SkPaint & paint)321 void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
322                                          const SkBitmap& bitmap,
323                                          const SkMatrix& matrix,
324                                          const SkPaint& paint) {
325   // Load the temporary matrix. This is what will translate, rotate and resize
326   // the bitmap.
327   SkMatrix actual_transform(transform_);
328   actual_transform.preConcat(matrix);
329   LoadTransformToDC(hdc_, actual_transform);
330 
331   InternalDrawBitmap(bitmap, 0, 0, paint);
332 
333   // Restore the original matrix.
334   LoadTransformToDC(hdc_, transform_);
335 }
336 
drawSprite(const SkDraw & draw,const SkBitmap & bitmap,int x,int y,const SkPaint & paint)337 void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
338                                          const SkBitmap& bitmap,
339                                          int x, int y,
340                                          const SkPaint& paint) {
341   SkMatrix identity;
342   identity.reset();
343   LoadTransformToDC(hdc_, identity);
344 
345   InternalDrawBitmap(bitmap, x, y, paint);
346 
347   // Restore the original matrix.
348   LoadTransformToDC(hdc_, transform_);
349 }
350 
351 /////////////////////////////////////////////////////////////////////////
352 
gdiCanHandleText(const SkPaint & paint)353 static bool gdiCanHandleText(const SkPaint& paint) {
354   return !paint.getShader() &&
355          !paint.getPathEffect() &&
356          (SkPaint::kFill_Style == paint.getStyle()) &&
357          (255 == paint.getAlpha());
358 }
359 
360 class SkGDIFontSetup {
361  public:
SkGDIFontSetup()362   SkGDIFontSetup() :
363       fHDC(NULL),
364       fNewFont(NULL),
365       fSavedFont(NULL),
366       fSavedTextColor(0),
367       fUseGDI(false) {
368     SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
369   }
370   ~SkGDIFontSetup();
371 
372   // can only be called once
373   bool useGDI(HDC hdc, const SkPaint&);
374 
375  private:
376   HDC      fHDC;
377   HFONT    fNewFont;
378   HFONT    fSavedFont;
379   COLORREF fSavedTextColor;
380   bool     fUseGDI;
381   SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
382 };
383 
useGDI(HDC hdc,const SkPaint & paint)384 bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
385   SkASSERT(!fUseGDIHasBeenCalled);
386   SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
387 
388   fUseGDI = gdiCanHandleText(paint);
389   if (fUseGDI) {
390     fSavedTextColor = GetTextColor(hdc);
391     SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
392 
393     LOGFONT lf = {0};
394     SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
395     lf.lfHeight = -SkScalarRoundToInt(paint.getTextSize());
396     fNewFont = CreateFontIndirect(&lf);
397     fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
398     fHDC = hdc;
399   }
400   return fUseGDI;
401 }
402 
~SkGDIFontSetup()403 SkGDIFontSetup::~SkGDIFontSetup() {
404   if (fUseGDI) {
405     ::SelectObject(fHDC, fSavedFont);
406     ::DeleteObject(fNewFont);
407     SetTextColor(fHDC, fSavedTextColor);
408   }
409 }
410 
getAscent(const SkPaint & paint)411 static SkScalar getAscent(const SkPaint& paint) {
412   SkPaint::FontMetrics fm;
413   paint.getFontMetrics(&fm);
414   return fm.fAscent;
415 }
416 
417 // return the options int for ExtTextOut. Only valid if the paint's text
418 // encoding is not UTF8 (in which case ExtTextOut can't be used).
getTextOutOptions(const SkPaint & paint)419 static UINT getTextOutOptions(const SkPaint& paint) {
420   if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
421     return ETO_GLYPH_INDEX;
422   } else {
423     SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
424     return 0;
425   }
426 }
427 
428 static SkiaEnsureTypefaceCharactersAccessible
429     g_skia_ensure_typeface_characters_accessible = NULL;
430 
SetSkiaEnsureTypefaceCharactersAccessible(SkiaEnsureTypefaceCharactersAccessible func)431 SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
432     SkiaEnsureTypefaceCharactersAccessible func) {
433   // This function is supposed to be called once in process life time.
434   SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL);
435   g_skia_ensure_typeface_characters_accessible = func;
436 }
437 
EnsureTypefaceCharactersAccessible(const SkTypeface & typeface,const wchar_t * text,unsigned int text_length)438 void EnsureTypefaceCharactersAccessible(
439     const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
440   LOGFONT lf = {0};
441   SkLOGFONTFromTypeface(&typeface, &lf);
442   g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
443 }
444 
EnsureExtTextOut(HDC hdc,int x,int y,UINT options,const RECT * lprect,LPCWSTR text,unsigned int characters,const int * lpDx,SkTypeface * const typeface)445 bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect,
446                       LPCWSTR text, unsigned int characters, const int * lpDx,
447                       SkTypeface* const typeface) {
448   bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
449   if (!success) {
450     if (typeface) {
451       EnsureTypefaceCharactersAccessible(*typeface,
452                                          text,
453                                          characters);
454       success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
455       if (!success) {
456         LOGFONT lf = {0};
457         SkLOGFONTFromTypeface(typeface, &lf);
458         VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
459                 << " FaceName = " << lf.lfFaceName
460                 << " and characters: " << base::string16(text, characters);
461       }
462     } else {
463       VLOG(1) << "ExtTextOut FAILED for default FaceName "
464               << " and characters: " << base::string16(text, characters);
465     }
466   }
467   return success;
468 }
469 
drawText(const SkDraw & draw,const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)470 void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
471                                        const void* text,
472                                        size_t byteLength,
473                                        SkScalar x,
474                                        SkScalar y,
475                                        const SkPaint& paint) {
476   SkGDIFontSetup setup;
477   bool useDrawPath = true;
478 
479   if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
480       && setup.useGDI(hdc_, paint)) {
481     UINT options = getTextOutOptions(paint);
482     UINT count = byteLength >> 1;
483     useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRoundToInt(x),
484         SkScalarRoundToInt(y + getAscent(paint)), options, 0,
485         reinterpret_cast<const wchar_t*>(text), count, NULL,
486         paint.getTypeface());
487   }
488 
489   if (useDrawPath) {
490     SkPath path;
491     paint.getTextPath(text, byteLength, x, y, &path);
492     drawPath(draw, path, paint);
493   }
494 }
495 
size_utf8(const char * text)496 static size_t size_utf8(const char* text) {
497   return SkUTF8_CountUTF8Bytes(text);
498 }
499 
size_utf16(const char * text)500 static size_t size_utf16(const char* text) {
501   uint16_t c = *reinterpret_cast<const uint16_t*>(text);
502   return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
503 }
504 
size_glyphid(const char * text)505 static size_t size_glyphid(const char* text) {
506   return 2;
507 }
508 
drawPosText(const SkDraw & draw,const void * text,size_t len,const SkScalar pos[],SkScalar constY,int scalarsPerPos,const SkPaint & paint)509 void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
510                                           const void* text,
511                                           size_t len,
512                                           const SkScalar pos[],
513                                           SkScalar constY,
514                                           int scalarsPerPos,
515                                           const SkPaint& paint) {
516   SkGDIFontSetup setup;
517   bool useDrawText = true;
518 
519   if (scalarsPerPos == 2 && len >= 2 &&
520       SkPaint::kUTF8_TextEncoding != paint.getTextEncoding() &&
521       setup.useGDI(hdc_, paint)) {
522     int startX = SkScalarRoundToInt(pos[0]);
523     int startY = SkScalarRoundToInt(pos[1] + getAscent(paint));
524     const int count = len >> 1;
525     SkAutoSTMalloc<64, INT> storage(count);
526     INT* advances = storage.get();
527     for (int i = 0; i < count - 1; ++i) {
528       advances[i] = SkScalarRoundToInt(pos[2] - pos[0]);
529       pos += 2;
530     }
531     advances[count - 1] = 0;
532     useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
533         getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
534         count, advances, paint.getTypeface());
535   }
536 
537   if (useDrawText) {
538     size_t (*bytesPerCodePoint)(const char*);
539     switch (paint.getTextEncoding()) {
540     case SkPaint::kUTF8_TextEncoding:
541       bytesPerCodePoint = size_utf8;
542       break;
543     case SkPaint::kUTF16_TextEncoding:
544       bytesPerCodePoint = size_utf16;
545       break;
546     default:
547       SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
548       bytesPerCodePoint = size_glyphid;
549       break;
550     }
551 
552     const char* curr = reinterpret_cast<const char*>(text);
553     const char* stop = curr + len;
554     while (curr < stop) {
555       SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
556       size_t bytes = bytesPerCodePoint(curr);
557       drawText(draw, curr, bytes, pos[0], y, paint);
558       curr += bytes;
559       pos += scalarsPerPos;
560     }
561   }
562 }
563 
drawTextOnPath(const SkDraw & draw,const void * text,size_t len,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)564 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
565                                              const void* text,
566                                              size_t len,
567                                              const SkPath& path,
568                                              const SkMatrix* matrix,
569                                              const SkPaint& paint) {
570   // This function isn't used in the code. Verify this assumption.
571   SkASSERT(false);
572 }
573 
drawVertices(const SkDraw & draw,SkCanvas::VertexMode vmode,int vertexCount,const SkPoint vertices[],const SkPoint texs[],const SkColor colors[],SkXfermode * xmode,const uint16_t indices[],int indexCount,const SkPaint & paint)574 void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
575                                            SkCanvas::VertexMode vmode,
576                                            int vertexCount,
577                                            const SkPoint vertices[],
578                                            const SkPoint texs[],
579                                            const SkColor colors[],
580                                            SkXfermode* xmode,
581                                            const uint16_t indices[],
582                                            int indexCount,
583                                            const SkPaint& paint) {
584   // This function isn't used in the code. Verify this assumption.
585   SkASSERT(false);
586 }
587 
drawDevice(const SkDraw & draw,SkBaseDevice * device,int x,int y,const SkPaint & paint)588 void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
589                                          SkBaseDevice* device,
590                                          int x,
591                                          int y,
592                                          const SkPaint& paint) {
593   // TODO(maruel):  http://b/1183870 Playback the EMF buffer at printer's dpi if
594   // it is a vectorial device.
595   drawSprite(draw, device->accessBitmap(false), x, y, paint);
596 }
597 
ApplyPaint(const SkPaint & paint)598 bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
599   // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
600   // This function does not execute the SkPaint drawing commands. These should
601   // be executed in drawPaint().
602 
603   SkPaint::Style style = paint.getStyle();
604   if (!paint.getAlpha())
605       style = (SkPaint::Style) SkPaint::kStyleCount;
606 
607   switch (style) {
608     case SkPaint::kFill_Style:
609       if (!CreateBrush(true, paint) ||
610           !CreatePen(false, paint))
611         return false;
612       break;
613     case SkPaint::kStroke_Style:
614       if (!CreateBrush(false, paint) ||
615           !CreatePen(true, paint))
616         return false;
617       break;
618     case SkPaint::kStrokeAndFill_Style:
619       if (!CreateBrush(true, paint) ||
620           !CreatePen(true, paint))
621         return false;
622       break;
623     default:
624       if (!CreateBrush(false, paint) ||
625           !CreatePen(false, paint))
626         return false;
627       break;
628   }
629 
630   /*
631   getFlags();
632     isAntiAlias();
633     isDither()
634     isLinearText()
635     isSubpixelText()
636     isUnderlineText()
637     isStrikeThruText()
638     isFakeBoldText()
639     isDevKernText()
640     isFilterBitmap()
641 
642   // Skia's text is not used. This should be fixed.
643   getTextAlign()
644   getTextScaleX()
645   getTextSkewX()
646   getTextEncoding()
647   getFontMetrics()
648   getFontSpacing()
649   */
650 
651   // BUG 1094907: Implement shaders. Shaders currently in use:
652   //  SkShader::CreateBitmapShader
653   //  SkGradientShader::CreateRadial
654   //  SkGradientShader::CreateLinear
655   // SkASSERT(!paint.getShader());
656 
657   // http://b/1106647 Implement loopers and mask filter. Looper currently in
658   // use:
659   //   SkBlurDrawLooper is used for shadows.
660   // SkASSERT(!paint.getLooper());
661   // SkASSERT(!paint.getMaskFilter());
662 
663   // http://b/1165900 Implement xfermode.
664   // SkASSERT(!paint.getXfermode());
665 
666   // The path effect should be processed before arriving here.
667   SkASSERT(!paint.getPathEffect());
668 
669   // This isn't used in the code. Verify this assumption.
670   SkASSERT(!paint.getRasterizer());
671   // Reuse code to load Win32 Fonts.
672   return true;
673 }
674 
setMatrixClip(const SkMatrix & transform,const SkRegion & region,const SkClipStack &)675 void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
676                                             const SkRegion& region,
677                                             const SkClipStack&) {
678   transform_ = transform;
679   LoadTransformToDC(hdc_, transform_);
680   clip_region_ = region;
681   if (!clip_region_.isEmpty())
682     LoadClipRegion();
683 }
684 
DrawToNativeContext(HDC dc,int x,int y,const RECT * src_rect)685 void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
686                                                   const RECT* src_rect) {
687   SkASSERT(false);
688 }
689 
LoadClipRegion()690 void VectorPlatformDeviceEmf::LoadClipRegion() {
691   SkMatrix t;
692   t.reset();
693   LoadClippingRegionToDC(hdc_, clip_region_, t);
694 }
695 
onCreateDevice(const SkImageInfo & info,Usage)696 SkBaseDevice* VectorPlatformDeviceEmf::onCreateDevice(const SkImageInfo& info,
697                                                       Usage /*usage*/) {
698   SkASSERT(info.colorType() == kN32_SkColorType);
699   return VectorPlatformDeviceEmf::CreateDevice(
700       info.width(), info.height(), info.isOpaque(), NULL);
701 }
702 
CreateBrush(bool use_brush,COLORREF color)703 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
704   SkASSERT(previous_brush_ == NULL);
705   // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
706   // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
707   // WHITE_BRUSH instead.
708 
709   if (!use_brush) {
710     // Set the transparency.
711     if (0 == SetBkMode(hdc_, TRANSPARENT)) {
712       SkASSERT(false);
713       return false;
714     }
715 
716     // Select the NULL brush.
717     previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
718     return previous_brush_ != NULL;
719   }
720 
721   // Set the opacity.
722   if (0 == SetBkMode(hdc_, OPAQUE)) {
723     SkASSERT(false);
724     return false;
725   }
726 
727   // Create and select the brush.
728   previous_brush_ = SelectObject(CreateSolidBrush(color));
729   return previous_brush_ != NULL;
730 }
731 
CreatePen(bool use_pen,COLORREF color,int stroke_width,float stroke_miter,DWORD pen_style)732 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
733                                         COLORREF color,
734                                         int stroke_width,
735                                         float stroke_miter,
736                                         DWORD pen_style) {
737   SkASSERT(previous_pen_ == NULL);
738   // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
739   // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
740   // instead.
741 
742   // No pen case
743   if (!use_pen) {
744     previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
745     return previous_pen_ != NULL;
746   }
747 
748   // Use the stock pen if the stroke width is 0.
749   if (stroke_width == 0) {
750     // Create a pen with the right color.
751     previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
752     return previous_pen_ != NULL;
753   }
754 
755   // Load a custom pen.
756   LOGBRUSH brush = {0};
757   brush.lbStyle = BS_SOLID;
758   brush.lbColor = color;
759   brush.lbHatch = 0;
760   HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
761   SkASSERT(pen != NULL);
762   previous_pen_ = SelectObject(pen);
763   if (previous_pen_ == NULL)
764     return false;
765 
766   if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
767     SkASSERT(false);
768     return false;
769   }
770   return true;
771 }
772 
Cleanup()773 void VectorPlatformDeviceEmf::Cleanup() {
774   if (previous_brush_) {
775     HGDIOBJ result = SelectObject(previous_brush_);
776     previous_brush_ = NULL;
777     if (result) {
778       BOOL res = DeleteObject(result);
779       SkASSERT(res != 0);
780     }
781   }
782   if (previous_pen_) {
783     HGDIOBJ result = SelectObject(previous_pen_);
784     previous_pen_ = NULL;
785     if (result) {
786       BOOL res = DeleteObject(result);
787       SkASSERT(res != 0);
788     }
789   }
790   // Remove any loaded path from the context.
791   AbortPath(hdc_);
792 }
793 
SelectObject(HGDIOBJ object)794 HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
795   HGDIOBJ result = ::SelectObject(hdc_, object);
796   SkASSERT(result != HGDI_ERROR);
797   if (result == HGDI_ERROR)
798     return NULL;
799   return result;
800 }
801 
CreateBrush(bool use_brush,const SkPaint & paint)802 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
803                                           const SkPaint& paint) {
804   // Make sure that for transparent color, no brush is used.
805   if (paint.getAlpha() == 0) {
806     use_brush = false;
807   }
808 
809   return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
810 }
811 
CreatePen(bool use_pen,const SkPaint & paint)812 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
813   // Make sure that for transparent color, no pen is used.
814   if (paint.getAlpha() == 0) {
815     use_pen = false;
816   }
817 
818   DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
819   switch (paint.getStrokeJoin()) {
820     case SkPaint::kMiter_Join:
821       // Connects path segments with a sharp join.
822       pen_style |= PS_JOIN_MITER;
823       break;
824     case SkPaint::kRound_Join:
825       // Connects path segments with a round join.
826       pen_style |= PS_JOIN_ROUND;
827       break;
828     case SkPaint::kBevel_Join:
829       // Connects path segments with a flat bevel join.
830       pen_style |= PS_JOIN_BEVEL;
831       break;
832     default:
833       SkASSERT(false);
834       break;
835   }
836   switch (paint.getStrokeCap()) {
837     case SkPaint::kButt_Cap:
838       // Begin/end contours with no extension.
839       pen_style |= PS_ENDCAP_FLAT;
840       break;
841     case SkPaint::kRound_Cap:
842       // Begin/end contours with a semi-circle extension.
843       pen_style |= PS_ENDCAP_ROUND;
844       break;
845     case SkPaint::kSquare_Cap:
846       // Begin/end contours with a half square extension.
847       pen_style |= PS_ENDCAP_SQUARE;
848       break;
849     default:
850       SkASSERT(false);
851       break;
852   }
853 
854   return CreatePen(use_pen,
855                    SkColorToCOLORREF(paint.getColor()),
856                    SkScalarRoundToInt(paint.getStrokeWidth()),
857                    paint.getStrokeMiter(),
858                    pen_style);
859 }
860 
InternalDrawBitmap(const SkBitmap & bitmap,int x,int y,const SkPaint & paint)861 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
862                                                  int x, int y,
863                                                  const SkPaint& paint) {
864   unsigned char alpha = paint.getAlpha();
865   if (alpha == 0)
866     return;
867 
868   bool is_translucent;
869   if (alpha != 255) {
870     // ApplyPaint expect an opaque color.
871     SkPaint tmp_paint(paint);
872     tmp_paint.setAlpha(255);
873     if (!ApplyPaint(tmp_paint))
874       return;
875     is_translucent = true;
876   } else {
877     if (!ApplyPaint(paint))
878       return;
879     is_translucent = false;
880   }
881   int src_size_x = bitmap.width();
882   int src_size_y = bitmap.height();
883   if (!src_size_x || !src_size_y)
884     return;
885 
886   // Create a BMP v4 header that we can serialize. We use the shared "V3"
887   // fillter to fill the stardard items, then add in the "V4" stuff we want.
888   BITMAPV4HEADER bitmap_header = {0};
889   FillBitmapInfoHeader(src_size_x, src_size_y,
890                        reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
891   bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
892   bitmap_header.bV4RedMask   = 0x00ff0000;
893   bitmap_header.bV4GreenMask = 0x0000ff00;
894   bitmap_header.bV4BlueMask  = 0x000000ff;
895   bitmap_header.bV4AlphaMask = 0xff000000;
896 
897   SkAutoLockPixels lock(bitmap);
898   SkASSERT(bitmap.colorType() == kN32_SkColorType);
899   const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
900   if (pixels == NULL) {
901     SkASSERT(false);
902     return;
903   }
904 
905   if (!is_translucent) {
906     int row_length = bitmap.rowBytesAsPixels();
907     // There is no quick way to determine if an image is opaque.
908     for (int y2 = 0; y2 < src_size_y; ++y2) {
909       for (int x2 = 0; x2 < src_size_x; ++x2) {
910         if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
911           is_translucent = true;
912           y2 = src_size_y;
913           break;
914         }
915       }
916     }
917   }
918 
919   HDC dc = BeginPlatformPaint();
920   BITMAPINFOHEADER hdr = {0};
921   FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
922   if (is_translucent) {
923     // The image must be loaded as a bitmap inside a device context.
924     HDC bitmap_dc = ::CreateCompatibleDC(dc);
925     void* bits = NULL;
926     HBITMAP hbitmap = ::CreateDIBSection(
927         bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
928         DIB_RGB_COLORS, &bits, NULL, 0);
929 
930     // static cast to a char so we can do byte ptr arithmatic to
931     // get the offset.
932     unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
933 
934     // We will copy row by row to avoid having to worry about
935     // the row strides being different.
936     const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
937     for (int row = 0; row < bitmap.height(); ++row) {
938       int dest_offset = row * dest_row_size;
939       // pixels_offset in terms of pixel count.
940       int src_offset = row * bitmap.rowBytesAsPixels();
941       memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
942     }
943     SkASSERT(hbitmap);
944     HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
945 
946     // After some analysis of IE7's behavior, this is the thing to do. I was
947     // sure IE7 was doing so kind of bitmasking due to the way translucent image
948     // where renderered but after some windbg tracing, it is being done by the
949     // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
950     // for bitmasked images. The trick seems to switch the stretching mode in
951     // what the driver expects.
952     DWORD previous_mode = GetStretchBltMode(dc);
953     BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
954     SkASSERT(result);
955     // Note that this function expect premultiplied colors (!)
956     BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
957     result = GdiAlphaBlend(dc,
958                            x, y,  // Destination origin.
959                            src_size_x, src_size_y,  // Destination size.
960                            bitmap_dc,
961                            0, 0,  // Source origin.
962                            src_size_x, src_size_y,  // Source size.
963                            blend_function);
964     SkASSERT(result);
965     result = SetStretchBltMode(dc, previous_mode);
966     SkASSERT(result);
967 
968     ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
969     DeleteObject(hbitmap);
970     DeleteDC(bitmap_dc);
971   } else {
972     int nCopied = StretchDIBits(dc,
973                                 x, y,  // Destination origin.
974                                 src_size_x, src_size_y,
975                                 0, 0,  // Source origin.
976                                 src_size_x, src_size_y,  // Source size.
977                                 pixels,
978                                 reinterpret_cast<const BITMAPINFO*>(&hdr),
979                                 DIB_RGB_COLORS,
980                                 SRCCOPY);
981   }
982   EndPlatformPaint();
983   Cleanup();
984 }
985 
986 }  // namespace skia
987