// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "printing/image.h"
#include <algorithm>
#include "base/file_util.h"
#include "base/md5.h"
#include "base/safe_numerics.h"
#include "base/strings/string_number_conversions.h"
#include "printing/metafile.h"
#include "printing/metafile_impl.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/codec/png_codec.h"
namespace printing {
Image::Image(const base::FilePath& path)
: row_length_(0),
ignore_alpha_(true) {
std::string data;
base::ReadFileToString(path, &data);
bool success = false;
if (path.MatchesExtension(FILE_PATH_LITERAL(".png"))) {
success = LoadPng(data);
} else if (path.MatchesExtension(FILE_PATH_LITERAL(".emf"))) {
success = LoadMetafile(data);
} else {
DCHECK(false);
}
if (!success) {
size_.SetSize(0, 0);
row_length_ = 0;
data_.clear();
}
}
Image::Image(const Metafile& metafile)
: row_length_(0),
ignore_alpha_(true) {
LoadMetafile(metafile);
}
Image::Image(const Image& image)
: size_(image.size_),
row_length_(image.row_length_),
data_(image.data_),
ignore_alpha_(image.ignore_alpha_) {
}
Image::~Image() {}
std::string Image::checksum() const {
base::MD5Digest digest;
base::MD5Sum(&data_[0], data_.size(), &digest);
return base::HexEncode(&digest, sizeof(digest));
}
bool Image::SaveToPng(const base::FilePath& filepath) const {
DCHECK(!data_.empty());
std::vector<unsigned char> compressed;
bool success = gfx::PNGCodec::Encode(&*data_.begin(),
gfx::PNGCodec::FORMAT_BGRA,
size_,
row_length_,
true,
std::vector<gfx::PNGCodec::Comment>(),
&compressed);
DCHECK(success && compressed.size());
if (success) {
int write_bytes = file_util::WriteFile(
filepath,
reinterpret_cast<char*>(&*compressed.begin()),
base::checked_numeric_cast<int>(compressed.size()));
success = (write_bytes == static_cast<int>(compressed.size()));
DCHECK(success);
}
return success;
}
double Image::PercentageDifferent(const Image& rhs) const {
if (size_.width() == 0 || size_.height() == 0 ||
rhs.size_.width() == 0 || rhs.size_.height() == 0)
return 100.;
int width = std::min(size_.width(), rhs.size_.width());
int height = std::min(size_.height(), rhs.size_.height());
// Compute pixels different in the overlap
int pixels_different = 0;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
uint32 lhs_pixel = pixel_at(x, y);
uint32 rhs_pixel = rhs.pixel_at(x, y);
if (lhs_pixel != rhs_pixel)
++pixels_different;
}
// Look for extra right lhs pixels. They should be white.
for (int x = width; x < size_.width(); ++x) {
uint32 lhs_pixel = pixel_at(x, y);
if (lhs_pixel != Color(SK_ColorWHITE))
++pixels_different;
}
// Look for extra right rhs pixels. They should be white.
for (int x = width; x < rhs.size_.width(); ++x) {
uint32 rhs_pixel = rhs.pixel_at(x, y);
if (rhs_pixel != Color(SK_ColorWHITE))
++pixels_different;
}
}
// Look for extra bottom lhs pixels. They should be white.
for (int y = height; y < size_.height(); ++y) {
for (int x = 0; x < size_.width(); ++x) {
uint32 lhs_pixel = pixel_at(x, y);
if (lhs_pixel != Color(SK_ColorWHITE))
++pixels_different;
}
}
// Look for extra bottom rhs pixels. They should be white.
for (int y = height; y < rhs.size_.height(); ++y) {
for (int x = 0; x < rhs.size_.width(); ++x) {
uint32 rhs_pixel = rhs.pixel_at(x, y);
if (rhs_pixel != Color(SK_ColorWHITE))
++pixels_different;
}
}
// Like the WebKit ImageDiff tool, we define percentage different in terms
// of the size of the 'actual' bitmap.
double total_pixels = static_cast<double>(size_.width()) *
static_cast<double>(height);
return static_cast<double>(pixels_different) / total_pixels * 100.;
}
bool Image::LoadPng(const std::string& compressed) {
int w;
int h;
bool success = gfx::PNGCodec::Decode(
reinterpret_cast<const unsigned char*>(compressed.c_str()),
compressed.size(), gfx::PNGCodec::FORMAT_BGRA, &data_, &w, &h);
size_.SetSize(w, h);
row_length_ = size_.width() * sizeof(uint32);
return success;
}
bool Image::LoadMetafile(const std::string& data) {
DCHECK(!data.empty());
NativeMetafile metafile;
if (!metafile.InitFromData(data.data(),
base::checked_numeric_cast<uint32>(data.size())))
return false;
return LoadMetafile(metafile);
}
} // namespace printing