| // Copyright 2013 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 "components/pwg_encoder/pwg_encoder.h" | 
 |  | 
 | #include <limits.h> | 
 | #include <string.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <memory> | 
 |  | 
 | #include "base/big_endian.h" | 
 | #include "base/logging.h" | 
 | #include "components/pwg_encoder/bitmap_image.h" | 
 |  | 
 | namespace pwg_encoder { | 
 |  | 
 | namespace { | 
 |  | 
 | const uint32_t kBitsPerColor = 8; | 
 | const uint32_t kColorOrder = 0;  // chunky. | 
 |  | 
 | // Coefficients used to convert from RGB to monochrome. | 
 | const uint32_t kRedCoefficient = 2125; | 
 | const uint32_t kGreenCoefficient = 7154; | 
 | const uint32_t kBlueCoefficient = 721; | 
 | const uint32_t kColorCoefficientDenominator = 10000; | 
 |  | 
 | const char kPwgKeyword[] = "RaS2"; | 
 |  | 
 | const uint32_t kHeaderSize = 1796; | 
 | const uint32_t kHeaderCupsDuplex = 272; | 
 | const uint32_t kHeaderCupsHwResolutionHorizontal = 276; | 
 | const uint32_t kHeaderCupsHwResolutionVertical = 280; | 
 | const uint32_t kHeaderCupsTumble = 368; | 
 | const uint32_t kHeaderCupsWidth = 372; | 
 | const uint32_t kHeaderCupsHeight = 376; | 
 | const uint32_t kHeaderCupsBitsPerColor = 384; | 
 | const uint32_t kHeaderCupsBitsPerPixel = 388; | 
 | const uint32_t kHeaderCupsBytesPerLine = 392; | 
 | const uint32_t kHeaderCupsColorOrder = 396; | 
 | const uint32_t kHeaderCupsColorSpace = 400; | 
 | const uint32_t kHeaderCupsNumColors = 420; | 
 | const uint32_t kHeaderPwgTotalPageCount = 452; | 
 | const uint32_t kHeaderPwgCrossFeedTransform = 456; | 
 | const uint32_t kHeaderPwgFeedTransform = 460; | 
 |  | 
 | const int kPwgMaxPackedRows = 256; | 
 |  | 
 | const int kPwgMaxPackedPixels = 128; | 
 |  | 
 | struct RGBA8 { | 
 |   uint8_t red; | 
 |   uint8_t green; | 
 |   uint8_t blue; | 
 |   uint8_t alpha; | 
 | }; | 
 |  | 
 | struct BGRA8 { | 
 |   uint8_t blue; | 
 |   uint8_t green; | 
 |   uint8_t red; | 
 |   uint8_t alpha; | 
 | }; | 
 |  | 
 | template <class InputStruct> | 
 | void EncodePixelToRGB(const void* pixel, std::string* output) { | 
 |   const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel); | 
 |   output->push_back(static_cast<char>(i->red)); | 
 |   output->push_back(static_cast<char>(i->green)); | 
 |   output->push_back(static_cast<char>(i->blue)); | 
 | } | 
 |  | 
 | template <class InputStruct> | 
 | void EncodePixelToMonochrome(const void* pixel, std::string* output) { | 
 |   const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel); | 
 |   output->push_back(static_cast<char>((i->red * kRedCoefficient + | 
 |                                        i->green * kGreenCoefficient + | 
 |                                        i->blue * kBlueCoefficient) / | 
 |                                       kColorCoefficientDenominator)); | 
 | } | 
 |  | 
 | std::string EncodePageHeader(const BitmapImage& image, | 
 |                              const PwgHeaderInfo& pwg_header_info) { | 
 |   char header[kHeaderSize]; | 
 |   memset(header, 0, kHeaderSize); | 
 |  | 
 |   uint32_t num_colors = | 
 |       pwg_header_info.color_space == PwgHeaderInfo::SGRAY ? 1 : 3; | 
 |   uint32_t bits_per_pixel = num_colors * kBitsPerColor; | 
 |  | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsDuplex, | 
 |                                  pwg_header_info.duplex ? 1 : 0); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsHwResolutionHorizontal, | 
 |                                  pwg_header_info.dpi.width()); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsHwResolutionVertical, | 
 |                                  pwg_header_info.dpi.height()); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsTumble, | 
 |                                  pwg_header_info.tumble ? 1 : 0); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsWidth, | 
 |                                  image.size().width()); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsHeight, | 
 |                                  image.size().height()); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsBitsPerColor, | 
 |                                  kBitsPerColor); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsBitsPerPixel, | 
 |                                  bits_per_pixel); | 
 |   base::WriteBigEndian<uint32_t>( | 
 |       header + kHeaderCupsBytesPerLine, | 
 |       (bits_per_pixel * image.size().width() + 7) / 8); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsColorOrder, kColorOrder); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsColorSpace, | 
 |                                  pwg_header_info.color_space); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderCupsNumColors, num_colors); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderPwgCrossFeedTransform, | 
 |                                  pwg_header_info.flipx ? -1 : 1); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderPwgFeedTransform, | 
 |                                  pwg_header_info.flipy ? -1 : 1); | 
 |   base::WriteBigEndian<uint32_t>(header + kHeaderPwgTotalPageCount, | 
 |                                  pwg_header_info.total_pages); | 
 |   return std::string(header, kHeaderSize); | 
 | } | 
 |  | 
 | template <typename InputStruct, class RandomAccessIterator> | 
 | void EncodeRow(RandomAccessIterator pos, | 
 |                RandomAccessIterator row_end, | 
 |                bool monochrome, | 
 |                std::string* output) { | 
 |   // According to PWG-raster, a sequence of N identical pixels (up to 128) | 
 |   // can be encoded by a byte N-1, followed by the information on | 
 |   // that pixel. Any generic sequence of N pixels (up to 129) can be encoded | 
 |   // with (signed) byte 1-N, followed by the information on the N pixels. | 
 |   // Notice that for sequences of 1 pixel there is no difference between | 
 |   // the two encodings. | 
 |  | 
 |   // We encode every largest sequence of identical pixels together because it | 
 |   // usually saves the most space. Every other pixel should be encoded in the | 
 |   // smallest number of generic sequences. | 
 |   // NOTE: the algorithm is not optimal especially in case of monochrome. | 
 |   while (pos != row_end) { | 
 |     RandomAccessIterator it = pos + 1; | 
 |     RandomAccessIterator end = std::min(pos + kPwgMaxPackedPixels, row_end); | 
 |  | 
 |     // Counts how many identical pixels (up to 128). | 
 |     while (it != end && *pos == *it) { | 
 |       ++it; | 
 |     } | 
 |     if (it != pos + 1) {  // More than one pixel | 
 |       output->push_back(static_cast<char>((it - pos) - 1)); | 
 |       if (monochrome) | 
 |         EncodePixelToMonochrome<InputStruct>(&*pos, output); | 
 |       else | 
 |         EncodePixelToRGB<InputStruct>(&*pos, output); | 
 |       pos = it; | 
 |     } else { | 
 |       // Finds how many pixels there are each different from the previous one. | 
 |       // IMPORTANT: even if sequences of different pixels can contain as many | 
 |       // as 129 pixels, we restrict to 128 because some decoders don't manage | 
 |       // it correctly. So iterating until it != end is correct. | 
 |       while (it != end && *it != *(it - 1)) { | 
 |         ++it; | 
 |       } | 
 |       // Optimization: ignores the last pixel of the sequence if it is followed | 
 |       // by an identical pixel, as it is more convenient for it to be the start | 
 |       // of a new sequence of identical pixels. Notice that we don't compare | 
 |       // to end, but row_end. | 
 |       if (it != row_end && *it == *(it - 1)) { | 
 |         --it; | 
 |       } | 
 |       output->push_back(static_cast<char>(1 - (it - pos))); | 
 |       while (pos != it) { | 
 |         if (monochrome) | 
 |           EncodePixelToMonochrome<InputStruct>(&*pos, output); | 
 |         else | 
 |           EncodePixelToRGB<InputStruct>(&*pos, output); | 
 |         ++pos; | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | const uint8_t* GetRow(const BitmapImage& image, int row, bool flipy) { | 
 |   return image.GetPixel( | 
 |       gfx::Point(0, flipy ? image.size().height() - 1 - row : row)); | 
 | } | 
 |  | 
 | template <typename InputStruct> | 
 | std::string EncodePageWithColorspace(const BitmapImage& image, | 
 |                                      const PwgHeaderInfo& pwg_header_info) { | 
 |   bool monochrome = pwg_header_info.color_space == PwgHeaderInfo::SGRAY; | 
 |   std::string output = EncodePageHeader(image, pwg_header_info); | 
 |  | 
 |   // Ensure no integer overflow. | 
 |   CHECK(image.size().width() < INT_MAX / image.channels()); | 
 |   int row_size = image.size().width() * image.channels(); | 
 |  | 
 |   int row_number = 0; | 
 |   while (row_number < image.size().height()) { | 
 |     const uint8_t* current_row = | 
 |         GetRow(image, row_number++, pwg_header_info.flipy); | 
 |     int num_identical_rows = 1; | 
 |     // We count how many times the current row is repeated. | 
 |     while (num_identical_rows < kPwgMaxPackedRows && | 
 |            row_number < image.size().height() && | 
 |            !memcmp(current_row, | 
 |                    GetRow(image, row_number, pwg_header_info.flipy), | 
 |                    row_size)) { | 
 |       num_identical_rows++; | 
 |       row_number++; | 
 |     } | 
 |     output.push_back(static_cast<char>(num_identical_rows - 1)); | 
 |  | 
 |     // Both supported colorspaces have a 32-bit pixels information. | 
 |     // Converts the list of uint8_t to uint32_t as every pixels contains 4 bytes | 
 |     // of information and comparison of elements is easier. The actual | 
 |     // Management of the bytes of the pixel is done by pixel_encoder function | 
 |     // on the original array to avoid endian problems. | 
 |     const uint32_t* pos = reinterpret_cast<const uint32_t*>(current_row); | 
 |     const uint32_t* row_end = pos + image.size().width(); | 
 |     if (!pwg_header_info.flipx) { | 
 |       EncodeRow<InputStruct>(pos, row_end, monochrome, &output); | 
 |     } else { | 
 |       // We reverse the iterators. | 
 |       EncodeRow<InputStruct>(std::reverse_iterator<const uint32_t*>(row_end), | 
 |                              std::reverse_iterator<const uint32_t*>(pos), | 
 |                              monochrome, &output); | 
 |     } | 
 |   } | 
 |   return output; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | std::string PwgEncoder::GetDocumentHeader() { | 
 |   std::string output; | 
 |   output.append(kPwgKeyword, 4); | 
 |   return output; | 
 | } | 
 |  | 
 | // static | 
 | std::string PwgEncoder::EncodePage(const BitmapImage& image, | 
 |                                    const PwgHeaderInfo& pwg_header_info) { | 
 |   // pwg_header_info.color_space can only contain color spaces that are | 
 |   // supported, so no sanity check is needed. | 
 |   std::string data; | 
 |   switch (image.colorspace()) { | 
 |     case BitmapImage::RGBA: | 
 |       data = EncodePageWithColorspace<RGBA8>(image, pwg_header_info); | 
 |       break; | 
 |  | 
 |     case BitmapImage::BGRA: | 
 |       data = EncodePageWithColorspace<BGRA8>(image, pwg_header_info); | 
 |       break; | 
 |  | 
 |     default: | 
 |       LOG(ERROR) << "Unsupported colorspace."; | 
 |       break; | 
 |   } | 
 |   return data; | 
 | } | 
 |  | 
 | }  // namespace pwg_encoder |