1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include <windows.h>
33
34 #include "AffineTransform.h"
35 #include "GraphicsContext.h"
36 #include "ImageBuffer.h"
37 #include "PlatformContextSkia.h"
38 #include "SimpleFontData.h"
39 #include "TransparencyWin.h"
40
41 #include "SkColorPriv.h"
42 #include "skia/ext/platform_canvas.h"
43
44 namespace WebCore {
45
46 namespace {
47
48 // The maximum size in pixels of the buffer we'll keep around for drawing text
49 // into. Buffers larger than this will be destroyed when we're done with them.
50 const int maxCachedBufferPixelSize = 65536;
51
canvasForContext(const GraphicsContext & context)52 inline SkCanvas* canvasForContext(const GraphicsContext& context)
53 {
54 return context.platformContext()->canvas();
55 }
56
bitmapForContext(const GraphicsContext & context)57 inline const SkBitmap& bitmapForContext(const GraphicsContext& context)
58 {
59 return canvasForContext(context)->getTopDevice()->accessBitmap(false);
60 }
61
compositeToCopy(const GraphicsContext & sourceLayers,GraphicsContext & destContext,const AffineTransform & matrix)62 void compositeToCopy(const GraphicsContext& sourceLayers,
63 GraphicsContext& destContext,
64 const AffineTransform& matrix)
65 {
66 // Make a list of all devices. The iterator goes top-down, and we want
67 // bottom-up. Note that each layer can also have an offset in canvas
68 // coordinates, which is the (x, y) position.
69 struct DeviceInfo {
70 DeviceInfo(SkDevice* d, int lx, int ly)
71 : device(d)
72 , x(lx)
73 , y(ly) {}
74 SkDevice* device;
75 int x;
76 int y;
77 };
78 Vector<DeviceInfo> devices;
79 SkCanvas* sourceCanvas = canvasForContext(sourceLayers);
80 SkCanvas::LayerIter iter(sourceCanvas, false);
81 while (!iter.done()) {
82 devices.append(DeviceInfo(iter.device(), iter.x(), iter.y()));
83 iter.next();
84 }
85
86 // Create a temporary canvas for the compositing into the destination.
87 SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext));
88 SkCanvas destCanvas(*destBmp);
89 destCanvas.setMatrix(matrix);
90
91 for (int i = devices.size() - 1; i >= 0; i--) {
92 const SkBitmap& srcBmp = devices[i].device->accessBitmap(false);
93
94 SkRect destRect;
95 destRect.fLeft = devices[i].x;
96 destRect.fTop = devices[i].y;
97 destRect.fRight = destRect.fLeft + srcBmp.width();
98 destRect.fBottom = destRect.fTop + srcBmp.height();
99
100 destCanvas.drawBitmapRect(srcBmp, 0, destRect);
101 }
102 }
103
104 } // namespace
105
106 // If either of these pointers is non-null, both must be valid and point to
107 // bitmaps of the same size.
108 class TransparencyWin::OwnedBuffers {
109 public:
OwnedBuffers(const IntSize & size,bool needReferenceBuffer)110 OwnedBuffers(const IntSize& size, bool needReferenceBuffer)
111 {
112 m_destBitmap = ImageBuffer::create(size);
113
114 if (needReferenceBuffer) {
115 m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
116 m_referenceBitmap.allocPixels();
117 m_referenceBitmap.eraseARGB(0, 0, 0, 0);
118 }
119 }
120
destBitmap()121 ImageBuffer* destBitmap() { return m_destBitmap.get(); }
122
123 // This bitmap will be empty if you don't specify needReferenceBuffer to the
124 // constructor.
referenceBitmap()125 SkBitmap* referenceBitmap() { return &m_referenceBitmap; }
126
127 // Returns whether the current layer will fix a buffer of the given size.
canHandleSize(const IntSize & size) const128 bool canHandleSize(const IntSize& size) const
129 {
130 return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height();
131 }
132
133 private:
134 // The destination bitmap we're drawing into.
135 OwnPtr<ImageBuffer> m_destBitmap;
136
137 // This could be an ImageBuffer but this is an optimization. Since this is
138 // only ever used as a reference, we don't need to make a full
139 // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap
140 // is much faster since it's just a Malloc rather than a GDI call.
141 SkBitmap m_referenceBitmap;
142 };
143
144 TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0;
145
TransparencyWin()146 TransparencyWin::TransparencyWin()
147 : m_destContext(0)
148 , m_orgTransform()
149 , m_layerMode(NoLayer)
150 , m_transformMode(KeepTransform)
151 , m_drawContext(0)
152 , m_savedOnDrawContext(false)
153 , m_layerBuffer(0)
154 , m_referenceBitmap(0)
155 , m_validLayer(false)
156 {
157 }
158
~TransparencyWin()159 TransparencyWin::~TransparencyWin()
160 {
161 // This should be false, since calling composite() is mandatory.
162 ASSERT(!m_savedOnDrawContext);
163 }
164
composite()165 void TransparencyWin::composite()
166 {
167 // Matches the save() in initializeNewTextContext (or the constructor for
168 // SCALE) to put the context back into the same state we found it.
169 if (m_savedOnDrawContext) {
170 m_drawContext->restore();
171 m_savedOnDrawContext = false;
172 }
173
174 switch (m_layerMode) {
175 case NoLayer:
176 break;
177 case OpaqueCompositeLayer:
178 case WhiteLayer:
179 compositeOpaqueComposite();
180 break;
181 case TextComposite:
182 compositeTextComposite();
183 break;
184 }
185 }
186
init(GraphicsContext * dest,LayerMode layerMode,TransformMode transformMode,const IntRect & region)187 void TransparencyWin::init(GraphicsContext* dest,
188 LayerMode layerMode,
189 TransformMode transformMode,
190 const IntRect& region)
191 {
192 m_destContext = dest;
193 m_orgTransform = dest->getCTM();
194 m_layerMode = layerMode;
195 m_transformMode = transformMode;
196 m_sourceRect = region;
197
198 computeLayerSize();
199 setupLayer();
200 setupTransform(region);
201 }
202
computeLayerSize()203 void TransparencyWin::computeLayerSize()
204 {
205 if (m_transformMode == Untransform) {
206 // The meaning of the "transformed" source rect is a little ambigous
207 // here. The rest of the code doesn't care about it in the Untransform
208 // case since we're using our own happy coordinate system. So we set it
209 // to be the source rect since that matches how the code below actually
210 // uses the variable: to determine how to translate things to account
211 // for the offset of the layer.
212 m_transformedSourceRect = m_sourceRect;
213 m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height());
214 } else {
215 m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect);
216 m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height());
217 }
218 }
219
setupLayer()220 void TransparencyWin::setupLayer()
221 {
222 switch (m_layerMode) {
223 case NoLayer:
224 setupLayerForNoLayer();
225 break;
226 case OpaqueCompositeLayer:
227 setupLayerForOpaqueCompositeLayer();
228 break;
229 case TextComposite:
230 setupLayerForTextComposite();
231 break;
232 case WhiteLayer:
233 setupLayerForWhiteLayer();
234 break;
235 }
236 }
237
setupLayerForNoLayer()238 void TransparencyWin::setupLayerForNoLayer()
239 {
240 m_drawContext = m_destContext; // Draw to the source context.
241 m_validLayer = true;
242 }
243
setupLayerForOpaqueCompositeLayer()244 void TransparencyWin::setupLayerForOpaqueCompositeLayer()
245 {
246 initializeNewContext();
247 if (!m_validLayer)
248 return;
249
250 AffineTransform mapping;
251 mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());
252 if (m_transformMode == Untransform){
253 // Compute the inverse mapping from the canvas space to the
254 // coordinate space of our bitmap.
255 mapping *= m_orgTransform.inverse();
256 }
257 compositeToCopy(*m_destContext, *m_drawContext, mapping);
258
259 // Save the reference layer so we can tell what changed.
260 SkCanvas referenceCanvas(*m_referenceBitmap);
261 referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0);
262 // Layer rect represents the part of the original layer.
263 }
264
setupLayerForTextComposite()265 void TransparencyWin::setupLayerForTextComposite()
266 {
267 ASSERT(m_transformMode == KeepTransform);
268 // Fall through to filling with white.
269 setupLayerForWhiteLayer();
270 }
271
setupLayerForWhiteLayer()272 void TransparencyWin::setupLayerForWhiteLayer()
273 {
274 initializeNewContext();
275 if (!m_validLayer)
276 return;
277
278 m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white, ColorSpaceDeviceRGB);
279 // Layer rect represents the part of the original layer.
280 }
281
setupTransform(const IntRect & region)282 void TransparencyWin::setupTransform(const IntRect& region)
283 {
284 switch (m_transformMode) {
285 case KeepTransform:
286 setupTransformForKeepTransform(region);
287 break;
288 case Untransform:
289 setupTransformForUntransform();
290 break;
291 case ScaleTransform:
292 setupTransformForScaleTransform();
293 break;
294 }
295 }
296
setupTransformForKeepTransform(const IntRect & region)297 void TransparencyWin::setupTransformForKeepTransform(const IntRect& region)
298 {
299 if (!m_validLayer)
300 return;
301
302 if (m_layerMode != NoLayer) {
303 // Need to save things since we're modifying the transform.
304 m_drawContext->save();
305 m_savedOnDrawContext = true;
306
307 // Account for the fact that the layer may be offset from the
308 // original. This only happens when we create a layer that has the
309 // same coordinate space as the parent.
310 AffineTransform xform;
311 xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());
312
313 // We're making a layer, so apply the old transform to the new one
314 // so it's maintained. We know the new layer has the identity
315 // transform now, we we can just multiply it.
316 xform *= m_orgTransform;
317 m_drawContext->concatCTM(xform);
318 }
319 m_drawRect = m_sourceRect;
320 }
321
setupTransformForUntransform()322 void TransparencyWin::setupTransformForUntransform()
323 {
324 ASSERT(m_layerMode != NoLayer);
325 // We now have a new layer with the identity transform, which is the
326 // Untransformed space we'll use for drawing.
327 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize);
328 }
329
setupTransformForScaleTransform()330 void TransparencyWin::setupTransformForScaleTransform()
331 {
332 if (!m_validLayer)
333 return;
334
335 if (m_layerMode == NoLayer) {
336 // Need to save things since we're modifying the layer.
337 m_drawContext->save();
338 m_savedOnDrawContext = true;
339
340 // Undo the transform on the current layer when we're re-using the
341 // current one.
342 m_drawContext->concatCTM(m_drawContext->getCTM().inverse());
343
344 // We're drawing to the original layer with just a different size.
345 m_drawRect = m_transformedSourceRect;
346 } else {
347 // Just go ahead and use the layer's coordinate space to draw into.
348 // It will have the scaled size, and an identity transform loaded.
349 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize);
350 }
351 }
352
setTextCompositeColor(Color color)353 void TransparencyWin::setTextCompositeColor(Color color)
354 {
355 m_textCompositeColor = color;
356 }
357
initializeNewContext()358 void TransparencyWin::initializeNewContext()
359 {
360 int pixelSize = m_layerSize.width() * m_layerSize.height();
361 if (pixelSize <= 0)
362 return;
363
364 if (pixelSize > maxCachedBufferPixelSize) {
365 // Create a 1-off buffer for drawing into. We only need the reference
366 // buffer if we're making an OpaqueCompositeLayer.
367 bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer;
368 m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap));
369 m_layerBuffer = m_ownedBuffers->destBitmap();
370 if (!m_layerBuffer)
371 return;
372
373 m_drawContext = m_layerBuffer->context();
374 if (needReferenceBitmap) {
375 m_referenceBitmap = m_ownedBuffers->referenceBitmap();
376 if (!m_referenceBitmap || !m_referenceBitmap->getPixels())
377 return;
378 }
379 m_validLayer = true;
380 return;
381 }
382
383 if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) {
384 // We can re-use the existing buffer. We don't need to clear it since
385 // all layer modes will clear it in their initialization.
386 m_layerBuffer = m_cachedBuffers->destBitmap();
387 m_drawContext = m_cachedBuffers->destBitmap()->context();
388 bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0);
389 m_referenceBitmap = m_cachedBuffers->referenceBitmap();
390 m_referenceBitmap->eraseARGB(0, 0, 0, 0);
391 m_validLayer = true;
392 return;
393 }
394
395 // Create a new cached buffer.
396 if (m_cachedBuffers)
397 delete m_cachedBuffers;
398 m_cachedBuffers = new OwnedBuffers(m_layerSize, true);
399
400 m_layerBuffer = m_cachedBuffers->destBitmap();
401 m_drawContext = m_cachedBuffers->destBitmap()->context();
402 m_referenceBitmap = m_cachedBuffers->referenceBitmap();
403 m_validLayer = true;
404 }
405
compositeOpaqueComposite()406 void TransparencyWin::compositeOpaqueComposite()
407 {
408 if (!m_validLayer)
409 return;
410
411 SkCanvas* destCanvas = canvasForContext(*m_destContext);
412 destCanvas->save();
413
414 SkBitmap* bitmap = const_cast<SkBitmap*>(
415 &bitmapForContext(*m_layerBuffer->context()));
416
417 // This function will be called for WhiteLayer as well, which we don't want
418 // to change.
419 if (m_layerMode == OpaqueCompositeLayer) {
420 // Fix up our bitmap, making it contain only the pixels which changed
421 // and transparent everywhere else.
422 SkAutoLockPixels sourceLock(*m_referenceBitmap);
423 SkAutoLockPixels lock(*bitmap);
424 for (int y = 0; y < bitmap->height(); y++) {
425 uint32_t* source = m_referenceBitmap->getAddr32(0, y);
426 uint32_t* dest = bitmap->getAddr32(0, y);
427 for (int x = 0; x < bitmap->width(); x++) {
428 // Clear out any pixels that were untouched.
429 if (dest[x] == source[x])
430 dest[x] = 0;
431 else
432 dest[x] |= (0xFF << SK_A32_SHIFT);
433 }
434 }
435 } else
436 makeLayerOpaque();
437
438 SkRect destRect;
439 if (m_transformMode != Untransform) {
440 // We want to use Untransformed space.
441 //
442 // Note that we DON'T call m_layerBuffer->image() here. This actually
443 // makes a copy of the image, which is unnecessary and slow. Instead, we
444 // just draw the image from inside the destination context.
445 SkMatrix identity;
446 identity.reset();
447 destCanvas->setMatrix(identity);
448
449 destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY());
450 } else
451 destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY());
452
453 SkPaint paint;
454 paint.setFilterBitmap(true);
455 paint.setAntiAlias(true);
456
457 // Note that we need to specify the source layer subset, since the bitmap
458 // may have been cached and it could be larger than what we're using.
459 SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() };
460 destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint);
461 destCanvas->restore();
462 }
463
compositeTextComposite()464 void TransparencyWin::compositeTextComposite()
465 {
466 if (!m_validLayer)
467 return;
468
469 const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopDevice()->accessBitmap(true);
470 SkColor textColor = m_textCompositeColor.rgb();
471 for (int y = 0; y < m_layerSize.height(); y++) {
472 uint32_t* row = bitmap.getAddr32(0, y);
473 for (int x = 0; x < m_layerSize.width(); x++) {
474 // The alpha is the average of the R, G, and B channels.
475 int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3;
476
477 // Apply that alpha to the text color and write the result.
478 row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha));
479 }
480 }
481
482 // Now the layer has text with the proper color and opacity.
483 SkCanvas* destCanvas = canvasForContext(*m_destContext);
484 destCanvas->save();
485
486 // We want to use Untransformed space (see above)
487 SkMatrix identity;
488 identity.reset();
489 destCanvas->setMatrix(identity);
490 SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() };
491
492 // Note that we need to specify the source layer subset, since the bitmap
493 // may have been cached and it could be larger than what we're using.
494 SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() };
495 destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0);
496 destCanvas->restore();
497 }
498
makeLayerOpaque()499 void TransparencyWin::makeLayerOpaque()
500 {
501 if (!m_validLayer)
502 return;
503
504 SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()->
505 canvas()->getTopDevice()->accessBitmap(true));
506 for (int y = 0; y < m_layerSize.height(); y++) {
507 uint32_t* row = bitmap.getAddr32(0, y);
508 for (int x = 0; x < m_layerSize.width(); x++)
509 row[x] |= 0xFF000000;
510 }
511 }
512
513 } // namespace WebCore
514