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/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"
20 #define CHECK_FOR_NODRAW_ANNOTATION(paint) \
21 do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
24 SkBaseDevice
* VectorPlatformDeviceEmf::CreateDevice(
25 int width
, int height
, bool is_opaque
, HANDLE shared_section
) {
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
,
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
);
53 static void FillBitmapInfoHeader(int width
, int height
, BITMAPINFOHEADER
* hdr
) {
54 hdr
->biSize
= sizeof(BITMAPINFOHEADER
);
56 hdr
->biHeight
= -height
; // Minus means top-down bitmap.
59 hdr
->biCompression
= BI_RGB
; // no compression
61 hdr
->biXPelsPerMeter
= 1;
62 hdr
->biYPelsPerMeter
= 1;
64 hdr
->biClrImportant
= 0;
67 SkBaseDevice
* VectorPlatformDeviceEmf::create(HDC dc
, int width
, int height
) {
70 // Link the SkBitmap to the current selected bitmap in the device context.
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
) ==
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
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
);
92 bitmap
.setInfo(SkImageInfo::MakeUnknown(width
, height
));
94 return new VectorPlatformDeviceEmf(dc
, bitmap
);
97 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc
, const SkBitmap
& bitmap
)
98 : SkBitmapDevice(bitmap
),
100 previous_brush_(NULL
),
101 previous_pen_(NULL
) {
103 SetPlatformDevice(this, this);
106 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
107 SkASSERT(previous_brush_
== NULL
);
108 SkASSERT(previous_pen_
== NULL
);
111 HDC
VectorPlatformDeviceEmf::BeginPlatformPaint() {
115 void VectorPlatformDeviceEmf::drawPaint(const SkDraw
& draw
,
116 const SkPaint
& paint
) {
117 // TODO(maruel): Bypass the current transformation matrix.
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
,
130 const SkPaint
& paint
) {
134 if (mode
== SkCanvas::kPoints_PointMode
) {
139 SkPaint
tmp_paint(paint
);
140 tmp_paint
.setStyle(SkPaint::kStroke_Style
);
142 // Draw a path instead.
145 case SkCanvas::kLines_PointMode
:
150 for (size_t i
= 0; i
< count
/ 2; ++i
) {
151 path
.moveTo(pts
[2 * i
]);
152 path
.lineTo(pts
[2 * i
+ 1]);
155 case SkCanvas::kPolygon_PointMode
:
157 for (size_t i
= 1; i
< count
; ++i
) {
165 // Draw the calculated path.
166 drawPath(draw
, path
, tmp_paint
);
169 void VectorPlatformDeviceEmf::drawRect(const SkDraw
& draw
,
171 const SkPaint
& paint
) {
172 CHECK_FOR_NODRAW_ANNOTATION(paint
);
173 if (paint
.getPathEffect()) {
174 // Draw a path instead.
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
);
191 if (!ApplyPaint(paint
)) {
194 HDC dc
= BeginPlatformPaint();
195 if (!Rectangle(dc
, SkScalarRoundToInt(rect
.fLeft
),
196 SkScalarRoundToInt(rect
.fTop
),
197 SkScalarRoundToInt(rect
.fRight
),
198 SkScalarRoundToInt(rect
.fBottom
))) {
205 void VectorPlatformDeviceEmf::drawRRect(const SkDraw
& draw
, const SkRRect
& rr
,
206 const SkPaint
& paint
) {
209 this->drawPath(draw
, path
, paint
, NULL
, true);
212 void VectorPlatformDeviceEmf::drawPath(const SkDraw
& draw
,
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
);
232 if (!ApplyPaint(paint
)) {
235 HDC dc
= BeginPlatformPaint();
236 if (PlatformDevice::LoadPathToDC(dc
, path
)) {
237 switch (paint
.getStyle()) {
238 case SkPaint::kFill_Style
: {
239 BOOL res
= StrokeAndFillPath(dc
);
243 case SkPaint::kStroke_Style
: {
244 BOOL res
= StrokePath(dc
);
248 case SkPaint::kStrokeAndFill_Style
: {
249 BOOL res
= StrokeAndFillPath(dc
);
262 void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw
& draw
,
263 const SkBitmap
& bitmap
,
266 const SkPaint
& paint
,
267 SkCanvas::DrawBitmapRectFlags flags
) {
269 SkRect bitmapBounds
, tmpSrc
, tmpDst
;
272 bitmapBounds
.isetWH(bitmap
.width(), bitmap
.height());
274 // Compute matrix from the two rectangles
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.
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.
299 tmpSrc
.roundOut(&srcIR
);
300 if (!bitmap
.extractSubset(&tmpBitmap
, srcIR
)) {
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
);
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
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
,
339 const SkPaint
& paint
) {
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
{
367 SkDEBUGCODE(fUseGDIHasBeenCalled
= false;)
371 // can only be called once
372 bool useGDI(HDC hdc
, const SkPaint
&);
378 COLORREF fSavedTextColor
;
380 SkDEBUGCODE(bool fUseGDIHasBeenCalled
;)
383 bool SkGDIFontSetup::useGDI(HDC hdc
, const SkPaint
& paint
) {
384 SkASSERT(!fUseGDIHasBeenCalled
);
385 SkDEBUGCODE(fUseGDIHasBeenCalled
= true;)
387 fUseGDI
= gdiCanHandleText(paint
);
389 fSavedTextColor
= GetTextColor(hdc
);
390 SetTextColor(hdc
, skia::SkColorToCOLORREF(paint
.getColor()));
393 SkLOGFONTFromTypeface(paint
.getTypeface(), &lf
);
394 lf
.lfHeight
= -SkScalarRoundToInt(paint
.getTextSize());
395 fNewFont
= CreateFontIndirect(&lf
);
396 fSavedFont
= (HFONT
)::SelectObject(hdc
, fNewFont
);
402 SkGDIFontSetup::~SkGDIFontSetup() {
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
);
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
;
422 SkASSERT(SkPaint::kUTF16_TextEncoding
== paint
.getTextEncoding());
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
) {
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
);
450 EnsureTypefaceCharactersAccessible(*typeface
,
453 success
= ExtTextOut(hdc
, x
, y
, options
, lprect
, text
, characters
, lpDx
);
456 SkLOGFONTFromTypeface(typeface
, &lf
);
457 VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
458 << " FaceName = " << lf
.lfFaceName
459 << " and characters: " << base::string16(text
, characters
);
462 VLOG(1) << "ExtTextOut FAILED for default FaceName "
463 << " and characters: " << base::string16(text
, characters
);
469 void VectorPlatformDeviceEmf::drawText(const SkDraw
& draw
,
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());
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
) {
508 void VectorPlatformDeviceEmf::drawPosText(const SkDraw
& draw
,
511 const SkScalar pos
[],
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]);
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());
537 size_t (*bytesPerCodePoint
)(const char*);
538 switch (paint
.getTextEncoding()) {
539 case SkPaint::kUTF8_TextEncoding
:
540 bytesPerCodePoint
= size_utf8
;
542 case SkPaint::kUTF16_TextEncoding
:
543 bytesPerCodePoint
= size_utf16
;
546 SkASSERT(SkPaint::kGlyphID_TextEncoding
== paint
.getTextEncoding());
547 bytesPerCodePoint
= size_glyphid
;
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
);
560 pos
+= scalarsPerPos
;
565 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw
& draw
,
569 const SkMatrix
* matrix
,
570 const SkPaint
& paint
) {
571 // This function isn't used in the code. Verify this assumption.
575 void VectorPlatformDeviceEmf::drawVertices(const SkDraw
& draw
,
576 SkCanvas::VertexMode vmode
,
578 const SkPoint vertices
[],
579 const SkPoint texs
[],
580 const SkColor colors
[],
582 const uint16_t indices
[],
584 const SkPaint
& paint
) {
585 // This function isn't used in the code. Verify this assumption.
589 void VectorPlatformDeviceEmf::drawDevice(const SkDraw
& draw
,
590 SkBaseDevice
* device
,
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
;
609 case SkPaint::kFill_Style
:
610 if (!CreateBrush(true, paint
) ||
611 !CreatePen(false, paint
))
614 case SkPaint::kStroke_Style
:
615 if (!CreateBrush(false, paint
) ||
616 !CreatePen(true, paint
))
619 case SkPaint::kStrokeAndFill_Style
:
620 if (!CreateBrush(true, paint
) ||
621 !CreatePen(true, paint
))
625 if (!CreateBrush(false, paint
) ||
626 !CreatePen(false, paint
))
643 // Skia's text is not used. This should be fixed.
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
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.
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())
686 void VectorPlatformDeviceEmf::LoadClipRegion() {
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.
706 // Set the transparency.
707 if (0 == SetBkMode(hdc_
, TRANSPARENT
)) {
712 // Select the NULL brush.
713 previous_brush_
= SelectObject(GetStockObject(NULL_BRUSH
));
714 return previous_brush_
!= NULL
;
718 if (0 == SetBkMode(hdc_
, OPAQUE
)) {
723 // Create and select the brush.
724 previous_brush_
= SelectObject(CreateSolidBrush(color
));
725 return previous_brush_
!= NULL
;
728 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen
,
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
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
;
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
)
762 if (!SetMiterLimit(hdc_
, stroke_miter
, NULL
)) {
769 void VectorPlatformDeviceEmf::Cleanup() {
770 if (previous_brush_
) {
771 HGDIOBJ result
= SelectObject(previous_brush_
);
772 previous_brush_
= NULL
;
774 BOOL res
= DeleteObject(result
);
779 HGDIOBJ result
= SelectObject(previous_pen_
);
780 previous_pen_
= NULL
;
782 BOOL res
= DeleteObject(result
);
786 // Remove any loaded path from the context.
790 HGDIOBJ
VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object
) {
791 HGDIOBJ result
= ::SelectObject(hdc_
, object
);
792 SkASSERT(result
!= HGDI_ERROR
);
793 if (result
== HGDI_ERROR
)
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) {
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) {
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
;
820 case SkPaint::kRound_Join
:
821 // Connects path segments with a round join.
822 pen_style
|= PS_JOIN_ROUND
;
824 case SkPaint::kBevel_Join
:
825 // Connects path segments with a flat bevel join.
826 pen_style
|= PS_JOIN_BEVEL
;
832 switch (paint
.getStrokeCap()) {
833 case SkPaint::kButt_Cap
:
834 // Begin/end contours with no extension.
835 pen_style
|= PS_ENDCAP_FLAT
;
837 case SkPaint::kRound_Cap
:
838 // Begin/end contours with a semi-circle extension.
839 pen_style
|= PS_ENDCAP_ROUND
;
841 case SkPaint::kSquare_Cap
:
842 // Begin/end contours with a half square extension.
843 pen_style
|= PS_ENDCAP_SQUARE
;
850 return CreatePen(use_pen
,
851 SkColorToCOLORREF(paint
.getColor()),
852 SkScalarRoundToInt(paint
.getStrokeWidth()),
853 paint
.getStrokeMiter(),
857 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap
& bitmap
,
859 const SkPaint
& paint
) {
860 unsigned char alpha
= paint
.getAlpha();
866 // ApplyPaint expect an opaque color.
867 SkPaint
tmp_paint(paint
);
868 tmp_paint
.setAlpha(255);
869 if (!ApplyPaint(tmp_paint
))
871 is_translucent
= true;
873 if (!ApplyPaint(paint
))
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
)
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
) {
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;
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
);
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
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
);
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
);
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.
957 0, 0, // Source origin.
958 src_size_x
, src_size_y
, // Source size.
961 result
= SetStretchBltMode(dc
, previous_mode
);
964 ::SelectObject(bitmap_dc
, static_cast<HBITMAP
>(old_bitmap
));
965 DeleteObject(hbitmap
);
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.
974 reinterpret_cast<const BITMAPINFO
*>(&hdr
),