1 // Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "libcef/browser/image_impl.h"
6
7 #include <algorithm>
8
9 #include "skia/ext/skia_utils_base.h"
10 #include "ui/gfx/codec/jpeg_codec.h"
11 #include "ui/gfx/codec/png_codec.h"
12 #include "ui/gfx/image/image_png_rep.h"
13 #include "ui/gfx/image/image_skia.h"
14
15 namespace {
16
GetSkColorType(cef_color_type_t color_type)17 SkColorType GetSkColorType(cef_color_type_t color_type) {
18 switch (color_type) {
19 case CEF_COLOR_TYPE_RGBA_8888:
20 return kRGBA_8888_SkColorType;
21 case CEF_COLOR_TYPE_BGRA_8888:
22 return kBGRA_8888_SkColorType;
23 default:
24 break;
25 }
26
27 NOTREACHED();
28 return kUnknown_SkColorType;
29 }
30
GetSkAlphaType(cef_alpha_type_t alpha_type)31 SkAlphaType GetSkAlphaType(cef_alpha_type_t alpha_type) {
32 switch (alpha_type) {
33 case CEF_ALPHA_TYPE_OPAQUE:
34 return kOpaque_SkAlphaType;
35 case CEF_ALPHA_TYPE_PREMULTIPLIED:
36 return kPremul_SkAlphaType;
37 case CEF_ALPHA_TYPE_POSTMULTIPLIED:
38 return kUnpremul_SkAlphaType;
39 default:
40 break;
41 }
42
43 NOTREACHED();
44 return kUnknown_SkAlphaType;
45 }
46
47 // Compress as PNG. Requires post-multiplied alpha.
PNGMethod(bool with_transparency,const SkBitmap & bitmap,std::vector<unsigned char> * compressed)48 bool PNGMethod(bool with_transparency,
49 const SkBitmap& bitmap,
50 std::vector<unsigned char>* compressed) {
51 return gfx::PNGCodec::Encode(
52 reinterpret_cast<unsigned char*>(bitmap.getPixels()),
53 bitmap.colorType() == kBGRA_8888_SkColorType ? gfx::PNGCodec::FORMAT_BGRA
54 : gfx::PNGCodec::FORMAT_RGBA,
55 gfx::Size(bitmap.width(), bitmap.height()),
56 static_cast<int>(bitmap.rowBytes()),
57 bitmap.alphaType() == kOpaque_SkAlphaType || !with_transparency,
58 std::vector<gfx::PNGCodec::Comment>(), compressed);
59 }
60
61 // Compress as JPEG. This internally uses JCS_EXT_RGBX or JCS_EXT_BGRX which
62 // causes the alpha channel to be ignored. Requires post-multiplied alpha.
JPEGMethod(int quality,const SkBitmap & bitmap,std::vector<unsigned char> * compressed)63 bool JPEGMethod(int quality,
64 const SkBitmap& bitmap,
65 std::vector<unsigned char>* compressed) {
66 return gfx::JPEGCodec::Encode(bitmap, quality, compressed);
67 }
68
69 } // namespace
70
71 // static
CreateImage()72 CefRefPtr<CefImage> CefImage::CreateImage() {
73 return new CefImageImpl();
74 }
75
CefImageImpl(const gfx::ImageSkia & image_skia)76 CefImageImpl::CefImageImpl(const gfx::ImageSkia& image_skia)
77 : image_(image_skia) {}
78
IsEmpty()79 bool CefImageImpl::IsEmpty() {
80 base::AutoLock lock_scope(lock_);
81 return image_.IsEmpty();
82 }
83
IsSame(CefRefPtr<CefImage> that)84 bool CefImageImpl::IsSame(CefRefPtr<CefImage> that) {
85 CefImageImpl* that_impl = static_cast<CefImageImpl*>(that.get());
86 if (!that_impl)
87 return false;
88
89 // Quick check for the same object.
90 if (this == that_impl)
91 return true;
92
93 base::AutoLock lock_scope(lock_);
94 return image_.AsImageSkia().BackedBySameObjectAs(
95 that_impl->image_.AsImageSkia());
96 }
97
AddBitmap(float scale_factor,int pixel_width,int pixel_height,cef_color_type_t color_type,cef_alpha_type_t alpha_type,const void * pixel_data,size_t pixel_data_size)98 bool CefImageImpl::AddBitmap(float scale_factor,
99 int pixel_width,
100 int pixel_height,
101 cef_color_type_t color_type,
102 cef_alpha_type_t alpha_type,
103 const void* pixel_data,
104 size_t pixel_data_size) {
105 const SkColorType ct = GetSkColorType(color_type);
106 const SkAlphaType at = GetSkAlphaType(alpha_type);
107
108 // Make sure the client passed in the expected values.
109 if (ct != kBGRA_8888_SkColorType && ct != kRGBA_8888_SkColorType)
110 return false;
111 if (pixel_data_size != pixel_width * pixel_height * 4U)
112 return false;
113
114 SkBitmap bitmap;
115 if (!bitmap.tryAllocPixels(
116 SkImageInfo::Make(pixel_width, pixel_height, ct, at))) {
117 return false;
118 }
119
120 DCHECK_EQ(pixel_data_size, bitmap.computeByteSize());
121 memcpy(bitmap.getPixels(), pixel_data, pixel_data_size);
122
123 return AddBitmap(scale_factor, bitmap);
124 }
125
AddPNG(float scale_factor,const void * png_data,size_t png_data_size)126 bool CefImageImpl::AddPNG(float scale_factor,
127 const void* png_data,
128 size_t png_data_size) {
129 SkBitmap bitmap;
130 if (!gfx::PNGCodec::Decode(static_cast<const unsigned char*>(png_data),
131 png_data_size, &bitmap)) {
132 return false;
133 }
134
135 return AddBitmap(scale_factor, bitmap);
136 }
137
AddJPEG(float scale_factor,const void * jpeg_data,size_t jpeg_data_size)138 bool CefImageImpl::AddJPEG(float scale_factor,
139 const void* jpeg_data,
140 size_t jpeg_data_size) {
141 std::unique_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(
142 static_cast<const unsigned char*>(jpeg_data), jpeg_data_size));
143 if (!bitmap.get())
144 return false;
145
146 return AddBitmap(scale_factor, *bitmap);
147 }
148
GetWidth()149 size_t CefImageImpl::GetWidth() {
150 base::AutoLock lock_scope(lock_);
151 return image_.Width();
152 }
153
GetHeight()154 size_t CefImageImpl::GetHeight() {
155 base::AutoLock lock_scope(lock_);
156 return image_.Height();
157 }
158
HasRepresentation(float scale_factor)159 bool CefImageImpl::HasRepresentation(float scale_factor) {
160 base::AutoLock lock_scope(lock_);
161 return image_.AsImageSkia().HasRepresentation(scale_factor);
162 }
163
RemoveRepresentation(float scale_factor)164 bool CefImageImpl::RemoveRepresentation(float scale_factor) {
165 base::AutoLock lock_scope(lock_);
166 gfx::ImageSkia image_skia = image_.AsImageSkia();
167 if (image_skia.HasRepresentation(scale_factor)) {
168 image_skia.RemoveRepresentation(scale_factor);
169 return true;
170 }
171 return false;
172 }
173
GetRepresentationInfo(float scale_factor,float & actual_scale_factor,int & pixel_width,int & pixel_height)174 bool CefImageImpl::GetRepresentationInfo(float scale_factor,
175 float& actual_scale_factor,
176 int& pixel_width,
177 int& pixel_height) {
178 base::AutoLock lock_scope(lock_);
179 gfx::ImageSkia image_skia = image_.AsImageSkia();
180 if (image_skia.isNull())
181 return false;
182
183 const gfx::ImageSkiaRep& rep = image_skia.GetRepresentation(scale_factor);
184 if (rep.is_null())
185 return false;
186
187 actual_scale_factor = rep.scale();
188 pixel_width = rep.GetBitmap().width();
189 pixel_height = rep.GetBitmap().height();
190 return true;
191 }
192
GetAsBitmap(float scale_factor,cef_color_type_t color_type,cef_alpha_type_t alpha_type,int & pixel_width,int & pixel_height)193 CefRefPtr<CefBinaryValue> CefImageImpl::GetAsBitmap(float scale_factor,
194 cef_color_type_t color_type,
195 cef_alpha_type_t alpha_type,
196 int& pixel_width,
197 int& pixel_height) {
198 const SkColorType desired_ct = GetSkColorType(color_type);
199 const SkAlphaType desired_at = GetSkAlphaType(alpha_type);
200
201 base::AutoLock lock_scope(lock_);
202 const SkBitmap* bitmap = GetBitmap(scale_factor);
203 if (!bitmap)
204 return nullptr;
205
206 DCHECK(bitmap->readyToDraw());
207
208 pixel_width = bitmap->width();
209 pixel_height = bitmap->height();
210
211 if (bitmap->colorType() == desired_ct && bitmap->alphaType() == desired_at) {
212 // No conversion necessary.
213 return CefBinaryValue::Create(bitmap->getPixels(),
214 bitmap->computeByteSize());
215 } else {
216 SkBitmap desired_bitmap;
217 if (!ConvertBitmap(*bitmap, &desired_bitmap, desired_ct, desired_at))
218 return nullptr;
219 DCHECK(desired_bitmap.readyToDraw());
220 return CefBinaryValue::Create(desired_bitmap.getPixels(),
221 desired_bitmap.computeByteSize());
222 }
223 }
224
GetAsPNG(float scale_factor,bool with_transparency,int & pixel_width,int & pixel_height)225 CefRefPtr<CefBinaryValue> CefImageImpl::GetAsPNG(float scale_factor,
226 bool with_transparency,
227 int& pixel_width,
228 int& pixel_height) {
229 base::AutoLock lock_scope(lock_);
230 const SkBitmap* bitmap = GetBitmap(scale_factor);
231 if (!bitmap)
232 return nullptr;
233
234 std::vector<unsigned char> compressed;
235 if (!WritePNG(*bitmap, &compressed, with_transparency))
236 return nullptr;
237
238 pixel_width = bitmap->width();
239 pixel_height = bitmap->height();
240
241 return CefBinaryValue::Create(&compressed.front(), compressed.size());
242 }
243
GetAsJPEG(float scale_factor,int quality,int & pixel_width,int & pixel_height)244 CefRefPtr<CefBinaryValue> CefImageImpl::GetAsJPEG(float scale_factor,
245 int quality,
246 int& pixel_width,
247 int& pixel_height) {
248 base::AutoLock lock_scope(lock_);
249 const SkBitmap* bitmap = GetBitmap(scale_factor);
250 if (!bitmap)
251 return nullptr;
252
253 std::vector<unsigned char> compressed;
254 if (!WriteJPEG(*bitmap, &compressed, quality))
255 return nullptr;
256
257 pixel_width = bitmap->width();
258 pixel_height = bitmap->height();
259
260 return CefBinaryValue::Create(&compressed.front(), compressed.size());
261 }
262
AddBitmaps(int32_t scale_1x_size,const std::vector<SkBitmap> & bitmaps)263 void CefImageImpl::AddBitmaps(int32_t scale_1x_size,
264 const std::vector<SkBitmap>& bitmaps) {
265 if (scale_1x_size == 0) {
266 // Set the scale 1x size to the smallest bitmap pixel size.
267 int32_t min_size = std::numeric_limits<int32_t>::max();
268 for (const SkBitmap& bitmap : bitmaps) {
269 const int32_t size = std::max(bitmap.width(), bitmap.height());
270 if (size < min_size)
271 min_size = size;
272 }
273 scale_1x_size = min_size;
274 }
275
276 for (const SkBitmap& bitmap : bitmaps) {
277 const int32_t size = std::max(bitmap.width(), bitmap.height());
278 const float scale_factor =
279 static_cast<float>(size) / static_cast<float>(scale_1x_size);
280 AddBitmap(scale_factor, bitmap);
281 }
282 }
283
GetForced1xScaleRepresentation(float scale_factor) const284 gfx::ImageSkia CefImageImpl::GetForced1xScaleRepresentation(
285 float scale_factor) const {
286 base::AutoLock lock_scope(lock_);
287 if (scale_factor == 1.0f) {
288 // We can use the existing image without modification.
289 return image_.AsImageSkia();
290 }
291
292 const SkBitmap* bitmap = GetBitmap(scale_factor);
293 gfx::ImageSkia image_skia;
294 if (bitmap)
295 image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap, 1.0f));
296 return image_skia;
297 }
298
AsImageSkia() const299 gfx::ImageSkia CefImageImpl::AsImageSkia() const {
300 base::AutoLock lock_scope(lock_);
301 return image_.AsImageSkia();
302 }
303
AddBitmap(float scale_factor,const SkBitmap & bitmap)304 bool CefImageImpl::AddBitmap(float scale_factor, const SkBitmap& bitmap) {
305 #if DCHECK_IS_ON()
306 DCHECK(bitmap.readyToDraw());
307 #endif
308 DCHECK(bitmap.colorType() == kBGRA_8888_SkColorType ||
309 bitmap.colorType() == kRGBA_8888_SkColorType);
310
311 // Convert to N32 (e.g. native encoding) format if not already in that format.
312 // N32 is expected by the Views framework and this early conversion avoids
313 // CHECKs in ImageSkiaRep and eventual conversion to N32 at some later point
314 // in the compositing pipeline.
315 SkBitmap n32_bitmap;
316 if (!skia::SkBitmapToN32OpaqueOrPremul(bitmap, &n32_bitmap))
317 return false;
318
319 gfx::ImageSkiaRep skia_rep(n32_bitmap, scale_factor);
320 base::AutoLock lock_scope(lock_);
321 if (image_.IsEmpty()) {
322 image_ = gfx::Image(gfx::ImageSkia(skia_rep));
323 } else {
324 image_.AsImageSkia().AddRepresentation(skia_rep);
325 }
326 return true;
327 }
328
GetBitmap(float scale_factor) const329 const SkBitmap* CefImageImpl::GetBitmap(float scale_factor) const {
330 lock_.AssertAcquired();
331 gfx::ImageSkia image_skia = image_.AsImageSkia();
332 if (image_skia.isNull())
333 return nullptr;
334
335 const gfx::ImageSkiaRep& rep = image_skia.GetRepresentation(scale_factor);
336 if (rep.is_null())
337 return nullptr;
338
339 return &rep.GetBitmap();
340 }
341
342 // static
ConvertBitmap(const SkBitmap & src_bitmap,SkBitmap * target_bitmap,SkColorType target_ct,SkAlphaType target_at)343 bool CefImageImpl::ConvertBitmap(const SkBitmap& src_bitmap,
344 SkBitmap* target_bitmap,
345 SkColorType target_ct,
346 SkAlphaType target_at) {
347 DCHECK(src_bitmap.readyToDraw());
348 DCHECK(src_bitmap.colorType() != target_ct ||
349 src_bitmap.alphaType() != target_at);
350 DCHECK(target_bitmap);
351
352 SkImageInfo target_info = SkImageInfo::Make(
353 src_bitmap.width(), src_bitmap.height(), target_ct, target_at);
354 if (!target_bitmap->tryAllocPixels(target_info))
355 return false;
356
357 if (!src_bitmap.readPixels(target_info, target_bitmap->getPixels(),
358 target_bitmap->rowBytes(), 0, 0)) {
359 return false;
360 }
361
362 DCHECK(target_bitmap->readyToDraw());
363 return true;
364 }
365
366 // static
WriteCompressedFormat(const SkBitmap & bitmap,std::vector<unsigned char> * compressed,CompressionMethod method)367 bool CefImageImpl::WriteCompressedFormat(const SkBitmap& bitmap,
368 std::vector<unsigned char>* compressed,
369 CompressionMethod method) {
370 const SkBitmap* bitmap_ptr = nullptr;
371 SkBitmap bitmap_postalpha;
372 if (bitmap.alphaType() == kPremul_SkAlphaType) {
373 // Compression methods require post-multiplied alpha values.
374 if (!ConvertBitmap(bitmap, &bitmap_postalpha, bitmap.colorType(),
375 kUnpremul_SkAlphaType)) {
376 return false;
377 }
378 bitmap_ptr = &bitmap_postalpha;
379 } else {
380 bitmap_ptr = &bitmap;
381 }
382
383 DCHECK(bitmap_ptr->readyToDraw());
384 DCHECK(bitmap_ptr->colorType() == kBGRA_8888_SkColorType ||
385 bitmap_ptr->colorType() == kRGBA_8888_SkColorType);
386 DCHECK(bitmap_ptr->alphaType() == kOpaque_SkAlphaType ||
387 bitmap_ptr->alphaType() == kUnpremul_SkAlphaType);
388
389 return std::move(method).Run(*bitmap_ptr, compressed);
390 }
391
392 // static
WritePNG(const SkBitmap & bitmap,std::vector<unsigned char> * compressed,bool with_transparency)393 bool CefImageImpl::WritePNG(const SkBitmap& bitmap,
394 std::vector<unsigned char>* compressed,
395 bool with_transparency) {
396 return WriteCompressedFormat(bitmap, compressed,
397 base::BindOnce(PNGMethod, with_transparency));
398 }
399
400 // static
WriteJPEG(const SkBitmap & bitmap,std::vector<unsigned char> * compressed,int quality)401 bool CefImageImpl::WriteJPEG(const SkBitmap& bitmap,
402 std::vector<unsigned char>* compressed,
403 int quality) {
404 return WriteCompressedFormat(bitmap, compressed,
405 base::BindOnce(JPEGMethod, quality));
406 }
407