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 = {0};
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 && height == bitmap_data.bmHeight) {
85 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
86 succeeded = bitmap.installPixels(info, bitmap_data.bmBits,
87 bitmap_data.bmWidthBytes);
88 }
89 }
90 }
91
92 if (!succeeded)
93 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
94
95 return new VectorPlatformDeviceEmf(dc, bitmap);
96 }
97
VectorPlatformDeviceEmf(HDC dc,const SkBitmap & bitmap)98 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
99 : SkBitmapDevice(bitmap),
100 hdc_(dc),
101 previous_brush_(NULL),
102 previous_pen_(NULL) {
103 transform_.reset();
104 SetPlatformDevice(this, this);
105 }
106
~VectorPlatformDeviceEmf()107 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
108 SkASSERT(previous_brush_ == NULL);
109 SkASSERT(previous_pen_ == NULL);
110 }
111
BeginPlatformPaint()112 HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
113 return hdc_;
114 }
115
drawPaint(const SkDraw & draw,const SkPaint & paint)116 void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
117 const SkPaint& paint) {
118 // TODO(maruel): Bypass the current transformation matrix.
119 SkRect rect;
120 rect.fLeft = 0;
121 rect.fTop = 0;
122 rect.fRight = SkIntToScalar(width() + 1);
123 rect.fBottom = SkIntToScalar(height() + 1);
124 drawRect(draw, rect, paint);
125 }
126
drawPoints(const SkDraw & draw,SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)127 void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
128 SkCanvas::PointMode mode,
129 size_t count,
130 const SkPoint pts[],
131 const SkPaint& paint) {
132 if (!count)
133 return;
134
135 if (mode == SkCanvas::kPoints_PointMode) {
136 SkASSERT(false);
137 return;
138 }
139
140 SkPaint tmp_paint(paint);
141 tmp_paint.setStyle(SkPaint::kStroke_Style);
142
143 // Draw a path instead.
144 SkPath path;
145 switch (mode) {
146 case SkCanvas::kLines_PointMode:
147 if (count % 2) {
148 SkASSERT(false);
149 return;
150 }
151 for (size_t i = 0; i < count / 2; ++i) {
152 path.moveTo(pts[2 * i]);
153 path.lineTo(pts[2 * i + 1]);
154 }
155 break;
156 case SkCanvas::kPolygon_PointMode:
157 path.moveTo(pts[0]);
158 for (size_t i = 1; i < count; ++i) {
159 path.lineTo(pts[i]);
160 }
161 break;
162 default:
163 SkASSERT(false);
164 return;
165 }
166 // Draw the calculated path.
167 drawPath(draw, path, tmp_paint);
168 }
169
drawRect(const SkDraw & draw,const SkRect & rect,const SkPaint & paint)170 void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
171 const SkRect& rect,
172 const SkPaint& paint) {
173 CHECK_FOR_NODRAW_ANNOTATION(paint);
174 if (paint.getPathEffect()) {
175 // Draw a path instead.
176 SkPath path_orginal;
177 path_orginal.addRect(rect);
178
179 // Apply the path effect to the rect.
180 SkPath path_modified;
181 paint.getFillPath(path_orginal, &path_modified);
182
183 // Removes the path effect from the temporary SkPaint object.
184 SkPaint paint_no_effet(paint);
185 paint_no_effet.setPathEffect(NULL);
186
187 // Draw the calculated path.
188 drawPath(draw, path_modified, paint_no_effet);
189 return;
190 }
191
192 if (!ApplyPaint(paint)) {
193 return;
194 }
195 HDC dc = BeginPlatformPaint();
196 if (!Rectangle(dc, SkScalarRoundToInt(rect.fLeft),
197 SkScalarRoundToInt(rect.fTop),
198 SkScalarRoundToInt(rect.fRight),
199 SkScalarRoundToInt(rect.fBottom))) {
200 SkASSERT(false);
201 }
202 EndPlatformPaint();
203 Cleanup();
204 }
205
drawRRect(const SkDraw & draw,const SkRRect & rr,const SkPaint & paint)206 void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr,
207 const SkPaint& paint) {
208 SkPath path;
209 path.addRRect(rr);
210 this->drawPath(draw, path, paint, NULL, true);
211 }
212
drawPath(const SkDraw & draw,const SkPath & path,const SkPaint & paint,const SkMatrix * prePathMatrix,bool pathIsMutable)213 void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
214 const SkPath& path,
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);
223
224 // Removes the path effect from the temporary SkPaint object.
225 SkPaint paint_no_effet(paint);
226 paint_no_effet.setPathEffect(NULL);
227
228 // Draw the calculated path.
229 drawPath(draw, path_modified, paint_no_effet);
230 return;
231 }
232
233 if (!ApplyPaint(paint)) {
234 return;
235 }
236 HDC dc = BeginPlatformPaint();
237 if (PlatformDevice::LoadPathToDC(dc, path)) {
238 switch (paint.getStyle()) {
239 case SkPaint::kFill_Style: {
240 BOOL res = StrokeAndFillPath(dc);
241 SkASSERT(res != 0);
242 break;
243 }
244 case SkPaint::kStroke_Style: {
245 BOOL res = StrokePath(dc);
246 SkASSERT(res != 0);
247 break;
248 }
249 case SkPaint::kStrokeAndFill_Style: {
250 BOOL res = StrokeAndFillPath(dc);
251 SkASSERT(res != 0);
252 break;
253 }
254 default:
255 SkASSERT(false);
256 break;
257 }
258 }
259 EndPlatformPaint();
260 Cleanup();
261 }
262
drawBitmapRect(const SkDraw & draw,const SkBitmap & bitmap,const SkRect * src,const SkRect & dst,const SkPaint & paint,SkCanvas::DrawBitmapRectFlags flags)263 void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
264 const SkBitmap& bitmap,
265 const SkRect* src,
266 const SkRect& dst,
267 const SkPaint& paint,
268 SkCanvas::DrawBitmapRectFlags flags) {
269 SkMatrix matrix;
270 SkRect bitmapBounds, tmpSrc, tmpDst;
271 SkBitmap tmpBitmap;
272
273 bitmapBounds.isetWH(bitmap.width(), bitmap.height());
274
275 // Compute matrix from the two rectangles
276 if (src) {
277 tmpSrc = *src;
278 } else {
279 tmpSrc = bitmapBounds;
280 }
281 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
282
283 const SkBitmap* bitmapPtr = &bitmap;
284
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.
287 if (src) {
288 if (!bitmapBounds.contains(*src)) {
289 if (!tmpSrc.intersect(bitmapBounds)) {
290 return; // nothing to draw
291 }
292 // recompute dst, based on the smaller tmpSrc
293 matrix.mapRect(&tmpDst, tmpSrc);
294 }
295
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.
299 SkIRect srcIR;
300 tmpSrc.roundOut(&srcIR);
301 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
302 return;
303 }
304 bitmapPtr = &tmpBitmap;
305
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);
310 }
311 if (srcIR.fTop > 0) {
312 dy = SkIntToScalar(srcIR.fTop);
313 }
314 if (dx || dy) {
315 matrix.preTranslate(dx, dy);
316 }
317 }
318 this->drawBitmap(draw, *bitmapPtr, matrix, paint);
319 }
320
drawBitmap(const SkDraw & draw,const SkBitmap & bitmap,const SkMatrix & matrix,const SkPaint & 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
326 // the bitmap.
327 SkMatrix actual_transform(transform_);
328 actual_transform.preConcat(matrix);
329 LoadTransformToDC(hdc_, actual_transform);
330
331 InternalDrawBitmap(bitmap, 0, 0, paint);
332
333 // Restore the original matrix.
334 LoadTransformToDC(hdc_, transform_);
335 }
336
drawSprite(const SkDraw & draw,const SkBitmap & bitmap,int x,int y,const SkPaint & paint)337 void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
338 const SkBitmap& bitmap,
339 int x, int y,
340 const SkPaint& paint) {
341 SkMatrix identity;
342 identity.reset();
343 LoadTransformToDC(hdc_, identity);
344
345 InternalDrawBitmap(bitmap, x, y, paint);
346
347 // Restore the original matrix.
348 LoadTransformToDC(hdc_, transform_);
349 }
350
351 /////////////////////////////////////////////////////////////////////////
352
gdiCanHandleText(const SkPaint & paint)353 static bool gdiCanHandleText(const SkPaint& paint) {
354 return !paint.getShader() &&
355 !paint.getPathEffect() &&
356 (SkPaint::kFill_Style == paint.getStyle()) &&
357 (255 == paint.getAlpha());
358 }
359
360 class SkGDIFontSetup {
361 public:
SkGDIFontSetup()362 SkGDIFontSetup() :
363 fHDC(NULL),
364 fNewFont(NULL),
365 fSavedFont(NULL),
366 fSavedTextColor(0),
367 fUseGDI(false) {
368 SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
369 }
370 ~SkGDIFontSetup();
371
372 // can only be called once
373 bool useGDI(HDC hdc, const SkPaint&);
374
375 private:
376 HDC fHDC;
377 HFONT fNewFont;
378 HFONT fSavedFont;
379 COLORREF fSavedTextColor;
380 bool fUseGDI;
381 SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
382 };
383
useGDI(HDC hdc,const SkPaint & paint)384 bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
385 SkASSERT(!fUseGDIHasBeenCalled);
386 SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
387
388 fUseGDI = gdiCanHandleText(paint);
389 if (fUseGDI) {
390 fSavedTextColor = GetTextColor(hdc);
391 SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
392
393 LOGFONT lf = {0};
394 SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
395 lf.lfHeight = -SkScalarRoundToInt(paint.getTextSize());
396 fNewFont = CreateFontIndirect(&lf);
397 fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
398 fHDC = hdc;
399 }
400 return fUseGDI;
401 }
402
~SkGDIFontSetup()403 SkGDIFontSetup::~SkGDIFontSetup() {
404 if (fUseGDI) {
405 ::SelectObject(fHDC, fSavedFont);
406 ::DeleteObject(fNewFont);
407 SetTextColor(fHDC, fSavedTextColor);
408 }
409 }
410
getAscent(const SkPaint & paint)411 static SkScalar getAscent(const SkPaint& paint) {
412 SkPaint::FontMetrics fm;
413 paint.getFontMetrics(&fm);
414 return fm.fAscent;
415 }
416
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).
getTextOutOptions(const SkPaint & paint)419 static UINT getTextOutOptions(const SkPaint& paint) {
420 if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
421 return ETO_GLYPH_INDEX;
422 } else {
423 SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
424 return 0;
425 }
426 }
427
428 static SkiaEnsureTypefaceCharactersAccessible
429 g_skia_ensure_typeface_characters_accessible = NULL;
430
SetSkiaEnsureTypefaceCharactersAccessible(SkiaEnsureTypefaceCharactersAccessible func)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;
436 }
437
EnsureTypefaceCharactersAccessible(const SkTypeface & typeface,const wchar_t * text,unsigned int text_length)438 void EnsureTypefaceCharactersAccessible(
439 const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
440 LOGFONT lf = {0};
441 SkLOGFONTFromTypeface(&typeface, &lf);
442 g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
443 }
444
EnsureExtTextOut(HDC hdc,int x,int y,UINT options,const RECT * lprect,LPCWSTR text,unsigned int characters,const int * lpDx,SkTypeface * const typeface)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);
449 if (!success) {
450 if (typeface) {
451 EnsureTypefaceCharactersAccessible(*typeface,
452 text,
453 characters);
454 success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
455 if (!success) {
456 LOGFONT lf = {0};
457 SkLOGFONTFromTypeface(typeface, &lf);
458 VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
459 << " FaceName = " << lf.lfFaceName
460 << " and characters: " << base::string16(text, characters);
461 }
462 } else {
463 VLOG(1) << "ExtTextOut FAILED for default FaceName "
464 << " and characters: " << base::string16(text, characters);
465 }
466 }
467 return success;
468 }
469
drawText(const SkDraw & draw,const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & paint)470 void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
471 const void* text,
472 size_t byteLength,
473 SkScalar x,
474 SkScalar y,
475 const SkPaint& paint) {
476 SkGDIFontSetup setup;
477 bool useDrawPath = true;
478
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());
487 }
488
489 if (useDrawPath) {
490 SkPath path;
491 paint.getTextPath(text, byteLength, x, y, &path);
492 drawPath(draw, path, paint);
493 }
494 }
495
size_utf8(const char * text)496 static size_t size_utf8(const char* text) {
497 return SkUTF8_CountUTF8Bytes(text);
498 }
499
size_utf16(const char * 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;
503 }
504
size_glyphid(const char * text)505 static size_t size_glyphid(const char* text) {
506 return 2;
507 }
508
drawPosText(const SkDraw & draw,const void * text,size_t len,const SkScalar pos[],SkScalar constY,int scalarsPerPos,const SkPaint & paint)509 void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
510 const void* text,
511 size_t len,
512 const SkScalar pos[],
513 SkScalar constY,
514 int scalarsPerPos,
515 const SkPaint& paint) {
516 SkGDIFontSetup setup;
517 bool useDrawText = true;
518
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]);
529 pos += 2;
530 }
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());
535 }
536
537 if (useDrawText) {
538 size_t (*bytesPerCodePoint)(const char*);
539 switch (paint.getTextEncoding()) {
540 case SkPaint::kUTF8_TextEncoding:
541 bytesPerCodePoint = size_utf8;
542 break;
543 case SkPaint::kUTF16_TextEncoding:
544 bytesPerCodePoint = size_utf16;
545 break;
546 default:
547 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
548 bytesPerCodePoint = size_glyphid;
549 break;
550 }
551
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);
558 curr += bytes;
559 pos += scalarsPerPos;
560 }
561 }
562 }
563
drawTextOnPath(const SkDraw & draw,const void * text,size_t len,const SkPath & path,const SkMatrix * matrix,const SkPaint & paint)564 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
565 const void* text,
566 size_t len,
567 const SkPath& path,
568 const SkMatrix* matrix,
569 const SkPaint& paint) {
570 // This function isn't used in the code. Verify this assumption.
571 SkASSERT(false);
572 }
573
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)574 void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
575 SkCanvas::VertexMode vmode,
576 int vertexCount,
577 const SkPoint vertices[],
578 const SkPoint texs[],
579 const SkColor colors[],
580 SkXfermode* xmode,
581 const uint16_t indices[],
582 int indexCount,
583 const SkPaint& paint) {
584 // This function isn't used in the code. Verify this assumption.
585 SkASSERT(false);
586 }
587
drawDevice(const SkDraw & draw,SkBaseDevice * device,int x,int y,const SkPaint & paint)588 void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
589 SkBaseDevice* device,
590 int x,
591 int y,
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);
596 }
597
ApplyPaint(const SkPaint & 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().
602
603 SkPaint::Style style = paint.getStyle();
604 if (!paint.getAlpha())
605 style = (SkPaint::Style) SkPaint::kStyleCount;
606
607 switch (style) {
608 case SkPaint::kFill_Style:
609 if (!CreateBrush(true, paint) ||
610 !CreatePen(false, paint))
611 return false;
612 break;
613 case SkPaint::kStroke_Style:
614 if (!CreateBrush(false, paint) ||
615 !CreatePen(true, paint))
616 return false;
617 break;
618 case SkPaint::kStrokeAndFill_Style:
619 if (!CreateBrush(true, paint) ||
620 !CreatePen(true, paint))
621 return false;
622 break;
623 default:
624 if (!CreateBrush(false, paint) ||
625 !CreatePen(false, paint))
626 return false;
627 break;
628 }
629
630 /*
631 getFlags();
632 isAntiAlias();
633 isDither()
634 isLinearText()
635 isSubpixelText()
636 isUnderlineText()
637 isStrikeThruText()
638 isFakeBoldText()
639 isDevKernText()
640 isFilterBitmap()
641
642 // Skia's text is not used. This should be fixed.
643 getTextAlign()
644 getTextScaleX()
645 getTextSkewX()
646 getTextEncoding()
647 getFontMetrics()
648 getFontSpacing()
649 */
650
651 // BUG 1094907: Implement shaders. Shaders currently in use:
652 // SkShader::CreateBitmapShader
653 // SkGradientShader::CreateRadial
654 // SkGradientShader::CreateLinear
655 // SkASSERT(!paint.getShader());
656
657 // http://b/1106647 Implement loopers and mask filter. Looper currently in
658 // use:
659 // SkBlurDrawLooper is used for shadows.
660 // SkASSERT(!paint.getLooper());
661 // SkASSERT(!paint.getMaskFilter());
662
663 // http://b/1165900 Implement xfermode.
664 // SkASSERT(!paint.getXfermode());
665
666 // The path effect should be processed before arriving here.
667 SkASSERT(!paint.getPathEffect());
668
669 // This isn't used in the code. Verify this assumption.
670 SkASSERT(!paint.getRasterizer());
671 // Reuse code to load Win32 Fonts.
672 return true;
673 }
674
setMatrixClip(const SkMatrix & transform,const SkRegion & region,const SkClipStack &)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())
682 LoadClipRegion();
683 }
684
DrawToNativeContext(HDC dc,int x,int y,const RECT * src_rect)685 void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
686 const RECT* src_rect) {
687 SkASSERT(false);
688 }
689
LoadClipRegion()690 void VectorPlatformDeviceEmf::LoadClipRegion() {
691 SkMatrix t;
692 t.reset();
693 LoadClippingRegionToDC(hdc_, clip_region_, t);
694 }
695
onCreateDevice(const SkImageInfo & info,Usage)696 SkBaseDevice* VectorPlatformDeviceEmf::onCreateDevice(const SkImageInfo& info,
697 Usage /*usage*/) {
698 SkASSERT(info.colorType() == kN32_SkColorType);
699 return VectorPlatformDeviceEmf::CreateDevice(
700 info.width(), info.height(), info.isOpaque(), NULL);
701 }
702
CreateBrush(bool use_brush,COLORREF color)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.
708
709 if (!use_brush) {
710 // Set the transparency.
711 if (0 == SetBkMode(hdc_, TRANSPARENT)) {
712 SkASSERT(false);
713 return false;
714 }
715
716 // Select the NULL brush.
717 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
718 return previous_brush_ != NULL;
719 }
720
721 // Set the opacity.
722 if (0 == SetBkMode(hdc_, OPAQUE)) {
723 SkASSERT(false);
724 return false;
725 }
726
727 // Create and select the brush.
728 previous_brush_ = SelectObject(CreateSolidBrush(color));
729 return previous_brush_ != NULL;
730 }
731
CreatePen(bool use_pen,COLORREF color,int stroke_width,float stroke_miter,DWORD pen_style)732 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
733 COLORREF color,
734 int stroke_width,
735 float stroke_miter,
736 DWORD pen_style) {
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
740 // instead.
741
742 // No pen case
743 if (!use_pen) {
744 previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
745 return previous_pen_ != NULL;
746 }
747
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;
753 }
754
755 // Load a custom pen.
756 LOGBRUSH brush = {0};
757 brush.lbStyle = BS_SOLID;
758 brush.lbColor = color;
759 brush.lbHatch = 0;
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)
764 return false;
765
766 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
767 SkASSERT(false);
768 return false;
769 }
770 return true;
771 }
772
Cleanup()773 void VectorPlatformDeviceEmf::Cleanup() {
774 if (previous_brush_) {
775 HGDIOBJ result = SelectObject(previous_brush_);
776 previous_brush_ = NULL;
777 if (result) {
778 BOOL res = DeleteObject(result);
779 SkASSERT(res != 0);
780 }
781 }
782 if (previous_pen_) {
783 HGDIOBJ result = SelectObject(previous_pen_);
784 previous_pen_ = NULL;
785 if (result) {
786 BOOL res = DeleteObject(result);
787 SkASSERT(res != 0);
788 }
789 }
790 // Remove any loaded path from the context.
791 AbortPath(hdc_);
792 }
793
SelectObject(HGDIOBJ object)794 HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
795 HGDIOBJ result = ::SelectObject(hdc_, object);
796 SkASSERT(result != HGDI_ERROR);
797 if (result == HGDI_ERROR)
798 return NULL;
799 return result;
800 }
801
CreateBrush(bool use_brush,const SkPaint & paint)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) {
806 use_brush = false;
807 }
808
809 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
810 }
811
CreatePen(bool use_pen,const SkPaint & paint)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) {
815 use_pen = false;
816 }
817
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;
823 break;
824 case SkPaint::kRound_Join:
825 // Connects path segments with a round join.
826 pen_style |= PS_JOIN_ROUND;
827 break;
828 case SkPaint::kBevel_Join:
829 // Connects path segments with a flat bevel join.
830 pen_style |= PS_JOIN_BEVEL;
831 break;
832 default:
833 SkASSERT(false);
834 break;
835 }
836 switch (paint.getStrokeCap()) {
837 case SkPaint::kButt_Cap:
838 // Begin/end contours with no extension.
839 pen_style |= PS_ENDCAP_FLAT;
840 break;
841 case SkPaint::kRound_Cap:
842 // Begin/end contours with a semi-circle extension.
843 pen_style |= PS_ENDCAP_ROUND;
844 break;
845 case SkPaint::kSquare_Cap:
846 // Begin/end contours with a half square extension.
847 pen_style |= PS_ENDCAP_SQUARE;
848 break;
849 default:
850 SkASSERT(false);
851 break;
852 }
853
854 return CreatePen(use_pen,
855 SkColorToCOLORREF(paint.getColor()),
856 SkScalarRoundToInt(paint.getStrokeWidth()),
857 paint.getStrokeMiter(),
858 pen_style);
859 }
860
InternalDrawBitmap(const SkBitmap & bitmap,int x,int y,const SkPaint & paint)861 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
862 int x, int y,
863 const SkPaint& paint) {
864 unsigned char alpha = paint.getAlpha();
865 if (alpha == 0)
866 return;
867
868 bool is_translucent;
869 if (alpha != 255) {
870 // ApplyPaint expect an opaque color.
871 SkPaint tmp_paint(paint);
872 tmp_paint.setAlpha(255);
873 if (!ApplyPaint(tmp_paint))
874 return;
875 is_translucent = true;
876 } else {
877 if (!ApplyPaint(paint))
878 return;
879 is_translucent = false;
880 }
881 int src_size_x = bitmap.width();
882 int src_size_y = bitmap.height();
883 if (!src_size_x || !src_size_y)
884 return;
885
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;
896
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) {
901 SkASSERT(false);
902 return;
903 }
904
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;
912 y2 = src_size_y;
913 break;
914 }
915 }
916 }
917 }
918
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);
925 void* bits = NULL;
926 HBITMAP hbitmap = ::CreateDIBSection(
927 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
928 DIB_RGB_COLORS, &bits, NULL, 0);
929
930 // static cast to a char so we can do byte ptr arithmatic to
931 // get the offset.
932 unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
933
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);
942 }
943 SkASSERT(hbitmap);
944 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
945
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);
954 SkASSERT(result);
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.
960 bitmap_dc,
961 0, 0, // Source origin.
962 src_size_x, src_size_y, // Source size.
963 blend_function);
964 SkASSERT(result);
965 result = SetStretchBltMode(dc, previous_mode);
966 SkASSERT(result);
967
968 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
969 DeleteObject(hbitmap);
970 DeleteDC(bitmap_dc);
971 } else {
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.
977 pixels,
978 reinterpret_cast<const BITMAPINFO*>(&hdr),
979 DIB_RGB_COLORS,
980 SRCCOPY);
981 }
982 EndPlatformPaint();
983 Cleanup();
984 }
985
986 } // namespace skia
987