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/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "chrome/utility/cloud_print/bitmap_image.h"
     12 #include "net/base/big_endian.h"
     13 
     14 namespace cloud_print {
     15 
     16 namespace {
     17 
     18 const uint32 kBitsPerColor = 8;
     19 const uint32 kColorSpace = 19;  // sRGB.
     20 const uint32 kColorOrder = 0;  // chunky.
     21 const uint32 kNumColors = 3;
     22 const uint32 kBitsPerPixel = kNumColors * kBitsPerColor;
     23 
     24 const char kPwgKeyword[] = "RaS2";
     25 
     26 const uint32 kHeaderSize = 1796;
     27 const uint32 kHeaderHwResolutionHorizontal = 276;
     28 const uint32 kHeaderHwResolutionVertical = 280;
     29 const uint32 kHeaderCupsWidth = 372;
     30 const uint32 kHeaderCupsHeight = 376;
     31 const uint32 kHeaderCupsBitsPerColor = 384;
     32 const uint32 kHeaderCupsBitsPerPixel = 388;
     33 const uint32 kHeaderCupsBytesPerLine = 392;
     34 const uint32 kHeaderCupsColorOrder = 396;
     35 const uint32 kHeaderCupsColorSpace = 400;
     36 const uint32 kHeaderCupsNumColors = 420;
     37 const uint32 kHeaderPwgTotalPageCount = 452;
     38 
     39 const int kPwgMaxPackedRows = 256;
     40 
     41 const int kPwgMaxPackedPixels = 128;
     42 
     43 }  // namespace
     44 
     45 PwgEncoder::PwgEncoder() {}
     46 
     47 inline void encodePixelFromRGBA(const uint8* pixel, std::string* output) {
     48   output->push_back(static_cast<char>(pixel[0]));
     49   output->push_back(static_cast<char>(pixel[1]));
     50   output->push_back(static_cast<char>(pixel[2]));
     51 }
     52 
     53 inline void encodePixelFromBGRA(const uint8* pixel, std::string* output) {
     54   output->push_back(static_cast<char>(pixel[2]));
     55   output->push_back(static_cast<char>(pixel[1]));
     56   output->push_back(static_cast<char>(pixel[0]));
     57 }
     58 
     59 void PwgEncoder::EncodeDocumentHeader(std::string* output) const {
     60   output->clear();
     61   output->append(kPwgKeyword, 4);
     62 }
     63 
     64 void PwgEncoder::EncodePageHeader(const BitmapImage& image, const uint32 dpi,
     65                                   const uint32 total_pages,
     66                                   std::string* output) const {
     67   char header[kHeaderSize];
     68   memset(header, 0, kHeaderSize);
     69   net::WriteBigEndian<uint32>(header + kHeaderHwResolutionHorizontal, dpi);
     70   net::WriteBigEndian<uint32>(header + kHeaderHwResolutionVertical, dpi);
     71   net::WriteBigEndian<uint32>(header + kHeaderCupsWidth, image.size().width());
     72   net::WriteBigEndian<uint32>(header + kHeaderCupsHeight,
     73                               image.size().height());
     74   net::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerColor, kBitsPerColor);
     75   net::WriteBigEndian<uint32>(header + kHeaderCupsBitsPerPixel, kBitsPerPixel);
     76   net::WriteBigEndian<uint32>(header + kHeaderCupsBytesPerLine,
     77                               (kBitsPerPixel * image.size().width() + 7) / 8);
     78   net::WriteBigEndian<uint32>(header + kHeaderCupsColorOrder, kColorOrder);
     79   net::WriteBigEndian<uint32>(header + kHeaderCupsColorSpace, kColorSpace);
     80   net::WriteBigEndian<uint32>(header + kHeaderCupsNumColors, kNumColors);
     81   net::WriteBigEndian<uint32>(header + kHeaderPwgTotalPageCount, total_pages);
     82   output->append(header, kHeaderSize);
     83 }
     84 
     85 bool PwgEncoder::EncodeRowFrom32Bit(const uint8* row, const int width,
     86                                     const int color_space,
     87                                     std::string* output) const {
     88   void (*pixel_encoder)(const uint8*, std::string*);
     89   switch (color_space) {
     90     case BitmapImage::RGBA:
     91       pixel_encoder = &encodePixelFromRGBA;
     92       break;
     93     case BitmapImage::BGRA:
     94       pixel_encoder = &encodePixelFromBGRA;
     95       break;
     96     default:
     97       LOG(ERROR) << "Unsupported colorspace.";
     98       return false;
     99   }
    100 
    101   // Converts the list of uint8 to uint32 as every pixels contains 4 bytes
    102   // of information and comparison of elements is easier. The actual management
    103   // of the bytes of the pixel is done by template function P on the original
    104   // array to avoid endian problems.
    105   const uint32* pos = reinterpret_cast<const uint32*>(row);
    106   const uint32* row_end = pos + width;
    107   // According to PWG-raster, a sequence of N identical pixels (up to 128)
    108   // can be encoded by a byte N-1, followed by the information on
    109   // that pixel. Any generic sequence of N pixels (up to 128) can be encoded
    110   // with (signed) byte 1-N, followed by the information on the N pixels.
    111   // Notice that for sequences of 1 pixel there is no difference between
    112   // the two encodings.
    113 
    114   // It is usually better to encode every largest sequence of > 2 identical
    115   // pixels together because it saves the most space. Every other pixel should
    116   // be encoded in the smallest number of generic sequences.
    117   while (pos != row_end) {
    118     const uint32* it = pos + 1;
    119     const uint32* end = std::min(pos + kPwgMaxPackedPixels, row_end);
    120 
    121     // Counts how many identical pixels (up to 128).
    122     while (it != end && *pos == *it) {
    123       it++;
    124     }
    125     if (it != pos + 1) {  // More than one pixel
    126       output->push_back(static_cast<char>((it - pos) - 1));
    127       pixel_encoder(reinterpret_cast<const uint8*>(pos), output);
    128       pos = it;
    129     } else {
    130       // Finds how many pixels each different from the previous one (up to 128).
    131       while (it != end && *it != *(it - 1)) {
    132         it++;
    133       }
    134       // Optimization: ignores the last pixel of the sequence if it is followed
    135       // by an identical pixel, as it is more convenient for it to be the start
    136       // of a new sequence of identical pixels. Notice that we don't compare
    137       // to end, but row_end.
    138       if (it != row_end && *it == *(it - 1)) {
    139         it--;
    140       }
    141       output->push_back(static_cast<char>(1 - (it - pos)));
    142       while (pos != it) {
    143         pixel_encoder(reinterpret_cast<const uint8*>(pos++), output);
    144       }
    145     }
    146   }
    147   return true;
    148 }
    149 
    150 inline const uint8* PwgEncoder::GetRow(const BitmapImage& image,
    151                                        int row) const {
    152   return image.pixel_data() + row * image.size().width() * image.channels();
    153 }
    154 
    155 // Given a pointer to a struct Image, create a PWG of the image and
    156 // put the compressed image data in the std::string.  Returns true on success.
    157 // The content of the std::string is undefined on failure.
    158 bool PwgEncoder::EncodePage(const BitmapImage& image,
    159                             const uint32 dpi,
    160                             const uint32 total_pages,
    161                             std::string* output) const {
    162   // For now only some 4-channel colorspaces are supported.
    163   if (image.channels() != 4) {
    164     LOG(ERROR) << "Unsupported colorspace.";
    165     return false;
    166   }
    167 
    168   EncodePageHeader(image, dpi, total_pages, output);
    169 
    170   int row_size = image.size().width() * image.channels();
    171 
    172   int row_number = 0;
    173   while (row_number < image.size().height()) {
    174     const uint8* current_row = GetRow(image, row_number++);
    175     int num_identical_rows = 1;
    176     // We count how many times the current row is repeated.
    177     while (num_identical_rows < kPwgMaxPackedRows
    178            && row_number < image.size().height()
    179            && !memcmp(current_row, GetRow(image, row_number), row_size)) {
    180       num_identical_rows++;
    181       row_number++;
    182     }
    183     output->push_back(static_cast<char>(num_identical_rows - 1));
    184 
    185     // Both supported colorspaces have a 32-bit pixels information.
    186     if (!EncodeRowFrom32Bit(
    187             current_row, image.size().width(), image.colorspace(), output)) {
    188       return false;
    189     }
    190   }
    191   return true;
    192 }
    193 
    194 }  // namespace cloud_print
    195