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