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 "third_party/blink/renderer/platform/graphics/image_data_buffer.h"
34 
35 #include <memory>
36 
37 #include "base/compiler_specific.h"
38 #include "base/memory/ptr_util.h"
39 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
40 #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
41 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
42 #include "third_party/blink/renderer/platform/wtf/text/base64.h"
43 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
44 #include "third_party/blink/renderer/platform/wtf/vector.h"
45 #include "third_party/skia/include/core/SkSurface.h"
46 #include "third_party/skia/include/core/SkSwizzle.h"
47 #include "third_party/skia/include/encode/SkJpegEncoder.h"
48 
49 namespace blink {
50 
ImageDataBuffer(scoped_refptr<StaticBitmapImage> image)51 ImageDataBuffer::ImageDataBuffer(scoped_refptr<StaticBitmapImage> image) {
52   if (!image)
53     return;
54   retained_image_ = image->PaintImageForCurrentFrame().GetSkImage();
55   if (!retained_image_)
56     return;
57 #if defined(MEMORY_SANITIZER)
58   // Test if retained_image has an initialized pixmap.
59   SkPixmap pixmap;
60   if (retained_image_->peekPixels(&pixmap))
61     MSAN_CHECK_MEM_IS_INITIALIZED(pixmap.addr(), pixmap.computeByteSize());
62 #endif
63 
64   if (retained_image_->isTextureBacked() ||
65       retained_image_->isLazyGenerated() ||
66       retained_image_->alphaType() != kUnpremul_SkAlphaType) {
67     // Unpremul is handled upfront, using readPixels, which will correctly clamp
68     // premul color values that would otherwise cause overflows in the skia
69     // encoder unpremul logic.
70     SkColorType colorType = retained_image_->colorType();
71     if (colorType == kRGBA_8888_SkColorType ||
72         colorType == kBGRA_8888_SkColorType)
73       colorType = kN32_SkColorType;  // Work around for bug with JPEG encoder
74     const SkImageInfo info =
75         SkImageInfo::Make(retained_image_->width(), retained_image_->height(),
76                           retained_image_->colorType(), kUnpremul_SkAlphaType,
77                           retained_image_->refColorSpace());
78     const size_t rowBytes = info.minRowBytes();
79     size_t size = info.computeByteSize(rowBytes);
80     if (SkImageInfo::ByteSizeOverflowed(size))
81       return;
82 
83     sk_sp<SkData> data = SkData::MakeUninitialized(size);
84     pixmap_ = {info, data->writable_data(), info.minRowBytes()};
85     if (!retained_image_->readPixels(pixmap_, 0, 0)) {
86       pixmap_.reset();
87       return;
88     }
89     MSAN_CHECK_MEM_IS_INITIALIZED(pixmap_.addr(), pixmap_.computeByteSize());
90     retained_image_ = SkImage::MakeRasterData(info, std::move(data), rowBytes);
91   } else {
92     if (!retained_image_->peekPixels(&pixmap_))
93       return;
94     MSAN_CHECK_MEM_IS_INITIALIZED(pixmap_.addr(), pixmap_.computeByteSize());
95   }
96   is_valid_ = true;
97   size_ = IntSize(image->width(), image->height());
98 }
99 
ImageDataBuffer(const SkPixmap & pixmap)100 ImageDataBuffer::ImageDataBuffer(const SkPixmap& pixmap)
101     : pixmap_(pixmap), size_(IntSize(pixmap.width(), pixmap.height())) {
102   is_valid_ = pixmap_.addr() && !size_.IsEmpty();
103 }
104 
Create(scoped_refptr<StaticBitmapImage> image)105 std::unique_ptr<ImageDataBuffer> ImageDataBuffer::Create(
106     scoped_refptr<StaticBitmapImage> image) {
107   std::unique_ptr<ImageDataBuffer> buffer =
108       base::WrapUnique(new ImageDataBuffer(image));
109   if (!buffer->IsValid())
110     return nullptr;
111   return buffer;
112 }
113 
Create(const SkPixmap & pixmap)114 std::unique_ptr<ImageDataBuffer> ImageDataBuffer::Create(
115     const SkPixmap& pixmap) {
116   std::unique_ptr<ImageDataBuffer> buffer =
117       base::WrapUnique(new ImageDataBuffer(pixmap));
118   if (!buffer->IsValid())
119     return nullptr;
120   return buffer;
121 }
122 
Pixels() const123 const unsigned char* ImageDataBuffer::Pixels() const {
124   DCHECK(is_valid_);
125   return static_cast<const unsigned char*>(pixmap_.addr());
126 }
127 
EncodeImage(const ImageEncodingMimeType mime_type,const double & quality,Vector<unsigned char> * encoded_image) const128 bool ImageDataBuffer::EncodeImage(const ImageEncodingMimeType mime_type,
129                                   const double& quality,
130                                   Vector<unsigned char>* encoded_image) const {
131   return EncodeImageInternal(mime_type, quality, encoded_image, pixmap_);
132 }
133 
EncodeImageInternal(const ImageEncodingMimeType mime_type,const double & quality,Vector<unsigned char> * encoded_image,const SkPixmap & pixmap) const134 bool ImageDataBuffer::EncodeImageInternal(const ImageEncodingMimeType mime_type,
135                                           const double& quality,
136                                           Vector<unsigned char>* encoded_image,
137                                           const SkPixmap& pixmap) const {
138   DCHECK(is_valid_);
139 
140   if (mime_type == kMimeTypeJpeg) {
141     SkJpegEncoder::Options options;
142     options.fQuality = ImageEncoder::ComputeJpegQuality(quality);
143     options.fAlphaOption = SkJpegEncoder::AlphaOption::kBlendOnBlack;
144     if (options.fQuality == 100) {
145       options.fDownsample = SkJpegEncoder::Downsample::k444;
146     }
147     return ImageEncoder::Encode(encoded_image, pixmap, options);
148   }
149 
150   if (mime_type == kMimeTypeWebp) {
151     SkWebpEncoder::Options options = ImageEncoder::ComputeWebpOptions(quality);
152     return ImageEncoder::Encode(encoded_image, pixmap, options);
153   }
154 
155   DCHECK_EQ(mime_type, kMimeTypePng);
156   SkPngEncoder::Options options;
157   options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
158   options.fZLibLevel = 3;
159   return ImageEncoder::Encode(encoded_image, pixmap, options);
160 }
161 
ToDataURL(const ImageEncodingMimeType mime_type,const double & quality) const162 String ImageDataBuffer::ToDataURL(const ImageEncodingMimeType mime_type,
163                                   const double& quality) const {
164   DCHECK(is_valid_);
165 
166   // toDataURL always encodes in sRGB and does not include the color space
167   // information.
168   sk_sp<SkImage> skia_image = nullptr;
169   SkPixmap pixmap = pixmap_;
170   if (pixmap.colorSpace()) {
171     if (!pixmap.colorSpace()->isSRGB()) {
172       skia_image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
173       skia_image = skia_image->makeColorSpace(SkColorSpace::MakeSRGB());
174       if (!skia_image->peekPixels(&pixmap))
175         return "data:,";
176       MSAN_CHECK_MEM_IS_INITIALIZED(pixmap.addr(), pixmap.computeByteSize());
177     }
178     pixmap.setColorSpace(nullptr);
179   }
180 
181   Vector<unsigned char> result;
182   if (!EncodeImageInternal(mime_type, quality, &result, pixmap))
183     return "data:,";
184 
185   return "data:" + ImageEncodingMimeTypeName(mime_type) + ";base64," +
186          Base64Encode(result);
187 }
188 
189 }  // namespace blink
190