This source file includes following definitions.
- GetCanvasCopyInfo
- ProcessBitmap
- CalculateBoringScore
- GetClippedBitmap
- GetCopySizeForThumbnail
- GetClippingRect
- ComputeTargetSizeAtMaximumScale
- CreateThumbnail
#include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
#include "base/metrics/histogram.h"
#include "content/public/browser/browser_thread.h"
#include "skia/ext/platform_canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/scrollbar_size.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/skbitmap_operations.h"
namespace {
static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
}
namespace thumbnails {
SimpleThumbnailCrop::SimpleThumbnailCrop(const gfx::Size& target_size)
: target_size_(target_size) {
DCHECK(!target_size.IsEmpty());
}
ClipResult SimpleThumbnailCrop::GetCanvasCopyInfo(
const gfx::Size& source_size,
ui::ScaleFactor scale_factor,
gfx::Rect* clipping_rect,
gfx::Size* target_size) const {
DCHECK(!source_size.IsEmpty());
ClipResult clip_result = thumbnails::CLIP_RESULT_NOT_CLIPPED;
*clipping_rect = GetClippingRect(source_size, target_size_, &clip_result);
*target_size = GetCopySizeForThumbnail(scale_factor, target_size_);
return clip_result;
}
void SimpleThumbnailCrop::ProcessBitmap(
scoped_refptr<ThumbnailingContext> context,
const ConsumerCallback& callback,
const SkBitmap& bitmap) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (bitmap.isNull() || bitmap.empty())
return;
SkBitmap thumbnail = CreateThumbnail(
bitmap,
ComputeTargetSizeAtMaximumScale(target_size_),
&context->clip_result);
context->score.boring_score = CalculateBoringScore(thumbnail);
context->score.good_clipping =
(context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
context->clip_result == CLIP_RESULT_NOT_CLIPPED);
callback.Run(*context.get(), thumbnail);
}
double SimpleThumbnailCrop::CalculateBoringScore(const SkBitmap& bitmap) {
if (bitmap.isNull() || bitmap.empty())
return 1.0;
int histogram[256] = {0};
color_utils::BuildLumaHistogram(bitmap, histogram);
int color_count = *std::max_element(histogram, histogram + 256);
int pixel_count = bitmap.width() * bitmap.height();
return static_cast<double>(color_count) / pixel_count;
}
SkBitmap SimpleThumbnailCrop::GetClippedBitmap(const SkBitmap& bitmap,
int desired_width,
int desired_height,
ClipResult* clip_result) {
gfx::Rect clipping_rect =
GetClippingRect(gfx::Size(bitmap.width(), bitmap.height()),
gfx::Size(desired_width, desired_height),
clip_result);
SkIRect src_rect = { clipping_rect.x(), clipping_rect.y(),
clipping_rect.right(), clipping_rect.bottom() };
SkBitmap clipped_bitmap;
bitmap.extractSubset(&clipped_bitmap, src_rect);
return clipped_bitmap;
}
gfx::Size SimpleThumbnailCrop::GetCopySizeForThumbnail(
ui::ScaleFactor scale_factor,
const gfx::Size& thumbnail_size) {
gfx::Size copy_size(thumbnail_size);
switch (scale_factor) {
case ui::SCALE_FACTOR_100P:
copy_size = gfx::ToFlooredSize(gfx::ScaleSize(
copy_size, ui::GetImageScale(ui::SCALE_FACTOR_200P)));
break;
case ui::SCALE_FACTOR_200P:
break;
default:
DLOG(WARNING) << "Unsupported scale factor. Use the same copy size as "
<< "ui::SCALE_FACTOR_100P";
copy_size = gfx::ToFlooredSize(gfx::ScaleSize(
copy_size, gfx::ImageSkia::GetMaxSupportedScale()));
break;
}
return copy_size;
}
gfx::Rect SimpleThumbnailCrop::GetClippingRect(const gfx::Size& source_size,
const gfx::Size& desired_size,
ClipResult* clip_result) {
DCHECK(clip_result);
float desired_aspect =
static_cast<float>(desired_size.width()) / desired_size.height();
gfx::Rect clipping_rect;
if (source_size.width() < desired_size.width() ||
source_size.height() < desired_size.height()) {
clipping_rect = gfx::Rect(desired_size);
*clip_result = thumbnails::CLIP_RESULT_SOURCE_IS_SMALLER;
} else {
float src_aspect =
static_cast<float>(source_size.width()) / source_size.height();
if (src_aspect > desired_aspect) {
int new_width = static_cast<int>(source_size.height() * desired_aspect);
int x_offset = (source_size.width() - new_width) / 2;
clipping_rect.SetRect(x_offset, 0, new_width, source_size.height());
*clip_result = (src_aspect >= ThumbnailScore::kTooWideAspectRatio) ?
thumbnails::CLIP_RESULT_MUCH_WIDER_THAN_TALL :
thumbnails::CLIP_RESULT_WIDER_THAN_TALL;
} else if (src_aspect < desired_aspect) {
clipping_rect =
gfx::Rect(source_size.width(), source_size.width() / desired_aspect);
*clip_result = thumbnails::CLIP_RESULT_TALLER_THAN_WIDE;
} else {
clipping_rect = gfx::Rect(source_size);
*clip_result = thumbnails::CLIP_RESULT_NOT_CLIPPED;
}
}
return clipping_rect;
}
gfx::Size SimpleThumbnailCrop::ComputeTargetSizeAtMaximumScale(
const gfx::Size& given_size) {
float max_scale_factor = gfx::ImageSkia::GetMaxSupportedScale();
return gfx::ToFlooredSize(gfx::ScaleSize(given_size, max_scale_factor));
}
SimpleThumbnailCrop::~SimpleThumbnailCrop() {
}
SkBitmap SimpleThumbnailCrop::CreateThumbnail(const SkBitmap& bitmap,
const gfx::Size& desired_size,
ClipResult* clip_result) {
base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
SkBitmap clipped_bitmap;
if (*clip_result == thumbnails::CLIP_RESULT_UNPROCESSED) {
int scrollbar_size = gfx::scrollbar_size();
SkIRect scrollbarless_rect =
{ 0, 0,
std::max(1, bitmap.width() - scrollbar_size),
std::max(1, bitmap.height() - scrollbar_size) };
SkBitmap bmp;
bitmap.extractSubset(&bmp, scrollbarless_rect);
clipped_bitmap = GetClippedBitmap(
bmp, desired_size.width(), desired_size.height(), clip_result);
} else {
clipped_bitmap = bitmap;
}
SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize(
clipped_bitmap, desired_size.width(), desired_size.height());
#if !defined(USE_AURA)
if (clipped_bitmap.width() == result.width() &&
clipped_bitmap.height() == result.height())
clipped_bitmap.copyTo(&result, kPMColor_SkColorType);
#endif
HISTOGRAM_TIMES(kThumbnailHistogramName,
base::TimeTicks::Now() - begin_compute_thumbnail);
return result;
}
}