Home | History | Annotate | Download | only in cloud_print
      1 // Copyright 2013 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 "chrome/utility/cloud_print/pwg_encoder.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/big_endian.h"
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "chrome/utility/cloud_print/bitmap_image.h"
     13 
     14 namespace cloud_print {
     15 
     16 namespace {
     17 
     18 const uint32 kBitsPerColor = 8;
     19 const uint32 kColorOrder = 0;  // chunky.
     20 
     21 // Coefficients used to convert from RGB to monochrome.
     22 const uint32 kRedCoefficient = 2125;
     23 const uint32 kGreenCoefficient = 7154;
     24 const uint32 kBlueCoefficient = 0721;
     25 const uint32 kColorCoefficientDenominator = 10000;
     26 
     27 const char* kPwgKeyword = "RaS2";
     28 
     29 const uint32 kHeaderSize = 1796;
     30 const uint32 kHeaderCupsDuplex = 272;
     31 const uint32 kHeaderCupsHwResolutionHorizontal = 276;
     32 const uint32 kHeaderCupsHwResolutionVertical = 280;
     33 const uint32 kHeaderCupsTumble = 368;
     34 const uint32 kHeaderCupsWidth = 372;
     35 const uint32 kHeaderCupsHeight = 376;
     36 const uint32 kHeaderCupsBitsPerColor = 384;
     37 const uint32 kHeaderCupsBitsPerPixel = 388;
     38 const uint32 kHeaderCupsBytesPerLine = 392;
     39 const uint32 kHeaderCupsColorOrder = 396;
     40 const uint32 kHeaderCupsColorSpace = 400;
     41 const uint32 kHeaderCupsNumColors = 420;
     42 const uint32 kHeaderPwgTotalPageCount = 452;
     43 const uint32 kHeaderPwgCrossFeedTransform = 456;
     44 const uint32 kHeaderPwgFeedTransform = 460;
     45 
     46 const int kPwgMaxPackedRows = 256;
     47 
     48 const int kPwgMaxPackedPixels = 128;
     49 
     50 struct RGBA8 {
     51   uint8 red;
     52   uint8 green;
     53   uint8 blue;
     54   uint8 alpha;
     55 };
     56 
     57 struct BGRA8 {
     58   uint8 blue;
     59   uint8 green;
     60   uint8 red;
     61   uint8 alpha;
     62 };
     63 
     64 template <class InputStruct>
     65 inline void encodePixelToRGB(const void* pixel, std::string* output) {
     66   const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel);
     67   output->push_back(static_cast<char>(i->red));
     68   output->push_back(static_cast<char>(i->green));
     69   output->push_back(static_cast<char>(i->blue));
     70 }
     71 
     72 template <class InputStruct>
     73 inline void encodePixelToMonochrome(const void* pixel, std::string* output) {
     74   const InputStruct* i = reinterpret_cast<const InputStruct*>(pixel);
     75   output->push_back(static_cast<char>((i->red * kRedCoefficient +
     76                                        i->green * kGreenCoefficient +
     77                                        i->blue * kBlueCoefficient) /
     78                                       kColorCoefficientDenominator));
     79 }
     80 
     81 }  // namespace
     82 
     83 PwgEncoder::PwgEncoder() {}
     84 
     85 void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
     86   output->clear();
     87   output->append(kPwgKeyword, 4);
     88 }
     89 
     90 void PwgEncoder::EncodePageHeader(const BitmapImage& image,
     91                                   const PwgHeaderInfo& pwg_header_info,
     92                                   std::string* output) const {
     93   char header[kHeaderSize];
     94   memset(header, 0, kHeaderSize);
     95 
     96   uint32 num_colors =
     97       pwg_header_info.color_space == PwgHeaderInfo::SGRAY ? 1 : 3;
     98   uint32 bits_per_pixel = num_colors * kBitsPerColor;
     99 
    100   base::WriteBigEndian<uint32>(header + kHeaderCupsDuplex,
    101                                pwg_header_info.duplex ? 1 : 0);
    102   base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionHorizontal,
    103                                pwg_header_info.dpi);
    104   base::WriteBigEndian<uint32>(header + kHeaderCupsHwResolutionVertical,
    105                                pwg_header_info.dpi);
    106   base::WriteBigEndian<uint32>(header + kHeaderCupsTumble,
    107                                pwg_header_info.tumble ? 1 : 0);
    108   base::WriteBigEndian<uint32>(header + kHeaderCupsWidth, image.size().width());
    109   base::WriteBigEndian<uint32>(header + kHeaderCupsHeight,
    110                                image.size().height());
    111   base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerColor, kBitsPerColor);
    112   base::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel,
    113                                bits_per_pixel);
    114   base::WriteBigEndian<uint32>(header + kHeaderCupsBytesPerLine,
    115                                (bits_per_pixel * image.size().width() + 7) / 8);
    116   base::WriteBigEndian<uint32>(header + kHeaderCupsColorOrder, kColorOrder);
    117   base::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace,
    118                                pwg_header_info.color_space);
    119   base::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, num_colors);
    120   base::WriteBigEndian<uint32>(header + kHeaderPwgCrossFeedTransform,
    121                                pwg_header_info.flipx ? -1 : 1);
    122   base::WriteBigEndian<uint32>(header + kHeaderPwgFeedTransform,
    123                                pwg_header_info.flipy ? -1 : 1);
    124   base::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount,
    125                                pwg_header_info.total_pages);
    126   output->append(header, kHeaderSize);
    127 }
    128 
    129 template <typename InputStruct, class RandomAccessIterator>
    130 void PwgEncoder::EncodeRow(RandomAccessIterator pos,
    131                            RandomAccessIterator row_end,
    132                            bool monochrome,
    133                            std::string* output) const {
    134   // According to PWG-raster, a sequence of N identical pixels (up to 128)
    135   // can be encoded by a byte N-1, followed by the information on
    136   // that pixel. Any generic sequence of N pixels (up to 129) can be encoded
    137   // with (signed) byte 1-N, followed by the information on the N pixels.
    138   // Notice that for sequences of 1 pixel there is no difference between
    139   // the two encodings.
    140 
    141   // We encode every largest sequence of identical pixels together because it
    142   // usually saves the most space. Every other pixel should be encoded in the
    143   // smallest number of generic sequences.
    144   // NOTE: the algorithm is not optimal especially in case of monochrome.
    145   while (pos != row_end) {
    146     RandomAccessIterator it = pos + 1;
    147     RandomAccessIterator end = std::min(pos + kPwgMaxPackedPixels, row_end);
    148 
    149     // Counts how many identical pixels (up to 128).
    150     while (it != end && *pos == *it) {
    151       ++it;
    152     }
    153     if (it != pos + 1) {  // More than one pixel
    154       output->push_back(static_cast<char>((it - pos) - 1));
    155       if (monochrome)
    156         encodePixelToMonochrome<InputStruct>(&*pos, output);
    157       else
    158         encodePixelToRGB<InputStruct>(&*pos, output);
    159       pos = it;
    160     } else {
    161       // Finds how many pixels there are each different from the previous one.
    162       // IMPORTANT: even if sequences of different pixels can contain as many
    163       // as 129 pixels, we restrict to 128 because some decoders don't manage
    164       // it correctly. So iterating until it != end is correct.
    165       while (it != end && *it != *(it - 1)) {
    166         ++it;
    167       }
    168       // Optimization: ignores the last pixel of the sequence if it is followed
    169       // by an identical pixel, as it is more convenient for it to be the start
    170       // of a new sequence of identical pixels. Notice that we don't compare
    171       // to end, but row_end.
    172       if (it != row_end && *it == *(it - 1)) {
    173         --it;
    174       }
    175       output->push_back(static_cast<char>(1 - (it - pos)));
    176       while (pos != it) {
    177         if (monochrome)
    178           encodePixelToMonochrome<InputStruct>(&*pos, output);
    179         else
    180           encodePixelToRGB<InputStruct>(&*pos, output);
    181         ++pos;
    182       }
    183     }
    184   }
    185 }
    186 
    187 inline const uint8* PwgEncoder::GetRow(const BitmapImage& image,
    188                                        int row,
    189                                        bool flipy) const {
    190   return image.GetPixel(
    191       gfx::Point(0, flipy ? image.size().height() - 1 - row : row));
    192 }
    193 
    194 // Given a pointer to a struct Image, create a PWG of the image and
    195 // put the compressed image data in the string.  Returns true on success.
    196 // The content of the string is undefined on failure.
    197 bool PwgEncoder::EncodePage(const BitmapImage& image,
    198                             const PwgHeaderInfo& pwg_header_info,
    199                             std::string* output) const {
    200   // pwg_header_info.color_space can only contain color spaces that are
    201   // supported, so no sanity check is needed.
    202   switch (image.colorspace()) {
    203     case BitmapImage::RGBA:
    204       return EncodePageWithColorspace<RGBA8>(image, pwg_header_info, output);
    205 
    206     case BitmapImage::BGRA:
    207       return EncodePageWithColorspace<BGRA8>(image, pwg_header_info, output);
    208 
    209     default:
    210       LOG(ERROR) << "Unsupported colorspace.";
    211       return false;
    212   }
    213 }
    214 
    215 template <typename InputStruct>
    216 bool PwgEncoder::EncodePageWithColorspace(const BitmapImage& image,
    217                                           const PwgHeaderInfo& pwg_header_info,
    218                                           std::string* output) const {
    219   bool monochrome = pwg_header_info.color_space == PwgHeaderInfo::SGRAY;
    220   EncodePageHeader(image, pwg_header_info, output);
    221 
    222   // Ensure no integer overflow.
    223   CHECK(image.size().width() < INT_MAX / image.channels());
    224   int row_size = image.size().width() * image.channels();
    225 
    226   int row_number = 0;
    227   while (row_number < image.size().height()) {
    228     const uint8* current_row =
    229         GetRow(image, row_number++, pwg_header_info.flipy);
    230     int num_identical_rows = 1;
    231     // We count how many times the current row is repeated.
    232     while (num_identical_rows < kPwgMaxPackedRows &&
    233            row_number < image.size().height() &&
    234            !memcmp(current_row,
    235                    GetRow(image, row_number, pwg_header_info.flipy),
    236                    row_size)) {
    237       num_identical_rows++;
    238       row_number++;
    239     }
    240     output->push_back(static_cast<char>(num_identical_rows - 1));
    241 
    242     // Both supported colorspaces have a 32-bit pixels information.
    243     // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
    244     // of information and comparison of elements is easier. The actual
    245     // Management of the bytes of the pixel is done by pixel_encoder function
    246     // on the original array to avoid endian problems.
    247     const uint32* pos = reinterpret_cast<const uint32*>(current_row);
    248     const uint32* row_end = pos + image.size().width();
    249     if (!pwg_header_info.flipx) {
    250       EncodeRow<InputStruct>(pos, row_end, monochrome, output);
    251     } else {
    252       // We reverse the iterators.
    253       EncodeRow<InputStruct>(std::reverse_iterator<const uint32*>(row_end),
    254                              std::reverse_iterator<const uint32*>(pos),
    255                              monochrome,
    256                              output);
    257     }
    258   }
    259   return true;
    260 }
    261 
    262 }  // namespace cloud_print
    263