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 "GraphicsContext.h"
35 #include "ImageBuffer.h"
36 #include "PlatformContextSkia.h"
37 #include "SimpleFontData.h"
38 #include "TransformationMatrix.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 skia::PlatformCanvas* 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)->getTopPlatformDevice().accessBitmap(false);
60 }
61
compositeToCopy(const GraphicsContext & sourceLayers,GraphicsContext & destContext,const TransformationMatrix & matrix)62 void compositeToCopy(const GraphicsContext& sourceLayers,
63 GraphicsContext& destContext,
64 const TransformationMatrix& 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 TransformationMatrix 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() * mapping;
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);
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 TransformationMatrix 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 * xform;
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 m_validLayer = true;
377 return;
378 }
379
380 if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) {
381 // We can re-use the existing buffer. We don't need to clear it since
382 // all layer modes will clear it in their initialization.
383 m_layerBuffer = m_cachedBuffers->destBitmap();
384 m_drawContext = m_cachedBuffers->destBitmap()->context();
385 bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0);
386 m_referenceBitmap = m_cachedBuffers->referenceBitmap();
387 m_referenceBitmap->eraseARGB(0, 0, 0, 0);
388 m_validLayer = true;
389 return;
390 }
391
392 // Create a new cached buffer.
393 if (m_cachedBuffers)
394 delete m_cachedBuffers;
395 m_cachedBuffers = new OwnedBuffers(m_layerSize, true);
396
397 m_layerBuffer = m_cachedBuffers->destBitmap();
398 m_drawContext = m_cachedBuffers->destBitmap()->context();
399 m_referenceBitmap = m_cachedBuffers->referenceBitmap();
400 m_validLayer = true;
401 }
402
compositeOpaqueComposite()403 void TransparencyWin::compositeOpaqueComposite()
404 {
405 if (!m_validLayer)
406 return;
407
408 SkCanvas* destCanvas = canvasForContext(*m_destContext);
409 destCanvas->save();
410
411 SkBitmap* bitmap = const_cast<SkBitmap*>(
412 &bitmapForContext(*m_layerBuffer->context()));
413
414 // This function will be called for WhiteLayer as well, which we don't want
415 // to change.
416 if (m_layerMode == OpaqueCompositeLayer) {
417 // Fix up our bitmap, making it contain only the pixels which changed
418 // and transparent everywhere else.
419 SkAutoLockPixels sourceLock(*m_referenceBitmap);
420 SkAutoLockPixels lock(*bitmap);
421 for (int y = 0; y < bitmap->height(); y++) {
422 uint32_t* source = m_referenceBitmap->getAddr32(0, y);
423 uint32_t* dest = bitmap->getAddr32(0, y);
424 for (int x = 0; x < bitmap->width(); x++) {
425 // Clear out any pixels that were untouched.
426 if (dest[x] == source[x])
427 dest[x] = 0;
428 else
429 dest[x] |= (0xFF << SK_A32_SHIFT);
430 }
431 }
432 } else
433 makeLayerOpaque();
434
435 SkRect destRect;
436 if (m_transformMode != Untransform) {
437 // We want to use Untransformed space.
438 //
439 // Note that we DON'T call m_layerBuffer->image() here. This actually
440 // makes a copy of the image, which is unnecessary and slow. Instead, we
441 // just draw the image from inside the destination context.
442 SkMatrix identity;
443 identity.reset();
444 destCanvas->setMatrix(identity);
445
446 destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom());
447 } else
448 destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom());
449
450 SkPaint paint;
451 paint.setFilterBitmap(true);
452 paint.setAntiAlias(true);
453
454 // Note that we need to specify the source layer subset, since the bitmap
455 // may have been cached and it could be larger than what we're using.
456 SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() };
457 destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint);
458 destCanvas->restore();
459 }
460
compositeTextComposite()461 void TransparencyWin::compositeTextComposite()
462 {
463 if (!m_validLayer)
464 return;
465
466 const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true);
467 SkColor textColor = m_textCompositeColor.rgb();
468 for (int y = 0; y < m_layerSize.height(); y++) {
469 uint32_t* row = bitmap.getAddr32(0, y);
470 for (int x = 0; x < m_layerSize.width(); x++) {
471 // The alpha is the average of the R, G, and B channels.
472 int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3;
473
474 // Apply that alpha to the text color and write the result.
475 row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha));
476 }
477 }
478
479 // Now the layer has text with the proper color and opacity.
480 SkCanvas* destCanvas = canvasForContext(*m_destContext);
481 destCanvas->save();
482
483 // We want to use Untransformed space (see above)
484 SkMatrix identity;
485 identity.reset();
486 destCanvas->setMatrix(identity);
487 SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() };
488
489 // Note that we need to specify the source layer subset, since the bitmap
490 // may have been cached and it could be larger than what we're using.
491 SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() };
492 destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0);
493 destCanvas->restore();
494 }
495
makeLayerOpaque()496 void TransparencyWin::makeLayerOpaque()
497 {
498 if (!m_validLayer)
499 return;
500
501 SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()->
502 canvas()->getTopPlatformDevice().accessBitmap(true));
503 for (int y = 0; y < m_layerSize.height(); y++) {
504 uint32_t* row = bitmap.getAddr32(0, y);
505 for (int x = 0; x < m_layerSize.width(); x++)
506 row[x] |= 0xFF000000;
507 }
508 }
509
510 } // namespace WebCore
511