vector_platform_device_emf_win.cc revision f2477e01787aa58f445919b809d89e252beef54f
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;
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    : SkBitmapDevice(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 SkBitmapDevice::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::drawRRect(const SkDraw& draw, const SkRRect& rr,
215                                        const SkPaint& paint) {
216  SkPath path;
217  path.addRRect(rr);
218  this->drawPath(draw, path, paint, NULL, true);
219}
220
221void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
222                                       const SkPath& path,
223                                       const SkPaint& paint,
224                                       const SkMatrix* prePathMatrix,
225                                       bool pathIsMutable) {
226  CHECK_FOR_NODRAW_ANNOTATION(paint);
227  if (paint.getPathEffect()) {
228    // Apply the path effect forehand.
229    SkPath path_modified;
230    paint.getFillPath(path, &path_modified);
231
232    // Removes the path effect from the temporary SkPaint object.
233    SkPaint paint_no_effet(paint);
234    paint_no_effet.setPathEffect(NULL);
235
236    // Draw the calculated path.
237    drawPath(draw, path_modified, paint_no_effet);
238    return;
239  }
240
241  if (!ApplyPaint(paint)) {
242    return;
243  }
244  HDC dc = BeginPlatformPaint();
245  if (PlatformDevice::LoadPathToDC(dc, path)) {
246    switch (paint.getStyle()) {
247      case SkPaint::kFill_Style: {
248        BOOL res = StrokeAndFillPath(dc);
249        SkASSERT(res != 0);
250        break;
251      }
252      case SkPaint::kStroke_Style: {
253        BOOL res = StrokePath(dc);
254        SkASSERT(res != 0);
255        break;
256      }
257      case SkPaint::kStrokeAndFill_Style: {
258        BOOL res = StrokeAndFillPath(dc);
259        SkASSERT(res != 0);
260        break;
261      }
262      default:
263        SkASSERT(false);
264        break;
265    }
266  }
267  EndPlatformPaint();
268  Cleanup();
269}
270
271void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
272                                             const SkBitmap& bitmap,
273                                             const SkRect* src,
274                                             const SkRect& dst,
275                                             const SkPaint& paint,
276                                             SkCanvas::DrawBitmapRectFlags flags) {
277    SkMatrix    matrix;
278    SkRect      bitmapBounds, tmpSrc, tmpDst;
279    SkBitmap    tmpBitmap;
280
281    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
282
283    // Compute matrix from the two rectangles
284    if (src) {
285        tmpSrc = *src;
286    } else {
287        tmpSrc = bitmapBounds;
288    }
289    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
290
291    const SkBitmap* bitmapPtr = &bitmap;
292
293    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
294    // needed (if the src was clipped). No check needed if src==null.
295    if (src) {
296        if (!bitmapBounds.contains(*src)) {
297            if (!tmpSrc.intersect(bitmapBounds)) {
298                return; // nothing to draw
299            }
300            // recompute dst, based on the smaller tmpSrc
301            matrix.mapRect(&tmpDst, tmpSrc);
302        }
303
304        // since we may need to clamp to the borders of the src rect within
305        // the bitmap, we extract a subset.
306        // TODO: make sure this is handled in drawrect and remove it from here.
307        SkIRect srcIR;
308        tmpSrc.roundOut(&srcIR);
309        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
310            return;
311        }
312        bitmapPtr = &tmpBitmap;
313
314        // Since we did an extract, we need to adjust the matrix accordingly
315        SkScalar dx = 0, dy = 0;
316        if (srcIR.fLeft > 0) {
317            dx = SkIntToScalar(srcIR.fLeft);
318        }
319        if (srcIR.fTop > 0) {
320            dy = SkIntToScalar(srcIR.fTop);
321        }
322        if (dx || dy) {
323            matrix.preTranslate(dx, dy);
324        }
325    }
326    this->drawBitmap(draw, *bitmapPtr, matrix, paint);
327}
328
329void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
330                                         const SkBitmap& bitmap,
331                                         const SkMatrix& matrix,
332                                         const SkPaint& paint) {
333  // Load the temporary matrix. This is what will translate, rotate and resize
334  // the bitmap.
335  SkMatrix actual_transform(transform_);
336  actual_transform.preConcat(matrix);
337  LoadTransformToDC(hdc_, actual_transform);
338
339  InternalDrawBitmap(bitmap, 0, 0, paint);
340
341  // Restore the original matrix.
342  LoadTransformToDC(hdc_, transform_);
343}
344
345void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
346                                         const SkBitmap& bitmap,
347                                         int x, int y,
348                                         const SkPaint& paint) {
349  SkMatrix identity;
350  identity.reset();
351  LoadTransformToDC(hdc_, identity);
352
353  InternalDrawBitmap(bitmap, x, y, paint);
354
355  // Restore the original matrix.
356  LoadTransformToDC(hdc_, transform_);
357}
358
359/////////////////////////////////////////////////////////////////////////
360
361static bool gdiCanHandleText(const SkPaint& paint) {
362  return !paint.getShader() &&
363         !paint.getPathEffect() &&
364         (SkPaint::kFill_Style == paint.getStyle()) &&
365         (255 == paint.getAlpha());
366}
367
368class SkGDIFontSetup {
369 public:
370  SkGDIFontSetup() :
371      fHDC(NULL),
372      fNewFont(NULL),
373      fSavedFont(NULL),
374      fSavedTextColor(0),
375      fUseGDI(false) {
376    SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
377  }
378  ~SkGDIFontSetup();
379
380  // can only be called once
381  bool useGDI(HDC hdc, const SkPaint&);
382
383 private:
384  HDC      fHDC;
385  HFONT    fNewFont;
386  HFONT    fSavedFont;
387  COLORREF fSavedTextColor;
388  bool     fUseGDI;
389  SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
390};
391
392bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
393  SkASSERT(!fUseGDIHasBeenCalled);
394  SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
395
396  fUseGDI = gdiCanHandleText(paint);
397  if (fUseGDI) {
398    fSavedTextColor = GetTextColor(hdc);
399    SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
400
401    LOGFONT lf;
402    SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
403    lf.lfHeight = -SkScalarRound(paint.getTextSize());
404    fNewFont = CreateFontIndirect(&lf);
405    fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
406    fHDC = hdc;
407  }
408  return fUseGDI;
409}
410
411SkGDIFontSetup::~SkGDIFontSetup() {
412  if (fUseGDI) {
413    ::SelectObject(fHDC, fSavedFont);
414    ::DeleteObject(fNewFont);
415    SetTextColor(fHDC, fSavedTextColor);
416  }
417}
418
419static SkScalar getAscent(const SkPaint& paint) {
420  SkPaint::FontMetrics fm;
421  paint.getFontMetrics(&fm);
422  return fm.fAscent;
423}
424
425// return the options int for ExtTextOut. Only valid if the paint's text
426// encoding is not UTF8 (in which case ExtTextOut can't be used).
427static UINT getTextOutOptions(const SkPaint& paint) {
428  if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
429    return ETO_GLYPH_INDEX;
430  } else {
431    SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
432    return 0;
433  }
434}
435
436static SkiaEnsureTypefaceCharactersAccessible
437    g_skia_ensure_typeface_characters_accessible = NULL;
438
439SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
440    SkiaEnsureTypefaceCharactersAccessible func) {
441  // This function is supposed to be called once in process life time.
442  SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL);
443  g_skia_ensure_typeface_characters_accessible = func;
444}
445
446void EnsureTypefaceCharactersAccessible(
447    const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
448  LOGFONT lf;
449  SkLOGFONTFromTypeface(&typeface, &lf);
450  g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
451}
452
453bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect,
454                      LPCWSTR text, unsigned int characters, const int * lpDx,
455                      SkTypeface* const typeface) {
456  bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
457  if (!success) {
458    if (typeface) {
459      EnsureTypefaceCharactersAccessible(*typeface,
460                                         text,
461                                         characters);
462      success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
463      if (!success) {
464        LOGFONT lf;
465        SkLOGFONTFromTypeface(typeface, &lf);
466        VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
467                << " FaceName = " << lf.lfFaceName
468                << " and characters: " << string16(text, characters);
469      }
470    } else {
471      VLOG(1) << "ExtTextOut FAILED for default FaceName "
472              << " and characters: " << string16(text, characters);
473    }
474  }
475  return success;
476}
477
478void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
479                                       const void* text,
480                                       size_t byteLength,
481                                       SkScalar x,
482                                       SkScalar y,
483                                       const SkPaint& paint) {
484  SkGDIFontSetup setup;
485  bool useDrawPath = true;
486
487  if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
488      && setup.useGDI(hdc_, paint)) {
489    UINT options = getTextOutOptions(paint);
490    UINT count = byteLength >> 1;
491    useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRound(x),
492        SkScalarRound(y + getAscent(paint)), options, 0,
493        reinterpret_cast<const wchar_t*>(text), count, NULL,
494        paint.getTypeface());
495  }
496
497  if (useDrawPath) {
498    SkPath path;
499    paint.getTextPath(text, byteLength, x, y, &path);
500    drawPath(draw, path, paint);
501  }
502}
503
504static size_t size_utf8(const char* text) {
505  return SkUTF8_CountUTF8Bytes(text);
506}
507
508static size_t size_utf16(const char* text) {
509  uint16_t c = *reinterpret_cast<const uint16_t*>(text);
510  return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
511}
512
513static size_t size_glyphid(const char* text) {
514  return 2;
515}
516
517void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
518                                          const void* text,
519                                          size_t len,
520                                          const SkScalar pos[],
521                                          SkScalar constY,
522                                          int scalarsPerPos,
523                                          const SkPaint& paint) {
524  SkGDIFontSetup setup;
525  bool useDrawText = true;
526
527  if (2 == scalarsPerPos
528      && SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
529      && setup.useGDI(hdc_, paint)) {
530    int startX = SkScalarRound(pos[0]);
531    int startY = SkScalarRound(pos[1] + getAscent(paint));
532    const int count = len >> 1;
533    SkAutoSTMalloc<64, INT> storage(count);
534    INT* advances = storage.get();
535    for (int i = 0; i < count - 1; ++i) {
536      advances[i] = SkScalarRound(pos[2] - pos[0]);
537      pos += 2;
538    }
539    useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
540        getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
541        count, advances, paint.getTypeface());
542  }
543
544  if (useDrawText) {
545    size_t (*bytesPerCodePoint)(const char*);
546    switch (paint.getTextEncoding()) {
547    case SkPaint::kUTF8_TextEncoding:
548      bytesPerCodePoint = size_utf8;
549      break;
550    case SkPaint::kUTF16_TextEncoding:
551      bytesPerCodePoint = size_utf16;
552      break;
553    default:
554      SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
555      bytesPerCodePoint = size_glyphid;
556      break;
557    }
558
559    const char* curr = reinterpret_cast<const char*>(text);
560    const char* stop = curr + len;
561    while (curr < stop) {
562      SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
563      size_t bytes = bytesPerCodePoint(curr);
564      drawText(draw, curr, bytes, pos[0], y, paint);
565      curr += bytes;
566      pos += scalarsPerPos;
567    }
568  }
569}
570
571void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
572                                             const void* text,
573                                             size_t len,
574                                             const SkPath& path,
575                                             const SkMatrix* matrix,
576                                             const SkPaint& paint) {
577  // This function isn't used in the code. Verify this assumption.
578  SkASSERT(false);
579}
580
581void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
582                                           SkCanvas::VertexMode vmode,
583                                           int vertexCount,
584                                           const SkPoint vertices[],
585                                           const SkPoint texs[],
586                                           const SkColor colors[],
587                                           SkXfermode* xmode,
588                                           const uint16_t indices[],
589                                           int indexCount,
590                                           const SkPaint& paint) {
591  // This function isn't used in the code. Verify this assumption.
592  SkASSERT(false);
593}
594
595void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
596                                         SkBaseDevice* device,
597                                         int x,
598                                         int y,
599                                         const SkPaint& paint) {
600  // TODO(maruel):  http://b/1183870 Playback the EMF buffer at printer's dpi if
601  // it is a vectorial device.
602  drawSprite(draw, device->accessBitmap(false), x, y, paint);
603}
604
605bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
606  // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
607  // This function does not execute the SkPaint drawing commands. These should
608  // be executed in drawPaint().
609
610  SkPaint::Style style = paint.getStyle();
611  if (!paint.getAlpha())
612      style = (SkPaint::Style) SkPaint::kStyleCount;
613
614  switch (style) {
615    case SkPaint::kFill_Style:
616      if (!CreateBrush(true, paint) ||
617          !CreatePen(false, paint))
618        return false;
619      break;
620    case SkPaint::kStroke_Style:
621      if (!CreateBrush(false, paint) ||
622          !CreatePen(true, paint))
623        return false;
624      break;
625    case SkPaint::kStrokeAndFill_Style:
626      if (!CreateBrush(true, paint) ||
627          !CreatePen(true, paint))
628        return false;
629      break;
630    default:
631      if (!CreateBrush(false, paint) ||
632          !CreatePen(false, paint))
633        return false;
634      break;
635  }
636
637  /*
638  getFlags();
639    isAntiAlias();
640    isDither()
641    isLinearText()
642    isSubpixelText()
643    isUnderlineText()
644    isStrikeThruText()
645    isFakeBoldText()
646    isDevKernText()
647    isFilterBitmap()
648
649  // Skia's text is not used. This should be fixed.
650  getTextAlign()
651  getTextScaleX()
652  getTextSkewX()
653  getTextEncoding()
654  getFontMetrics()
655  getFontSpacing()
656  */
657
658  // BUG 1094907: Implement shaders. Shaders currently in use:
659  //  SkShader::CreateBitmapShader
660  //  SkGradientShader::CreateRadial
661  //  SkGradientShader::CreateLinear
662  // SkASSERT(!paint.getShader());
663
664  // http://b/1106647 Implement loopers and mask filter. Looper currently in
665  // use:
666  //   SkBlurDrawLooper is used for shadows.
667  // SkASSERT(!paint.getLooper());
668  // SkASSERT(!paint.getMaskFilter());
669
670  // http://b/1165900 Implement xfermode.
671  // SkASSERT(!paint.getXfermode());
672
673  // The path effect should be processed before arriving here.
674  SkASSERT(!paint.getPathEffect());
675
676  // This isn't used in the code. Verify this assumption.
677  SkASSERT(!paint.getRasterizer());
678  // Reuse code to load Win32 Fonts.
679  return true;
680}
681
682void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
683                                            const SkRegion& region,
684                                            const SkClipStack&) {
685  transform_ = transform;
686  LoadTransformToDC(hdc_, transform_);
687  clip_region_ = region;
688  if (!clip_region_.isEmpty())
689    LoadClipRegion();
690}
691
692void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
693                                                  const RECT* src_rect) {
694  SkASSERT(false);
695}
696
697void VectorPlatformDeviceEmf::LoadClipRegion() {
698  SkMatrix t;
699  t.reset();
700  LoadClippingRegionToDC(hdc_, clip_region_, t);
701}
702
703SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
704    SkBitmap::Config config, int width, int height, bool isOpaque,
705    Usage /*usage*/) {
706  SkASSERT(config == SkBitmap::kARGB_8888_Config);
707  return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL);
708}
709
710bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
711  SkASSERT(previous_brush_ == NULL);
712  // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
713  // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
714  // WHITE_BRUSH instead.
715
716  if (!use_brush) {
717    // Set the transparency.
718    if (0 == SetBkMode(hdc_, TRANSPARENT)) {
719      SkASSERT(false);
720      return false;
721    }
722
723    // Select the NULL brush.
724    previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
725    return previous_brush_ != NULL;
726  }
727
728  // Set the opacity.
729  if (0 == SetBkMode(hdc_, OPAQUE)) {
730    SkASSERT(false);
731    return false;
732  }
733
734  // Create and select the brush.
735  previous_brush_ = SelectObject(CreateSolidBrush(color));
736  return previous_brush_ != NULL;
737}
738
739bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
740                                        COLORREF color,
741                                        int stroke_width,
742                                        float stroke_miter,
743                                        DWORD pen_style) {
744  SkASSERT(previous_pen_ == NULL);
745  // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
746  // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
747  // instead.
748
749  // No pen case
750  if (!use_pen) {
751    previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
752    return previous_pen_ != NULL;
753  }
754
755  // Use the stock pen if the stroke width is 0.
756  if (stroke_width == 0) {
757    // Create a pen with the right color.
758    previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
759    return previous_pen_ != NULL;
760  }
761
762  // Load a custom pen.
763  LOGBRUSH brush;
764  brush.lbStyle = BS_SOLID;
765  brush.lbColor = color;
766  brush.lbHatch = 0;
767  HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
768  SkASSERT(pen != NULL);
769  previous_pen_ = SelectObject(pen);
770  if (previous_pen_ == NULL)
771    return false;
772
773  if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
774    SkASSERT(false);
775    return false;
776  }
777  return true;
778}
779
780void VectorPlatformDeviceEmf::Cleanup() {
781  if (previous_brush_) {
782    HGDIOBJ result = SelectObject(previous_brush_);
783    previous_brush_ = NULL;
784    if (result) {
785      BOOL res = DeleteObject(result);
786      SkASSERT(res != 0);
787    }
788  }
789  if (previous_pen_) {
790    HGDIOBJ result = SelectObject(previous_pen_);
791    previous_pen_ = NULL;
792    if (result) {
793      BOOL res = DeleteObject(result);
794      SkASSERT(res != 0);
795    }
796  }
797  // Remove any loaded path from the context.
798  AbortPath(hdc_);
799}
800
801HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
802  HGDIOBJ result = ::SelectObject(hdc_, object);
803  SkASSERT(result != HGDI_ERROR);
804  if (result == HGDI_ERROR)
805    return NULL;
806  return result;
807}
808
809bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
810                                          const SkPaint& paint) {
811  // Make sure that for transparent color, no brush is used.
812  if (paint.getAlpha() == 0) {
813    use_brush = false;
814  }
815
816  return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
817}
818
819bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
820  // Make sure that for transparent color, no pen is used.
821  if (paint.getAlpha() == 0) {
822    use_pen = false;
823  }
824
825  DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
826  switch (paint.getStrokeJoin()) {
827    case SkPaint::kMiter_Join:
828      // Connects path segments with a sharp join.
829      pen_style |= PS_JOIN_MITER;
830      break;
831    case SkPaint::kRound_Join:
832      // Connects path segments with a round join.
833      pen_style |= PS_JOIN_ROUND;
834      break;
835    case SkPaint::kBevel_Join:
836      // Connects path segments with a flat bevel join.
837      pen_style |= PS_JOIN_BEVEL;
838      break;
839    default:
840      SkASSERT(false);
841      break;
842  }
843  switch (paint.getStrokeCap()) {
844    case SkPaint::kButt_Cap:
845      // Begin/end contours with no extension.
846      pen_style |= PS_ENDCAP_FLAT;
847      break;
848    case SkPaint::kRound_Cap:
849      // Begin/end contours with a semi-circle extension.
850      pen_style |= PS_ENDCAP_ROUND;
851      break;
852    case SkPaint::kSquare_Cap:
853      // Begin/end contours with a half square extension.
854      pen_style |= PS_ENDCAP_SQUARE;
855      break;
856    default:
857      SkASSERT(false);
858      break;
859  }
860
861  return CreatePen(use_pen,
862                   SkColorToCOLORREF(paint.getColor()),
863                   SkScalarRound(paint.getStrokeWidth()),
864                   paint.getStrokeMiter(),
865                   pen_style);
866}
867
868void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
869                                                 int x, int y,
870                                                 const SkPaint& paint) {
871  unsigned char alpha = paint.getAlpha();
872  if (alpha == 0)
873    return;
874
875  bool is_translucent;
876  if (alpha != 255) {
877    // ApplyPaint expect an opaque color.
878    SkPaint tmp_paint(paint);
879    tmp_paint.setAlpha(255);
880    if (!ApplyPaint(tmp_paint))
881      return;
882    is_translucent = true;
883  } else {
884    if (!ApplyPaint(paint))
885      return;
886    is_translucent = false;
887  }
888  int src_size_x = bitmap.width();
889  int src_size_y = bitmap.height();
890  if (!src_size_x || !src_size_y)
891    return;
892
893  // Create a BMP v4 header that we can serialize. We use the shared "V3"
894  // fillter to fill the stardard items, then add in the "V4" stuff we want.
895  BITMAPV4HEADER bitmap_header;
896  memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER));
897  FillBitmapInfoHeader(src_size_x, src_size_y,
898                       reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
899  bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
900  bitmap_header.bV4RedMask   = 0x00ff0000;
901  bitmap_header.bV4GreenMask = 0x0000ff00;
902  bitmap_header.bV4BlueMask  = 0x000000ff;
903  bitmap_header.bV4AlphaMask = 0xff000000;
904
905  SkAutoLockPixels lock(bitmap);
906  SkASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
907  const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
908  if (pixels == NULL) {
909    SkASSERT(false);
910    return;
911  }
912
913  if (!is_translucent) {
914    int row_length = bitmap.rowBytesAsPixels();
915    // There is no quick way to determine if an image is opaque.
916    for (int y2 = 0; y2 < src_size_y; ++y2) {
917      for (int x2 = 0; x2 < src_size_x; ++x2) {
918        if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
919          is_translucent = true;
920          y2 = src_size_y;
921          break;
922        }
923      }
924    }
925  }
926
927  HDC dc = BeginPlatformPaint();
928  BITMAPINFOHEADER hdr;
929  FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
930  if (is_translucent) {
931    // The image must be loaded as a bitmap inside a device context.
932    HDC bitmap_dc = ::CreateCompatibleDC(dc);
933    void* bits = NULL;
934    HBITMAP hbitmap = ::CreateDIBSection(
935        bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
936        DIB_RGB_COLORS, &bits, NULL, 0);
937
938    // static cast to a char so we can do byte ptr arithmatic to
939    // get the offset.
940    unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
941
942    // We will copy row by row to avoid having to worry about
943    // the row strides being different.
944    const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
945    for (int row = 0; row < bitmap.height(); ++row) {
946      int dest_offset = row * dest_row_size;
947      // pixels_offset in terms of pixel count.
948      int src_offset = row * bitmap.rowBytesAsPixels();
949      memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
950    }
951    SkASSERT(hbitmap);
952    HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
953
954    // After some analysis of IE7's behavior, this is the thing to do. I was
955    // sure IE7 was doing so kind of bitmasking due to the way translucent image
956    // where renderered but after some windbg tracing, it is being done by the
957    // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
958    // for bitmasked images. The trick seems to switch the stretching mode in
959    // what the driver expects.
960    DWORD previous_mode = GetStretchBltMode(dc);
961    BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
962    SkASSERT(result);
963    // Note that this function expect premultiplied colors (!)
964    BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
965    result = GdiAlphaBlend(dc,
966                           x, y,  // Destination origin.
967                           src_size_x, src_size_y,  // Destination size.
968                           bitmap_dc,
969                           0, 0,  // Source origin.
970                           src_size_x, src_size_y,  // Source size.
971                           blend_function);
972    SkASSERT(result);
973    result = SetStretchBltMode(dc, previous_mode);
974    SkASSERT(result);
975
976    ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
977    DeleteObject(hbitmap);
978    DeleteDC(bitmap_dc);
979  } else {
980    int nCopied = StretchDIBits(dc,
981                                x, y,  // Destination origin.
982                                src_size_x, src_size_y,
983                                0, 0,  // Source origin.
984                                src_size_x, src_size_y,  // Source size.
985                                pixels,
986                                reinterpret_cast<const BITMAPINFO*>(&hdr),
987                                DIB_RGB_COLORS,
988                                SRCCOPY);
989  }
990  EndPlatformPaint();
991  Cleanup();
992}
993
994}  // namespace skia
995