• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008, Google Inc. All rights reserved.
3  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "config.h"
34 #include "platform/graphics/ImageBuffer.h"
35 
36 #include "GrContext.h"
37 #include "platform/MIMETypeRegistry.h"
38 #include "platform/geometry/IntRect.h"
39 #include "platform/graphics/BitmapImage.h"
40 #include "platform/graphics/GraphicsContext.h"
41 #include "platform/graphics/GraphicsTypes3D.h"
42 #include "platform/graphics/ImageBufferClient.h"
43 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
44 #include "platform/graphics/gpu/DrawingBuffer.h"
45 #include "platform/graphics/gpu/Extensions3DUtil.h"
46 #include "platform/graphics/skia/NativeImageSkia.h"
47 #include "platform/graphics/skia/SkiaUtils.h"
48 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
49 #include "platform/image-encoders/skia/PNGImageEncoder.h"
50 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
51 #include "public/platform/Platform.h"
52 #include "public/platform/WebExternalTextureMailbox.h"
53 #include "public/platform/WebGraphicsContext3D.h"
54 #include "public/platform/WebGraphicsContext3DProvider.h"
55 #include "third_party/skia/include/core/SkPicture.h"
56 #include "third_party/skia/include/effects/SkTableColorFilter.h"
57 #include "wtf/MathExtras.h"
58 #include "wtf/Vector.h"
59 #include "wtf/text/Base64.h"
60 #include "wtf/text/WTFString.h"
61 
62 namespace blink {
63 
create(PassOwnPtr<ImageBufferSurface> surface)64 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
65 {
66     if (!surface->isValid())
67         return nullptr;
68     return adoptPtr(new ImageBuffer(surface));
69 }
70 
create(const IntSize & size,OpacityMode opacityMode)71 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
72 {
73     OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
74     if (!surface->isValid())
75         return nullptr;
76     return adoptPtr(new ImageBuffer(surface.release()));
77 }
78 
ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)79 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
80     : m_surface(surface)
81     , m_client(0)
82 {
83     if (m_surface->canvas()) {
84         m_context = adoptPtr(new GraphicsContext(m_surface->canvas()));
85         m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque);
86         m_context->setAccelerated(m_surface->isAccelerated());
87     }
88     m_surface->setImageBuffer(this);
89 }
90 
~ImageBuffer()91 ImageBuffer::~ImageBuffer()
92 {
93 }
94 
context() const95 GraphicsContext* ImageBuffer::context() const
96 {
97     if (!isSurfaceValid())
98         return 0;
99     ASSERT(m_context.get());
100     return m_context.get();
101 }
102 
bitmap() const103 const SkBitmap& ImageBuffer::bitmap() const
104 {
105     return m_surface->bitmap();
106 }
107 
isSurfaceValid() const108 bool ImageBuffer::isSurfaceValid() const
109 {
110     return m_surface->isValid();
111 }
112 
isDirty()113 bool ImageBuffer::isDirty()
114 {
115     return m_client ? m_client->isDirty() : false;
116 }
117 
didFinalizeFrame()118 void ImageBuffer::didFinalizeFrame()
119 {
120     if (m_client)
121         m_client->didFinalizeFrame();
122 }
123 
finalizeFrame(const FloatRect & dirtyRect)124 void ImageBuffer::finalizeFrame(const FloatRect &dirtyRect)
125 {
126     m_surface->finalizeFrame(dirtyRect);
127     didFinalizeFrame();
128 }
129 
restoreSurface() const130 bool ImageBuffer::restoreSurface() const
131 {
132     return m_surface->isValid() || m_surface->restore();
133 }
134 
notifySurfaceInvalid()135 void ImageBuffer::notifySurfaceInvalid()
136 {
137     if (m_client)
138         m_client->notifySurfaceInvalid();
139 }
140 
deepSkBitmapCopy(const SkBitmap & bitmap)141 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
142 {
143     SkBitmap tmp;
144     if (!bitmap.deepCopyTo(&tmp))
145         bitmap.copyTo(&tmp, bitmap.colorType());
146 
147     return tmp;
148 }
149 
copyImage(BackingStoreCopy copyBehavior,ScaleBehavior) const150 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
151 {
152     if (!isSurfaceValid())
153         return BitmapImage::create(NativeImageSkia::create());
154 
155     const SkBitmap& bitmap = m_surface->bitmap();
156     return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
157 }
158 
fastCopyImageMode()159 BackingStoreCopy ImageBuffer::fastCopyImageMode()
160 {
161     return DontCopyBackingStore;
162 }
163 
platformLayer() const164 WebLayer* ImageBuffer::platformLayer() const
165 {
166     return m_surface->layer();
167 }
168 
copyToPlatformTexture(WebGraphicsContext3D * context,Platform3DObject texture,GLenum internalFormat,GLenum destType,GLint level,bool premultiplyAlpha,bool flipY)169 bool ImageBuffer::copyToPlatformTexture(WebGraphicsContext3D* context, Platform3DObject texture, GLenum internalFormat, GLenum destType, GLint level, bool premultiplyAlpha, bool flipY)
170 {
171     if (!m_surface->isAccelerated() || !getBackingTexture() || !isSurfaceValid())
172         return false;
173 
174     if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(internalFormat, destType, level))
175         return false;
176 
177     OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
178     if (!provider)
179         return false;
180     WebGraphicsContext3D* sharedContext = provider->context3d();
181     if (!sharedContext)
182         return false;
183 
184     OwnPtr<WebExternalTextureMailbox> mailbox = adoptPtr(new WebExternalTextureMailbox);
185 
186     // Contexts may be in a different share group. We must transfer the texture through a mailbox first
187     sharedContext->genMailboxCHROMIUM(mailbox->name);
188     sharedContext->produceTextureDirectCHROMIUM(getBackingTexture(), GL_TEXTURE_2D, mailbox->name);
189     sharedContext->flush();
190 
191     mailbox->syncPoint = sharedContext->insertSyncPoint();
192 
193     context->waitSyncPoint(mailbox->syncPoint);
194     Platform3DObject sourceTexture = context->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox->name);
195 
196     // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
197     context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
198 
199     // The canvas is stored in an inverted position, so the flip semantics are reversed.
200     context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, !flipY);
201     context->copyTextureCHROMIUM(GL_TEXTURE_2D, sourceTexture, texture, level, internalFormat, destType);
202 
203     context->pixelStorei(GC3D_UNPACK_FLIP_Y_CHROMIUM, false);
204     context->pixelStorei(GC3D_UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
205 
206     context->deleteTexture(sourceTexture);
207 
208     context->flush();
209     sharedContext->waitSyncPoint(context->insertSyncPoint());
210 
211     // Undo grContext texture binding changes introduced in this function
212     provider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
213 
214     return true;
215 }
216 
drawNeedsCopy(GraphicsContext * src,GraphicsContext * dst)217 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
218 {
219     ASSERT(dst);
220     return (src == dst);
221 }
222 
getBackingTexture()223 Platform3DObject ImageBuffer::getBackingTexture()
224 {
225     return m_surface->getBackingTexture();
226 }
227 
didModifyBackingTexture()228 void ImageBuffer::didModifyBackingTexture()
229 {
230     m_surface->didModifyBackingTexture();
231 }
232 
copyRenderingResultsFromDrawingBuffer(DrawingBuffer * drawingBuffer,bool fromFrontBuffer)233 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer, bool fromFrontBuffer)
234 {
235     if (!drawingBuffer)
236         return false;
237     OwnPtr<WebGraphicsContext3DProvider> provider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
238     if (!provider)
239         return false;
240     WebGraphicsContext3D* context3D = provider->context3d();
241     Platform3DObject tex = m_surface->getBackingTexture();
242     if (!context3D || !tex)
243         return false;
244 
245     m_surface->invalidateCachedBitmap();
246     bool result = drawingBuffer->copyToPlatformTexture(context3D, tex, GL_RGBA,
247         GL_UNSIGNED_BYTE, 0, true, false, fromFrontBuffer);
248 
249     if (result) {
250         m_surface->didModifyBackingTexture();
251     }
252 
253     return result;
254 }
255 
draw(GraphicsContext * context,const FloatRect & destRect,const FloatRect * srcPtr,CompositeOperator op,WebBlendMode blendMode)256 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect* srcPtr, CompositeOperator op, WebBlendMode blendMode)
257 {
258     if (!isSurfaceValid())
259         return;
260 
261     FloatRect srcRect = srcPtr ? *srcPtr : FloatRect(FloatPoint(), size());
262     RefPtr<SkPicture> picture = m_surface->getPicture();
263     if (picture) {
264         context->drawPicture(picture.release(), destRect, srcRect, op, blendMode);
265         return;
266     }
267 
268     SkBitmap bitmap = m_surface->bitmap();
269     // For ImageBufferSurface that enables cachedBitmap, Use the cached Bitmap for CPU side usage
270     // if it is available, otherwise generate and use it.
271     if (!context->isAccelerated() && m_surface->isAccelerated() && m_surface->cachedBitmapEnabled() && isSurfaceValid()) {
272         m_surface->updateCachedBitmapIfNeeded();
273         bitmap = m_surface->cachedBitmap();
274     }
275 
276     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
277 
278     context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation);
279 }
280 
flush()281 void ImageBuffer::flush()
282 {
283     if (m_surface->canvas()) {
284         m_surface->canvas()->flush();
285     }
286 }
287 
drawPattern(GraphicsContext * context,const FloatRect & srcRect,const FloatSize & scale,const FloatPoint & phase,CompositeOperator op,const FloatRect & destRect,WebBlendMode blendMode,const IntSize & repeatSpacing)288 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale,
289     const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, WebBlendMode blendMode, const IntSize& repeatSpacing)
290 {
291     if (!isSurfaceValid())
292         return;
293 
294     const SkBitmap& bitmap = m_surface->bitmap();
295     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
296     image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing);
297 }
298 
transformColorSpace(ColorSpace srcColorSpace,ColorSpace dstColorSpace)299 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
300 {
301     const uint8_t* lookUpTable = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
302     if (!lookUpTable)
303         return;
304 
305     // FIXME: Disable color space conversions on accelerated canvases (for now).
306     if (context()->isAccelerated() || !isSurfaceValid())
307         return;
308 
309     const SkBitmap& bitmap = m_surface->bitmap();
310     if (bitmap.isNull())
311         return;
312 
313     ASSERT(bitmap.colorType() == kN32_SkColorType);
314     IntSize size = m_surface->size();
315     SkAutoLockPixels bitmapLock(bitmap);
316     for (int y = 0; y < size.height(); ++y) {
317         uint32_t* srcRow = bitmap.getAddr32(0, y);
318         for (int x = 0; x < size.width(); ++x) {
319             SkColor color = SkPMColorToColor(srcRow[x]);
320             srcRow[x] = SkPreMultiplyARGB(
321                 SkColorGetA(color),
322                 lookUpTable[SkColorGetR(color)],
323                 lookUpTable[SkColorGetG(color)],
324                 lookUpTable[SkColorGetB(color)]);
325         }
326     }
327 }
328 
createColorSpaceFilter(ColorSpace srcColorSpace,ColorSpace dstColorSpace)329 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
330     ColorSpace dstColorSpace)
331 {
332     const uint8_t* lut = ColorSpaceUtilities::getConversionLUT(dstColorSpace, srcColorSpace);
333     if (!lut)
334         return nullptr;
335 
336     return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
337 }
338 
getImageData(Multiply multiplied,const IntRect & rect) const339 PassRefPtr<Uint8ClampedArray> ImageBuffer::getImageData(Multiply multiplied, const IntRect& rect) const
340 {
341     if (!isSurfaceValid())
342         return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
343 
344     float area = 4.0f * rect.width() * rect.height();
345     if (area > static_cast<float>(std::numeric_limits<int>::max()))
346         return nullptr;
347 
348     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
349 
350     if (rect.x() < 0
351         || rect.y() < 0
352         || rect.maxX() > m_surface->size().width()
353         || rect.maxY() > m_surface->size().height())
354         result->zeroFill();
355 
356     SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
357     SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), kRGBA_8888_SkColorType, alphaType);
358 
359     m_surface->willAccessPixels();
360     context()->readPixels(info, result->data(), 4 * rect.width(), rect.x(), rect.y());
361     return result.release();
362 }
363 
putByteArray(Multiply multiplied,Uint8ClampedArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)364 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
365 {
366     if (!isSurfaceValid())
367         return;
368 
369     ASSERT(sourceRect.width() > 0);
370     ASSERT(sourceRect.height() > 0);
371 
372     int originX = sourceRect.x();
373     int destX = destPoint.x() + sourceRect.x();
374     ASSERT(destX >= 0);
375     ASSERT(destX < m_surface->size().width());
376     ASSERT(originX >= 0);
377     ASSERT(originX < sourceRect.maxX());
378 
379     int originY = sourceRect.y();
380     int destY = destPoint.y() + sourceRect.y();
381     ASSERT(destY >= 0);
382     ASSERT(destY < m_surface->size().height());
383     ASSERT(originY >= 0);
384     ASSERT(originY < sourceRect.maxY());
385 
386     const size_t srcBytesPerRow = 4 * sourceSize.width();
387     const void* srcAddr = source->data() + originY * srcBytesPerRow + originX * 4;
388     SkAlphaType alphaType = (multiplied == Premultiplied) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
389     SkImageInfo info = SkImageInfo::Make(sourceRect.width(), sourceRect.height(), kRGBA_8888_SkColorType, alphaType);
390 
391     m_surface->willAccessPixels();
392 
393     context()->writePixels(info, srcAddr, srcBytesPerRow, destX, destY);
394 }
395 
396 template <typename T>
encodeImage(T & source,const String & mimeType,const double * quality,Vector<char> * output)397 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
398 {
399     Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
400 
401     if (mimeType == "image/jpeg") {
402         int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
403         if (quality && *quality >= 0.0 && *quality <= 1.0)
404             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
405         if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage))
406             return false;
407     } else if (mimeType == "image/webp") {
408         int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
409         if (quality && *quality >= 0.0 && *quality <= 1.0)
410             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
411         if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage))
412             return false;
413     } else {
414         if (!PNGImageEncoder::encode(source, encodedImage))
415             return false;
416         ASSERT(mimeType == "image/png");
417     }
418 
419     return true;
420 }
421 
toDataURL(const String & mimeType,const double * quality) const422 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
423 {
424     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
425 
426     Vector<char> encodedImage;
427     if (!isSurfaceValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
428         return "data:,";
429 
430     return "data:" + mimeType + ";base64," + base64Encode(encodedImage);
431 }
432 
ImageDataToDataURL(const ImageDataBuffer & imageData,const String & mimeType,const double * quality)433 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
434 {
435     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
436 
437     Vector<char> encodedImage;
438     if (!encodeImage(imageData, mimeType, quality, &encodedImage))
439         return "data:,";
440 
441     return "data:" + mimeType + ";base64," + base64Encode(encodedImage);
442 }
443 
444 } // namespace blink
445