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