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"
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"
21 #define CHECK_FOR_NODRAW_ANNOTATION(paint) \
22 do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
25 SkBaseDevice
* VectorPlatformDeviceEmf::CreateDevice(
26 int width
, int height
, bool is_opaque
, HANDLE shared_section
) {
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
,
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
);
54 static void FillBitmapInfoHeader(int width
, int height
, BITMAPINFOHEADER
* hdr
) {
55 hdr
->biSize
= sizeof(BITMAPINFOHEADER
);
57 hdr
->biHeight
= -height
; // Minus means top-down bitmap.
60 hdr
->biCompression
= BI_RGB
; // no compression
62 hdr
->biXPelsPerMeter
= 1;
63 hdr
->biYPelsPerMeter
= 1;
65 hdr
->biClrImportant
= 0;
68 SkBaseDevice
* VectorPlatformDeviceEmf::create(HDC dc
, int width
, int height
) {
71 // Link the SkBitmap to the current selected bitmap in the device context.
73 HGDIOBJ selected_bitmap
= GetCurrentObject(dc
, OBJ_BITMAP
);
74 bool succeeded
= false;
75 if (selected_bitmap
!= NULL
) {
76 BITMAP bitmap_data
= {0};
77 if (GetObject(selected_bitmap
, sizeof(BITMAP
), &bitmap_data
) ==
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
84 if (width
== bitmap_data
.bmWidth
&& height
== bitmap_data
.bmHeight
) {
85 SkImageInfo info
= SkImageInfo::MakeN32Premul(width
, height
);
86 succeeded
= bitmap
.installPixels(info
, bitmap_data
.bmBits
,
87 bitmap_data
.bmWidthBytes
);
93 bitmap
.setInfo(SkImageInfo::MakeUnknown(width
, height
));
95 return new VectorPlatformDeviceEmf(dc
, bitmap
);
98 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc
, const SkBitmap
& bitmap
)
99 : SkBitmapDevice(bitmap
),
101 previous_brush_(NULL
),
102 previous_pen_(NULL
) {
104 SetPlatformDevice(this, this);
107 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
108 SkASSERT(previous_brush_
== NULL
);
109 SkASSERT(previous_pen_
== NULL
);
112 HDC
VectorPlatformDeviceEmf::BeginPlatformPaint() {
116 void VectorPlatformDeviceEmf::drawPaint(const SkDraw
& draw
,
117 const SkPaint
& paint
) {
118 // TODO(maruel): Bypass the current transformation matrix.
122 rect
.fRight
= SkIntToScalar(width() + 1);
123 rect
.fBottom
= SkIntToScalar(height() + 1);
124 drawRect(draw
, rect
, paint
);
127 void VectorPlatformDeviceEmf::drawPoints(const SkDraw
& draw
,
128 SkCanvas::PointMode mode
,
131 const SkPaint
& paint
) {
135 if (mode
== SkCanvas::kPoints_PointMode
) {
140 SkPaint
tmp_paint(paint
);
141 tmp_paint
.setStyle(SkPaint::kStroke_Style
);
143 // Draw a path instead.
146 case SkCanvas::kLines_PointMode
:
151 for (size_t i
= 0; i
< count
/ 2; ++i
) {
152 path
.moveTo(pts
[2 * i
]);
153 path
.lineTo(pts
[2 * i
+ 1]);
156 case SkCanvas::kPolygon_PointMode
:
158 for (size_t i
= 1; i
< count
; ++i
) {
166 // Draw the calculated path.
167 drawPath(draw
, path
, tmp_paint
);
170 void VectorPlatformDeviceEmf::drawRect(const SkDraw
& draw
,
172 const SkPaint
& paint
) {
173 CHECK_FOR_NODRAW_ANNOTATION(paint
);
174 if (paint
.getPathEffect()) {
175 // Draw a path instead.
177 path_orginal
.addRect(rect
);
179 // Apply the path effect to the rect.
180 SkPath path_modified
;
181 paint
.getFillPath(path_orginal
, &path_modified
);
183 // Removes the path effect from the temporary SkPaint object.
184 SkPaint
paint_no_effet(paint
);
185 paint_no_effet
.setPathEffect(NULL
);
187 // Draw the calculated path.
188 drawPath(draw
, path_modified
, paint_no_effet
);
192 if (!ApplyPaint(paint
)) {
195 HDC dc
= BeginPlatformPaint();
196 if (!Rectangle(dc
, SkScalarRoundToInt(rect
.fLeft
),
197 SkScalarRoundToInt(rect
.fTop
),
198 SkScalarRoundToInt(rect
.fRight
),
199 SkScalarRoundToInt(rect
.fBottom
))) {
206 void VectorPlatformDeviceEmf::drawRRect(const SkDraw
& draw
, const SkRRect
& rr
,
207 const SkPaint
& paint
) {
210 this->drawPath(draw
, path
, paint
, NULL
, true);
213 void VectorPlatformDeviceEmf::drawPath(const SkDraw
& draw
,
215 const SkPaint
& paint
,
216 const SkMatrix
* prePathMatrix
,
217 bool pathIsMutable
) {
218 CHECK_FOR_NODRAW_ANNOTATION(paint
);
219 if (paint
.getPathEffect()) {
220 // Apply the path effect forehand.
221 SkPath path_modified
;
222 paint
.getFillPath(path
, &path_modified
);
224 // Removes the path effect from the temporary SkPaint object.
225 SkPaint
paint_no_effet(paint
);
226 paint_no_effet
.setPathEffect(NULL
);
228 // Draw the calculated path.
229 drawPath(draw
, path_modified
, paint_no_effet
);
233 if (!ApplyPaint(paint
)) {
236 HDC dc
= BeginPlatformPaint();
237 if (PlatformDevice::LoadPathToDC(dc
, path
)) {
238 switch (paint
.getStyle()) {
239 case SkPaint::kFill_Style
: {
240 BOOL res
= StrokeAndFillPath(dc
);
244 case SkPaint::kStroke_Style
: {
245 BOOL res
= StrokePath(dc
);
249 case SkPaint::kStrokeAndFill_Style
: {
250 BOOL res
= StrokeAndFillPath(dc
);
263 void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw
& draw
,
264 const SkBitmap
& bitmap
,
267 const SkPaint
& paint
,
268 SkCanvas::DrawBitmapRectFlags flags
) {
270 SkRect bitmapBounds
, tmpSrc
, tmpDst
;
273 bitmapBounds
.isetWH(bitmap
.width(), bitmap
.height());
275 // Compute matrix from the two rectangles
279 tmpSrc
= bitmapBounds
;
281 matrix
.setRectToRect(tmpSrc
, dst
, SkMatrix::kFill_ScaleToFit
);
283 const SkBitmap
* bitmapPtr
= &bitmap
;
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.
288 if (!bitmapBounds
.contains(*src
)) {
289 if (!tmpSrc
.intersect(bitmapBounds
)) {
290 return; // nothing to draw
292 // recompute dst, based on the smaller tmpSrc
293 matrix
.mapRect(&tmpDst
, tmpSrc
);
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.
300 tmpSrc
.roundOut(&srcIR
);
301 if (!bitmap
.extractSubset(&tmpBitmap
, srcIR
)) {
304 bitmapPtr
= &tmpBitmap
;
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
);
311 if (srcIR
.fTop
> 0) {
312 dy
= SkIntToScalar(srcIR
.fTop
);
315 matrix
.preTranslate(dx
, dy
);
318 this->drawBitmap(draw
, *bitmapPtr
, matrix
, paint
);
321 void VectorPlatformDeviceEmf::drawBitmap(const SkDraw
& draw
,
322 const SkBitmap
& bitmap
,
323 const SkMatrix
& matrix
,
324 const SkPaint
& paint
) {
325 // Load the temporary matrix. This is what will translate, rotate and resize
327 SkMatrix
actual_transform(transform_
);
328 actual_transform
.preConcat(matrix
);
329 LoadTransformToDC(hdc_
, actual_transform
);
331 InternalDrawBitmap(bitmap
, 0, 0, paint
);
333 // Restore the original matrix.
334 LoadTransformToDC(hdc_
, transform_
);
337 void VectorPlatformDeviceEmf::drawSprite(const SkDraw
& draw
,
338 const SkBitmap
& bitmap
,
340 const SkPaint
& paint
) {
343 LoadTransformToDC(hdc_
, identity
);
345 InternalDrawBitmap(bitmap
, x
, y
, paint
);
347 // Restore the original matrix.
348 LoadTransformToDC(hdc_
, transform_
);
351 /////////////////////////////////////////////////////////////////////////
353 static bool gdiCanHandleText(const SkPaint
& paint
) {
354 return !paint
.getShader() &&
355 !paint
.getPathEffect() &&
356 (SkPaint::kFill_Style
== paint
.getStyle()) &&
357 (255 == paint
.getAlpha());
360 class SkGDIFontSetup
{
368 SkDEBUGCODE(fUseGDIHasBeenCalled
= false;)
372 // can only be called once
373 bool useGDI(HDC hdc
, const SkPaint
&);
379 COLORREF fSavedTextColor
;
381 SkDEBUGCODE(bool fUseGDIHasBeenCalled
;)
384 bool SkGDIFontSetup::useGDI(HDC hdc
, const SkPaint
& paint
) {
385 SkASSERT(!fUseGDIHasBeenCalled
);
386 SkDEBUGCODE(fUseGDIHasBeenCalled
= true;)
388 fUseGDI
= gdiCanHandleText(paint
);
390 fSavedTextColor
= GetTextColor(hdc
);
391 SetTextColor(hdc
, skia::SkColorToCOLORREF(paint
.getColor()));
394 SkLOGFONTFromTypeface(paint
.getTypeface(), &lf
);
395 lf
.lfHeight
= -SkScalarRoundToInt(paint
.getTextSize());
396 fNewFont
= CreateFontIndirect(&lf
);
397 fSavedFont
= (HFONT
)::SelectObject(hdc
, fNewFont
);
403 SkGDIFontSetup::~SkGDIFontSetup() {
405 ::SelectObject(fHDC
, fSavedFont
);
406 ::DeleteObject(fNewFont
);
407 SetTextColor(fHDC
, fSavedTextColor
);
411 static SkScalar
getAscent(const SkPaint
& paint
) {
412 SkPaint::FontMetrics fm
;
413 paint
.getFontMetrics(&fm
);
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).
419 static UINT
getTextOutOptions(const SkPaint
& paint
) {
420 if (SkPaint::kGlyphID_TextEncoding
== paint
.getTextEncoding()) {
421 return ETO_GLYPH_INDEX
;
423 SkASSERT(SkPaint::kUTF16_TextEncoding
== paint
.getTextEncoding());
428 static SkiaEnsureTypefaceCharactersAccessible
429 g_skia_ensure_typeface_characters_accessible
= NULL
;
431 SK_API
void SetSkiaEnsureTypefaceCharactersAccessible(
432 SkiaEnsureTypefaceCharactersAccessible func
) {
433 // This function is supposed to be called once in process life time.
434 SkASSERT(g_skia_ensure_typeface_characters_accessible
== NULL
);
435 g_skia_ensure_typeface_characters_accessible
= func
;
438 void EnsureTypefaceCharactersAccessible(
439 const SkTypeface
& typeface
, const wchar_t* text
, unsigned int text_length
) {
441 SkLOGFONTFromTypeface(&typeface
, &lf
);
442 g_skia_ensure_typeface_characters_accessible(lf
, text
, text_length
);
445 bool EnsureExtTextOut(HDC hdc
, int x
, int y
, UINT options
, const RECT
* lprect
,
446 LPCWSTR text
, unsigned int characters
, const int * lpDx
,
447 SkTypeface
* const typeface
) {
448 bool success
= ExtTextOut(hdc
, x
, y
, options
, lprect
, text
, characters
, lpDx
);
451 EnsureTypefaceCharactersAccessible(*typeface
,
454 success
= ExtTextOut(hdc
, x
, y
, options
, lprect
, text
, characters
, lpDx
);
457 SkLOGFONTFromTypeface(typeface
, &lf
);
458 VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
459 << " FaceName = " << lf
.lfFaceName
460 << " and characters: " << base::string16(text
, characters
);
463 VLOG(1) << "ExtTextOut FAILED for default FaceName "
464 << " and characters: " << base::string16(text
, characters
);
470 void VectorPlatformDeviceEmf::drawText(const SkDraw
& draw
,
475 const SkPaint
& paint
) {
476 SkGDIFontSetup setup
;
477 bool useDrawPath
= true;
479 if (SkPaint::kUTF8_TextEncoding
!= paint
.getTextEncoding()
480 && setup
.useGDI(hdc_
, paint
)) {
481 UINT options
= getTextOutOptions(paint
);
482 UINT count
= byteLength
>> 1;
483 useDrawPath
= !EnsureExtTextOut(hdc_
, SkScalarRoundToInt(x
),
484 SkScalarRoundToInt(y
+ getAscent(paint
)), options
, 0,
485 reinterpret_cast<const wchar_t*>(text
), count
, NULL
,
486 paint
.getTypeface());
491 paint
.getTextPath(text
, byteLength
, x
, y
, &path
);
492 drawPath(draw
, path
, paint
);
496 static size_t size_utf8(const char* text
) {
497 return SkUTF8_CountUTF8Bytes(text
);
500 static size_t size_utf16(const char* text
) {
501 uint16_t c
= *reinterpret_cast<const uint16_t*>(text
);
502 return SkUTF16_IsHighSurrogate(c
) ? 4 : 2;
505 static size_t size_glyphid(const char* text
) {
509 void VectorPlatformDeviceEmf::drawPosText(const SkDraw
& draw
,
512 const SkScalar pos
[],
515 const SkPaint
& paint
) {
516 SkGDIFontSetup setup
;
517 bool useDrawText
= true;
519 if (scalarsPerPos
== 2 && len
>= 2 &&
520 SkPaint::kUTF8_TextEncoding
!= paint
.getTextEncoding() &&
521 setup
.useGDI(hdc_
, paint
)) {
522 int startX
= SkScalarRoundToInt(pos
[0]);
523 int startY
= SkScalarRoundToInt(pos
[1] + getAscent(paint
));
524 const int count
= len
>> 1;
525 SkAutoSTMalloc
<64, INT
> storage(count
);
526 INT
* advances
= storage
.get();
527 for (int i
= 0; i
< count
- 1; ++i
) {
528 advances
[i
] = SkScalarRoundToInt(pos
[2] - pos
[0]);
531 advances
[count
- 1] = 0;
532 useDrawText
= !EnsureExtTextOut(hdc_
, startX
, startY
,
533 getTextOutOptions(paint
), 0, reinterpret_cast<const wchar_t*>(text
),
534 count
, advances
, paint
.getTypeface());
538 size_t (*bytesPerCodePoint
)(const char*);
539 switch (paint
.getTextEncoding()) {
540 case SkPaint::kUTF8_TextEncoding
:
541 bytesPerCodePoint
= size_utf8
;
543 case SkPaint::kUTF16_TextEncoding
:
544 bytesPerCodePoint
= size_utf16
;
547 SkASSERT(SkPaint::kGlyphID_TextEncoding
== paint
.getTextEncoding());
548 bytesPerCodePoint
= size_glyphid
;
552 const char* curr
= reinterpret_cast<const char*>(text
);
553 const char* stop
= curr
+ len
;
554 while (curr
< stop
) {
555 SkScalar y
= (1 == scalarsPerPos
) ? constY
: pos
[1];
556 size_t bytes
= bytesPerCodePoint(curr
);
557 drawText(draw
, curr
, bytes
, pos
[0], y
, paint
);
559 pos
+= scalarsPerPos
;
564 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw
& draw
,
568 const SkMatrix
* matrix
,
569 const SkPaint
& paint
) {
570 // This function isn't used in the code. Verify this assumption.
574 void VectorPlatformDeviceEmf::drawVertices(const SkDraw
& draw
,
575 SkCanvas::VertexMode vmode
,
577 const SkPoint vertices
[],
578 const SkPoint texs
[],
579 const SkColor colors
[],
581 const uint16_t indices
[],
583 const SkPaint
& paint
) {
584 // This function isn't used in the code. Verify this assumption.
588 void VectorPlatformDeviceEmf::drawDevice(const SkDraw
& draw
,
589 SkBaseDevice
* device
,
592 const SkPaint
& paint
) {
593 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
594 // it is a vectorial device.
595 drawSprite(draw
, device
->accessBitmap(false), x
, y
, paint
);
598 bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint
& paint
) {
599 // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
600 // This function does not execute the SkPaint drawing commands. These should
601 // be executed in drawPaint().
603 SkPaint::Style style
= paint
.getStyle();
604 if (!paint
.getAlpha())
605 style
= (SkPaint::Style
) SkPaint::kStyleCount
;
608 case SkPaint::kFill_Style
:
609 if (!CreateBrush(true, paint
) ||
610 !CreatePen(false, paint
))
613 case SkPaint::kStroke_Style
:
614 if (!CreateBrush(false, paint
) ||
615 !CreatePen(true, paint
))
618 case SkPaint::kStrokeAndFill_Style
:
619 if (!CreateBrush(true, paint
) ||
620 !CreatePen(true, paint
))
624 if (!CreateBrush(false, paint
) ||
625 !CreatePen(false, paint
))
642 // Skia's text is not used. This should be fixed.
651 // BUG 1094907: Implement shaders. Shaders currently in use:
652 // SkShader::CreateBitmapShader
653 // SkGradientShader::CreateRadial
654 // SkGradientShader::CreateLinear
655 // SkASSERT(!paint.getShader());
657 // http://b/1106647 Implement loopers and mask filter. Looper currently in
659 // SkBlurDrawLooper is used for shadows.
660 // SkASSERT(!paint.getLooper());
661 // SkASSERT(!paint.getMaskFilter());
663 // http://b/1165900 Implement xfermode.
664 // SkASSERT(!paint.getXfermode());
666 // The path effect should be processed before arriving here.
667 SkASSERT(!paint
.getPathEffect());
669 // This isn't used in the code. Verify this assumption.
670 SkASSERT(!paint
.getRasterizer());
671 // Reuse code to load Win32 Fonts.
675 void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix
& transform
,
676 const SkRegion
& region
,
677 const SkClipStack
&) {
678 transform_
= transform
;
679 LoadTransformToDC(hdc_
, transform_
);
680 clip_region_
= region
;
681 if (!clip_region_
.isEmpty())
685 void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc
, int x
, int y
,
686 const RECT
* src_rect
) {
690 void VectorPlatformDeviceEmf::LoadClipRegion() {
693 LoadClippingRegionToDC(hdc_
, clip_region_
, t
);
696 SkBaseDevice
* VectorPlatformDeviceEmf::onCreateDevice(const SkImageInfo
& info
,
698 SkASSERT(info
.colorType() == kN32_SkColorType
);
699 return VectorPlatformDeviceEmf::CreateDevice(
700 info
.width(), info
.height(), info
.isOpaque(), NULL
);
703 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush
, COLORREF color
) {
704 SkASSERT(previous_brush_
== NULL
);
705 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
706 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
707 // WHITE_BRUSH instead.
710 // Set the transparency.
711 if (0 == SetBkMode(hdc_
, TRANSPARENT
)) {
716 // Select the NULL brush.
717 previous_brush_
= SelectObject(GetStockObject(NULL_BRUSH
));
718 return previous_brush_
!= NULL
;
722 if (0 == SetBkMode(hdc_
, OPAQUE
)) {
727 // Create and select the brush.
728 previous_brush_
= SelectObject(CreateSolidBrush(color
));
729 return previous_brush_
!= NULL
;
732 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen
,
737 SkASSERT(previous_pen_
== NULL
);
738 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
739 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
744 previous_pen_
= SelectObject(GetStockObject(NULL_PEN
));
745 return previous_pen_
!= NULL
;
748 // Use the stock pen if the stroke width is 0.
749 if (stroke_width
== 0) {
750 // Create a pen with the right color.
751 previous_pen_
= SelectObject(::CreatePen(PS_SOLID
, 0, color
));
752 return previous_pen_
!= NULL
;
755 // Load a custom pen.
756 LOGBRUSH brush
= {0};
757 brush
.lbStyle
= BS_SOLID
;
758 brush
.lbColor
= color
;
760 HPEN pen
= ExtCreatePen(pen_style
, stroke_width
, &brush
, 0, NULL
);
761 SkASSERT(pen
!= NULL
);
762 previous_pen_
= SelectObject(pen
);
763 if (previous_pen_
== NULL
)
766 if (!SetMiterLimit(hdc_
, stroke_miter
, NULL
)) {
773 void VectorPlatformDeviceEmf::Cleanup() {
774 if (previous_brush_
) {
775 HGDIOBJ result
= SelectObject(previous_brush_
);
776 previous_brush_
= NULL
;
778 BOOL res
= DeleteObject(result
);
783 HGDIOBJ result
= SelectObject(previous_pen_
);
784 previous_pen_
= NULL
;
786 BOOL res
= DeleteObject(result
);
790 // Remove any loaded path from the context.
794 HGDIOBJ
VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object
) {
795 HGDIOBJ result
= ::SelectObject(hdc_
, object
);
796 SkASSERT(result
!= HGDI_ERROR
);
797 if (result
== HGDI_ERROR
)
802 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush
,
803 const SkPaint
& paint
) {
804 // Make sure that for transparent color, no brush is used.
805 if (paint
.getAlpha() == 0) {
809 return CreateBrush(use_brush
, SkColorToCOLORREF(paint
.getColor()));
812 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen
, const SkPaint
& paint
) {
813 // Make sure that for transparent color, no pen is used.
814 if (paint
.getAlpha() == 0) {
818 DWORD pen_style
= PS_GEOMETRIC
| PS_SOLID
;
819 switch (paint
.getStrokeJoin()) {
820 case SkPaint::kMiter_Join
:
821 // Connects path segments with a sharp join.
822 pen_style
|= PS_JOIN_MITER
;
824 case SkPaint::kRound_Join
:
825 // Connects path segments with a round join.
826 pen_style
|= PS_JOIN_ROUND
;
828 case SkPaint::kBevel_Join
:
829 // Connects path segments with a flat bevel join.
830 pen_style
|= PS_JOIN_BEVEL
;
836 switch (paint
.getStrokeCap()) {
837 case SkPaint::kButt_Cap
:
838 // Begin/end contours with no extension.
839 pen_style
|= PS_ENDCAP_FLAT
;
841 case SkPaint::kRound_Cap
:
842 // Begin/end contours with a semi-circle extension.
843 pen_style
|= PS_ENDCAP_ROUND
;
845 case SkPaint::kSquare_Cap
:
846 // Begin/end contours with a half square extension.
847 pen_style
|= PS_ENDCAP_SQUARE
;
854 return CreatePen(use_pen
,
855 SkColorToCOLORREF(paint
.getColor()),
856 SkScalarRoundToInt(paint
.getStrokeWidth()),
857 paint
.getStrokeMiter(),
861 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap
& bitmap
,
863 const SkPaint
& paint
) {
864 unsigned char alpha
= paint
.getAlpha();
870 // ApplyPaint expect an opaque color.
871 SkPaint
tmp_paint(paint
);
872 tmp_paint
.setAlpha(255);
873 if (!ApplyPaint(tmp_paint
))
875 is_translucent
= true;
877 if (!ApplyPaint(paint
))
879 is_translucent
= false;
881 int src_size_x
= bitmap
.width();
882 int src_size_y
= bitmap
.height();
883 if (!src_size_x
|| !src_size_y
)
886 // Create a BMP v4 header that we can serialize. We use the shared "V3"
887 // fillter to fill the stardard items, then add in the "V4" stuff we want.
888 BITMAPV4HEADER bitmap_header
= {0};
889 FillBitmapInfoHeader(src_size_x
, src_size_y
,
890 reinterpret_cast<BITMAPINFOHEADER
*>(&bitmap_header
));
891 bitmap_header
.bV4Size
= sizeof(BITMAPV4HEADER
);
892 bitmap_header
.bV4RedMask
= 0x00ff0000;
893 bitmap_header
.bV4GreenMask
= 0x0000ff00;
894 bitmap_header
.bV4BlueMask
= 0x000000ff;
895 bitmap_header
.bV4AlphaMask
= 0xff000000;
897 SkAutoLockPixels
lock(bitmap
);
898 SkASSERT(bitmap
.colorType() == kN32_SkColorType
);
899 const uint32_t* pixels
= static_cast<const uint32_t*>(bitmap
.getPixels());
900 if (pixels
== NULL
) {
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;
919 HDC dc
= BeginPlatformPaint();
920 BITMAPINFOHEADER hdr
= {0};
921 FillBitmapInfoHeader(src_size_x
, src_size_y
, &hdr
);
922 if (is_translucent
) {
923 // The image must be loaded as a bitmap inside a device context.
924 HDC bitmap_dc
= ::CreateCompatibleDC(dc
);
926 HBITMAP hbitmap
= ::CreateDIBSection(
927 bitmap_dc
, reinterpret_cast<const BITMAPINFO
*>(&hdr
),
928 DIB_RGB_COLORS
, &bits
, NULL
, 0);
930 // static cast to a char so we can do byte ptr arithmatic to
932 unsigned char* dest_buffer
= static_cast<unsigned char *>(bits
);
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
);
944 HGDIOBJ old_bitmap
= ::SelectObject(bitmap_dc
, hbitmap
);
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
);
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.
961 0, 0, // Source origin.
962 src_size_x
, src_size_y
, // Source size.
965 result
= SetStretchBltMode(dc
, previous_mode
);
968 ::SelectObject(bitmap_dc
, static_cast<HBITMAP
>(old_bitmap
));
969 DeleteObject(hbitmap
);
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.
978 reinterpret_cast<const BITMAPINFO
*>(&hdr
),