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.
7 #include "skia/ext/vector_platform_device_emf_win.h"
9 #include "skia/ext/bitmap_platform_device.h"
10 #include "skia/ext/skia_utils_win.h"
11 #include "third_party/skia/include/core/SkTemplates.h"
12 #include "third_party/skia/include/core/SkUtils.h"
13 #include "third_party/skia/include/ports/SkTypeface_win.h"
18 SkDevice
* VectorPlatformDeviceEmf::CreateDevice(
19 int width
, int height
, bool is_opaque
, HANDLE shared_section
) {
21 // TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent
22 // layer, i.e. merging it, we need to rasterize it because GDI doesn't
23 // support transparency except for AlphaBlend(). Right now, a
24 // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
25 // call is being done. The way to save a layer would be to create an
26 // EMF-based VectorDevice and have this device registers the drawing. When
27 // playing back the device into a bitmap, do it at the printer's dpi instead
28 // of the layout's dpi (which is much lower).
29 return BitmapPlatformDevice::create(width
, height
, is_opaque
,
33 // TODO(maruel): http://crbug.com/18383 Look if it would be worth to
34 // increase the resolution by ~10x (any worthy factor) to increase the
35 // rendering precision (think about printing) while using a relatively
36 // low dpi. This happens because we receive float as input but the GDI
37 // functions works with integers. The idea is to premultiply the matrix
38 // with this factor and multiply each SkScalar that are passed to
39 // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
40 // doing the same for text rendering.
41 SkASSERT(shared_section
);
42 SkDevice
* device
= VectorPlatformDeviceEmf::create(
43 reinterpret_cast<HDC
>(shared_section
), width
, height
);
47 static void FillBitmapInfoHeader(int width
, int height
, BITMAPINFOHEADER
* hdr
) {
48 hdr
->biSize
= sizeof(BITMAPINFOHEADER
);
50 hdr
->biHeight
= -height
; // Minus means top-down bitmap.
53 hdr
->biCompression
= BI_RGB
; // no compression
55 hdr
->biXPelsPerMeter
= 1;
56 hdr
->biYPelsPerMeter
= 1;
58 hdr
->biClrImportant
= 0;
61 SkDevice
* VectorPlatformDeviceEmf::create(HDC dc
, int width
, int height
) {
64 // Link the SkBitmap to the current selected bitmap in the device context.
66 HGDIOBJ selected_bitmap
= GetCurrentObject(dc
, OBJ_BITMAP
);
67 bool succeeded
= false;
68 if (selected_bitmap
!= NULL
) {
70 if (GetObject(selected_bitmap
, sizeof(BITMAP
), &bitmap_data
) ==
72 // The context has a bitmap attached. Attach our SkBitmap to it.
73 // Warning: If the bitmap gets unselected from the HDC,
74 // VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP
75 // could be released while SkBitmap still has a reference to it. Be
77 if (width
== bitmap_data
.bmWidth
&&
78 height
== bitmap_data
.bmHeight
) {
79 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
,
82 bitmap_data
.bmWidthBytes
);
83 bitmap
.setPixels(bitmap_data
.bmBits
);
90 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
, width
, height
);
92 return new VectorPlatformDeviceEmf(dc
, bitmap
);
95 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc
, const SkBitmap
& bitmap
)
98 previous_brush_(NULL
),
100 alpha_blend_used_(false) {
102 SetPlatformDevice(this, this);
105 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
106 SkASSERT(previous_brush_
== NULL
);
107 SkASSERT(previous_pen_
== NULL
);
110 HDC
VectorPlatformDeviceEmf::BeginPlatformPaint() {
114 uint32_t VectorPlatformDeviceEmf::getDeviceCapabilities() {
115 return SkDevice::getDeviceCapabilities() | kVector_Capability
;
118 void VectorPlatformDeviceEmf::drawPaint(const SkDraw
& draw
,
119 const SkPaint
& paint
) {
120 // TODO(maruel): Bypass the current transformation matrix.
124 rect
.fRight
= SkIntToScalar(width() + 1);
125 rect
.fBottom
= SkIntToScalar(height() + 1);
126 drawRect(draw
, rect
, paint
);
129 void VectorPlatformDeviceEmf::drawPoints(const SkDraw
& draw
,
130 SkCanvas::PointMode mode
,
133 const SkPaint
& paint
) {
137 if (mode
== SkCanvas::kPoints_PointMode
) {
142 SkPaint
tmp_paint(paint
);
143 tmp_paint
.setStyle(SkPaint::kStroke_Style
);
145 // Draw a path instead.
148 case SkCanvas::kLines_PointMode
:
153 for (size_t i
= 0; i
< count
/ 2; ++i
) {
154 path
.moveTo(pts
[2 * i
]);
155 path
.lineTo(pts
[2 * i
+ 1]);
158 case SkCanvas::kPolygon_PointMode
:
160 for (size_t i
= 1; i
< count
; ++i
) {
168 // Draw the calculated path.
169 drawPath(draw
, path
, tmp_paint
);
172 void VectorPlatformDeviceEmf::drawRect(const SkDraw
& draw
,
174 const SkPaint
& paint
) {
175 if (paint
.getPathEffect()) {
176 // Draw a path instead.
178 path_orginal
.addRect(rect
);
180 // Apply the path effect to the rect.
181 SkPath path_modified
;
182 paint
.getFillPath(path_orginal
, &path_modified
);
184 // Removes the path effect from the temporary SkPaint object.
185 SkPaint
paint_no_effet(paint
);
186 SkSafeUnref(paint_no_effet
.setPathEffect(NULL
));
188 // Draw the calculated path.
189 drawPath(draw
, path_modified
, paint_no_effet
);
193 if (!ApplyPaint(paint
)) {
196 HDC dc
= BeginPlatformPaint();
197 if (!Rectangle(dc
, SkScalarRound(rect
.fLeft
),
198 SkScalarRound(rect
.fTop
),
199 SkScalarRound(rect
.fRight
),
200 SkScalarRound(rect
.fBottom
))) {
207 void VectorPlatformDeviceEmf::drawPath(const SkDraw
& draw
,
209 const SkPaint
& paint
,
210 const SkMatrix
* prePathMatrix
,
211 bool pathIsMutable
) {
212 if (paint
.getPathEffect()) {
213 // Apply the path effect forehand.
214 SkPath path_modified
;
215 paint
.getFillPath(path
, &path_modified
);
217 // Removes the path effect from the temporary SkPaint object.
218 SkPaint
paint_no_effet(paint
);
219 SkSafeUnref(paint_no_effet
.setPathEffect(NULL
));
221 // Draw the calculated path.
222 drawPath(draw
, path_modified
, paint_no_effet
);
226 if (!ApplyPaint(paint
)) {
229 HDC dc
= BeginPlatformPaint();
230 if (PlatformDevice::LoadPathToDC(dc
, path
)) {
231 switch (paint
.getStyle()) {
232 case SkPaint::kFill_Style
: {
233 BOOL res
= StrokeAndFillPath(dc
);
237 case SkPaint::kStroke_Style
: {
238 BOOL res
= StrokePath(dc
);
242 case SkPaint::kStrokeAndFill_Style
: {
243 BOOL res
= StrokeAndFillPath(dc
);
256 void VectorPlatformDeviceEmf::drawBitmap(const SkDraw
& draw
,
257 const SkBitmap
& bitmap
,
258 const SkIRect
* srcRectOrNull
,
259 const SkMatrix
& matrix
,
260 const SkPaint
& paint
) {
261 // Load the temporary matrix. This is what will translate, rotate and resize
263 SkMatrix
actual_transform(transform_
);
264 actual_transform
.preConcat(matrix
);
265 LoadTransformToDC(hdc_
, actual_transform
);
267 InternalDrawBitmap(bitmap
, 0, 0, paint
);
269 // Restore the original matrix.
270 LoadTransformToDC(hdc_
, transform_
);
273 void VectorPlatformDeviceEmf::drawSprite(const SkDraw
& draw
,
274 const SkBitmap
& bitmap
,
276 const SkPaint
& paint
) {
279 LoadTransformToDC(hdc_
, identity
);
281 InternalDrawBitmap(bitmap
, x
, y
, paint
);
283 // Restore the original matrix.
284 LoadTransformToDC(hdc_
, transform_
);
287 /////////////////////////////////////////////////////////////////////////
289 static bool gdiCanHandleText(const SkPaint
& paint
) {
290 return !paint
.getShader() &&
291 !paint
.getPathEffect() &&
292 (SkPaint::kFill_Style
== paint
.getStyle()) &&
293 (255 == paint
.getAlpha());
296 class SkGDIFontSetup
{
304 SkDEBUGCODE(fUseGDIHasBeenCalled
= false;)
308 // can only be called once
309 bool useGDI(HDC hdc
, const SkPaint
&);
315 COLORREF fSavedTextColor
;
317 SkDEBUGCODE(bool fUseGDIHasBeenCalled
;)
320 bool SkGDIFontSetup::useGDI(HDC hdc
, const SkPaint
& paint
) {
321 SkASSERT(!fUseGDIHasBeenCalled
);
322 SkDEBUGCODE(fUseGDIHasBeenCalled
= true;)
324 fUseGDI
= gdiCanHandleText(paint
);
326 fSavedTextColor
= GetTextColor(hdc
);
327 SetTextColor(hdc
, skia::SkColorToCOLORREF(paint
.getColor()));
330 SkLOGFONTFromTypeface(paint
.getTypeface(), &lf
);
331 lf
.lfHeight
= -SkScalarRound(paint
.getTextSize());
332 fNewFont
= CreateFontIndirect(&lf
);
333 fSavedFont
= (HFONT
)::SelectObject(hdc
, fNewFont
);
339 SkGDIFontSetup::~SkGDIFontSetup() {
341 ::SelectObject(fHDC
, fSavedFont
);
342 ::DeleteObject(fNewFont
);
343 SetTextColor(fHDC
, fSavedTextColor
);
347 static SkScalar
getAscent(const SkPaint
& paint
) {
348 SkPaint::FontMetrics fm
;
349 paint
.getFontMetrics(&fm
);
353 // return the options int for ExtTextOut. Only valid if the paint's text
354 // encoding is not UTF8 (in which case ExtTextOut can't be used).
355 static UINT
getTextOutOptions(const SkPaint
& paint
) {
356 if (SkPaint::kGlyphID_TextEncoding
== paint
.getTextEncoding()) {
357 return ETO_GLYPH_INDEX
;
359 SkASSERT(SkPaint::kUTF16_TextEncoding
== paint
.getTextEncoding());
364 void VectorPlatformDeviceEmf::drawText(const SkDraw
& draw
,
369 const SkPaint
& paint
) {
370 SkGDIFontSetup setup
;
371 if (SkPaint::kUTF8_TextEncoding
!= paint
.getTextEncoding()
372 && setup
.useGDI(hdc_
, paint
)) {
373 UINT options
= getTextOutOptions(paint
);
374 UINT count
= byteLength
>> 1;
375 ExtTextOut(hdc_
, SkScalarRound(x
), SkScalarRound(y
+ getAscent(paint
)),
376 options
, 0, reinterpret_cast<const wchar_t*>(text
), count
, NULL
);
379 paint
.getTextPath(text
, byteLength
, x
, y
, &path
);
380 drawPath(draw
, path
, paint
);
384 static size_t size_utf8(const char* text
) {
385 return SkUTF8_CountUTF8Bytes(text
);
388 static size_t size_utf16(const char* text
) {
389 uint16_t c
= *reinterpret_cast<const uint16_t*>(text
);
390 return SkUTF16_IsHighSurrogate(c
) ? 4 : 2;
393 static size_t size_glyphid(const char* text
) {
397 void VectorPlatformDeviceEmf::drawPosText(const SkDraw
& draw
,
400 const SkScalar pos
[],
403 const SkPaint
& paint
) {
404 SkGDIFontSetup setup
;
405 if (2 == scalarsPerPos
406 && SkPaint::kUTF8_TextEncoding
!= paint
.getTextEncoding()
407 && setup
.useGDI(hdc_
, paint
)) {
408 int startX
= SkScalarRound(pos
[0]);
409 int startY
= SkScalarRound(pos
[1] + getAscent(paint
));
410 const int count
= len
>> 1;
411 SkAutoSTMalloc
<64, INT
> storage(count
);
412 INT
* advances
= storage
.get();
413 for (int i
= 0; i
< count
- 1; ++i
) {
414 advances
[i
] = SkScalarRound(pos
[2] - pos
[0]);
417 ExtTextOut(hdc_
, startX
, startY
, getTextOutOptions(paint
), 0,
418 reinterpret_cast<const wchar_t*>(text
), count
, advances
);
420 size_t (*bytesPerCodePoint
)(const char*);
421 switch (paint
.getTextEncoding()) {
422 case SkPaint::kUTF8_TextEncoding
:
423 bytesPerCodePoint
= size_utf8
;
425 case SkPaint::kUTF16_TextEncoding
:
426 bytesPerCodePoint
= size_utf16
;
429 SkASSERT(SkPaint::kGlyphID_TextEncoding
== paint
.getTextEncoding());
430 bytesPerCodePoint
= size_glyphid
;
434 const char* curr
= reinterpret_cast<const char*>(text
);
435 const char* stop
= curr
+ len
;
436 while (curr
< stop
) {
437 SkScalar y
= (1 == scalarsPerPos
) ? constY
: pos
[1];
438 size_t bytes
= bytesPerCodePoint(curr
);
439 drawText(draw
, curr
, bytes
, pos
[0], y
, paint
);
441 pos
+= scalarsPerPos
;
446 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw
& draw
,
450 const SkMatrix
* matrix
,
451 const SkPaint
& paint
) {
452 // This function isn't used in the code. Verify this assumption.
456 void VectorPlatformDeviceEmf::drawVertices(const SkDraw
& draw
,
457 SkCanvas::VertexMode vmode
,
459 const SkPoint vertices
[],
460 const SkPoint texs
[],
461 const SkColor colors
[],
463 const uint16_t indices
[],
465 const SkPaint
& paint
) {
466 // This function isn't used in the code. Verify this assumption.
470 void VectorPlatformDeviceEmf::drawDevice(const SkDraw
& draw
,
474 const SkPaint
& paint
) {
475 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
476 // it is a vectorial device.
477 drawSprite(draw
, device
->accessBitmap(false), x
, y
, paint
);
480 bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint
& paint
) {
481 // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
482 // This function does not execute the SkPaint drawing commands. These should
483 // be executed in drawPaint().
485 SkPaint::Style style
= paint
.getStyle();
486 if (!paint
.getAlpha())
487 style
= SkPaint::kStyleCount
;
490 case SkPaint::kFill_Style
:
491 if (!CreateBrush(true, paint
) ||
492 !CreatePen(false, paint
))
495 case SkPaint::kStroke_Style
:
496 if (!CreateBrush(false, paint
) ||
497 !CreatePen(true, paint
))
500 case SkPaint::kStrokeAndFill_Style
:
501 if (!CreateBrush(true, paint
) ||
502 !CreatePen(true, paint
))
506 if (!CreateBrush(false, paint
) ||
507 !CreatePen(false, paint
))
524 // Skia's text is not used. This should be fixed.
533 // BUG 1094907: Implement shaders. Shaders currently in use:
534 // SkShader::CreateBitmapShader
535 // SkGradientShader::CreateRadial
536 // SkGradientShader::CreateLinear
537 // SkASSERT(!paint.getShader());
539 // http://b/1106647 Implement loopers and mask filter. Looper currently in
541 // SkBlurDrawLooper is used for shadows.
542 // SkASSERT(!paint.getLooper());
543 // SkASSERT(!paint.getMaskFilter());
545 // http://b/1165900 Implement xfermode.
546 // SkASSERT(!paint.getXfermode());
548 // The path effect should be processed before arriving here.
549 SkASSERT(!paint
.getPathEffect());
551 // This isn't used in the code. Verify this assumption.
552 SkASSERT(!paint
.getRasterizer());
553 // Reuse code to load Win32 Fonts.
557 void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix
& transform
,
558 const SkRegion
& region
,
559 const SkClipStack
&) {
560 transform_
= transform
;
561 LoadTransformToDC(hdc_
, transform_
);
562 clip_region_
= region
;
563 if (!clip_region_
.isEmpty())
567 void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc
, int x
, int y
,
568 const RECT
* src_rect
) {
572 void VectorPlatformDeviceEmf::LoadClipRegion() {
575 LoadClippingRegionToDC(hdc_
, clip_region_
, t
);
578 SkDevice
* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
579 SkBitmap::Config config
, int width
, int height
, bool isOpaque
,
581 SkASSERT(config
== SkBitmap::kARGB_8888_Config
);
582 return VectorPlatformDeviceEmf::CreateDevice(width
, height
, isOpaque
, NULL
);
585 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush
, COLORREF color
) {
586 SkASSERT(previous_brush_
== NULL
);
587 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
588 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
589 // WHITE_BRUSH instead.
592 // Set the transparency.
593 if (0 == SetBkMode(hdc_
, TRANSPARENT
)) {
598 // Select the NULL brush.
599 previous_brush_
= SelectObject(GetStockObject(NULL_BRUSH
));
600 return previous_brush_
!= NULL
;
604 if (0 == SetBkMode(hdc_
, OPAQUE
)) {
609 // Create and select the brush.
610 previous_brush_
= SelectObject(CreateSolidBrush(color
));
611 return previous_brush_
!= NULL
;
614 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen
,
619 SkASSERT(previous_pen_
== NULL
);
620 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
621 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
626 previous_pen_
= SelectObject(GetStockObject(NULL_PEN
));
627 return previous_pen_
!= NULL
;
630 // Use the stock pen if the stroke width is 0.
631 if (stroke_width
== 0) {
632 // Create a pen with the right color.
633 previous_pen_
= SelectObject(::CreatePen(PS_SOLID
, 0, color
));
634 return previous_pen_
!= NULL
;
637 // Load a custom pen.
639 brush
.lbStyle
= BS_SOLID
;
640 brush
.lbColor
= color
;
642 HPEN pen
= ExtCreatePen(pen_style
, stroke_width
, &brush
, 0, NULL
);
643 SkASSERT(pen
!= NULL
);
644 previous_pen_
= SelectObject(pen
);
645 if (previous_pen_
== NULL
)
648 if (!SetMiterLimit(hdc_
, stroke_miter
, NULL
)) {
655 void VectorPlatformDeviceEmf::Cleanup() {
656 if (previous_brush_
) {
657 HGDIOBJ result
= SelectObject(previous_brush_
);
658 previous_brush_
= NULL
;
660 BOOL res
= DeleteObject(result
);
665 HGDIOBJ result
= SelectObject(previous_pen_
);
666 previous_pen_
= NULL
;
668 BOOL res
= DeleteObject(result
);
672 // Remove any loaded path from the context.
676 HGDIOBJ
VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object
) {
677 HGDIOBJ result
= ::SelectObject(hdc_
, object
);
678 SkASSERT(result
!= HGDI_ERROR
);
679 if (result
== HGDI_ERROR
)
684 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush
,
685 const SkPaint
& paint
) {
686 // Make sure that for transparent color, no brush is used.
687 if (paint
.getAlpha() == 0) {
691 return CreateBrush(use_brush
, SkColorToCOLORREF(paint
.getColor()));
694 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen
, const SkPaint
& paint
) {
695 // Make sure that for transparent color, no pen is used.
696 if (paint
.getAlpha() == 0) {
700 DWORD pen_style
= PS_GEOMETRIC
| PS_SOLID
;
701 switch (paint
.getStrokeJoin()) {
702 case SkPaint::kMiter_Join
:
703 // Connects path segments with a sharp join.
704 pen_style
|= PS_JOIN_MITER
;
706 case SkPaint::kRound_Join
:
707 // Connects path segments with a round join.
708 pen_style
|= PS_JOIN_ROUND
;
710 case SkPaint::kBevel_Join
:
711 // Connects path segments with a flat bevel join.
712 pen_style
|= PS_JOIN_BEVEL
;
718 switch (paint
.getStrokeCap()) {
719 case SkPaint::kButt_Cap
:
720 // Begin/end contours with no extension.
721 pen_style
|= PS_ENDCAP_FLAT
;
723 case SkPaint::kRound_Cap
:
724 // Begin/end contours with a semi-circle extension.
725 pen_style
|= PS_ENDCAP_ROUND
;
727 case SkPaint::kSquare_Cap
:
728 // Begin/end contours with a half square extension.
729 pen_style
|= PS_ENDCAP_SQUARE
;
736 return CreatePen(use_pen
,
737 SkColorToCOLORREF(paint
.getColor()),
738 SkScalarRound(paint
.getStrokeWidth()),
739 paint
.getStrokeMiter(),
743 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap
& bitmap
,
745 const SkPaint
& paint
) {
746 unsigned char alpha
= paint
.getAlpha();
752 // ApplyPaint expect an opaque color.
753 SkPaint
tmp_paint(paint
);
754 tmp_paint
.setAlpha(255);
755 if (!ApplyPaint(tmp_paint
))
757 is_translucent
= true;
759 if (!ApplyPaint(paint
))
761 is_translucent
= false;
763 int src_size_x
= bitmap
.width();
764 int src_size_y
= bitmap
.height();
765 if (!src_size_x
|| !src_size_y
)
768 // Create a BMP v4 header that we can serialize. We use the shared "V3"
769 // fillter to fill the stardard items, then add in the "V4" stuff we want.
770 BITMAPV4HEADER bitmap_header
;
771 memset(&bitmap_header
, 0, sizeof(BITMAPV4HEADER
));
772 FillBitmapInfoHeader(src_size_x
, src_size_y
,
773 reinterpret_cast<BITMAPINFOHEADER
*>(&bitmap_header
));
774 bitmap_header
.bV4Size
= sizeof(BITMAPV4HEADER
);
775 bitmap_header
.bV4RedMask
= 0x00ff0000;
776 bitmap_header
.bV4GreenMask
= 0x0000ff00;
777 bitmap_header
.bV4BlueMask
= 0x000000ff;
778 bitmap_header
.bV4AlphaMask
= 0xff000000;
780 SkAutoLockPixels
lock(bitmap
);
781 SkASSERT(bitmap
.getConfig() == SkBitmap::kARGB_8888_Config
);
782 const uint32_t* pixels
= static_cast<const uint32_t*>(bitmap
.getPixels());
783 if (pixels
== NULL
) {
788 if (!is_translucent
) {
789 int row_length
= bitmap
.rowBytesAsPixels();
790 // There is no quick way to determine if an image is opaque.
791 for (int y2
= 0; y2
< src_size_y
; ++y2
) {
792 for (int x2
= 0; x2
< src_size_x
; ++x2
) {
793 if (SkColorGetA(pixels
[(y2
* row_length
) + x2
]) != 255) {
794 is_translucent
= true;
802 HDC dc
= BeginPlatformPaint();
803 BITMAPINFOHEADER hdr
;
804 FillBitmapInfoHeader(src_size_x
, src_size_y
, &hdr
);
805 if (is_translucent
) {
806 // The image must be loaded as a bitmap inside a device context.
807 HDC bitmap_dc
= ::CreateCompatibleDC(dc
);
809 HBITMAP hbitmap
= ::CreateDIBSection(
810 bitmap_dc
, reinterpret_cast<const BITMAPINFO
*>(&hdr
),
811 DIB_RGB_COLORS
, &bits
, NULL
, 0);
813 // static cast to a char so we can do byte ptr arithmatic to
815 unsigned char* dest_buffer
= static_cast<unsigned char *>(bits
);
817 // We will copy row by row to avoid having to worry about
818 // the row strides being different.
819 const int dest_row_size
= hdr
.biBitCount
/ 8 * hdr
.biWidth
;
820 for (int row
= 0; row
< bitmap
.height(); ++row
) {
821 int dest_offset
= row
* dest_row_size
;
822 // pixels_offset in terms of pixel count.
823 int src_offset
= row
* bitmap
.rowBytesAsPixels();
824 memcpy(dest_buffer
+ dest_offset
, pixels
+ src_offset
, dest_row_size
);
827 HGDIOBJ old_bitmap
= ::SelectObject(bitmap_dc
, hbitmap
);
829 // After some analysis of IE7's behavior, this is the thing to do. I was
830 // sure IE7 was doing so kind of bitmasking due to the way translucent image
831 // where renderered but after some windbg tracing, it is being done by the
832 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
833 // for bitmasked images. The trick seems to switch the stretching mode in
834 // what the driver expects.
835 DWORD previous_mode
= GetStretchBltMode(dc
);
836 BOOL result
= SetStretchBltMode(dc
, COLORONCOLOR
);
838 // Note that this function expect premultiplied colors (!)
839 BLENDFUNCTION blend_function
= {AC_SRC_OVER
, 0, alpha
, AC_SRC_ALPHA
};
840 result
= GdiAlphaBlend(dc
,
841 x
, y
, // Destination origin.
842 src_size_x
, src_size_y
, // Destination size.
844 0, 0, // Source origin.
845 src_size_x
, src_size_y
, // Source size.
848 result
= SetStretchBltMode(dc
, previous_mode
);
851 alpha_blend_used_
= true;
853 ::SelectObject(bitmap_dc
, static_cast<HBITMAP
>(old_bitmap
));
854 DeleteObject(hbitmap
);
857 int nCopied
= StretchDIBits(dc
,
858 x
, y
, // Destination origin.
859 src_size_x
, src_size_y
,
860 0, 0, // Source origin.
861 src_size_x
, src_size_y
, // Source size.
863 reinterpret_cast<const BITMAPINFO
*>(&hdr
),