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
19namespace skia {
20
21#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
22    do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
23
24// static
25SkBaseDevice* 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
54static 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
68SkBaseDevice* 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
98VectorPlatformDeviceEmf::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
107VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
108  SkASSERT(previous_brush_ == NULL);
109  SkASSERT(previous_pen_ == NULL);
110}
111
112HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
113  return hdc_;
114}
115
116void 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
127void 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
170void 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
206void 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
213void 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
263void 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
321void 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
337void 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
353static bool gdiCanHandleText(const SkPaint& paint) {
354  return !paint.getShader() &&
355         !paint.getPathEffect() &&
356         (SkPaint::kFill_Style == paint.getStyle()) &&
357         (255 == paint.getAlpha());
358}
359
360class SkGDIFontSetup {
361 public:
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
384bool 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
403SkGDIFontSetup::~SkGDIFontSetup() {
404  if (fUseGDI) {
405    ::SelectObject(fHDC, fSavedFont);
406    ::DeleteObject(fNewFont);
407    SetTextColor(fHDC, fSavedTextColor);
408  }
409}
410
411static 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).
419static 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
428static SkiaEnsureTypefaceCharactersAccessible
429    g_skia_ensure_typeface_characters_accessible = NULL;
430
431SK_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
438void 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
445bool 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
470void 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
496static size_t size_utf8(const char* text) {
497  return SkUTF8_CountUTF8Bytes(text);
498}
499
500static 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
505static size_t size_glyphid(const char* text) {
506  return 2;
507}
508
509void 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
564void 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
574void 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
588void 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
598bool 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
675void 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
685void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
686                                                  const RECT* src_rect) {
687  SkASSERT(false);
688}
689
690void VectorPlatformDeviceEmf::LoadClipRegion() {
691  SkMatrix t;
692  t.reset();
693  LoadClippingRegionToDC(hdc_, clip_region_, t);
694}
695
696SkBaseDevice* 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
703bool 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
732bool 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
773void 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
794HGDIOBJ 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
802bool 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
812bool 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
861void 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