Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / win / TransparencyWin.cpp
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
33 #include "platform/graphics/win/TransparencyWin.h"
34
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"
40
41 namespace WebCore {
42
43 namespace {
44
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;
48
49 inline const SkBitmap& bitmapForContext(const GraphicsContext& context)
50 {
51     return context.layerBitmap();
52 }
53
54 void compositeToCopy(GraphicsContext& sourceLayers, GraphicsContext& destContext, const AffineTransform& matrix)
55 {
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.
59     struct DeviceInfo {
60         DeviceInfo(SkBaseDevice* d, int lx, int ly)
61             : device(d)
62             , x(lx)
63             , y(ly) { }
64         SkBaseDevice* device;
65         int x;
66         int y;
67     };
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()));
73         iter.next();
74     }
75
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));
80
81     for (int i = devices.size() - 1; i >= 0; i--) {
82         const SkBitmap& srcBmp = devices[i].device->accessBitmap(false);
83
84         SkRect destRect;
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();
89
90         destCanvas.drawBitmapRect(srcBmp, 0, destRect);
91     }
92 }
93
94 } // namespace
95
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 {
99 public:
100     OwnedBuffers(const IntSize& size, bool needReferenceBuffer)
101     {
102         m_destBitmap = ImageBuffer::create(size);
103
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);
108         }
109     }
110
111     ImageBuffer* destBitmap() { return m_destBitmap.get(); }
112
113     // This bitmap will be empty if you don't specify needReferenceBuffer to the
114     // constructor.
115     SkBitmap* referenceBitmap() { return &m_referenceBitmap; }
116
117     // Returns whether the current layer will fix a buffer of the given size.
118     bool canHandleSize(const IntSize& size) const
119     {
120         return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height();
121     }
122
123 private:
124     // The destination bitmap we're drawing into.
125     OwnPtr<ImageBuffer> m_destBitmap;
126
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;
132 };
133
134 TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0;
135
136 TransparencyWin::TransparencyWin()
137     : m_destContext(0)
138     , m_orgTransform()
139     , m_layerMode(NoLayer)
140     , m_transformMode(KeepTransform)
141     , m_drawContext(0)
142     , m_savedOnDrawContext(false)
143     , m_layerBuffer(0)
144     , m_referenceBitmap(0)
145     , m_validLayer(false)
146 {
147 }
148
149 TransparencyWin::~TransparencyWin()
150 {
151     // This should be false, since calling composite() is mandatory.
152     ASSERT(!m_savedOnDrawContext);
153 }
154
155 void TransparencyWin::composite()
156 {
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;
162     }
163
164     switch (m_layerMode) {
165     case NoLayer:
166         break;
167     case OpaqueCompositeLayer:
168     case WhiteLayer:
169         compositeOpaqueComposite();
170         break;
171     case TextComposite:
172         compositeTextComposite();
173         break;
174     }
175 }
176
177 void TransparencyWin::init(GraphicsContext* dest, LayerMode layerMode, TransformMode transformMode, const IntRect& region)
178 {
179     m_destContext = dest;
180     m_orgTransform = dest->getCTM();
181     m_layerMode = layerMode;
182     m_transformMode = transformMode;
183     m_sourceRect = region;
184
185     computeLayerSize();
186     setupLayer();
187     setupTransform(region);
188 }
189
190 void TransparencyWin::computeLayerSize()
191 {
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));
206         }
207         m_transformedSourceRect = m_orgTransform.mapRect(clippedSourceRect);
208     } else {
209         m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect);
210     }
211
212     m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height());
213 }
214
215 void TransparencyWin::setupLayer()
216 {
217     switch (m_layerMode) {
218     case NoLayer:
219         setupLayerForNoLayer();
220         break;
221     case OpaqueCompositeLayer:
222         setupLayerForOpaqueCompositeLayer();
223         break;
224     case TextComposite:
225         setupLayerForTextComposite();
226         break;
227     case WhiteLayer:
228         setupLayerForWhiteLayer();
229         break;
230     }
231 }
232
233 void TransparencyWin::setupLayerForNoLayer()
234 {
235     m_drawContext = m_destContext; // Draw to the source context.
236     m_validLayer = true;
237 }
238
239 void TransparencyWin::setupLayerForOpaqueCompositeLayer()
240 {
241     initializeNewContext();
242     if (!m_validLayer)
243         return;
244
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();
251     }
252     compositeToCopy(*m_destContext, *m_drawContext, mapping);
253
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.
258 }
259
260 void TransparencyWin::setupLayerForTextComposite()
261 {
262     ASSERT(m_transformMode == KeepTransform);
263     // Fall through to filling with white.
264     setupLayerForWhiteLayer();
265 }
266
267 void TransparencyWin::setupLayerForWhiteLayer()
268 {
269     initializeNewContext();
270     if (!m_validLayer)
271         return;
272
273     m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white);
274     // Layer rect represents the part of the original layer.
275 }
276
277 void TransparencyWin::setupTransform(const IntRect& region)
278 {
279     switch (m_transformMode) {
280     case KeepTransform:
281         setupTransformForKeepTransform(region);
282         break;
283     case Untransform:
284         setupTransformForUntransform();
285         break;
286     case ScaleTransform:
287         setupTransformForScaleTransform();
288         break;
289     }
290 }
291
292 void TransparencyWin::setupTransformForKeepTransform(const IntRect& region)
293 {
294     if (!m_validLayer)
295         return;
296
297     if (m_layerMode != NoLayer) {
298         // Need to save things since we're modifying the transform.
299         m_drawContext->save();
300         m_savedOnDrawContext = true;
301
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());
307
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);
313     }
314     m_drawRect = m_sourceRect;
315 }
316
317 void TransparencyWin::setupTransformForUntransform()
318 {
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);
323 }
324
325 void TransparencyWin::setupTransformForScaleTransform()
326 {
327     if (!m_validLayer)
328         return;
329
330     if (m_layerMode == NoLayer) {
331         // Need to save things since we're modifying the layer.
332         m_drawContext->save();
333         m_savedOnDrawContext = true;
334
335         // Undo the transform on the current layer when we're re-using the
336         // current one.
337         m_drawContext->concatCTM(m_drawContext->getCTM().inverse());
338
339         // We're drawing to the original layer with just a different size.
340         m_drawRect = m_transformedSourceRect;
341     } else {
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);
345     }
346 }
347
348 void TransparencyWin::setTextCompositeColor(Color color)
349 {
350     m_textCompositeColor = color;
351 }
352
353 void TransparencyWin::initializeNewContext()
354 {
355     int pixelSize = m_layerSize.width() * m_layerSize.height();
356     if (pixelSize <= 0)
357         return;
358
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();
365         if (!m_layerBuffer)
366             return;
367
368         m_drawContext = m_layerBuffer->context();
369         if (needReferenceBitmap) {
370             m_referenceBitmap = m_ownedBuffers->referenceBitmap();
371             if (!m_referenceBitmap || !m_referenceBitmap->getPixels())
372                 return;
373         }
374         m_validLayer = true;
375         return;
376     }
377
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);
386         m_validLayer = true;
387         return;
388     }
389
390     // Create a new cached buffer.
391     if (m_cachedBuffers)
392         delete m_cachedBuffers;
393     m_cachedBuffers = new OwnedBuffers(m_layerSize, true);
394
395     m_layerBuffer = m_cachedBuffers->destBitmap();
396     m_drawContext = m_cachedBuffers->destBitmap()->context();
397     m_referenceBitmap = m_cachedBuffers->referenceBitmap();
398     m_validLayer = true;
399 }
400
401 void TransparencyWin::compositeOpaqueComposite()
402 {
403     if (!m_validLayer)
404         return;
405
406     m_destContext->save();
407
408     SkBitmap* bitmap = const_cast<SkBitmap*>(
409         &bitmapForContext(*m_layerBuffer->context()));
410
411     // This function will be called for WhiteLayer as well, which we don't want
412     // to change.
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])
424                     dest[x] = 0;
425                 else
426                     dest[x] |= (0xFF << SK_A32_SHIFT);
427             }
428         }
429     } else {
430         makeLayerOpaque();
431     }
432
433     SkRect destRect;
434     if (m_transformMode != Untransform) {
435         // We want to use Untransformed space.
436         //
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.
440         SkMatrix identity;
441         identity.reset();
442         m_destContext->setMatrix(identity);
443
444         destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY());
445     } else {
446         destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY());
447     }
448
449     SkPaint paint;
450     paint.setFilterBitmap(true);
451     paint.setAntiAlias(true);
452
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();
460 }
461
462 void TransparencyWin::compositeTextComposite()
463 {
464     if (!m_validLayer)
465         return;
466
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;
474
475             // Apply that alpha to the text color and write the result.
476             row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha));
477         }
478     }
479
480     // Now the layer has text with the proper color and opacity.
481     m_destContext->save();
482
483     // We want to use Untransformed space (see above)
484     SkMatrix identity;
485     identity.reset();
486     m_destContext->setMatrix(identity);
487     SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() };
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     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();
496 }
497
498 void TransparencyWin::makeLayerOpaque()
499 {
500     if (!m_validLayer)
501         return;
502
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;
508     }
509 }
510
511 } // namespace WebCore