• 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 "platform/MIMETypeRegistry.h"
37 #include "platform/geometry/IntRect.h"
38 #include "platform/graphics/BitmapImage.h"
39 #include "platform/graphics/Extensions3D.h"
40 #include "platform/graphics/GraphicsContext.h"
41 #include "platform/graphics/GraphicsContext3D.h"
42 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
43 #include "platform/graphics/gpu/DrawingBuffer.h"
44 #include "platform/graphics/gpu/SharedGraphicsContext3D.h"
45 #include "platform/graphics/skia/NativeImageSkia.h"
46 #include "platform/graphics/skia/SkiaUtils.h"
47 #include "platform/image-encoders/skia/JPEGImageEncoder.h"
48 #include "platform/image-encoders/skia/PNGImageEncoder.h"
49 #include "platform/image-encoders/skia/WEBPImageEncoder.h"
50 #include "public/platform/Platform.h"
51 #include "third_party/skia/include/effects/SkTableColorFilter.h"
52 #include "wtf/MathExtras.h"
53 #include "wtf/text/Base64.h"
54 #include "wtf/text/WTFString.h"
55 
56 using namespace std;
57 
58 namespace WebCore {
59 
create(PassOwnPtr<ImageBufferSurface> surface)60 PassOwnPtr<ImageBuffer> ImageBuffer::create(PassOwnPtr<ImageBufferSurface> surface)
61 {
62     if (!surface->isValid())
63         return nullptr;
64     return adoptPtr(new ImageBuffer(surface));
65 }
66 
create(const IntSize & size,OpacityMode opacityMode)67 PassOwnPtr<ImageBuffer> ImageBuffer::create(const IntSize& size, OpacityMode opacityMode)
68 {
69     OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(size, opacityMode));
70     if (!surface->isValid())
71         return nullptr;
72     return adoptPtr(new ImageBuffer(surface.release()));
73 }
74 
ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)75 ImageBuffer::ImageBuffer(PassOwnPtr<ImageBufferSurface> surface)
76     : m_surface(surface)
77 {
78     if (m_surface->canvas()) {
79         m_context = adoptPtr(new GraphicsContext(m_surface->canvas()));
80         m_context->setCertainlyOpaque(m_surface->opacityMode() == Opaque);
81         m_context->setAccelerated(m_surface->isAccelerated());
82     }
83 }
84 
~ImageBuffer()85 ImageBuffer::~ImageBuffer()
86 {
87 }
88 
context() const89 GraphicsContext* ImageBuffer::context() const
90 {
91     m_surface->willUse();
92     ASSERT(m_context.get());
93     return m_context.get();
94 }
95 
bitmap() const96 const SkBitmap& ImageBuffer::bitmap() const
97 {
98     m_surface->willUse();
99     return m_surface->bitmap();
100 }
101 
isValid() const102 bool ImageBuffer::isValid() const
103 {
104     return m_surface->isValid();
105 }
106 
deepSkBitmapCopy(const SkBitmap & bitmap)107 static SkBitmap deepSkBitmapCopy(const SkBitmap& bitmap)
108 {
109     SkBitmap tmp;
110     if (!bitmap.deepCopyTo(&tmp, bitmap.config()))
111         bitmap.copyTo(&tmp, bitmap.config());
112 
113     return tmp;
114 }
115 
copyImage(BackingStoreCopy copyBehavior,ScaleBehavior) const116 PassRefPtr<Image> ImageBuffer::copyImage(BackingStoreCopy copyBehavior, ScaleBehavior) const
117 {
118     if (!isValid())
119         return BitmapImage::create(NativeImageSkia::create());
120 
121     const SkBitmap& bitmap = m_surface->bitmap();
122     return BitmapImage::create(NativeImageSkia::create(copyBehavior == CopyBackingStore ? deepSkBitmapCopy(bitmap) : bitmap));
123 }
124 
fastCopyImageMode()125 BackingStoreCopy ImageBuffer::fastCopyImageMode()
126 {
127     return DontCopyBackingStore;
128 }
129 
platformLayer() const130 blink::WebLayer* ImageBuffer::platformLayer() const
131 {
132     return m_surface->layer();
133 }
134 
copyToPlatformTexture(GraphicsContext3D & context,Platform3DObject texture,GC3Denum internalFormat,GC3Denum destType,GC3Dint level,bool premultiplyAlpha,bool flipY)135 bool ImageBuffer::copyToPlatformTexture(GraphicsContext3D& context, Platform3DObject texture, GC3Denum internalFormat, GC3Denum destType, GC3Dint level, bool premultiplyAlpha, bool flipY)
136 {
137     if (!m_surface->isAccelerated() || !platformLayer() || !isValid())
138         return false;
139 
140     if (!context.makeContextCurrent())
141         return false;
142 
143     Extensions3D* extensions = context.extensions();
144     if (!extensions->supports("GL_CHROMIUM_copy_texture") || !extensions->supports("GL_CHROMIUM_flipy")
145         || !extensions->canUseCopyTextureCHROMIUM(internalFormat, destType, level))
146         return false;
147 
148     // The canvas is stored in a premultiplied format, so unpremultiply if necessary.
149     context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, !premultiplyAlpha);
150 
151     // The canvas is stored in an inverted position, so the flip semantics are reversed.
152     context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, !flipY);
153     extensions->copyTextureCHROMIUM(GL_TEXTURE_2D, getBackingTexture(), texture, level, internalFormat, destType);
154 
155     context.pixelStorei(Extensions3D::UNPACK_FLIP_Y_CHROMIUM, false);
156     context.pixelStorei(Extensions3D::UNPACK_UNPREMULTIPLY_ALPHA_CHROMIUM, false);
157     context.flush();
158     return true;
159 }
160 
drawNeedsCopy(GraphicsContext * src,GraphicsContext * dst)161 static bool drawNeedsCopy(GraphicsContext* src, GraphicsContext* dst)
162 {
163     ASSERT(dst);
164     return (src == dst);
165 }
166 
getBackingTexture()167 Platform3DObject ImageBuffer::getBackingTexture()
168 {
169     return m_surface->getBackingTexture();
170 }
171 
copyRenderingResultsFromDrawingBuffer(DrawingBuffer * drawingBuffer)172 bool ImageBuffer::copyRenderingResultsFromDrawingBuffer(DrawingBuffer* drawingBuffer)
173 {
174     if (!drawingBuffer)
175         return false;
176     RefPtr<GraphicsContext3D> context3D = SharedGraphicsContext3D::get();
177     Platform3DObject tex = m_surface->getBackingTexture();
178     if (!context3D || !tex)
179         return false;
180 
181     return drawingBuffer->copyToPlatformTexture(*(context3D.get()), tex, GL_RGBA,
182         GL_UNSIGNED_BYTE, 0, true, false);
183 }
184 
draw(GraphicsContext * context,const FloatRect & destRect,const FloatRect & srcRect,CompositeOperator op,blink::WebBlendMode blendMode,bool useLowQualityScale)185 void ImageBuffer::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect,
186     CompositeOperator op, blink::WebBlendMode blendMode, bool useLowQualityScale)
187 {
188     if (!isValid())
189         return;
190 
191     const SkBitmap& bitmap = m_surface->bitmap();
192     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
193     context->drawImage(image.get(), destRect, srcRect, op, blendMode, DoNotRespectImageOrientation, useLowQualityScale);
194 }
195 
flush()196 void ImageBuffer::flush()
197 {
198     if (m_surface->canvas()) {
199         m_surface->canvas()->flush();
200     }
201 }
202 
drawPattern(GraphicsContext * context,const FloatRect & srcRect,const FloatSize & scale,const FloatPoint & phase,CompositeOperator op,const FloatRect & destRect,blink::WebBlendMode blendMode,const IntSize & repeatSpacing)203 void ImageBuffer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const FloatSize& scale,
204     const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect, blink::WebBlendMode blendMode, const IntSize& repeatSpacing)
205 {
206     if (!isValid())
207         return;
208 
209     const SkBitmap& bitmap = m_surface->bitmap();
210     RefPtr<Image> image = BitmapImage::create(NativeImageSkia::create(drawNeedsCopy(m_context.get(), context) ? deepSkBitmapCopy(bitmap) : bitmap));
211     image->drawPattern(context, srcRect, scale, phase, op, destRect, blendMode, repeatSpacing);
212 }
213 
getLinearRgbLUT()214 static const Vector<uint8_t>& getLinearRgbLUT()
215 {
216     DEFINE_STATIC_LOCAL(Vector<uint8_t>, linearRgbLUT, ());
217     if (linearRgbLUT.isEmpty()) {
218         linearRgbLUT.reserveCapacity(256);
219         for (unsigned i = 0; i < 256; i++) {
220             float color = i  / 255.0f;
221             color = (color <= 0.04045f ? color / 12.92f : pow((color + 0.055f) / 1.055f, 2.4f));
222             color = std::max(0.0f, color);
223             color = std::min(1.0f, color);
224             linearRgbLUT.append(static_cast<uint8_t>(round(color * 255)));
225         }
226     }
227     return linearRgbLUT;
228 }
229 
getDeviceRgbLUT()230 static const Vector<uint8_t>& getDeviceRgbLUT()
231 {
232     DEFINE_STATIC_LOCAL(Vector<uint8_t>, deviceRgbLUT, ());
233     if (deviceRgbLUT.isEmpty()) {
234         deviceRgbLUT.reserveCapacity(256);
235         for (unsigned i = 0; i < 256; i++) {
236             float color = i / 255.0f;
237             color = (powf(color, 1.0f / 2.4f) * 1.055f) - 0.055f;
238             color = std::max(0.0f, color);
239             color = std::min(1.0f, color);
240             deviceRgbLUT.append(static_cast<uint8_t>(round(color * 255)));
241         }
242     }
243     return deviceRgbLUT;
244 }
245 
transformColorSpace(ColorSpace srcColorSpace,ColorSpace dstColorSpace)246 void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace)
247 {
248     if (srcColorSpace == dstColorSpace)
249         return;
250 
251     // only sRGB <-> linearRGB are supported at the moment
252     if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB)
253         || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB))
254         return;
255 
256     // FIXME: Disable color space conversions on accelerated canvases (for now).
257     if (context()->isAccelerated() || !isValid())
258         return;
259 
260     const SkBitmap& bitmap = m_surface->bitmap();
261     if (bitmap.isNull())
262         return;
263 
264     const Vector<uint8_t>& lookUpTable = dstColorSpace == ColorSpaceLinearRGB ?
265         getLinearRgbLUT() : getDeviceRgbLUT();
266 
267     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
268     IntSize size = m_surface->size();
269     SkAutoLockPixels bitmapLock(bitmap);
270     for (int y = 0; y < size.height(); ++y) {
271         uint32_t* srcRow = bitmap.getAddr32(0, y);
272         for (int x = 0; x < size.width(); ++x) {
273             SkColor color = SkPMColorToColor(srcRow[x]);
274             srcRow[x] = SkPreMultiplyARGB(
275                 SkColorGetA(color),
276                 lookUpTable[SkColorGetR(color)],
277                 lookUpTable[SkColorGetG(color)],
278                 lookUpTable[SkColorGetB(color)]);
279         }
280     }
281 }
282 
createColorSpaceFilter(ColorSpace srcColorSpace,ColorSpace dstColorSpace)283 PassRefPtr<SkColorFilter> ImageBuffer::createColorSpaceFilter(ColorSpace srcColorSpace,
284     ColorSpace dstColorSpace)
285 {
286     if ((srcColorSpace == dstColorSpace)
287         || (srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB)
288         || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB))
289         return 0;
290 
291     const uint8_t* lut = 0;
292     if (dstColorSpace == ColorSpaceLinearRGB)
293         lut = &getLinearRgbLUT()[0];
294     else if (dstColorSpace == ColorSpaceDeviceRGB)
295         lut = &getDeviceRgbLUT()[0];
296     else
297         return 0;
298 
299     return adoptRef(SkTableColorFilter::CreateARGB(0, lut, lut, lut));
300 }
301 
302 template <Multiply multiplied>
getImageData(const IntRect & rect,GraphicsContext * context,const IntSize & size)303 PassRefPtr<Uint8ClampedArray> getImageData(const IntRect& rect, GraphicsContext* context, const IntSize& size)
304 {
305     float area = 4.0f * rect.width() * rect.height();
306     if (area > static_cast<float>(std::numeric_limits<int>::max()))
307         return 0;
308 
309     RefPtr<Uint8ClampedArray> result = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
310 
311     unsigned char* data = result->data();
312 
313     if (rect.x() < 0
314         || rect.y() < 0
315         || rect.maxX() > size.width()
316         || rect.maxY() > size.height())
317         result->zeroFill();
318 
319     unsigned destBytesPerRow = 4 * rect.width();
320     SkBitmap destBitmap;
321     destBitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height(), destBytesPerRow);
322     destBitmap.setPixels(data);
323 
324     SkCanvas::Config8888 config8888;
325     if (multiplied == Premultiplied)
326         config8888 = SkCanvas::kRGBA_Premul_Config8888;
327     else
328         config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
329 
330     context->readPixels(&destBitmap, rect.x(), rect.y(), config8888);
331     return result.release();
332 }
333 
getUnmultipliedImageData(const IntRect & rect) const334 PassRefPtr<Uint8ClampedArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const
335 {
336     if (!isValid())
337         return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
338     return getImageData<Unmultiplied>(rect, context(), m_surface->size());
339 }
340 
getPremultipliedImageData(const IntRect & rect) const341 PassRefPtr<Uint8ClampedArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const
342 {
343     if (!isValid())
344         return Uint8ClampedArray::create(rect.width() * rect.height() * 4);
345     return getImageData<Premultiplied>(rect, context(), m_surface->size());
346 }
347 
putByteArray(Multiply multiplied,Uint8ClampedArray * source,const IntSize & sourceSize,const IntRect & sourceRect,const IntPoint & destPoint)348 void ImageBuffer::putByteArray(Multiply multiplied, Uint8ClampedArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint)
349 {
350     if (!isValid())
351         return;
352 
353     ASSERT(sourceRect.width() > 0);
354     ASSERT(sourceRect.height() > 0);
355 
356     int originX = sourceRect.x();
357     int destX = destPoint.x() + sourceRect.x();
358     ASSERT(destX >= 0);
359     ASSERT(destX < m_surface->size().width());
360     ASSERT(originX >= 0);
361     ASSERT(originX < sourceRect.maxX());
362 
363     int endX = destPoint.x() + sourceRect.maxX();
364     ASSERT(endX <= m_surface->size().width());
365 
366     int numColumns = endX - destX;
367 
368     int originY = sourceRect.y();
369     int destY = destPoint.y() + sourceRect.y();
370     ASSERT(destY >= 0);
371     ASSERT(destY < m_surface->size().height());
372     ASSERT(originY >= 0);
373     ASSERT(originY < sourceRect.maxY());
374 
375     int endY = destPoint.y() + sourceRect.maxY();
376     ASSERT(endY <= m_surface->size().height());
377     int numRows = endY - destY;
378 
379     unsigned srcBytesPerRow = 4 * sourceSize.width();
380     SkBitmap srcBitmap;
381     srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow);
382     srcBitmap.setPixels(source->data() + originY * srcBytesPerRow + originX * 4);
383 
384     SkCanvas::Config8888 config8888;
385     if (multiplied == Premultiplied)
386         config8888 = SkCanvas::kRGBA_Premul_Config8888;
387     else
388         config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
389 
390     context()->writePixels(srcBitmap, destX, destY, config8888);
391 }
392 
393 template <typename T>
encodeImage(T & source,const String & mimeType,const double * quality,Vector<char> * output)394 static bool encodeImage(T& source, const String& mimeType, const double* quality, Vector<char>* output)
395 {
396     Vector<unsigned char>* encodedImage = reinterpret_cast<Vector<unsigned char>*>(output);
397 
398     if (mimeType == "image/jpeg") {
399         int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
400         if (quality && *quality >= 0.0 && *quality <= 1.0)
401             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
402         if (!JPEGImageEncoder::encode(source, compressionQuality, encodedImage))
403             return false;
404     } else if (mimeType == "image/webp") {
405         int compressionQuality = WEBPImageEncoder::DefaultCompressionQuality;
406         if (quality && *quality >= 0.0 && *quality <= 1.0)
407             compressionQuality = static_cast<int>(*quality * 100 + 0.5);
408         if (!WEBPImageEncoder::encode(source, compressionQuality, encodedImage))
409             return false;
410     } else {
411         if (!PNGImageEncoder::encode(source, encodedImage))
412             return false;
413         ASSERT(mimeType == "image/png");
414     }
415 
416     return true;
417 }
418 
toDataURL(const String & mimeType,const double * quality) const419 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
420 {
421     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
422 
423     Vector<char> encodedImage;
424     if (!isValid() || !encodeImage(m_surface->bitmap(), mimeType, quality, &encodedImage))
425         return "data:,";
426     Vector<char> base64Data;
427     base64Encode(encodedImage, base64Data);
428 
429     return "data:" + mimeType + ";base64," + base64Data;
430 }
431 
ImageDataToDataURL(const ImageDataBuffer & imageData,const String & mimeType,const double * quality)432 String ImageDataToDataURL(const ImageDataBuffer& imageData, const String& mimeType, const double* quality)
433 {
434     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
435 
436     Vector<char> encodedImage;
437     if (!encodeImage(imageData, mimeType, quality, &encodedImage))
438         return "data:,";
439 
440     Vector<char> base64Data;
441     base64Encode(encodedImage, base64Data);
442 
443     return "data:" + mimeType + ";base64," + base64Data;
444 }
445 
446 } // namespace WebCore
447