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