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