Home | History | Annotate | Download | only in printing
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "printing/image.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/file_util.h"
     10 #include "base/md5.h"
     11 #include "base/numerics/safe_conversions.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "printing/metafile.h"
     14 #include "printing/metafile_impl.h"
     15 #include "third_party/skia/include/core/SkColor.h"
     16 #include "ui/gfx/codec/png_codec.h"
     17 
     18 namespace printing {
     19 
     20 Image::Image(const base::FilePath& path)
     21     : row_length_(0),
     22       ignore_alpha_(true) {
     23   std::string data;
     24   base::ReadFileToString(path, &data);
     25   bool success = false;
     26   if (path.MatchesExtension(FILE_PATH_LITERAL(".png"))) {
     27     success = LoadPng(data);
     28   } else if (path.MatchesExtension(FILE_PATH_LITERAL(".emf"))) {
     29     success = LoadMetafile(data);
     30   } else {
     31     DCHECK(false);
     32   }
     33   if (!success) {
     34     size_.SetSize(0, 0);
     35     row_length_ = 0;
     36     data_.clear();
     37   }
     38 }
     39 
     40 Image::Image(const Metafile& metafile)
     41     : row_length_(0),
     42       ignore_alpha_(true) {
     43   LoadMetafile(metafile);
     44 }
     45 
     46 Image::Image(const Image& image)
     47     : size_(image.size_),
     48       row_length_(image.row_length_),
     49       data_(image.data_),
     50       ignore_alpha_(image.ignore_alpha_) {
     51 }
     52 
     53 Image::~Image() {}
     54 
     55 std::string Image::checksum() const {
     56   base::MD5Digest digest;
     57   base::MD5Sum(&data_[0], data_.size(), &digest);
     58   return base::HexEncode(&digest, sizeof(digest));
     59 }
     60 
     61 bool Image::SaveToPng(const base::FilePath& filepath) const {
     62   DCHECK(!data_.empty());
     63   std::vector<unsigned char> compressed;
     64   bool success = gfx::PNGCodec::Encode(&*data_.begin(),
     65                                        gfx::PNGCodec::FORMAT_BGRA,
     66                                        size_,
     67                                        row_length_,
     68                                        true,
     69                                        std::vector<gfx::PNGCodec::Comment>(),
     70                                        &compressed);
     71   DCHECK(success && compressed.size());
     72   if (success) {
     73     int write_bytes = base::WriteFile(
     74         filepath,
     75         reinterpret_cast<char*>(&*compressed.begin()),
     76         base::checked_cast<int>(compressed.size()));
     77     success = (write_bytes == static_cast<int>(compressed.size()));
     78     DCHECK(success);
     79   }
     80   return success;
     81 }
     82 
     83 double Image::PercentageDifferent(const Image& rhs) const {
     84   if (size_.width() == 0 || size_.height() == 0 ||
     85     rhs.size_.width() == 0 || rhs.size_.height() == 0)
     86     return 100.;
     87 
     88   int width = std::min(size_.width(), rhs.size_.width());
     89   int height = std::min(size_.height(), rhs.size_.height());
     90   // Compute pixels different in the overlap
     91   int pixels_different = 0;
     92   for (int y = 0; y < height; ++y) {
     93     for (int x = 0; x < width; ++x) {
     94       uint32 lhs_pixel = pixel_at(x, y);
     95       uint32 rhs_pixel = rhs.pixel_at(x, y);
     96       if (lhs_pixel != rhs_pixel)
     97         ++pixels_different;
     98     }
     99 
    100     // Look for extra right lhs pixels. They should be white.
    101     for (int x = width; x < size_.width(); ++x) {
    102       uint32 lhs_pixel = pixel_at(x, y);
    103       if (lhs_pixel != Color(SK_ColorWHITE))
    104         ++pixels_different;
    105     }
    106 
    107     // Look for extra right rhs pixels. They should be white.
    108     for (int x = width; x < rhs.size_.width(); ++x) {
    109       uint32 rhs_pixel = rhs.pixel_at(x, y);
    110       if (rhs_pixel != Color(SK_ColorWHITE))
    111         ++pixels_different;
    112     }
    113   }
    114 
    115   // Look for extra bottom lhs pixels. They should be white.
    116   for (int y = height; y < size_.height(); ++y) {
    117     for (int x = 0; x < size_.width(); ++x) {
    118       uint32 lhs_pixel = pixel_at(x, y);
    119       if (lhs_pixel != Color(SK_ColorWHITE))
    120         ++pixels_different;
    121     }
    122   }
    123 
    124   // Look for extra bottom rhs pixels. They should be white.
    125   for (int y = height; y < rhs.size_.height(); ++y) {
    126     for (int x = 0; x < rhs.size_.width(); ++x) {
    127       uint32 rhs_pixel = rhs.pixel_at(x, y);
    128       if (rhs_pixel != Color(SK_ColorWHITE))
    129         ++pixels_different;
    130     }
    131   }
    132 
    133   // Like the WebKit ImageDiff tool, we define percentage different in terms
    134   // of the size of the 'actual' bitmap.
    135   double total_pixels = static_cast<double>(size_.width()) *
    136       static_cast<double>(height);
    137   return static_cast<double>(pixels_different) / total_pixels * 100.;
    138 }
    139 
    140 bool Image::LoadPng(const std::string& compressed) {
    141   int w;
    142   int h;
    143   bool success = gfx::PNGCodec::Decode(
    144       reinterpret_cast<const unsigned char*>(compressed.c_str()),
    145       compressed.size(), gfx::PNGCodec::FORMAT_BGRA, &data_, &w, &h);
    146   size_.SetSize(w, h);
    147   row_length_ = size_.width() * sizeof(uint32);
    148   return success;
    149 }
    150 
    151 bool Image::LoadMetafile(const std::string& data) {
    152   DCHECK(!data.empty());
    153   NativeMetafile metafile;
    154   if (!metafile.InitFromData(data.data(),
    155                              base::checked_cast<uint32>(data.size())))
    156     return false;
    157   return LoadMetafile(metafile);
    158 }
    159 
    160 }  // namespace printing
    161