• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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