vector_platform_device_emf_win.cc revision 558790d6acca3451cf3a6b497803a5f07d0bec58
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
25SkDevice* 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  SkDevice* 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
68SkDevice* 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;
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 &&
85          height == bitmap_data.bmHeight) {
86        bitmap.setConfig(SkBitmap::kARGB_8888_Config,
87                         bitmap_data.bmWidth,
88                         bitmap_data.bmHeight,
89                         bitmap_data.bmWidthBytes);
90        bitmap.setPixels(bitmap_data.bmBits);
91        succeeded = true;
92      }
93    }
94  }
95
96  if (!succeeded)
97    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
98
99  return new VectorPlatformDeviceEmf(dc, bitmap);
100}
101
102VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
103    : SkDevice(bitmap),
104      hdc_(dc),
105      previous_brush_(NULL),
106      previous_pen_(NULL) {
107  transform_.reset();
108  SetPlatformDevice(this, this);
109}
110
111VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
112  SkASSERT(previous_brush_ == NULL);
113  SkASSERT(previous_pen_ == NULL);
114}
115
116HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
117  return hdc_;
118}
119
120uint32_t VectorPlatformDeviceEmf::getDeviceCapabilities() {
121  return SkDevice::getDeviceCapabilities() | kVector_Capability;
122}
123
124void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
125                                        const SkPaint& paint) {
126  // TODO(maruel):  Bypass the current transformation matrix.
127  SkRect rect;
128  rect.fLeft = 0;
129  rect.fTop = 0;
130  rect.fRight = SkIntToScalar(width() + 1);
131  rect.fBottom = SkIntToScalar(height() + 1);
132  drawRect(draw, rect, paint);
133}
134
135void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
136                                         SkCanvas::PointMode mode,
137                                         size_t count,
138                                         const SkPoint pts[],
139                                         const SkPaint& paint) {
140  if (!count)
141    return;
142
143  if (mode == SkCanvas::kPoints_PointMode) {
144    SkASSERT(false);
145    return;
146  }
147
148  SkPaint tmp_paint(paint);
149  tmp_paint.setStyle(SkPaint::kStroke_Style);
150
151  // Draw a path instead.
152  SkPath path;
153  switch (mode) {
154    case SkCanvas::kLines_PointMode:
155      if (count % 2) {
156        SkASSERT(false);
157        return;
158      }
159      for (size_t i = 0; i < count / 2; ++i) {
160        path.moveTo(pts[2 * i]);
161        path.lineTo(pts[2 * i + 1]);
162      }
163      break;
164    case SkCanvas::kPolygon_PointMode:
165      path.moveTo(pts[0]);
166      for (size_t i = 1; i < count; ++i) {
167        path.lineTo(pts[i]);
168      }
169      break;
170    default:
171      SkASSERT(false);
172      return;
173  }
174  // Draw the calculated path.
175  drawPath(draw, path, tmp_paint);
176}
177
178void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
179                                       const SkRect& rect,
180                                       const SkPaint& paint) {
181  CHECK_FOR_NODRAW_ANNOTATION(paint);
182  if (paint.getPathEffect()) {
183    // Draw a path instead.
184    SkPath path_orginal;
185    path_orginal.addRect(rect);
186
187    // Apply the path effect to the rect.
188    SkPath path_modified;
189    paint.getFillPath(path_orginal, &path_modified);
190
191    // Removes the path effect from the temporary SkPaint object.
192    SkPaint paint_no_effet(paint);
193    paint_no_effet.setPathEffect(NULL);
194
195    // Draw the calculated path.
196    drawPath(draw, path_modified, paint_no_effet);
197    return;
198  }
199
200  if (!ApplyPaint(paint)) {
201    return;
202  }
203  HDC dc = BeginPlatformPaint();
204  if (!Rectangle(dc, SkScalarRound(rect.fLeft),
205                 SkScalarRound(rect.fTop),
206                 SkScalarRound(rect.fRight),
207                 SkScalarRound(rect.fBottom))) {
208    SkASSERT(false);
209  }
210  EndPlatformPaint();
211  Cleanup();
212}
213
214void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
215                                       const SkPath& path,
216                                       const SkPaint& paint,
217                                       const SkMatrix* prePathMatrix,
218                                       bool pathIsMutable) {
219  CHECK_FOR_NODRAW_ANNOTATION(paint);
220  if (paint.getPathEffect()) {
221    // Apply the path effect forehand.
222    SkPath path_modified;
223    paint.getFillPath(path, &path_modified);
224
225    // Removes the path effect from the temporary SkPaint object.
226    SkPaint paint_no_effet(paint);
227    paint_no_effet.setPathEffect(NULL);
228
229    // Draw the calculated path.
230    drawPath(draw, path_modified, paint_no_effet);
231    return;
232  }
233
234  if (!ApplyPaint(paint)) {
235    return;
236  }
237  HDC dc = BeginPlatformPaint();
238  if (PlatformDevice::LoadPathToDC(dc, path)) {
239    switch (paint.getStyle()) {
240      case SkPaint::kFill_Style: {
241        BOOL res = StrokeAndFillPath(dc);
242        SkASSERT(res != 0);
243        break;
244      }
245      case SkPaint::kStroke_Style: {
246        BOOL res = StrokePath(dc);
247        SkASSERT(res != 0);
248        break;
249      }
250      case SkPaint::kStrokeAndFill_Style: {
251        BOOL res = StrokeAndFillPath(dc);
252        SkASSERT(res != 0);
253        break;
254      }
255      default:
256        SkASSERT(false);
257        break;
258    }
259  }
260  EndPlatformPaint();
261  Cleanup();
262}
263
264void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
265                                             const SkBitmap& bitmap,
266                                             const SkRect* src,
267                                             const SkRect& dst,
268                                             const SkPaint& paint) {
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;
394    SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
395    lf.lfHeight = -SkScalarRound(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;
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;
457        SkLOGFONTFromTypeface(typeface, &lf);
458        VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
459                << " FaceName = " << lf.lfFaceName
460                << " and characters: " << string16(text, characters);
461      }
462    } else {
463      VLOG(1) << "ExtTextOut FAILED for default FaceName "
464              << " and characters: " << 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_, SkScalarRound(x),
484        SkScalarRound(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 (2 == scalarsPerPos
520      && SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
521      && setup.useGDI(hdc_, paint)) {
522    int startX = SkScalarRound(pos[0]);
523    int startY = SkScalarRound(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] = SkScalarRound(pos[2] - pos[0]);
529      pos += 2;
530    }
531    useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
532        getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
533        count, advances, paint.getTypeface());
534  }
535
536  if (useDrawText) {
537    size_t (*bytesPerCodePoint)(const char*);
538    switch (paint.getTextEncoding()) {
539    case SkPaint::kUTF8_TextEncoding:
540      bytesPerCodePoint = size_utf8;
541      break;
542    case SkPaint::kUTF16_TextEncoding:
543      bytesPerCodePoint = size_utf16;
544      break;
545    default:
546      SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
547      bytesPerCodePoint = size_glyphid;
548      break;
549    }
550
551    const char* curr = reinterpret_cast<const char*>(text);
552    const char* stop = curr + len;
553    while (curr < stop) {
554      SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
555      size_t bytes = bytesPerCodePoint(curr);
556      drawText(draw, curr, bytes, pos[0], y, paint);
557      curr += bytes;
558      pos += scalarsPerPos;
559    }
560  }
561}
562
563void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
564                                             const void* text,
565                                             size_t len,
566                                             const SkPath& path,
567                                             const SkMatrix* matrix,
568                                             const SkPaint& paint) {
569  // This function isn't used in the code. Verify this assumption.
570  SkASSERT(false);
571}
572
573void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
574                                           SkCanvas::VertexMode vmode,
575                                           int vertexCount,
576                                           const SkPoint vertices[],
577                                           const SkPoint texs[],
578                                           const SkColor colors[],
579                                           SkXfermode* xmode,
580                                           const uint16_t indices[],
581                                           int indexCount,
582                                           const SkPaint& paint) {
583  // This function isn't used in the code. Verify this assumption.
584  SkASSERT(false);
585}
586
587void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
588                                         SkDevice* device,
589                                         int x,
590                                         int y,
591                                         const SkPaint& paint) {
592  // TODO(maruel):  http://b/1183870 Playback the EMF buffer at printer's dpi if
593  // it is a vectorial device.
594  drawSprite(draw, device->accessBitmap(false), x, y, paint);
595}
596
597bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
598  // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
599  // This function does not execute the SkPaint drawing commands. These should
600  // be executed in drawPaint().
601
602  SkPaint::Style style = paint.getStyle();
603  if (!paint.getAlpha())
604      style = (SkPaint::Style) SkPaint::kStyleCount;
605
606  switch (style) {
607    case SkPaint::kFill_Style:
608      if (!CreateBrush(true, paint) ||
609          !CreatePen(false, paint))
610        return false;
611      break;
612    case SkPaint::kStroke_Style:
613      if (!CreateBrush(false, paint) ||
614          !CreatePen(true, paint))
615        return false;
616      break;
617    case SkPaint::kStrokeAndFill_Style:
618      if (!CreateBrush(true, paint) ||
619          !CreatePen(true, paint))
620        return false;
621      break;
622    default:
623      if (!CreateBrush(false, paint) ||
624          !CreatePen(false, paint))
625        return false;
626      break;
627  }
628
629  /*
630  getFlags();
631    isAntiAlias();
632    isDither()
633    isLinearText()
634    isSubpixelText()
635    isUnderlineText()
636    isStrikeThruText()
637    isFakeBoldText()
638    isDevKernText()
639    isFilterBitmap()
640
641  // Skia's text is not used. This should be fixed.
642  getTextAlign()
643  getTextScaleX()
644  getTextSkewX()
645  getTextEncoding()
646  getFontMetrics()
647  getFontSpacing()
648  */
649
650  // BUG 1094907: Implement shaders. Shaders currently in use:
651  //  SkShader::CreateBitmapShader
652  //  SkGradientShader::CreateRadial
653  //  SkGradientShader::CreateLinear
654  // SkASSERT(!paint.getShader());
655
656  // http://b/1106647 Implement loopers and mask filter. Looper currently in
657  // use:
658  //   SkBlurDrawLooper is used for shadows.
659  // SkASSERT(!paint.getLooper());
660  // SkASSERT(!paint.getMaskFilter());
661
662  // http://b/1165900 Implement xfermode.
663  // SkASSERT(!paint.getXfermode());
664
665  // The path effect should be processed before arriving here.
666  SkASSERT(!paint.getPathEffect());
667
668  // This isn't used in the code. Verify this assumption.
669  SkASSERT(!paint.getRasterizer());
670  // Reuse code to load Win32 Fonts.
671  return true;
672}
673
674void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
675                                            const SkRegion& region,
676                                            const SkClipStack&) {
677  transform_ = transform;
678  LoadTransformToDC(hdc_, transform_);
679  clip_region_ = region;
680  if (!clip_region_.isEmpty())
681    LoadClipRegion();
682}
683
684void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
685                                                  const RECT* src_rect) {
686  SkASSERT(false);
687}
688
689void VectorPlatformDeviceEmf::LoadClipRegion() {
690  SkMatrix t;
691  t.reset();
692  LoadClippingRegionToDC(hdc_, clip_region_, t);
693}
694
695SkDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
696    SkBitmap::Config config, int width, int height, bool isOpaque,
697    Usage /*usage*/) {
698  SkASSERT(config == SkBitmap::kARGB_8888_Config);
699  return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL);
700}
701
702bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
703  SkASSERT(previous_brush_ == NULL);
704  // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
705  // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
706  // WHITE_BRUSH instead.
707
708  if (!use_brush) {
709    // Set the transparency.
710    if (0 == SetBkMode(hdc_, TRANSPARENT)) {
711      SkASSERT(false);
712      return false;
713    }
714
715    // Select the NULL brush.
716    previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
717    return previous_brush_ != NULL;
718  }
719
720  // Set the opacity.
721  if (0 == SetBkMode(hdc_, OPAQUE)) {
722    SkASSERT(false);
723    return false;
724  }
725
726  // Create and select the brush.
727  previous_brush_ = SelectObject(CreateSolidBrush(color));
728  return previous_brush_ != NULL;
729}
730
731bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
732                                        COLORREF color,
733                                        int stroke_width,
734                                        float stroke_miter,
735                                        DWORD pen_style) {
736  SkASSERT(previous_pen_ == NULL);
737  // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
738  // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
739  // instead.
740
741  // No pen case
742  if (!use_pen) {
743    previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
744    return previous_pen_ != NULL;
745  }
746
747  // Use the stock pen if the stroke width is 0.
748  if (stroke_width == 0) {
749    // Create a pen with the right color.
750    previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
751    return previous_pen_ != NULL;
752  }
753
754  // Load a custom pen.
755  LOGBRUSH brush;
756  brush.lbStyle = BS_SOLID;
757  brush.lbColor = color;
758  brush.lbHatch = 0;
759  HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
760  SkASSERT(pen != NULL);
761  previous_pen_ = SelectObject(pen);
762  if (previous_pen_ == NULL)
763    return false;
764
765  if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
766    SkASSERT(false);
767    return false;
768  }
769  return true;
770}
771
772void VectorPlatformDeviceEmf::Cleanup() {
773  if (previous_brush_) {
774    HGDIOBJ result = SelectObject(previous_brush_);
775    previous_brush_ = NULL;
776    if (result) {
777      BOOL res = DeleteObject(result);
778      SkASSERT(res != 0);
779    }
780  }
781  if (previous_pen_) {
782    HGDIOBJ result = SelectObject(previous_pen_);
783    previous_pen_ = NULL;
784    if (result) {
785      BOOL res = DeleteObject(result);
786      SkASSERT(res != 0);
787    }
788  }
789  // Remove any loaded path from the context.
790  AbortPath(hdc_);
791}
792
793HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
794  HGDIOBJ result = ::SelectObject(hdc_, object);
795  SkASSERT(result != HGDI_ERROR);
796  if (result == HGDI_ERROR)
797    return NULL;
798  return result;
799}
800
801bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
802                                          const SkPaint& paint) {
803  // Make sure that for transparent color, no brush is used.
804  if (paint.getAlpha() == 0) {
805    use_brush = false;
806  }
807
808  return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
809}
810
811bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
812  // Make sure that for transparent color, no pen is used.
813  if (paint.getAlpha() == 0) {
814    use_pen = false;
815  }
816
817  DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
818  switch (paint.getStrokeJoin()) {
819    case SkPaint::kMiter_Join:
820      // Connects path segments with a sharp join.
821      pen_style |= PS_JOIN_MITER;
822      break;
823    case SkPaint::kRound_Join:
824      // Connects path segments with a round join.
825      pen_style |= PS_JOIN_ROUND;
826      break;
827    case SkPaint::kBevel_Join:
828      // Connects path segments with a flat bevel join.
829      pen_style |= PS_JOIN_BEVEL;
830      break;
831    default:
832      SkASSERT(false);
833      break;
834  }
835  switch (paint.getStrokeCap()) {
836    case SkPaint::kButt_Cap:
837      // Begin/end contours with no extension.
838      pen_style |= PS_ENDCAP_FLAT;
839      break;
840    case SkPaint::kRound_Cap:
841      // Begin/end contours with a semi-circle extension.
842      pen_style |= PS_ENDCAP_ROUND;
843      break;
844    case SkPaint::kSquare_Cap:
845      // Begin/end contours with a half square extension.
846      pen_style |= PS_ENDCAP_SQUARE;
847      break;
848    default:
849      SkASSERT(false);
850      break;
851  }
852
853  return CreatePen(use_pen,
854                   SkColorToCOLORREF(paint.getColor()),
855                   SkScalarRound(paint.getStrokeWidth()),
856                   paint.getStrokeMiter(),
857                   pen_style);
858}
859
860void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
861                                                 int x, int y,
862                                                 const SkPaint& paint) {
863  unsigned char alpha = paint.getAlpha();
864  if (alpha == 0)
865    return;
866
867  bool is_translucent;
868  if (alpha != 255) {
869    // ApplyPaint expect an opaque color.
870    SkPaint tmp_paint(paint);
871    tmp_paint.setAlpha(255);
872    if (!ApplyPaint(tmp_paint))
873      return;
874    is_translucent = true;
875  } else {
876    if (!ApplyPaint(paint))
877      return;
878    is_translucent = false;
879  }
880  int src_size_x = bitmap.width();
881  int src_size_y = bitmap.height();
882  if (!src_size_x || !src_size_y)
883    return;
884
885  // Create a BMP v4 header that we can serialize. We use the shared "V3"
886  // fillter to fill the stardard items, then add in the "V4" stuff we want.
887  BITMAPV4HEADER bitmap_header;
888  memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER));
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.config() == SkBitmap::kARGB_8888_Config);
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;
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