2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "platform/graphics/win/TransparencyWin.h"
35 #include "SkColorPriv.h"
36 #include "platform/fonts/SimpleFontData.h"
37 #include "platform/graphics/GraphicsContext.h"
38 #include "platform/graphics/skia/SkiaUtils.h"
39 #include "skia/ext/platform_canvas.h"
45 // The maximum size in pixels of the buffer we'll keep around for drawing text
46 // into. Buffers larger than this will be destroyed when we're done with them.
47 const int maxCachedBufferPixelSize = 65536;
49 inline const SkBitmap& bitmapForContext(const GraphicsContext& context)
51 return context.layerBitmap();
54 void compositeToCopy(GraphicsContext& sourceLayers, GraphicsContext& destContext, const AffineTransform& matrix)
56 // Make a list of all devices. The iterator goes top-down, and we want
57 // bottom-up. Note that each layer can also have an offset in canvas
58 // coordinates, which is the (x, y) position.
60 DeviceInfo(SkBaseDevice* d, int lx, int ly)
68 Vector<DeviceInfo> devices;
69 SkCanvas* sourceCanvas = sourceLayers.canvas();
70 SkCanvas::LayerIter iter(sourceCanvas, false);
71 while (!iter.done()) {
72 devices.append(DeviceInfo(iter.device(), iter.x(), iter.y()));
76 // Create a temporary canvas for the compositing into the destination.
77 SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext));
78 SkCanvas destCanvas(*destBmp);
79 destCanvas.setMatrix(affineTransformToSkMatrix(matrix));
81 for (int i = devices.size() - 1; i >= 0; i--) {
82 const SkBitmap& srcBmp = devices[i].device->accessBitmap(false);
85 destRect.fLeft = devices[i].x;
86 destRect.fTop = devices[i].y;
87 destRect.fRight = destRect.fLeft + srcBmp.width();
88 destRect.fBottom = destRect.fTop + srcBmp.height();
90 destCanvas.drawBitmapRect(srcBmp, 0, destRect);
96 // If either of these pointers is non-null, both must be valid and point to
97 // bitmaps of the same size.
98 class TransparencyWin::OwnedBuffers {
100 OwnedBuffers(const IntSize& size, bool needReferenceBuffer)
102 m_destBitmap = ImageBuffer::create(size);
104 if (needReferenceBuffer) {
105 m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
106 m_referenceBitmap.allocPixels();
107 m_referenceBitmap.eraseARGB(0, 0, 0, 0);
111 ImageBuffer* destBitmap() { return m_destBitmap.get(); }
113 // This bitmap will be empty if you don't specify needReferenceBuffer to the
115 SkBitmap* referenceBitmap() { return &m_referenceBitmap; }
117 // Returns whether the current layer will fix a buffer of the given size.
118 bool canHandleSize(const IntSize& size) const
120 return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height();
124 // The destination bitmap we're drawing into.
125 OwnPtr<ImageBuffer> m_destBitmap;
127 // This could be an ImageBuffer but this is an optimization. Since this is
128 // only ever used as a reference, we don't need to make a full
129 // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap
130 // is much faster since it's just a Malloc rather than a GDI call.
131 SkBitmap m_referenceBitmap;
134 TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0;
136 TransparencyWin::TransparencyWin()
139 , m_layerMode(NoLayer)
140 , m_transformMode(KeepTransform)
142 , m_savedOnDrawContext(false)
144 , m_referenceBitmap(0)
145 , m_validLayer(false)
149 TransparencyWin::~TransparencyWin()
151 // This should be false, since calling composite() is mandatory.
152 ASSERT(!m_savedOnDrawContext);
155 void TransparencyWin::composite()
157 // Matches the save() in initializeNewTextContext (or the constructor for
158 // SCALE) to put the context back into the same state we found it.
159 if (m_savedOnDrawContext) {
160 m_drawContext->restore();
161 m_savedOnDrawContext = false;
164 switch (m_layerMode) {
167 case OpaqueCompositeLayer:
169 compositeOpaqueComposite();
172 compositeTextComposite();
177 void TransparencyWin::init(GraphicsContext* dest, LayerMode layerMode, TransformMode transformMode, const IntRect& region)
179 m_destContext = dest;
180 m_orgTransform = dest->getCTM();
181 m_layerMode = layerMode;
182 m_transformMode = transformMode;
183 m_sourceRect = region;
187 setupTransform(region);
190 void TransparencyWin::computeLayerSize()
192 if (m_transformMode == Untransform) {
193 // The meaning of the "transformed" source rect is a little ambigous
194 // here. The rest of the code doesn't care about it in the Untransform
195 // case since we're using our own happy coordinate system. So we set it
196 // to be the source rect since that matches how the code below actually
197 // uses the variable: to determine how to translate things to account
198 // for the offset of the layer.
199 m_transformedSourceRect = m_sourceRect;
200 } else if (m_transformMode == KeepTransform && m_layerMode != TextComposite) {
201 // FIXME: support clipping for other modes
202 IntRect clippedSourceRect = m_sourceRect;
203 FloatRect clipBounds = m_destContext->getClipBounds();
204 if (!clipBounds.isEmpty()) {
205 clippedSourceRect.intersect(enclosingIntRect(clipBounds));
207 m_transformedSourceRect = m_orgTransform.mapRect(clippedSourceRect);
209 m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect);
212 m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height());
215 void TransparencyWin::setupLayer()
217 switch (m_layerMode) {
219 setupLayerForNoLayer();
221 case OpaqueCompositeLayer:
222 setupLayerForOpaqueCompositeLayer();
225 setupLayerForTextComposite();
228 setupLayerForWhiteLayer();
233 void TransparencyWin::setupLayerForNoLayer()
235 m_drawContext = m_destContext; // Draw to the source context.
239 void TransparencyWin::setupLayerForOpaqueCompositeLayer()
241 initializeNewContext();
245 AffineTransform mapping;
246 mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());
247 if (m_transformMode == Untransform) {
248 // Compute the inverse mapping from the canvas space to the
249 // coordinate space of our bitmap.
250 mapping *= m_orgTransform.inverse();
252 compositeToCopy(*m_destContext, *m_drawContext, mapping);
254 // Save the reference layer so we can tell what changed.
255 SkCanvas referenceCanvas(*m_referenceBitmap);
256 referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0);
257 // Layer rect represents the part of the original layer.
260 void TransparencyWin::setupLayerForTextComposite()
262 ASSERT(m_transformMode == KeepTransform);
263 // Fall through to filling with white.
264 setupLayerForWhiteLayer();
267 void TransparencyWin::setupLayerForWhiteLayer()
269 initializeNewContext();
273 m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white);
274 // Layer rect represents the part of the original layer.
277 void TransparencyWin::setupTransform(const IntRect& region)
279 switch (m_transformMode) {
281 setupTransformForKeepTransform(region);
284 setupTransformForUntransform();
287 setupTransformForScaleTransform();
292 void TransparencyWin::setupTransformForKeepTransform(const IntRect& region)
297 if (m_layerMode != NoLayer) {
298 // Need to save things since we're modifying the transform.
299 m_drawContext->save();
300 m_savedOnDrawContext = true;
302 // Account for the fact that the layer may be offset from the
303 // original. This only happens when we create a layer that has the
304 // same coordinate space as the parent.
305 AffineTransform xform;
306 xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y());
308 // We're making a layer, so apply the old transform to the new one
309 // so it's maintained. We know the new layer has the identity
310 // transform now, we we can just multiply it.
311 xform *= m_orgTransform;
312 m_drawContext->concatCTM(xform);
314 m_drawRect = m_sourceRect;
317 void TransparencyWin::setupTransformForUntransform()
319 ASSERT(m_layerMode != NoLayer);
320 // We now have a new layer with the identity transform, which is the
321 // Untransformed space we'll use for drawing.
322 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize);
325 void TransparencyWin::setupTransformForScaleTransform()
330 if (m_layerMode == NoLayer) {
331 // Need to save things since we're modifying the layer.
332 m_drawContext->save();
333 m_savedOnDrawContext = true;
335 // Undo the transform on the current layer when we're re-using the
337 m_drawContext->concatCTM(m_drawContext->getCTM().inverse());
339 // We're drawing to the original layer with just a different size.
340 m_drawRect = m_transformedSourceRect;
342 // Just go ahead and use the layer's coordinate space to draw into.
343 // It will have the scaled size, and an identity transform loaded.
344 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize);
348 void TransparencyWin::setTextCompositeColor(Color color)
350 m_textCompositeColor = color;
353 void TransparencyWin::initializeNewContext()
355 int pixelSize = m_layerSize.width() * m_layerSize.height();
359 if (pixelSize > maxCachedBufferPixelSize) {
360 // Create a 1-off buffer for drawing into. We only need the reference
361 // buffer if we're making an OpaqueCompositeLayer.
362 bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer;
363 m_ownedBuffers = adoptPtr(new OwnedBuffers(m_layerSize, needReferenceBitmap));
364 m_layerBuffer = m_ownedBuffers->destBitmap();
368 m_drawContext = m_layerBuffer->context();
369 if (needReferenceBitmap) {
370 m_referenceBitmap = m_ownedBuffers->referenceBitmap();
371 if (!m_referenceBitmap || !m_referenceBitmap->getPixels())
378 if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) {
379 // We can re-use the existing buffer. We don't need to clear it since
380 // all layer modes will clear it in their initialization.
381 m_layerBuffer = m_cachedBuffers->destBitmap();
382 m_drawContext = m_cachedBuffers->destBitmap()->context();
383 bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0);
384 m_referenceBitmap = m_cachedBuffers->referenceBitmap();
385 m_referenceBitmap->eraseARGB(0, 0, 0, 0);
390 // Create a new cached buffer.
392 delete m_cachedBuffers;
393 m_cachedBuffers = new OwnedBuffers(m_layerSize, true);
395 m_layerBuffer = m_cachedBuffers->destBitmap();
396 m_drawContext = m_cachedBuffers->destBitmap()->context();
397 m_referenceBitmap = m_cachedBuffers->referenceBitmap();
401 void TransparencyWin::compositeOpaqueComposite()
406 m_destContext->save();
408 SkBitmap* bitmap = const_cast<SkBitmap*>(
409 &bitmapForContext(*m_layerBuffer->context()));
411 // This function will be called for WhiteLayer as well, which we don't want
413 if (m_layerMode == OpaqueCompositeLayer) {
414 // Fix up our bitmap, making it contain only the pixels which changed
415 // and transparent everywhere else.
416 SkAutoLockPixels sourceLock(*m_referenceBitmap);
417 SkAutoLockPixels lock(*bitmap);
418 for (int y = 0; y < bitmap->height(); y++) {
419 uint32_t* source = m_referenceBitmap->getAddr32(0, y);
420 uint32_t* dest = bitmap->getAddr32(0, y);
421 for (int x = 0; x < bitmap->width(); x++) {
422 // Clear out any pixels that were untouched.
423 if (dest[x] == source[x])
426 dest[x] |= (0xFF << SK_A32_SHIFT);
434 if (m_transformMode != Untransform) {
435 // We want to use Untransformed space.
437 // Note that we DON'T call m_layerBuffer->image() here. This actually
438 // makes a copy of the image, which is unnecessary and slow. Instead, we
439 // just draw the image from inside the destination context.
442 m_destContext->setMatrix(identity);
444 destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY());
446 destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY());
450 paint.setFilterBitmap(true);
451 paint.setAntiAlias(true);
453 // Note that we need to specify the source layer subset, since the bitmap
454 // may have been cached and it could be larger than what we're using.
455 SkRect sourceRect = SkRect::MakeWH(
456 SkIntToScalar(m_layerSize.width()),
457 SkIntToScalar(m_layerSize.height()));
458 m_destContext->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint);
459 m_destContext->restore();
462 void TransparencyWin::compositeTextComposite()
467 const SkBitmap& bitmap = m_layerBuffer->context()->layerBitmap(GraphicsContext::ReadWrite);
468 SkColor textColor = m_textCompositeColor.rgb();
469 for (int y = 0; y < m_layerSize.height(); y++) {
470 uint32_t* row = bitmap.getAddr32(0, y);
471 for (int x = 0; x < m_layerSize.width(); x++) {
472 // The alpha is the average of the R, G, and B channels.
473 int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3;
475 // Apply that alpha to the text color and write the result.
476 row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha));
480 // Now the layer has text with the proper color and opacity.
481 m_destContext->save();
483 // We want to use Untransformed space (see above)
486 m_destContext->setMatrix(identity);
487 SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() };
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 SkRect sourceRect = SkRect::MakeWH(
492 SkIntToScalar(m_layerSize.width()),
493 SkIntToScalar(m_layerSize.height()));
494 m_destContext->drawBitmapRect(bitmap, &sourceRect, destRect, 0);
495 m_destContext->restore();
498 void TransparencyWin::makeLayerOpaque()
503 SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->layerBitmap(GraphicsContext::ReadWrite));
504 for (int y = 0; y < m_layerSize.height(); y++) {
505 uint32_t* row = bitmap.getAddr32(0, y);
506 for (int x = 0; x < m_layerSize.width(); x++)
507 row[x] |= 0xFF000000;
511 } // namespace WebCore